Utilisation de SHA1 et RSA avec java.security.Signature vs MessageDigest et Cipher

J’essaie de comprendre ce que fait la classe Java java.security.Signature . Si je calcule un résumé de message SHA1, puis que je crypte ce résumé à l’aide de RSA, j’obtiens un résultat différent de celui de demander à la classe Signature de signer la même chose:

// Generate new key KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Ssortingng plaintext = "This is the message being signed"; // Compute signature Signature instance = Signature.getInstance("SHA1withRSA"); instance.initSign(privateKey); instance.update((plaintext).getBytes()); byte[] signature = instance.sign(); // Compute digest MessageDigest sha1 = MessageDigest.getInstance("SHA1"); byte[] digest = sha1.digest((plaintext).getBytes()); // Encrypt digest Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] cipherText = cipher.doFinal(digest); // Display results System.out.println("Input data: " + plaintext); System.out.println("Digest: " + bytes2Ssortingng(digest)); System.out.println("Cipher text: " + bytes2Ssortingng(cipherText)); System.out.println("Signature: " + bytes2Ssortingng(signature)); 

Résultats dans (par exemple):

Données d’entrée: Ceci est le message en cours de signature
Digest: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067
Texte crypté: 057dc0d2f7f54acc95d3cf5cba9f944619394711003bdd12 …
Signature: 7177c74bbbb871cc0af92e30d2808ebae146f25d3fd8ba1622 …

Je dois avoir une incompréhension fondamentale de ce que Signature est en train de faire – je l’ai parcouru et il semble appeler la mise à jour sur un object MessageDigest , avec l’algorithme défini sur SHA1 comme prévu, puis obtenir le résumé, puis cryptage. Qu’est-ce qui fait que les résultats diffèrent?

MODIFIER:

Leonidas m’a fait vérifier si le schéma de signature est censé faire ce que je pense. Il existe deux types de signature définis dans la RFC :

  • RSASSA-PKCS1-v1_5
  • RSASSA-PSS

Le premier (PKCS1) est celui que je décris ci-dessus. Il utilise une fonction de hachage pour créer un condensé, puis chiffre le résultat avec une clé privée.

Le deuxième algorithme utilise une valeur de sel aléatoire et est plus sûr mais non déterministe. La signature produite à partir du code ci-dessus ne change pas si la même clé est utilisée à plusieurs resockets, donc je ne pense pas que cela puisse être PSS.

MODIFIER:

