example bouncy java encryption cryptography digital-signature bouncycastle

bouncy castle java example



Forma correcta de firmar y verificar la firma usando bouncycastle (5)

Estoy usando bcmail-jdk16-1.46.jar y bcprov-jdk16-1.46.jar (bibliotecas de Bouncycastle) para firmar una string y luego verificar la signature .

Este es mi code para firmar una 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); } }

Ahora, la salida EnvelopedData se usará en el proceso para verify la signature de esta manera:

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

Todo funciona bien hasta signer.verify(..) debido a la siguiente 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)

Y realmente no sé lo que estoy haciendo mal. ¿Puede alguien darme una pista de lo que está pasando?

PD . Si alguien quiere probar el code anterior, necesitará el archivo de certificate prueba que estoy usando para replicar todo esto, simplemente download/save desde aquí:

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


Hay dos tipos de objetos CMSSignedData generados usando CMSSignedDataGenerator. Se generan de la siguiente manera:

El siguiente genera un objeto CMSSignedData que lleva una firma de CMS separada

gen.generate(cmsdata);

El código siguiente crea un CMSSignedData que lleva una firma de CMS separada, teniendo los datos encapsulados

gen.generate(cmsdata, true);

Así que verificarlos requiere 2 enfoques

Enfoque No.1 para verificar la firma separada con datos encapsulados

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

Enfoque No.2 para verificar la firma separada sin datos encapsulados, solo la firma separada

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

El resto del código para la verificación sigue siendo el mismo

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


Lo conseguí trabajando para la firma separada: 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"); } } } }


Puede encontrar la respuesta en este enlace here . Necesita simplemente agregar algunos encabezados para enviar mensajes o simplemente agregar una línea en blanco antes del mensaje, luego firme el mensaje y luego funcionará correctamente.


Si usamos signature.sign () como en la respuesta del OP, no podremos recuperar el mensaje original, porque es solo la firma.

Solo debe ingresar los bytes de texto originales en lugar del contenido firmado. Básicamente, ignora esta parte:

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

y solo ingrese como:

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


los

gen.generate(msg, false)

significa que los datos firmados no están encapsulados en la firma. Esto está bien si desea crear una firma separada, pero significa que cuando vaya a verificar el SignedData debe usar el constructor CMSSignedData que también toma una copia de los datos, en este caso el código está usando el único El constructor de argumentos que tiene que asumir que los datos firmados fueron encapsulados (por lo que para este caso estará vacío), con el resultado de que el intento de verificación está fallando.