【问题标题】:Correct way to sign and verify signature using bouncycastle使用 bouncycastle 签名和验证签名的正确方法
【发布时间】:2013-05-15 18:31:29
【问题描述】:

我正在使用bcmail-jdk16-1.46.jarbcprov-jdk16-1.46.jar (Bouncycastle 库) 签署string,然后验证signature

这是我的code 签名string

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

        BASE64Encoder encoder = new BASE64Encoder();

        String signedContent = encoder.encode((byte[]) sigData.getSignedContent().getContent());
        System.out.println("Signed content: " + signedContent + "\n");

        String envelopedData = encoder.encode(sigData.getEncoded());
        System.out.println("Enveloped data: " + envelopedData);
    }
}

现在,EnvelopedData 的输出将用于verify signature

package my.package;

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 {

        String envelopedData = "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIAwggLQMIIC" + 
                               "OQIEQ479uzANBgkqhkiG9w0BAQUFADCBrjEmMCQGCSqGSIb3DQEJARYXcm9zZXR0YW5ldEBtZW5k" + 
                               "ZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEi" +
                               "MCAGA1UEChMZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDEiMCAGA1UECxMZbWVuZGVsc29uLWUt" + 
                               "Y29tbWVyY2UgR21iSDENMAsGA1UEAxMEbWVuZDAeFw0wNTEyMDExMzQyMTlaFw0xOTA4MTAxMzQy" + 
                               "MTlaMIGuMSYwJAYJKoZIhvcNAQkBFhdyb3NldHRhbmV0QG1lbmRlbHNvbi5kZTELMAkGA1UEBhMC" + 
                               "REUxDzANBgNVBAgTBkJlcmxpbjEPMA0GA1UEBxMGQmVybGluMSIwIAYDVQQKExltZW5kZWxzb24t" + 
                               "ZS1jb21tZXJjZSBHbWJIMSIwIAYDVQQLExltZW5kZWxzb24tZS1jb21tZXJjZSBHbWJIMQ0wCwYD" + 
                               "VQQDEwRtZW5kMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+X1g6JvbdwJI6mQMNT41GcycH" + 
                               "UbwCFWKJ4qHDaHffz3n4h+uQJJoQvc8yLTCfnl109GB0yL2Y5YQtTohOS9IwyyMWBhh77WJtCN8r" + 
                               "dOfD2DW17877te+NlpugRvg6eOH6np9Vn3RZODVxxTyyJ8pI8VMnn13YeyMMw7VVaEO5hQIDAQAB" + 
                               "MA0GCSqGSIb3DQEBBQUAA4GBALwOIc/rWMAANdEh/GgO/DSkVMwxM5UBr3TkYbLU/5jg0Lwj3Y++" + 
                               "KhumYSrxnYewSLqK+JXA4Os9NJ+b3eZRZnnYQ9eKeUZgdE/QP9XE04y8WL6ZHLB4sDnmsgVaTU+p" + 
                               "0lFyH0Te9NyPBG0J88109CXKdXCTSN5gq0S1CfYn0staAAAxggG9MIIBuQIBATCBtzCBrjEmMCQG" + 
                               "CSqGSIb3DQEJARYXcm9zZXR0YW5ldEBtZW5kZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQI" + 
                               "EwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEiMCAGA1UEChMZbWVuZGVsc29uLWUtY29tbWVyY2Ug" + 
                               "R21iSDEiMCAGA1UECxMZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDENMAsGA1UEAxMEbWVuZAIE" + 
                               "Q479uzAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx" + 
                               "DxcNMTMwNTIxMDE1MDUzWjAjBgkqhkiG9w0BCQQxFgQU8mE6gw6iudxLUc9379lWK0lUSWcwDQYJ" + 
                               "KoZIhvcNAQEBBQAEgYB5mVhqJu1iX9nUqfqk7hTYJb1lR/hQiCaxruEuInkuVTglYuyzivZjAR54" + 
                               "zx7Cfm5lkcRyyxQ35ztqoq/V5JzBa+dYkisKcHGptJX3CbmmDIa1s65mEye4eLS4MTBvXCNCUTb9" + 
                               "STYSWvr4VPenN80mbpqSS6JpVxjM0gF3QTAhHwAAAAAAAA==";

        Security.addProvider(new BouncyCastleProvider());

        CMSSignedData cms = new CMSSignedData(Base64.decode(envelopedData.getBytes()));
        Store store = cms.getCertificates(); 
        SignerInformationStore signers = cms.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))) {
                System.out.println("verified");
            }
        }

    }

}

