{"record":{"id":"5aec0277d2eae76b","repo":"ruby/ruby","slug":"trusted-root-certificate-root-subject-checksum","errorCode":null,"errorMessage":"trusted root certificate #{root.subject} checksum does not match signing root certificate checksum","messagePattern":"trusted root certificate #(.+?) checksum does not match signing root certificate checksum","errorType":"exception","errorClass":"Gem::Security::Exception","httpStatus":null,"severity":"error","filePath":"lib/rubygems/security/policy.rb","lineNumber":173,"sourceCode":"\n    path = Gem::Security.trust_dir.cert_path root\n\n    unless File.exist? path\n      message = \"root cert #{root.subject} is not trusted\".dup\n\n      message << \" (root of signing cert #{chain.last.subject})\" if\n        chain.length > 1\n\n      raise Gem::Security::Exception, message\n    end\n\n    save_cert = OpenSSL::X509::Certificate.new File.read path\n    save_dgst = digester.digest save_cert.public_key.public_to_pem\n\n    pkey_str = root.public_key.public_to_pem\n    cert_dgst = digester.digest pkey_str\n\n    raise Gem::Security::Exception,\n          \"trusted root certificate #{root.subject} checksum \" \\\n          \"does not match signing root certificate checksum\" unless\n      save_dgst == cert_dgst\n\n    true\n  end\n\n  ##\n  # Extracts the email or subject from +certificate+\n\n  def subject(certificate) # :nodoc:\n    certificate.extensions.each do |extension|\n      next unless extension.oid == \"subjectAltName\"\n\n      return extension.value\n    end\n\n    certificate.subject.to_s","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/ruby/ruby/blob/0e5b888e1c355f3f728f2659f085820937dada48/lib/rubygems/security/policy.rb#L155-L191","documentation":"check_trust found a trusted certificate file for the root's subject in the trust dir, but the digest (default SHA-256) of the saved certificate's public key differs from the digest of the root in the gem's chain. 'trusted root certificate ... checksum does not match signing root certificate checksum' means the stored trusted copy and the presented root share identity but not key material — the root was re-issued/re-generated under the same subject, so the old trust entry no longer corresponds.","triggerScenarios":"Author regenerated their root key+cert (same subject/email) after losing the old one; your trust dir still holds the previous certificate; a re-issued CA cert with identical subject but a new key. Check `Gem::Security.trust_dir.cert_path(root)` file exists, then digests diverge.","commonSituations":"Lost CA keys followed by `gem cert --build` re-creation; certificate renewal that changes the key; importing a different organization's cert with the same subject string.","solutions":["Update the trust entry: remove the stale certificate from the trust dir and add the current one — `gem cert --list`, `gem cert --remove <subject>`, then `gem cert --add new_root.pem`","Get the new root cert from the gem author's authoritative source (verify out-of-band before trusting)","If you manage the CA: avoid re-issuing under the same subject with new keys; prefer a new subject or keep the key"],"exampleFix":"# refresh stale trust entry\ngem cert --remove '/CN=old-root/...'\ngem cert --add new-gem-public_cert.pem\ngem install signed-gem -P HighSecurity","handlingStrategy":"fallback","validationCode":"saved = OpenSSL::X509::Certificate.new(File.read(Gem::Security.trust_dir.cert_path(chain.first)))\nneeds_refresh = Gem::Security.create_digest.digest(saved.public_key.public_to_pem) != Gem::Security.create_digest.digest(chain.first.public_key.public_to_pem)","typeGuard":"def trust_entry_current?(root)\n  path = Gem::Security.trust_dir.cert_path(root)\n  return false unless File.exist?(path)\n  saved = OpenSSL::X509::Certificate.new(File.read(path))\n  saved.public_key.public_to_pem == root.public_key.public_to_pem\nend","tryCatchPattern":"begin\n  Gem::Package.new(gem_path, Gem::Security::Policies['HighSecurity']).verify\nrescue Gem::Security::Exception => e\n  refresh_trust_entry(root_cert) if e.message.include?('checksum')\nend","preventionTips":["When a CA/root is re-issued, refresh trust entries: gem cert --remove then --add","Confirm new root certs with the author out-of-band before trusting","As CA operator: never re-issue a subject with a different key; prefer a new subject"],"tags":["rubygems","security","trust-dir","checksum-mismatch","root-certificate"],"backgroundTag":"certificate-fingerprint-mismatch","analyzedSha":"0e5b888e1c355f3f728f2659f085820937dada48","analyzedAt":"2026-08-21T14:25:43.473Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}