2013-05-21 26 views
25

Używam bcmail-jdk16-1.46.jar i bcprov-jdk16-1.46.jar(biblioteki BouncyCastle) podpisać string a następnie zweryfikować signature.poprawny sposób podpisywania i weryfikacji podpisu używając BouncyCastle

To jest mój code do podpisania 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); 
    } 
} 

Teraz wyjście EnvelopedData zostaną wykorzystane w procesie verifysignature w ten sposób:

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

    } 

} 

Wszystko działa dobrze do signer.verify(..) ze względu na następujące 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) 

I naprawdę nie wiem, co robię źle. Czy ktoś może mi podpowiedzieć, co się dzieje?


PS. Jeśli ktoś chce przetestować powyższą code, trzeba będzie plik testowy certificate używam powtórzyć to wszystko, po prostu download/save to stąd:

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

+0

co uwalniania JDK używasz? wersje powyżej 1.7.4 mają problemy z bezpieczeństwem podczas pracy z tego rodzaju funkcjonalnością. Jeśli nie próbowałeś już zmienić Jdk na 1.7.2. –

+0

@MarceloTataje Używam wersji 'JDK' ** 1.7.0 **. –

+0

Masz pomysł, jak odzyskać wiadomość po weryfikacji znaku? – Raj

Odpowiedz

15

gen.generate(msg, false) 

oznacza podpisane dane nie są uwidocznione w podpisie. Jest to dobre, jeśli chcesz utworzyć odłączony podpis, ale oznacza to, że kiedy idziesz do zweryfikowania SignedData, musisz użyć konstruktora CMSSignedData, który pobiera również kopię danych - w tym przypadku kod używa pojedynczego konstruktor argumentu, który musi przyjąć, że podpisane dane zostały hermetyzowane (więc dla tego przypadku będzie pusty), w wyniku czego próba weryfikacji zakończy się niepowodzeniem.

1

Możesz znaleźć odpowiedź pod tym linkiem here Musisz tylko dodać kilka nagłówków do wiadomości lub po prostu dodać jedną pustą linię przed wiadomością, a następnie podpisać wiadomość, a następnie zadziała.

1

Got to praca dla jednorodzinnym podpisem: 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

Istnieją dwa rodzaje CMSSignedData obiektu generowane przy użyciu CMSSignedDataGenerator są generowane przez następujący sposób:

The jeden poniżej generuje obiekt CMSSignedData z odłączonym podpisem CMS

gen.generate(cmsdata); 

Poniższy kod ilustruje tworzenie CMSSignedData niosąc wolnostojący CMS podpis, mając dane obudowane

gen.generate(cmsdata, true); 

Więc weryfikacji ich wymaga 2 zbliża

Podejście nr 1 do weryfikacji podpisu jednorodzinnego otoczkowymi danych

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

Podejście nr 2 do weryfikacji odłączonego podpisu bez hermetyzacji danych, tylko odstrasz d podpis

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

Reszta kodu weryfikacji pozostaje taka sama

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

Dziękuję Ci Oscar Jara – Novice

4

Jeśli używamy signature.sign(), jak w odpowiedzi na PO, nie będzie w stanie odzyskać oryginalną wiadomość , ponieważ jest to tylko podpis.

Powinieneś po prostu wprowadzić oryginalne bajty tekstowe zamiast podpisanej treści. Zasadniczo, zignoruj ​​tę część:

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

i wystarczy wpisać jako:

CMSTypedData msg = new CMSProcessableByteArray(text.getBytes()); 
Powiązane problemy