Pergunta

Need some help with crypto routines in Java.

Given a PKCS#7 signature, I want to verify all certificates it contains against a trusted store. I assume that all certificates contained in signature are in the correct order to form a valid certificate path (or chain, whatever), so that

  • topmost (#0) is a signing certificate;
  • next one (#1) is an intermediate certificate, used to sign #0;
  • next one (#2) is another intermediate certificate, used to sign #1;
  • and so on.

The last certificate (#N) is signed by CA.

That's what I've managed to hack so far:

// Exception handling skipped for readability

//byte[] signature = ...
pkcs7 = new PKCS7(signature); // `sun.security.pkcs.PKCS7;`

// *** Checking some PKCS#7 parameters here

X509Certificate prevCert = null; // Previous certificate we've found
X509Certificate[] certs = pkcs7.getCertificates(); // `java.security.cert.X509Certificate`
for (int i = 0; i < certs.length; i++) {
    // *** Checking certificate validity period here

    if (cert != null) {
        // Verify previous certificate in chain against this one
        prevCert.verify(certs[i].getPublicKey());
    }
    prevCert = certs[i];
}

//String keyStorePath = ...
KeyStore keyStore = KeyStore.getInstance("JKS"); // `java.security.KeyStore`
keyStore.load(new FileInputStream(keyStorePath), null);

// Get trusted VeriSign class 1 certificate
Certificate caCert = keyStore.getCertificate("verisignclass1ca"); // `java.security.cert.Certificate`

// Verify last certificate against trusted certificate
cert.verify(caCert.getPublicKey());

So the question is -- how can this be done using standard Java classes like CertPath and friends? I have a strong feeling I'm re-inventing a bicycle. Or, if someone has an example with BouncyCastle library, that would also be fine.

Bonus question: how to verify a certificate against a trusted store so that root certificate is selected automatically?

Foi útil?

Solução

Found the solution myself. So, here's how one can extract and validate a certificate chain against the trusted store (exception handling skipped for readability):

CertificateFactory cf = CertificateFactory.getInstance("X.509");

// Get ContentInfo
//byte[] signature = ... // PKCS#7 signature bytes
InputStream signatureIn = new ByteArrayInputStream(signature);
DERObject obj = new ASN1InputStream(signatureIn).readObject();
ContentInfo contentInfo = ContentInfo.getInstance(obj);

// Extract certificates
SignedData signedData = SignedData.getInstance(contentInfo.getContent());
Enumeration certificates = signedData.getCertificates().getObjects();

// Build certificate path
List certList = new ArrayList();
while (certificates.hasMoreElements()) {
    DERObject certObj = (DERObject) certificates.nextElement();
    InputStream in = new ByteArrayInputStream(certObj.getDEREncoded());
    certList.add(cf.generateCertificate(in));
}
CertPath certPath = cf.generateCertPath(certList);

// Load key store
//String keyStorePath = ...
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(keyStorePath), null);

// Set validation parameters
PKIXParameters params = new PKIXParameters(keyStore);
params.setRevocationEnabled(false); // to avoid exception on empty CRL

// Validate certificate path
CertPathValidator validator = CertPathValidator.getInstance("PKIX");
CertPathValidatorResult result = validator.validate(certPath, params);

validate() will throw an exception if validation fails.

Docs: ASN1Set, ContentInfo, SignedData. All other exotic names and related docs can be found in java.security.cert.

No SUN-dependencies here, only BouncyCastle provider library is needed.

This question (and especially an answer) may help too.

Outras dicas

You want CertificateFactory. The last example in the javadocs do exactly what you want.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top