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?
- Calcular una firma con
Signature.getInstance("SHA256withRSA")
- Calcular SHA256 con
MessageDigest.getInstance("SHA-256")
y calcular el resumen conSignature.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
- ¿Cómo inspeccionas el objeto intermedio de BouncyCastle y
- ¿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.