由于以下Exception,在signer.verify(..) 之前一切正常:

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)

有人可以告诉我可能发生的事情吗?


PS。如果有人想在code 上进行测试,您将需要我用来复制它的测试certificate 文件,只需download 从这里:

https://www.dropbox.com/s/zs4jo1a86v8qamw/certificates.p12?dl=0

【问题讨论】:

  • 您使用的是哪个版本的 JDK?使用此类功能时,1.7.4 以上的版本存在安全问题。如果您尚未尝试将 Jdk 更改为 1.7.2。
  • @MarceloTataje 我正在使用JDK 1.7.0 版本。
  • 知道如何在签名验证后检索消息吗?
  • @Raj 根本不记得了,但我建议你从as2.mendelson-e-c.com 下载这个实现(它“就像”一个发送请求和接收响应的 Java 客户端,在这个过程中它将加密、解密、签名、验证消息)。安装后为了查看代码尝试使用Java反编译器检查生成的jar的来源,作为参考真的很有帮助。
  • @Sebi 没有 Java 代码,你只需要一个 openssl 命令,我不记得了(说实话),但我可以尝试找到它并让你发布。

标签: java encryption cryptography digital-signature bouncycastle


【解决方案1】:

gen.generate(msg, false)

表示签名数据不封装在签名中。如果您想创建一个分离的签名,这很好,但这确实意味着当您去验证 SignedData 时,您必须使用 CMSSignedData 构造函数来获取数据的副本 - 在这种情况下,代码使用单个必须假设签名数据已封装的参数构造函数(因此在这种情况下将为空),结果是验证尝试失败。

【讨论】:

    【解决方案2】:

    使用CMSSignedDataGenerator生成的CMSSignedData对象有两种,它们是通过以下方式生成的:

    下面的生成一个带有分离的 CMS 签名的 CMSSignedData 对象

    gen.generate(cmsdata);
    

    下面的代码创建了一个 CMSSignedData 携带一个分离的 CMS 签名,封装了数据

    gen.generate(cmsdata, true);
    

    所以验证它们需要两种方法

    使用封装数据验证分离签名的方法一

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

    验证分离签名的方法二,无需封装数据,只需分离签名

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

    用于验证的其余代码保持不变

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

    【讨论】:

      【解决方案3】:

      如果我们在 OP 的答案中使用 signature.sign(),我们将无法检索原始消息,因为它只是签名。

      您应该只输入原始文本字节而不是签名内容。基本上,忽略这部分:

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

      只需输入:

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

      【讨论】:

        【解决方案4】:

        您可以在此链接找到答案here 您只需在消息中添加一些标题或在消息前添加一个空行然后在消息上签名即可正常工作。

        【讨论】:

          【解决方案5】:

          得到它的工作分离签名:D

          package signature;
          
          import java.security.Provider;
          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.CMSProcessable;
          import org.bouncycastle.cms.CMSProcessableByteArray;
          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 {
          
              static final String DIGEST_SHA1 = "SHA1withRSA";
              static final String BC_PROVIDER = "BC";
          
              public static void main(String[] args) throws Exception {
          
                  String envelopedData = "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIAwggLQMIIC" + 
                                         "OQIEQ479uzANBgkqhkiG9w0BAQUFADCBrjEmMCQGCSqGSIb3DQEJARYXcm9zZXR0YW5ldEBtZW5k" + 
                                         "ZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEi" +
                                         "MCAGA1UEChMZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDEiMCAGA1UECxMZbWVuZGVsc29uLWUt" + 
                                         "Y29tbWVyY2UgR21iSDENMAsGA1UEAxMEbWVuZDAeFw0wNTEyMDExMzQyMTlaFw0xOTA4MTAxMzQy" + 
                                         "MTlaMIGuMSYwJAYJKoZIhvcNAQkBFhdyb3NldHRhbmV0QG1lbmRlbHNvbi5kZTELMAkGA1UEBhMC" + 
                                         "REUxDzANBgNVBAgTBkJlcmxpbjEPMA0GA1UEBxMGQmVybGluMSIwIAYDVQQKExltZW5kZWxzb24t" + 
                                         "ZS1jb21tZXJjZSBHbWJIMSIwIAYDVQQLExltZW5kZWxzb24tZS1jb21tZXJjZSBHbWJIMQ0wCwYD" + 
                                         "VQQDEwRtZW5kMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+X1g6JvbdwJI6mQMNT41GcycH" + 
                                         "UbwCFWKJ4qHDaHffz3n4h+uQJJoQvc8yLTCfnl109GB0yL2Y5YQtTohOS9IwyyMWBhh77WJtCN8r" + 
                                         "dOfD2DW17877te+NlpugRvg6eOH6np9Vn3RZODVxxTyyJ8pI8VMnn13YeyMMw7VVaEO5hQIDAQAB" + 
                                         "MA0GCSqGSIb3DQEBBQUAA4GBALwOIc/rWMAANdEh/GgO/DSkVMwxM5UBr3TkYbLU/5jg0Lwj3Y++" + 
                                         "KhumYSrxnYewSLqK+JXA4Os9NJ+b3eZRZnnYQ9eKeUZgdE/QP9XE04y8WL6ZHLB4sDnmsgVaTU+p" + 
                                         "0lFyH0Te9NyPBG0J88109CXKdXCTSN5gq0S1CfYn0staAAAxggG9MIIBuQIBATCBtzCBrjEmMCQG" + 
                                         "CSqGSIb3DQEJARYXcm9zZXR0YW5ldEBtZW5kZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQI" + 
                                         "EwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEiMCAGA1UEChMZbWVuZGVsc29uLWUtY29tbWVyY2Ug" + 
                                         "R21iSDEiMCAGA1UECxMZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDENMAsGA1UEAxMEbWVuZAIE" + 
                                         "Q479uzAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx" + 
                                         "DxcNMTMwNTIxMDE1MDUzWjAjBgkqhkiG9w0BCQQxFgQU8mE6gw6iudxLUc9379lWK0lUSWcwDQYJ" + 
                                         "KoZIhvcNAQEBBQAEgYB5mVhqJu1iX9nUqfqk7hTYJb1lR/hQiCaxruEuInkuVTglYuyzivZjAR54" + 
                                         "zx7Cfm5lkcRyyxQ35ztqoq/V5JzBa+dYkisKcHGptJX3CbmmDIa1s65mEye4eLS4MTBvXCNCUTb9" + 
                                         "STYSWvr4VPenN80mbpqSS6JpVxjM0gF3QTAhHwAAAAAAAA==";
                  String Sig_Bytes ="YduK22AlMLSXV3ajX5r/pX5OQ0xjj58uhGT9I9MvOrz912xNHo+9OiOKeMOD+Ys2/LUW3XaN6T+/"+
                          "tuRM5bi4RK7yjaqaJCZWtr/O4I968BQGgt0cyNvK8u0Jagbr9MYk6G7nnejbRXYHyAOaunqD05lW"+
                          "U/+g92i18dl0OMc50m4=";
          
                  Provider provider = new BouncyCastleProvider();
                  Security.addProvider(provider);
                  CMSSignedData signedData = new CMSSignedData(Base64.decode(envelopedData.getBytes()));
          
                  CMSProcessable cmsProcesableContent = new CMSProcessableByteArray(Base64.decode(Sig_Bytes.getBytes()));
                  signedData = new CMSSignedData(cmsProcesableContent, Base64.decode(envelopedData.getBytes()));
                  // 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");
                      }
                  }
              }
          
          
          
          }
          

          【讨论】:

            猜你喜欢
            • 2017-06-29
            • 1970-01-01
            • 2017-10-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多