Correct way to sign and verify signature using bouncycastle

The

gen.generate(msg, false)

means the signed data is not encapsulated in the signature. This is fine if you want to create a detached signature, but it does mean that when you go to verify the SignedData you have to use the CMSSignedData constructor that takes a copy of the data as well - in this case the code is using the single argument constructor which has to assume the signed data was encapsulated (so for this case will be empty), with the result that the attempt at verification is failing.


If we use signature.sign() as in the OP's answer, we won't be able to retrieve the original message, because its only the signature.

You should just input the original text bytes rather than the signed content. Basically, ignore this part:

//Sign
PrivateKey privKey = (PrivateKey) key;
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privKey);
signature.update(text.getBytes());

and just input as:

CMSTypedData msg = new CMSProcessableByteArray(text.getBytes());

There are two kinds of CMSSignedData object generated using CMSSignedDataGenerator They are generated by the following way:

The one below generates a CMSSignedData object carrying a detached CMS signature

gen.generate(cmsdata);

The code below creates a CMSSignedData carrying a detached CMS signature, having the data encapsulated

gen.generate(cmsdata, true);

So verifying them requires 2 approaches

Approach No.1 to verify detached signature with encapsulated data

//sig is the Signature object
CMSSignedData signedData = new CMSSignedData(Base64.decode(sig.getBytes()));

Approach No.2 to verify detached signature without encapsulated data, just the detached signature

//Create a CMSProcessable object, specify any encoding, I have used mine 
CMSProcessable signedContent = new CMSProcessableByteArray(content.getBytes("ISO-8859-1"));
//Create a InputStream object
InputStream is = new ByteArrayInputStream(Base64.decode(sig.getBytes()));
//Pass them both to CMSSignedData constructor
CMSSignedData signedData = new CMSSignedData(signedContent, is);

Rest of the code for verification remains the same

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); 

    if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
        ret = true; 
    }
}