org cmssigneddatagenerator bouncy java rsa digital-signature bouncycastle

java - cmssigneddatagenerator - download org bouncycastle



Diferencia entre SHA256withRSA y SHA256 luego RSA (1)

¿Cuál es la diferencia entre calcular una firma con los dos métodos siguientes?

  1. Calcular una firma con Signature.getInstance("SHA256withRSA")
  2. Calcular SHA256 con MessageDigest.getInstance("SHA-256") y calcular el resumen con Signature.getInstance("RSA"); para obtener la firma?

Si son diferentes, ¿hay una manera de modificar el método 2 para que ambos métodos den el mismo resultado?

Probé el siguiente código:

package mysha.mysha; import java.security.MessageDigest; import java.security.PrivateKey; import java.security.Security; import java.security.Signature; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class MySHA256 { public static void main(String[] args) throws Exception { //compute SHA256 first Security.addProvider(new BouncyCastleProvider()); String s = "1234"; MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(s.getBytes()); byte[] outputDigest = messageDigest.digest(); //sign SHA256 with RSA PrivateKey privateKey = Share.loadPk8("D:/key.pk8"); Signature rsaSignature = Signature.getInstance("RSA"); rsaSignature.initSign(privateKey); rsaSignature.update(outputDigest); byte[] signed = rsaSignature.sign(); System.out.println(bytesToHex(signed)); //compute SHA256withRSA as a single step Signature rsaSha256Signature = Signature.getInstance("SHA256withRSA"); rsaSha256Signature.initSign(privateKey); rsaSha256Signature.update(s.getBytes()); byte[] signed2 = rsaSha256Signature.sign(); System.out.println(bytesToHex(signed2)); } public static String bytesToHex(byte[] bytes) { final char[] hexArray = "0123456789ABCDEF".toCharArray(); char[] hexChars = new char[bytes.length * 2]; for ( int j = 0; j < bytes.length; j++ ) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } }

Sin embargo, las salidas no son las mismas.

La siguiente es la salida de muestra con mi clave de prueba:

método 1: 61427B2A2CF1902A4B15F80156AEB09D8096BA1271F89F1919C78B18D0BABA08AA043A0037934B5AE3FC0EB7702898AC5AE96517AFD93433DF540353BCCE72A470CFA4B765D5835E7EA77743F3C4A0ABB11414B0141EF7ECCD2D5285A69728D0D0709C2537D6A772418A928B0E168F81C99B538FD25BDA7496AE8E185AC46F39

método 2: BA9039B75CA8A40DC9A7AED51E174E2B3365B2D6A1CF94DF70A00D898074A51FDD9973672DDE95CBAC39EBE4F3BA529C538ED0FF9F0A3F9A8CE203F1DFFA907DC508643906AA86DA54DFF8A90B00F5F116D13A53731384C1C5C9C4E75A3E41DAF88F74D2F1BCCF818764A4AB144A081B641C1C488AC8B194EB14BC9D1928E4EA

Actualización 1:

De acuerdo con la respuesta de mkl, modifico mi código pero todavía no puedo hacerlo bien. ¿Todavía me falta algo?

package mysha.mysha; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.security.PrivateKey; import java.security.Security; import java.security.Signature; import org.bouncycastle.asn1.DEROutputStream; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.DigestInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class MySHA256 { public static void main(String[] args) throws Exception { //compute SHA256 first Security.addProvider(new BouncyCastleProvider()); String s = "1234"; MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(s.getBytes()); byte[] outputDigest = messageDigest.digest(); AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, null); DigestInfo di = new DigestInfo(sha256Aid, outputDigest); //sign SHA256 with RSA PrivateKey privateKey = Share.loadPk8("D:/key.pk8"); Signature rsaSignature = Signature.getInstance("RSA"); rsaSignature.initSign(privateKey); rsaSignature.update(di.toASN1Primitive().getEncoded()); byte[] signed = rsaSignature.sign(); System.out.println("method 1: "+bytesToHex(signed)); //compute SHA256withRSA as a single step Signature rsaSha256Signature = Signature.getInstance("SHA256withRSA"); rsaSha256Signature.initSign(privateKey); rsaSha256Signature.update(s.getBytes()); byte[] signed2 = rsaSha256Signature.sign(); System.out.println("method 2: "+bytesToHex(signed2)); } public static String bytesToHex(byte[] bytes) { final char[] hexArray = "0123456789ABCDEF".toCharArray(); char[] hexChars = new char[bytes.length * 2]; for ( int j = 0; j < bytes.length; j++ ) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } }

método 1: 675D868546777C5A9B5E74988E0CD41A46A929C1D0890B32B1FBE34F12D68F1FDB56E623294DB903F6AC60A2ADA61976B27C66056A16F5790A78168803AD2C685F9B4CF983C939305A9819CBA9D95441CD7214D40D06A98B4DDF9692A7D300DD51E808A6722A0D7C288DBD476DF4DEEBB3DAF41CFC0978F24424960F86F0284E

método 2: BA9039B75CA8A40DC9A7AED51E174E2B3365B2D6A1CF94DF70A00D898074A51FDD9973672DDE95CBAC39EBE4F3BA529C538ED0FF9F0A3F9A8CE203F1DFFA907DC508643906AA86DA54DFF8A90B00F5F116D13A53731384C1C5C9C4E75A3E41DAF88F74D2F1BCCF818764A4AB144A081B641C1C488AC8B194EB14BC9D1928E4EA


La diferencia

La diferencia entre firmar con "SHA256withRSA" y calcular el hash SHA256 y firmarlo con "RSA" (= "NONEwithRSA" ) es más importante que en el primer caso, el valor de hash SHA-256 calculado se encapsula primero en una estructura DigestInfo

DigestInfo ::= SEQUENCE { digestAlgorithm DigestAlgorithm, digest OCTET STRING }

antes de ser rellenado y luego encriptado, mientras que en este último caso el valor hash SHA256 desnudo se rellena y encripta.

Si son diferentes, ¿hay una manera de modificar el método 2 para que ambos métodos den el mismo resultado?

En primer lugar, tendrá que encapsular el valor hash en una estructura DigestInfo antes de firmar usando "NONEwithRSA" .

RFC 3447 Sección 9.2 ayuda aquí al indicar en la Nota 1 que

1. For the six hash functions mentioned in Appendix B.1, the DER encoding T of the DigestInfo value is equal to the following: ... SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H.

Haciendo que funcione

En respuesta a la sección anterior, el OP actualizó su pregunta con el código actualizado. Desafortunadamente, sin embargo, todavía no funcionó para él. Así,

El código del OP

SignInSteps.java el código del OP ( SignInSteps.java ). Como él no proporcionó la clave privada, usé una clave de prueba propia ( demo-rsa2048.p12 ). El resultado:

GreenhandOriginal: 1B9557B6A076226FA4C26A9370A0E9E91B627F14204D427B03294EC4BFC346FDEEFB3A483B1E5A0593F26E9DE87F9202E1064F4D75B24B8FA355B23A560AF263361BB94B2339C3A01952C447CAC862AA9DCAB64B09ABAA0AD50232CDB299D1E4B5F7138F448A87ED32BFF4B5B66F35FFA08F13FD98DFCEC7114710282E463245311DA7A56CBEA958D88137A8B507D8601464535978EFE36EE37EF721260DB7112484F244409F0BD64C823ACFB13D06ABA84A9A0C5AB207E19231D6A71CC80F07FDA2A9654F0F609C2C3396D6DFFBBB10EF4C3D4B5ADFC72EACC044E81F252B699F095CFEF8630B284B1F6BD7201367BD5FDF2BB4C20BD07B9CC20B214D86C729 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA GreenhandUpdated: method 1: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA method 2: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA

Por lo tanto, en contraste con las observaciones del OP, las firmas son iguales en el caso del código actualizado.

Al no asumir los errores de copiar y pegar, es posible que existan otras diferencias.

El entorno

Probé utilizando Java 8 (1.8.0_20) con archivos de jurisdicción ilimitados agregados y BouncyCastle 1.52, 1.49 y 1.46 (con una pequeña modificación en el código de prueba debido a los cambios en la API de BC).

El OP mencionado en un comentario:

Java es la actualización 66 de JRE 8. El BouncyCastle es bcprov-jdk15on-153.jar.

Así actualicé Java, aún no hay diferencia.

Luego actualicé BouncyCastle a 1.53. Y de hecho, de repente los resultados difirieron:

GreenhandOriginal: 1B9557B6A076226FA4C26A9370A0E9E91B627F14204D427B03294EC4BFC346FDEEFB3A483B1E5A0593F26E9DE87F9202E1064F4D75B24B8FA355B23A560AF263361BB94B2339C3A01952C447CAC862AA9DCAB64B09ABAA0AD50232CDB299D1E4B5F7138F448A87ED32BFF4B5B66F35FFA08F13FD98DFCEC7114710282E463245311DA7A56CBEA958D88137A8B507D8601464535978EFE36EE37EF721260DB7112484F244409F0BD64C823ACFB13D06ABA84A9A0C5AB207E19231D6A71CC80F07FDA2A9654F0F609C2C3396D6DFFBBB10EF4C3D4B5ADFC72EACC044E81F252B699F095CFEF8630B284B1F6BD7201367BD5FDF2BB4C20BD07B9CC20B214D86C729 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA GreenhandUpdated: method 1: 6BAAAC1060B6D0D56AD7D45A1BEECE82391088FF47A8D8179EFBBEB0925C4AC6C9DFC56F672E99F4A6E3C106A866B70513C25AE11B267286C584A136FBC20C4D1E7B10697352DF020BA5D67029A6EF890B2674F02C496CB1F1EBB0D4DBB580EB045DBB0FA0D7D73B418FF63F345658C6C73DA742FE260C9639C94967A928F74F61DACA03310B9986C32D83CAB8C7FC13E80612CCFC0B7E3E35BEA04EAEBDAA55FB8837B4661DC71499B4A0B1D36E1D23D9927CDB55C237D5AB2E5C088F29C6FAFAD9FE64DD4851CEC113560864E9923D485D0C6E092C8EBE82D29C312E5835B38EE9BD6B8B4BCC753EF4EE4D0977B2E781B391839E3EC31C36E5B1AA0CE90227 method 2: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA

Curiosamente, solo el valor para el método 1 en el código actualizado difiere. Así, miré los objetos intermedios en ese caso.

[BC 1.52] hash: 03AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4 algo: 2.16.840.1.101.3.4.2.1 info: 3031300D06096086480165030402010500042003AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4 [BC 1.53] hash: 03AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4 algo: 2.16.840.1.101.3.4.2.1 info: 302F300B0609608648016503040201042003AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4

Por lo tanto, BouncyCastle 1.53 codifica el objeto DigestInfo de manera diferente. Y la codificación en 1.52 (y más abajo) es la esperada por la RFC 3447 Sección 9.2 .

En cuanto a los volcados ASN.1, se observa que BC 1.52 codifica el AlgorithmIdentifier como

2 13: SEQUENCE { <06 09> 4 9: OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1) : (NIST Algorithm) <05 00> 15 0: NULL : }

mientras BC 1.53 crea

2 11: SEQUENCE { <06 09> 4 9: OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1) : (NIST Algorithm) : }

Así que en 1.53 los parámetros del algoritmo faltan por completo. Esto sugiere cambiar la línea.

AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, null);

a

AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);