Voici la méthode bytes2ssortingng j’utilisais:

 private static Ssortingng bytes2Ssortingng(byte[] bytes) { SsortingngBuilder ssortingng = new SsortingngBuilder(); for (byte b : bytes) { Ssortingng hexSsortingng = Integer.toHexSsortingng(0x00FF & b); ssortingng.append(hexSsortingng.length() == 1 ? "0" + hexSsortingng : hexSsortingng); } return ssortingng.toSsortingng(); } 

    OK, j’ai compris ce qui se passait. J’étais stupide. Leonidas a raison, ce n’est pas seulement le hachage qui est chiffré, c’est l’ID de l’algorithme de hachage concaténé avec le condensé:

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

    C’est pourquoi ils sont différents.

    Pour produire les mêmes résultats:

     MessageDigest sha1 = MessageDigest.getInstance("SHA1", BOUNCY_CASTLE_PROVIDER); byte[] digest = sha1.digest(content); DERObjectIdentifier sha1oid_ = new DERObjectIdentifier("1.3.14.3.2.26"); AlgorithmIdentifier sha1aid_ = new AlgorithmIdentifier(sha1oid_, null); DigestInfo di = new DigestInfo(sha1aid_, digest); byte[] plainSig = di.getDEREncoded(); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", BOUNCY_CASTLE_PROVIDER); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] signature = cipher.doFinal(plainSig); 

    Une version légèrement plus efficace de la méthode bytes2Ssortingng est

     private static final char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static Ssortingng byteArray2Hex(byte[] bytes) { SsortingngBuilder sb = new SsortingngBuilder(bytes.length * 2); for (final byte b : bytes) { sb.append(hex[(b & 0xF0) >> 4]); sb.append(hex[b & 0x0F]); } return sb.toSsortingng(); } 

    Euh, après avoir compris votre question: êtes-vous sûr que la méthode de signature ne crée qu’un SHA1 et le chiffre? GPG et al. Proposent de compresser / effacer les données. Peut-être que cette signature java-alg crée également une signature détachable / amovible.

    En prenant la réponse de @Mike Houston comme pointeur, voici un exemple de code complet qui fait la signature, le hachage et le chiffrement.

     /** * @param args */ public static void main(Ssortingng[] args) { try { boolean useBouncyCastleProvider = false; Provider provider = null; if (useBouncyCastleProvider) { provider = new BouncyCastleProvider(); Security.addProvider(provider); } Ssortingng plainText = "This is a plain text!!"; // KeyPair KeyPairGenerator keyPairGenerator = null; if (null != provider) keyPairGenerator = KeyPairGenerator.getInstance("RSA", provider); else keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // Signature Signature signatureProvider = null; if (null != provider) signatureProvider = Signature.getInstance("SHA256WithRSA", provider); else signatureProvider = Signature.getInstance("SHA256WithRSA"); signatureProvider.initSign(keyPair.getPrivate()); signatureProvider.update(plainText.getBytes()); byte[] signature = signatureProvider.sign(); System.out.println("Signature Output : "); System.out.println("\t" + new Ssortingng(Base64.encode(signature))); // Message Digest Ssortingng hashingAlgorithm = "SHA-256"; MessageDigest messageDigestProvider = null; if (null != provider) messageDigestProvider = MessageDigest.getInstance(hashingAlgorithm, provider); else messageDigestProvider = MessageDigest.getInstance(hashingAlgorithm); messageDigestProvider.update(plainText.getBytes()); byte[] hash = messageDigestProvider.digest(); DigestAlgorithmIdentifierFinder hashAlgorithmFinder = new DefaultDigestAlgorithmIdentifierFinder(); AlgorithmIdentifier hashingAlgorithmIdentifier = hashAlgorithmFinder.find(hashingAlgorithm); DigestInfo digestInfo = new DigestInfo(hashingAlgorithmIdentifier, hash); byte[] hashToEncrypt = digestInfo.getEncoded(); // Crypto // You could also use "RSA/ECB/PKCS1Padding" for both the BC and SUN Providers. Cipher encCipher = null; if (null != provider) encCipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", provider); else encCipher = Cipher.getInstance("RSA"); encCipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate()); byte[] encrypted = encCipher.doFinal(hashToEncrypt); System.out.println("Hash and Encryption Output : "); System.out.println("\t" + new Ssortingng(Base64.encode(encrypted))); } catch (Throwable e) { e.printStackTrace(); } } 

    Vous pouvez utiliser le fournisseur BouncyCastle ou le fournisseur Sun par défaut.

    J’ai un problème similaire, j’ai testé l’ajout de code et trouvé des résultats intéressants. Avec ce code que j’ajoute, je peux en déduire que selon le “fournisseur” à utiliser, l’entreprise peut être différente? (parce que les données incluses dans le cryptage ne sont pas toujours égales dans tous les fournisseurs).

    Résultats de mon test

    Conclusion.- Signature Decipher = ??? (corbeille) + DigestInfo (si nous connaissons la valeur de “corbeille”, les signatures numériques seront égales)

    IDE Eclipse OUTPUT …

    Données d’entrée: Ceci est le message en cours de signature

    Digest: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067

    Information Digest: 3021300906052b0e03021a0500041462b0a9ef15461c82766fb5bdaae9edbe4ac2e067

    Signature Decipher: 1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a0500041462b0a9ef15461c82766fb5bdaae9edbe4ac2e067

    CODE

     import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import org.bouncycastle.asn1.x509.DigestInfo; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; public class prueba { /** * @param args * @throws NoSuchProviderException * @throws NoSuchAlgorithmException * @throws InvalidKeyException * @throws SignatureException * @throws NoSuchPaddingException * @throws BadPaddingException * @throws IllegalBlockSizeException */// public static void main(Ssortingng[] args) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { // TODO Auto-generated method stub KeyPair keyPair = KeyPairGenerator.getInstance("RSA","BC").generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); PublicKey puKey = keyPair.getPublic(); Ssortingng plaintext = "This is the message being signed"; // Hacer la firma Signature instance = Signature.getInstance("SHA1withRSA","BC"); instance.initSign(privateKey); instance.update((plaintext).getBytes()); byte[] signature = instance.sign(); // En dos partes primero hago un Hash MessageDigest digest = MessageDigest.getInstance("SHA1", "BC"); byte[] hash = digest.digest((plaintext).getBytes()); // El digest es identico a openssl dgst -sha1 texto.txt //MessageDigest sha1 = MessageDigest.getInstance("SHA1","BC"); //byte[] digest = sha1.digest((plaintext).getBytes()); AlgorithmIdentifier digestAlgorithm = new AlgorithmIdentifier(new DERObjectIdentifier("1.3.14.3.2.26"), null); // create the digest info DigestInfo di = new DigestInfo(digestAlgorithm, hash); byte[] digestInfo = di.getDEREncoded(); //Luego cifro el hash Cipher cipher = Cipher.getInstance("RSA","BC"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] cipherText = cipher.doFinal(digestInfo); //byte[] cipherText = cipher.doFinal(digest2); Cipher cipher2 = Cipher.getInstance("RSA","BC"); cipher2.init(Cipher.DECRYPT_MODE, puKey); byte[] cipherText2 = cipher2.doFinal(signature); System.out.println("Input data: " + plaintext); System.out.println("Digest: " + bytes2Ssortingng(hash)); System.out.println("Signature: " + bytes2Ssortingng(signature)); System.out.println("Signature2: " + bytes2Ssortingng(cipherText)); System.out.println("DigestInfo: " + bytes2Ssortingng(digestInfo)); System.out.println("Signature Decipher: " + bytes2Ssortingng(cipherText2)); }