Skip to content

Certificate verify failed: cert chain from bundle with two roots with the same subject name #232

Closed
@Magisus

Description

@Magisus

On upgrading from JRuby 9.2.13.0 to 9.2.17.0, which upgraded this gem from 0.10.4 to 0.10.5, we found that an X509::Store object that previously was able to verify a given server cert no longer can. The store was created from a CA cert bundle that happened to contain two root certificates with the same subject name, but created from different keys and with slightly different extension values.

TL;DR: I think the fact that this PR switches the order of certificates in an X509::Store chain can cause issues for people that are very hard to debug.

The full story:

In 0.10.4, the verify succeeds, but in 0.10.5 it fails. Inspecting the store object after the call to verify, we can see that chain includes one intermediate CA cert and the matching root. However, in 0.10.5, the verify fails with unable to get local issuer certificate, and the intermediate cert is present in the chain but the root is not.

Example output:

store = OpenSSL::X509::Store.new
store.add_file(ca_bundle_path)
store.verify(server_cert)

# in 0.10.4
=> chain: [#<OpenSSL::X509::Certificate:0x70e94ecb subject=/CN=Server cert, issuer=/CN=Intermediate CA 2, serial=3, not_before=2021-06-10 21:50:27 UTC, not_after=2026-06-11 21:50:27 UTC>, #<OpenSSL::X509::Certificate:0x56cfe111 subject=/CN=Intermediate CA 2, issuer=/CN=Root CA, serial=2, not_before=2021-06-10 21:50:26 UTC, not_after=2026-06-11 21:50:26 UTC>, #<OpenSSL::X509::Certificate:0x7e446d92 subject=/CN=Root CA, issuer=/CN=Root CA, serial=1, not_before=2021-06-10 21:50:22 UTC, not_after=2026-06-11 21:50:22 UTC>], error_code: 0, error_string: ok

# in 0.10.5
=> chain: [#<OpenSSL::X509::Certificate:0x29149030 subject=/CN=Server cert, issuer=/CN=Intermediate CA 2, serial=3, not_before=2021-06-10 21:50:27 UTC, not_after=2026-06-11 21:50:27 UTC>], error_code: 20, error_string: unable to get local issuer certificate

In the CA bundle PEM file, the correct root was the first thing in the file, followed by our intermediate, then a whole bunch of other irrelevant certs, and then eventually another cert with the same subject name as the root but different keys. We found that removing the second root cert allowed the verify to succeed, as did switching the order of the two root certificates (putting the mismatched one higher up in the file than the correct one).

Unfortunately, I was not able to reproduce this issue with basic certificates (this issue was found by a customer of ours using their corporate CA cert bundle), so I suspect there's something odd/bad about the second root in the bundle we were testing with. Note that this same bundle can be verified successfully without modification in earlier versions of JRuby, in MRI Ruby, and via curl, so whatever is up with the cert bundle, it's not a problem generally.

I think this PR is likely the thing that broke it (perhaps by revealing an unrelated bug with cert parsing in jruby-openssl): #198. Seeing this change in the diff between the two versions prompted me to look for duplicate subject names in the bundle, which is how I found the second root. I noticed that when a duplicate is found, it is inserted in front of the thing it duplicates, which prompted me to try reversing the order of the certificates in our bundle (which succeeded). Is there a reason this code goes out of the way to do that? I think it may have the effect of breaking people like this, where maybe previously cert order was preventing bad certs from being reached, but with this change, the order is reversed. It also seemed odd to me that it wouldn't just skip over the bad one and proceed to verify against the good root. No error is issued about problems with loading or adding any of the certs: the verify just fails.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions