Extracting individual .cer certificate from a .p7b

2019-05-14 16:34发布

问题:

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

回答1:

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.



回答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.