Search code examples
javabouncycastleclient-certificatespempkcs#7

Create X509Certificate from PKCS7 PEM formatted certificate using BouncyCastle in Java


I have a PKCS7 certificate chain in PEM format. Basically I’d like to perform following openssl command in Java using BouncyCastle library.

openssl pkcs7 -in client-certificate_pkcs7.pem -out client-certificate_chain.pem

I was following this pdf provided from BouncyCastle (https://www.bouncycastle.org/fips-java/BCFipsIn100.pdf), but couldn’t really find anything that works on my need.

I’ve found that CMSSignedData is used for pkcs7 related operations in BouncyCastle so I tried using it, but I was getting an error and I assume it’s because I was using a raw String.

String pkcs7Pem = "-----BEGIN PKCS7-----\nMIIR...WTEA\n-----END PKCS7-----\n";
CMSSignedData data = new CMSSignedData(pkcs7Pem.getBytes());
Store certStore = data.getCertificates();
SignerInformationStore signerInfos = data.getSignerInfos();
Collection<SignerInformation> signers = signerInfos.getSigners();
List<X509Certificate> x509Certificates = new ArrayList<>();
for (SignerInformation signer : signers) {
    Collection<X509CertificateHolder> matches = certStore.getMatches(signer.getSID());
    for (X509CertificateHolder holder : matches) {
        x509Certificates.add(new JcaX509CertificateConverter().setProvider("BC").getCertificate(holder));
    }
}

This is the error I got on new CMSSignedData(pkcs7Pem.getBytes());

org.bouncycastle.cms.CMSException: IOException reading content.

I also have tried using PEMParser, but parser.readObject() below returns null.

String pkcs7Pem = "-----BEGIN PKCS7-----\nMIIR...WTEA\n-----END PKCS7-----\n";
PEMParser parser = new PEMParser(new StringReader(pkcs7Pem));
parser.readObject();

Any help would be appreciated!


Solution

  • openssl pkcs7 -in client-certificate_pkcs7.pem -out client-certificate_chain.pem

    That command only copies its input (if it is PKCS7 PEM) to its output without any change. That's not useful, nor do you need BouncyCastle to do the same in Java. I suspect you actually meant

    openssl pkcs7 -print_certs -in (pkcs7) -out (chain)

    which extracts the individual certificate(s), as a sequence of separate (PEM) objects.

    I’ve found that CMSSignedData is used for pkcs7 related operations in BouncyCastle

    Only some -- there are lots of PKCS7/CMS formats and operations that can be done, and lots of different BC classes to do them. But the PKCS7/CMS format used to carry a certificate chain, often labelled p7b or p7c, is the SignedData type and is implemented by the BouncyCastle CMSSignedData class.

    I also have tried using PEMParser, but parser.readObject() below returns null.

    It shouldn't, if you give it valid PEM input not the mangled version you posted. For me if I give it a valid PEM PKCS7 containing a p7b/c cert chain I get a ContentInfo which can then be decoded as follows (I use file I/O for convenience, but any Java Reader and Writer will work the same):

    static void SO70048115PKCS7Certs (String[] args) throws Exception {
        PEMParser p = new PEMParser(new FileReader(args[0])); 
        CMSSignedData sd = new CMSSignedData( (ContentInfo)p.readObject() );
        p.close();
        JcaPEMWriter w = new JcaPEMWriter(new FileWriter(args[1])); 
        for( X509CertificateHolder ch : sd.getCertificates().getMatches(null) ){
            // optionally put subject,issuer as 'comments' like OpenSSL does
            w.writeObject( new PemObject("CERTIFICATE",ch.getEncoded()) );
        }
        w.close();
    }
    

    Note a p7b/c by convention does not contain any signature(s) (in PKCS7/CMS terms, SignerInfo(s)) so the code you posted -- which is designed to find the certificate(s) associated with the signature(s) -- is inappropriate and does not work. You need to simply use the certificate(s) and not expect any SignerInfo(s).