Search code examples
javabouncycastlepkcs#7

create PKCS7 with presigned data using bouncy castle


I would like to create a detached signature in a PDF file using a PKCS7 container. The data (hash) is being signed beforehand on a different device with the private key. I want to create a PKCS7 containing the signed data along with the certificate with the public key. I can't seem to create the PKCS7 with bouncy castle without supplying the private key and having the library signing the data. This doesn't seem to work:

        InputStream inStream = new FileInputStream("1_public.pem");
        BufferedInputStream bis = new BufferedInputStream( inStream );

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

        List<Certificate> certList = new ArrayList<Certificate>();
        Certificate certificate = cf.generateCertificate(bis);
        certList.add(certificate);
        Store certs = new JcaCertStore(certList);

        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        gen.addCertificates( certs );
        CMSProcessableInputStream msg = new CMSProcessableInputStream( new ByteArrayInputStream( "signedhash".getBytes() ) );

        CMSSignedData signedData = gen.generate(msg, false);
        byte[] pkcs7 = signedData.getEncoded() ) );

Solution

  • I managed to do this by providing a ContentSigner that doesn't sign, actually quite simple:

            InputStream inStream = new FileInputStream("1_public.pem");
            BufferedInputStream bis = new BufferedInputStream( inStream );
    
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
    
            List<Certificate> certList = new ArrayList<Certificate>();
            Certificate certificate = cf.generateCertificate(bis);
            certList.add(certificate);
            Store certs = new JcaCertStore(certList);
            CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
            gen.addCertificates( certs );
    
            final byte[] signedHash = "signedhash".getBytes();
    
            ContentSigner nonSigner = new ContentSigner() {
    
                @Override
                public byte[] getSignature() {
                    return signedHash;
                }
    
                @Override
                public OutputStream getOutputStream() {
                    return new ByteArrayOutputStream();
                }
    
                @Override
                public AlgorithmIdentifier getAlgorithmIdentifier() {
                    return new DefaultSignatureAlgorithmIdentifierFinder().find( "SHA256WithRSA" );
                }
            };
    
            org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(certificate.getEncoded()));
            JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build());
            sigb.setDirectSignature( true );
            gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(cert)));
            CMSProcessableInputStream msg = new CMSProcessableInputStream( new ByteArrayInputStream( "not used".getBytes() ) );
    
            CMSSignedData signedData = gen.generate(msg, false);
            byte[] pkcs7 = signedData.getEncoded();