25

Ich bin mit bcmail-jdk16-1.46.jar und bcprov-jdk16-1.46.jar(BouncyCastle Bibliotheken) ein string zu unterzeichnen und dann die signature überprüfen.korrekte Art und Weise zu unterzeichnen und überprüfen Signatur bouncycastle

Das ist mein code zu unterzeichnen ein 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); 
    } 
} 

Nun wird die EnvelopedData Ausgang auf verify die signature auf diese Weise in dem Verfahren verwendet werden:

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

    } 

} 

Alles funktioniert gut bis signer.verify(..) wegen die folgenden 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) 

Und ich weiß wirklich nicht, was ich falsch mache. Kann mir bitte jemand einen Hinweis geben, was passiert?


PS. Wenn jemand die oben code testen möchte, werden Sie den Test certificate Datei muss ich all dies zu replizieren verwende, nur download/save es von hier:

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

+0

welche Version von JDK verwenden Sie? Versionen über 1.7.4 haben Sicherheitsprobleme bei der Arbeit mit dieser Art von Funktionalität. Wenn Sie nicht schon versucht haben, ändern Sie Ihre Jdk für eine 1.7.2. –

+0

@MarceloTataje Ich benutze 'JDK' ** 1.7.0 ** Version. –

+0

Haben Sie eine Idee, wie Sie die Nachricht nach der Zeichenüberprüfung abrufen können? – Raj

Antwort

15

Die

gen.generate(msg, false) 

bedeutet, dass die signierte Daten sind nicht in der Signatur eingeschlossen. Dies ist in Ordnung, wenn Sie eine eigenständige Signatur erstellen möchten, dies bedeutet jedoch, dass Sie bei der Überprüfung von SignedData den CMSSignedData-Konstruktor verwenden müssen, der auch eine Kopie der Daten übernimmt. In diesem Fall verwendet der Code die Single Argumentkonstruktor, der davon ausgehen muss, dass die signierten Daten gekapselt sind (also in diesem Fall leer sind), mit dem Ergebnis, dass der Versuch der Verifikation fehlschlägt.

1

Sie können die Antwort finden Sie unter diesem Link here Sie müssen nur einige Header zu Nachricht hinzufügen oder einfach eine leere Zeile vor der Nachricht dann unterschreiben Sie die Nachricht dann wird es gut funktionieren.

1

Verstanden für freistehende Signatur arbeiten: 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

Es gibt zwei Arten von CMSSignedData Objekt erzeugt unter Verwendung CMSSignedDataGenerator Sie werden durch die folgende Weise erzeugt:

Die eine unterhalb erzeugt ein CMSSignedData-Objekt, das eine abgetrennte CMS-Signatur

trägt So überprüfen, sie erfordert 2 nähert sich

Ansatz No.1 10

gen.generate(cmsdata); 

Der folgende Code erstellt eine CMSSignedData eine freistehende CMS-Signatur trägt,

gen.generate(cmsdata, true); 

verkapselt die Daten, die auf getrennte Signatur mit gekapselter Daten zu überprüfen

Ansatz Nr. 2, um abgetrennte Signatur ohne gekapselte Daten zu überprüfen, nur die detache d Unterschrift

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

Rest des Codes zur Überprüfung bleibt die gleiche

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

Danke Oscar Jara – Novice

4

Wenn wir signature.sign() verwenden, wie in der Antwort der OP, werden wir nicht in der Lage sein, die ursprüngliche Nachricht abzurufen , weil es nur die Unterschrift ist.

Sie sollten nur die ursprünglichen Textbytes und nicht den signierten Inhalt eingeben. Grundsätzlich ignorieren diesen Teil:

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

und nur Eingang als:

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