2013-05-21 200 views
25

我使用bcmail-jdk16-1.46.jarbcprov-jdk16-1.46.jar(BouncyCastle的庫)簽署string然後驗證signature正確的方式進行簽名和驗證使用簽名BouncyCastle的

這是我code簽署string

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

    } 

} 

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輸出會在這個過程中通過這種方式來verifysignature一切正常,直到signer.verify(..)由於以下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) 

我真的不知道我做錯了。有人可以給我提示發生了什麼嗎?


PS。如果有人想測試上述code,你需要我使用複製這一切的測試certificate文件,只需download/save它從這裏:

https://dl.dropboxusercontent.com/u/15208254/keys/certificates.p12

+0

您正在使用哪種JDK釋放?使用這種功能時,1.7.4以上的版本存在安全問題。如果你還沒有嘗試過,換一個1.7.2的Jdk。 –

+0

@MarceloTataje我使用'JDK' ** 1.7.0 **版本。 –

+0

任何想法如何檢索簽名驗證後的消息? – Raj

回答

15

gen.generate(msg, false) 

指簽名數據未封裝在簽名中。如果你想創建一個分離的簽名,這沒什麼問題,但這確實意味着當你去驗證簽名數據時,你必須使用接受數據副本的CMSSignedData構造函數 - 在這種情況下,代碼使用單個參數構造函數必須假定已簽名的數據已被封裝(因此對於這種情況將爲空),結果驗證嘗試失敗。

1

您可以在此鏈接找到答案here您只需在消息中添加一些標題或在消息前添加一個空行然後簽名消息即可正常工作。

1

免費得到它的獨立的簽名工作: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"); 
      } 
     } 
    } 



} 
9

有兩種CMSSignedData對象的使用CMSSignedDataGenerator它們由下列方式產生的生成:

的下面的一個生成一個攜帶獨立CMS簽名的CMSSignedData對象

gen.generate(cmsdata); 

下面的代碼創建了一個CMSSignedData攜帶分離的CMS簽名,具有數據封裝

gen.generate(cmsdata, true); 

所以驗證他們需要2接近

方法1號來驗證與封裝數據的分離的簽名

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

方法2驗證分離的簽名沒有封裝的數據,只是detache d簽名

//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; 
    } 
} 
+1

謝謝奧斯卡哈拉 – Novice

4

如果我們用signature.sign()作爲OP的答案,我們將無法找回原來的消息,因爲它只有簽名。

您應該只輸入原始文本字節而不是簽名內容。基本上,忽略這一部分:

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

,只是輸入爲:

CMSTypedData msg = new CMSProcessableByteArray(text.getBytes()); 
相關問題