y, de repente, también funciona con BouncyCastle 1.53, ¡los valores para el método 1 y el método 2 coinciden! ;)

TL; DR

No use null como los parámetros SHA-256 cuando DERNull.INSTANCE instancia del AlgorithmIdentifier , use DERNull.INSTANCE en DERNull.INSTANCE lugar.

Como hice

En un comentario, el OP indicó que le gustaría saber más sobre

  1. ¿Cómo inspeccionas el objeto intermedio de BouncyCastle y
  2. ¿Cómo se producen los vertederos ASN.1.

Asi que...

... inspeccionar el objeto intermedio

Bastante sencillo. Primero partí la línea

rsaSignature.update(di.toASN1Primitive().getEncoded());

en el código actualizado como

byte[] encodedDigestInfo = di.toASN1Primitive().getEncoded(); rsaSignature.update(encodedDigestInfo);

y luego agrega salidas de consola

System.out.println(" hash: " + bytesToHex(outputDigest)); System.out.println(" algo: " + sha256Aid.getAlgorithm()); System.out.println(" info: " + bytesToHex(encodedDigestInfo));

Finalmente ejecuté el código con las diferentes versiones de BouncyCastle.

... producir los vertederos ASN.1

Existe una utilidad conocida llamada dumpasn1 por Peter Gutmann que se ha convertido en el núcleo de muchas herramientas de línea de comandos y GUI para crear y mostrar volcados ASN.1. GUIdumpASN-ng que actualmente uso GUIdumpASN-ng .

En el caso en cuestión, byte[] encodedDigestInfo el contenido del byte[] encodedDigestInfo en un archivo (que se puede hacer utilizando, por ejemplo, Files.write ) y abrí estos archivos en GUIdumpASN-ng.