Create CMSSignedData with externally generated siganture

Viewed 77

I have a signature that is created by an external source and I need create a CMSSignedData instance for with this signature.

I thought perhaps, I could do this:

private ContentSigner contentSigner(AlgorithmIdentifier sigAlgId, byte[] signature) {
    ContentSigner signer = new ContentSigner() {

        @Override
        public AlgorithmIdentifier getAlgorithmIdentifier() {
            return sigAlgId;
        }

        @Override
        public OutputStream getOutputStream() {
            return new ByteArrayOutputStream();
        }

        @Override
        public byte[] getSignature() {
            return signature;
        }
    };

    return signer;
}

but the signatures don't verify.

I could use, this libary, https://j4sign.sourceforge.io/index.html but I think there must be a way to do this using BC.

Any ideas?

Thanks

Update... I actually got this to work, but I'm not sure if my solution is correct or good. Perhaps, someone can confirm that this approach is ok and explain why it works?

These are the steps:

Signature creation (for test purposes)

byte[] data = "yeehaw".getBytes(); 

PrivateKey privateKey = (PrivateKey) getKeyStoreManager().getKey("mykey");
X509Certificate cert = (X509Certificate) getKeyStoreManager().getCertificate("mykey");
Signature sign = Signature.getInstance("SHA1withRSA", "BC");
sign.initSign(privateKey);
sign.update(data);
byte[] signature = sign.sign();

Create ConentSigner - using method above

AlgorithmIdentifier sigAlgId = (new DefaultSignatureAlgorithmIdentifierFinder()).find("SHA1withRSA");
ContentSigner contentSigner = contentSigner(sigAlgId, signature);

Create CMSSignedDataGenerator

CMSSignedDataGenerator cmsSignedDataGen = new CMSSignedDataGenerator();
cmsSignedDataGen.addCertificate(new X509CertificateHolder(cert.getEncoded()));
cmsSignedDataGen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).setDirectSignature(true).build(contentSigner, cert));

Adding setDirectSignature(true) was what solved my problem (but I'm not sure this is correct)

Generate the CMSSignedData and verify - verify method is below

CMSSignedData sigData = cmsSignedDataGen.generate(msg);
verify((byte[]) sigData.getEncoded(), data);

The verify method

private void verify(byte[] data, byte[] signatureBytes) throws CMSException, CertificateException, OperatorCreationException {
    CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(signatureBytes), data);
    org.bouncycastle.util.Store store = signedData.getCertificates();
    SignerInformationStore signers = signedData.getSignerInfos();
    Collection c = signers.getSigners();
    Iterator it = c.iterator();

    while (it.hasNext()) {
        SignerInformation signer = (SignerInformation)it.next();

        Collection certCollection = store.getMatches(signer.getSID());
        Iterator certIt = certCollection.iterator();

        X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);

       boolean verified = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
       System.out.println("Verified = " + verified);
    }
}
0 Answers
Related