2013-05-21 25 views
25

Sto usando bcmail-jdk16-1.46.jar e bcprov-jdk16-1.46.jar(librerie BouncyCastle) di firmare un string e quindi verificare la signature.modo corretto di firmare e verificare la firma utilizzando BouncyCastle

Questo è il mio code di firmare un 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); 
    } 
} 

Ora, l'uscita EnvelopedData saranno utilizzati nel processo per verify il signature in questo modo:

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

    } 

} 

Tutto funziona bene fino al signer.verify(..) a causa di il seguente 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) 

E io davvero non so cosa sto facendo male. Qualcuno può darmi un suggerimento su cosa sta succedendo?


PS. Se qualcuno vuole mettere alla prova quanto sopra code, è necessario il file di prova certificate sto usando per replicare tutto questo, solo download/save da qui:

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

+0

ciò rilascio di JDK stai usando? le versioni sopra 1.7.4 hanno problemi di sicurezza quando si lavora con questo tipo di funzionalità. Se non hai ancora provato, modifica il tuo Jdk per 1.7.2. –

+0

@MarceloTataje Sto usando la versione di 'JDK' ** 1.7.0 **. –

+0

Qualche idea su come recuperare il messaggio dopo la verifica del segno? – Raj

risposta

15

Il

gen.generate(msg, false) 

significa che il i dati firmati non sono incapsulati nella firma. Questo va bene se vuoi creare una firma staccata, ma vuol dire che quando vai a verificare i SignedData devi usare il costruttore CMSSignedData che prende anche una copia dei dati - in questo caso il codice usa il singolo costruttore di argomenti che deve presupporre che i dati firmati siano stati incapsulati (quindi, per questo caso, sarà vuoto), con il risultato che il tentativo di verifica sta fallendo.

1

È possibile trovare la risposta a questo collegamento here È necessario aggiungere semplicemente alcune intestazioni al messaggio o semplicemente aggiungere una riga vuota prima del messaggio, quindi firmare il messaggio, quindi funzionerà correttamente.

1

ottenuto lavorando per firma separata: 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

Ci sono due tipi di CMSSignedData oggetto generati utilizzando CMSSignedDataGenerator Esse sono generate dal seguente senso:

L' uno di seguito genera un oggetto CMSSignedData con una firma CMS staccata

gen.generate(cmsdata); 

Il codice qui sotto crea un CMSSignedData trasporta una firma CMS indipendente, avendo i dati incapsulati

gen.generate(cmsdata, true); 

Così li verifica richiede 2 si avvicina

Approccio No.1 per verificare la firma indipendente con dati incapsulati

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

Approccio n. d firma

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

resto del codice per la verifica rimane lo stesso

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

Grazie Oscar Jara – Novice

4

Se usiamo signature.sign() come nella risposta del PO, non saremo in grado di recuperare il messaggio originale , perché è solo la firma.

È necessario immettere solo i byte del testo originale anziché il contenuto firmato. In sostanza, ignorare questa parte:

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

e proprio ingresso come:

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