Obtain root and intermediate certificates from end entities
Still new to cryptography, I stumble across something simple every day. Today is just one of those days.
I want to validate smime messages in Java using the bouncy castle library, I think I almost got it, but the current problem is the construction of the PKIXparameters object. Suppose, I have an end-entity x509 certificate with the following structure:
root certificate
+->intermediate certificate
+->end-entity certificate
In order to validate the message, I need to establish the chain of trust first, but I can't figure out how to extract the root and intermediate certificates from the end entity.
I tried using the terminal entity as root, but it didn't work:
InputStream isCert = GetFISCertificate();
List list = new ArrayList();
X509Certificate rootCert = (X509Certificate) certificateFactory.generateCertificate(isCert);
list.add(rootCert);
CollectionCertStoreParameters params = new CollectionCertStoreParameters(list);
CertStore store = CertStore.getInstance("Collection", params, BC);
//create cert path
List certChain = new ArrayList();
certChain.add(rootCert);
CertPath certPath = certificateFactory.generateCertPath(certChain);
Set trust = Collections.singleton(new TrustAnchor(rootCert, null));
//validation
CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX", BC);
PKIXParameters pKIXParameters = new PKIXParameters(trust);
pKIXParameters.addCertStore(store);
pKIXParameters.setDate(new Date());
try {
CertPathValidatorResult result = certPathValidator.validate(certPath, pKIXParameters);
System.out.println("certificate path validated");
} catch (CertPathValidatorException e) {
System.out.println("validation failed on certificate number " + e.getIndex() + ", details: " + e.getMessage());
}
Got this exception:
validation failed on certificate number -1, details: Trust anchor for certification path not found.
By the way, can I just use the end-entity certificate to verify the message as if it were a self-signed certificate?
I have used BouncyCastle 1.56 for this test .
One way to obtain an issuer certificate from an end entity is to look for the " Authorized Information Access" extension .
This extension MAY be present (not mandatory) and MAY contain a URL for obtaining an issuer certificate (the issuer is a certificate "on top" of the current certificate, so the issuer of the end entity is the intermediary and the intermediary entity's The issuer is the root).
You can get this expanded value via BouncyCastle:
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.extension.X509ExtensionUtil;
X509Certificate cert = // end entity certificate
// get Authority Information Access extension (will be null if extension is not present)
byte[] extVal = cert.getExtensionValue(Extension.authorityInfoAccess.getId());
AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(X509ExtensionUtil.fromExtensionValue(extVal));
// check if there is a URL to issuer's certificate
AccessDescription[] descriptions = aia.getAccessDescriptions();
for (AccessDescription ad : descriptions) {
// check if it's a URL to issuer's certificate
if (ad.getAccessMethod().equals(X509ObjectIdentifiers.id_ad_caIssuers)) {
GeneralName location = ad.getAccessLocation();
if (location.getTagNo() == GeneralName.uniformResourceIdentifier) {
String issuerUrl = location.getName().toString();
// http URL to issuer (test in your browser to see if it's a valid certificate)
// you can use java.net.URL.openStream() to create a InputStream and create
// the certificate with your CertificateFactory
URL url = new URL(issuerUrl);
X509Certificate issuer = (X509Certificate) certificateFactory.generateCertificate(url.openStream());
}
}
}
So you can use this code with end entity certificate to get intermediate certificate. Then, use it again with the intermediate to get the root.
Then, add the root directory to your directory and verify it works.TrustAnchor
Note: However, as I said, this extension is not mandatory and may not exist. In this case, getExtensionValue
will return null
, and the only alternative I know of is to search google for certificates and download them (these certificate chains are usually public and not hard to find)