Correct way to sign and verify signature using bouncycastle

匿名 (未验证) 提交于 2019-12-03 02:31:01

问题:

I am using bcmail-jdk16-1.46.jar and bcprov-jdk16-1.46.jar (Bouncycastle libraries) to sign a string and then verify the signature.

This is my code to sign a 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);     } } 

Now, the EnvelopedData output will be used in the process to verify the signature by this way:

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

Everything works good until signer.verify(..) due to the following 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) 

And I really don't know what I am doing wrong. Can someone please give me a hint of what is happening?


PS. If someone wants to test the above code, you will need the test certificate file I am using to replicate all this, just download/save it from here:

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

回答1:

The

gen.generate(msg, false) 

means the signed data is not encapsulated in the signature. This is fine if you want to create a detached signature, but it does mean that when you go to verify the SignedData you have to use the CMSSignedData constructor that takes a copy of the data as well - in this case the code is using the single argument constructor which has to assume the signed data was encapsulated (so for this case will be empty), with the result that the attempt at verification is failing.



回答2:

There are two kinds of CMSSignedData object generated using CMSSignedDataGenerator They are generated by the following way:

The one below generates a CMSSignedData object carrying a detached CMS signature

gen.generate(cmsdata); 

The code below creates a CMSSignedData carrying a detached CMS signature, having the data encapsulated

gen.generate(cmsdata, true); 

So verifying them requires 2 approaches

Approach No.1 to verify detached signature with encapsulated data

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

Approach No.2 to verify detached signature without encapsulated data, just the detached signature

//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 of the code for verification remains the same

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:

If we use signature.sign() as in the OP's answer, we won't be able to retrieve the original message, because its only the signature.

You should just input the original text bytes rather than the signed content. Basically, ignore this part:

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

and just input as:

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


回答4:

You can find answer at this link here You need to just add some headers to message or just add one blank line before message then sign the message then it will work fine.



回答5:

Got it working for detached signature :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");             }         }     }    } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!