jokarl
jokarl

Reputation: 2245

Should intermediate certificates be added as TrustAnchors when validating certificte chains?

I am trying to verify a chain of certificates that I have generated on my own, and I seem to get correct results but I am unsure if I am doing it the right way.

Say I have a chain of Intermediate -> Intermediate -> Leaf. I don't care about the root, I want to verify up to a certain point in a chain. This could be the root, or just a snippet of a longer chain, provided that it is intact.

To clarify; I want to verify that chain regardless of the ultimate root. Root -> Intermediate 1 -> Intermediate 2 -> Leaf should be valid, as well as only Intermediate 1 -> Intermediate 2 -> Leaf. The only difference here should probably (?) be what is considered the TrustAnchor.

To do this, I have followed this Oracle documentation.

Here's my code snippet:

public boolean validateChain(final X509Certificate... certificates) {
    try {
        final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");


        final X509Certificate intermediateCertificate = certificates[0];
        final X509Certificate leaf = certificates[1];

        final List<X509Certificate> path = Collections.singletonList(leaf);
        final CertPath certPath = certificateFactory.generateCertPath(path);

        Set<TrustAnchor> anchors = new HashSet<>();
        anchors.add(new TrustAnchor(intermediateCertificate, null));

        final X509Certificate root = this.certificate;
        anchors.add(new TrustAnchor(root, null));

        PKIXParameters params = new PKIXParameters(anchors);
        params.setRevocationEnabled(false);

        CertPathValidator validator = CertPathValidator.getInstance("PKIX");
        validator.validate(certPath, params);

        return true;
    } catch (final GeneralSecurityException e) {
        LOG.error("Could not validate certificate chain", e);
    }
    return false;
}

Never mind the rudimentary array handling, I will improve on that. This implementation assumes a base class of mine has been implemented and that is always the root against which the supplied certificates are validated.

Is this the right way to do it? I don't really understand the distinction between a TrustAnchor and the certificates placed in the CertPath.

If I add the certificate named intermediateCertificate to the construction of the CertPath, the execution fails with:

Caused by: java.security.cert.CertPathValidatorException: subject/issuer name chaining check failed

Say if I wanted to verify a chain with 10 certificates, should only the leaf certificate be placed in the CertPath and all other certificates above in the chain are considered TrustAnchors?

If anyone could shine a light on this distinction I'd be very grateful.

Additional information edit:

I know my chain is valid because I have verified my entire chain using OpenSSL. This is my entire chain, where each file contains only the public part of the certificate.

openssl verify -CAfile root.pem -untrusted intermediate1.pem -untrusted intermediate2.pem leaf.pem

I have also verified a partial chain:

openssl verify -no-CApath -partial_chain -trusted intermediate1.pem -trusted intermediate2.pem leaf.pem

Both of these respond with leaf.pem: OK.

Upvotes: 3

Views: 1863

Answers (2)

jokarl
jokarl

Reputation: 2245

I'll have to answer my question because I just figured it out and it was an error on my side, but I also can answer the question posed.

No, intermediate certificates should not be added as trust anchors. The trust anchor is the common denominator from where you will trust all certificates.

If I am reading the RFC 5280 right, a root of trust when dealing with x509 certificates is simply a public key which you verify that underlying certificate chains reach that origin and follows the given constraints. I.e. it doesn't matter if your trust anchor is a root or not.

The snippet in my original post is correct, but I have a static initializer in my class which sets the security provider:

Security.addProvider(new BouncyCastleProvider());

To get my code to work, I had to set the same provider for the certificate factory. Here's the full working sample in case anyone is in need of it:

public boolean validateChain(final X509Certificate... certificates) {
    try {
        final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); // <--- Here's the addition

        final List<X509Certificate> path = Arrays.asList(certificates);
        final CertPath certPath = certificateFactory.generateCertPath(path);

        PKIXParameters params = new PKIXParameters(Set.of(new TrustAnchor(this.certificate, null)));
        params.setRevocationEnabled(false);

        CertPathValidator validator = CertPathValidator.getInstance("PKIX");
        validator.validate(certPath, params);

        return true;
    } catch (final GeneralSecurityException e) {
        LOG.error("Could not validate certificate chain", e);
    }
    return false;
}

In this case, this.certificate is the root. I have a class that creates an object to handle CA capable certificates.

Upvotes: 1

reflexdemon
reflexdemon

Reputation: 902

It is not required to validate the chain. We are good to validate only the leaf in most use-cases. The problem of trusting root or any intermediate is you are trusting all the certificate issued by those intermediate and the root.

Let us take for example, if you trust a certificate issued by Let's Encrypt, and you trust the Root CA and the intermediate, the risk of trusting all certificates issues by Let's Encrypt certificates.

It is important to validate the Chain and only trust leaf if you want to make sure you are trusting the communication. TrustAnchors are only for

most-trusted CA

So It is fine to trust the leaf. This is just my opinion.

Upvotes: 0

Related Questions