Reputation: 2245
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
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
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