Verify a signature with bouncy castle

Deadly 提交于 2021-02-07 10:19:00

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!