**This is my code to sign a String.</br>**
package my.package;
import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import sun.misc.BASE64Encoder;
public class SignMessage {
static final String KEYSTORE_FILE = "keys/certificates.p12";
static final String KEYSTORE_INSTANCE = "PKCS12";
static final String KEYSTORE_PWD = "test";
static final String KEYSTORE_ALIAS = "Key1";
public static void main(String[] args) throws Exception {
String text = "This is a message";
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray());
//Sign
PrivateKey privKey = (PrivateKey) key;
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privKey);
signature.update(text.getBytes());
//Build CMS
X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
List certList = new ArrayList();
CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
certList.add(cert);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, cert));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, false);
FileOutputStream sigfos = new FileOutputStream("D:\\SBI-DATA\\file\\signature_1.txt");
sigfos.write(Base64.encodeBase64(sp.getEncoded()));
sigfos.close();
}
}
Now, the EnvelopedData output will be used in the process to verify the signature by this way:
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public class VerifySignature {
public static void main(String[] args) throws Exception {
File p7s = new File("D:\\SBI-DATA\\file\\signature_2.txt") ;
int size = ((int) p7s.length());
byte[] sig = new byte[size];
File f = new File("D:\\SBI-DATA\\file\\plain.txt") ;
int sizecontent = ((int) f.length());
byte[] Data_Bytes = new byte[sizecontent];
Security.addProvider(new BouncyCastleProvider());
CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(Data_Bytes), sig);
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 certFromSignedData = new JcaX509CertificateConverter().setProvider(BC_PROVIDER).getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC_PROVIDER).build(certFromSignedData))) {
System.out.println("Signature verified");
} else {
System.out.println("Signature verification failed");
}
}
}
}
Everything works good until signer.verify(..) due to the following Exception:
Exception in thread "main" org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value
at org.bouncycastle.cms.SignerInformation.doVerify(Unknown Source)
at org.bouncycastle.cms.SignerInformation.verify(Unknown Source)
at my.package.VerifySignature.main(VerifySignature.java:64)
And I really don't know what I am doing wrong. Can someone please give me a hint of what is happening?
I think you are signing it twice, once directly using
Signature
and the other one usingContentSigner
. You need only to sign the data, not the signature.So the solution should be to replace
signature.sign()
withtext.getBytes()
, ortext.getBytes(StandardCharsets.UTF_8)
if you want to explicitly define a character set fortext
instead of using some system default.