Retrieving Root and Intermediate Certificates from a Target

still being a noob in cryptography, i stumble upon simple things every day. And today is just one of those days.

I want to check smime messages in java using bouncy castle library and I think I almost figured it out, but the problem at this point is the creation of the PKIXparameters object. Let's say I have an x509 end entity certificate with the following structure:

root certificate
 +->intermediate certificate
    +->end-entity certificate

      

To validate the post, I need to create a chain of trust first, but I can't figure out how to extract the root and intermediate certificates from the target.

I tried to use the target 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());
}

      

Received this exception:

validation failed on certificate number -1, details: Trust anchor for certification path not found.

      

And btw, can I just use just the end-entity certificate to validate messages as if it were a self-signed certificate?

+4


source to share


3 answers


I used BouncyCastle 1.56 for this test.

One way to obtain the issuer certificate from the final object is to find the expansion of access to information about the credentials .

This extension can be present (it is optional) and can contain the URL to get the issuer certificate (the issuer is the certificate "above" the current one, so the issuer of the target is the intermediate, and the intermediate issuer is the root).

You can get this expansion value from 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 certificate
AccessDescription[] descriptions = aia.getAccessDescriptions();
for (AccessDescription ad : descriptions) {
    // check if it a URL to issuer 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 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());
        }
    }
}

      



Thus, you can use this code with an end entity certificate to get an intermediate. Then you use it again with an intermediate to get root.

Then you add the root to yours TrustAnchor

and the check should work.


Note. But as I said, this extension is optional and may be missing. In this case, getExtensionValue

will return a value null

, and the only alternative known to me is to search for certificates on google and download them (these certificate chains are usually public and not hard to find).

+6


source


btw, if we have the certificate installed in windows everything is much simpler:

KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
String alias = "your alias";
ArrayList<X509Certificate> certsChain = new ArrayList<>();
if (ks.isCertificateEntry(alias)) {
    Certificate[] chain = ks.getCertificateChain(alias);
    System.out.println("Chain length: " + chain.length);
        for(Certificate c : chain) certsChain.add((X509Certificate)c);
}

Collections.reverse(certsChain);
certsChain.forEach(MainClass::printDBG);

      



and the whole certificate chain is ready

+2


source


You can also refer to the GitHub project https://github.com/infotiate/sslproxy

0


source







All Articles