问题
I inherited some code using the deprecated bouncycastle API. Before updating it to the new API I wanted to write a test to verify that I didn't change its behaviour. However, I cannot work out the correct way to verify this signature that is generated. The code to be changed is:
public static byte[] createSignedData(byte[] content, X509Certificate cert, PrivateKey key)
throws NoSuchAlgorithmException, NoSuchProviderException, CMSException, IOException {
// set up the generator
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addSigner(key, cert, CMSSignedGenerator.DIGEST_SHA1);
// create the signed-data object
CMSProcessable data = new CMSProcessableByteArray(content);
CMSSignedData signed = gen.generate(data, BC_PROVIDER);
return signed.getEncoded();
}
What is being returned from this code? A detached signature? I tried to verify the signature with a small piece of code like this, but it always returns false:
Signature signer = Signature.getInstance(
"SHA1withRSA",
BouncyCastleProvider.PROVIDER_NAME);
signer.initVerify(cert.getPublicKey());
signer.update(data);
return signer.verify(sig);
回答1:
It will return a detached signature if you use:
gen.generate(data, false);
Otherwise it will return signed data encapsulated in the signature. So, if your signature is detached (which is not your case) in order to verify it you will need to create a new CMSSignedData
object from your signed data bytes or Base64
enconded string
and also a copy of the original data as well in the constructor.
I think you're not verifying your signature the right way, also I don't know which bouncycastle
library version are you using and you just provided a code snippet which is too vague to analyze.
This is what I did in order to sign and verify a signature using bcmail-jdk16-1.46.jar
, bcprov-jdk16-1.46.jar
and jdk1.6.0_45
(I'm assuming you are using Java since you never specified it, also in question tags - but hopefully .NET version is very similar).
You can download the keystore used in my example at: https://dl.dropboxusercontent.com/u/15208254/keys/certificates.p12
Also, if you want to check if the signature verification is working fine, you can change the certificate (since I'm verifying against my own certificate which doesn't make sense) by changing this line in the signature verification process, line 84:
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC_PROVIDER).build(certFromSignedData))) { ... }
And then loading another keystore to get another certificate from it. Just to know, the certificates.p12 file I am using, contains two certificates with alias Key1
and Key2
so you can play around with them by signing your content with Key1
and verifying your signature with Key2
for example.
e.g:
// Load 'Key2' certificate from 'certificates.p12' at any place on this class after 'ks' declaration
X509Certificate certFromKeystore2 = (X509Certificate) ks.getCertificate("Key2");
Then replace line 84 with this:
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC_PROVIDER).build(certFromKeystore2))) { ... }
Notice that certFromSignedData
changed to certFromKeystore2
and your signature verification will fail because your content was signed with Key1
.
Java code:
Don't forget to change the keystore path in the constants!
package com.example.main;
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.Collection;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
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;
public class VerifySignature {
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";
static final String DIGEST_SHA1 = "SHA1withRSA";
static final String BC_PROVIDER = "BC";
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) throws Exception {
// Content to be signed
String text = "My name is Oscar";
Security.addProvider(new BouncyCastleProvider());
// Get keystore
KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray());
// Get private key and sign
PrivateKey privKey = (PrivateKey) key;
Signature signature = Signature.getInstance(DIGEST_SHA1, BC_PROVIDER);
signature.initSign(privKey);
signature.update(text.getBytes());
// Build CMS
X509Certificate certFromKeystore = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
List certList = new ArrayList();
CMSTypedData data = new CMSProcessableByteArray(signature.sign());
certList.add(certFromKeystore);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder(DIGEST_SHA1).setProvider(BC_PROVIDER).build(privKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC_PROVIDER).build()).build(sha1Signer, certFromKeystore));
gen.addCertificates(certs);
CMSSignedData signedData = gen.generate(data, true);
// Verify signature
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");
}
}
}
}
来源:https://stackoverflow.com/questions/24451744/verify-a-signature-with-bouncy-castle