3

I am new to Cryptography and so please excuse me if you think this is a basic question

I have a .p7b file which I need to read and extract the individual public certificates i.e the .cer files and store it in the key store. I need not worry about persisting in the key store as there is already a service which takes in the .cer file as byte[] and saves that.

What i want to know is , how do i read the .p7b and extract the individual .cer file? I know that can be done via the openSSL commands, but i need to do the same in java. I need to also read the Issued By name as that will be used as a unique key to persist the certificate.

Thanks in advance

Tatha
  • 1,253
  • 2
  • 24
  • 42
  • As a start have a look in this post: http://stackoverflow.com/questions/11070601/encryption-using-pkcs7 – SubOptimal Oct 08 '13 at 10:35
  • Thanks @SubOptimal for the reference. I have started working on the Bouncy Castle Api and hopefully will be able to achieve what i am looking for. – Tatha Oct 09 '13 at 10:08
  • Getting an iterator from CertificateFactory is the best option to read p7b or any other file types. Reference solution is here which helped me. (I didn't have to read the file and strip off any BEGIN/END delimiters.) https://stackoverflow.com/questions/14809754/extract-raw-certificate-from-pkcs7-file-in-java – AnirbanDebnath Aug 21 '19 at 05:26

2 Answers2

3

You can get the certificates from a PKCS#7 object with BouncyCastle. Here is a quick code sample:

 public Collection<X59Certificate> getCertificates(String path) throws Exception
 {
     Security.addProvider(new BouncyCastleProvider());
     CMSSignedData sd = new CMSSignedData(new FileInputStream(path));
     X509Store store = sd.getCertificates("Collection", "BC");
     Collection<X509Certificate> certificates = store.getMatches(X509CertStoreSelector.getInstance(new X509CertSelector()));
     return certificates;
 }

Note that a PKCS#7 may contain more than one certificate. Most of the time it includes intermediate certification authority certificates required to build the certificate chain between the end-user certificate and the root CA.

Jcs
  • 13,279
  • 5
  • 53
  • 70
  • Thanks @Jcs for your input!!. I had tried this before but was getting the exception. org.bouncycastle.cms.CMSException: Malformed content. Caused by: java.lang.IllegalArgumentException: unknown object in factory: org.bouncycastle.asn1.DERUnknownTag. I guess (may be wrong) it was happening because the PKCS#7 was base64 encoded. I have found an alternate solution (may be not ideal) to solve the problem(provided an answer above), let me know if this looks right to you. Thanks!! – Tatha Nov 08 '13 at 11:43
  • Yes, my solutions assumed the data was DER-encoded. I'm not very familiar with the Bouncycastle API but I know CMS/PKCS#7 pretty well and it seems to me that your code is doing a lot of unnecessary encoding and decoding. I think that _dataParser_, _contentInfo_, _signedData_, _encapInfoBundle_, _encapMetaData_ represent the same data structure in your p7b data. Did you try to call `Enumeration certificates = dataParser.getCertificates().getObjects();` directly? – Jcs Nov 08 '13 at 16:33
2

I was successfully able to read the individual .X509 certificates from the p7b files. Here are the steps

  • First step includes, getting a byte[] from the java.io.File. The steps include to remove the -----BEGIN PKCS7----- and -----END PKCS7----- from the file, and decode the remaining base64 encoded String.

    BufferedReader reader = new BufferedReader(new FileReader(file));
    StringBuilder cerfile = new StringBuilder();
    String line = null;
    while(( line = reader.readLine())!=null){
      if(!line.contains("PKCS7")){
        cerfile.append(line);
      }
    }
    byte[] fileBytes = Base64.decode(cerfile.toString().getBytes());
    
  • The next step is to use the BouncyCastle api to parse the file

    CMSSignedData  dataParser = new CMSSignedData(trustBundleByte);
    ContentInfo contentInfo = dataParser.getContentInfo();
    SignedData signedData = SignedData.getInstance(contentInfo.getContent());
    
    CMSSignedData encapInfoBundle = new CMSSignedData(new CMSProcessableByteArray(signedData.getEncapContentInfo().getContent().getDERObject().getEncoded()),contentInfo);
    SignedData encapMetaData = SignedData.getInstance(encapInfoBundle.getContentInfo().getContent());
    
    CMSProcessableByteArray cin = new CMSProcessableByteArray(((ASN1OctetString)encapMetaData.getEncapContentInfo().getContent()).getOctets());
    CertificateFactory ucf = CertificateFactory.getInstance("X.509");
    
    CMSSignedData  unsignedParser = new CMSSignedData(cin.getInputStream());
    ContentInfo unsginedEncapInfo = unsignedParser.getContentInfo();
    SignedData metaData = SignedData.getInstance(unsginedEncapInfo.getContent());
    Enumeration certificates = metaData.getCertificates().getObjects();
    
    // Build certificate path
    
    while (certificates.hasMoreElements()) {
       DERObject certObj = (DERObject) certificates.nextElement();
       InputStream bin = new ByteArrayInputStream(certObj.getDEREncoded());
       X509Certificate cert = (X509Certificate) ucf.generateCertificate(bin);
     X500Name x500name = new JcaX509CertificateHolder(cert).getSubject();
    RDN cn = x500name.getRDNs(BCStyle.CN)[0];
    }
    
  • The above steps are working fine, but i am sure there are other solutions with less lines of code to achieve this. I am using bcjdk16 jars.

Tatha
  • 1,253
  • 2
  • 24
  • 42