Using SHA1 and RSA with java.security.Signature vs MessageDigest and Cipher

I am trying to understand what the Java class java.security.Signature does. If I calculate the digest of the SHA1 message and then encrypt this digest using RSA, I get a different result to ask the Signature class to sign the same:

// Generate new key KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); String 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: " + bytes2String(digest)); System.out.println("Cipher text: " + bytes2String(cipherText)); System.out.println("Signature: " + bytes2String(signature)); 

Results (for example):

Input: This is a signed message.
Digest: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067
Ciphertext: 057dc0d2f7f54acc95d3cf5cba9f944619394711003bdd12 ...
Signature: 7177c74bbbbb871cc0af92e30d2808ebae146f25d3fd8ba1622 ...

I should have a fundamental misunderstanding of what the signature does - I followed it up and it seems to cause the MessageDigest object to be updated, with the algorithm set to SHA1, as you would expect, then getting the digest, then do the encryption. What makes the results different?

EDIT:

Leonid made me check if the signature scheme should do what I think. There are two types of signatures defined in the RFC :

the first one (PKCS1) is the one I described above. It uses a hash function to create a digest, and then encrypts the result with a private key.

The second algorithm uses a random salt value and is safer, but not deterministic. The signature obtained from the above code does not change if the same key is reused, so I donโ€™t think it could be PSS.

EDIT:

Here is the bytes2string method that I used:

 private static String bytes2String(byte[] bytes) { StringBuilder string = new StringBuilder(); for (byte b : bytes) { String hexString = Integer.toHexString(0x00FF & b); string.append(hexString.length() == 1 ? "0" + hexString : hexString); } return string.toString(); } 
+61
java cryptography encryption rsa digital-signature
Feb 06 '09 at 16:51
source share
6 answers

Ok, I worked out what was going on. I was stupid. Leonidas is right, this is not only a hash that is encrypted, it is an identifier of a hash algorithm combined with a digest:

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

That is why they are different.

+50
Feb 06 '09 at 18:02
source share

To get the same results:

 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); 
+8
Jan 26 '11 at 15:53
source share

A slightly more efficient version of the bytes2String method

 private static final char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static String byteArray2Hex(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 2); for (final byte b : bytes) { sb.append(hex[(b & 0xF0) >> 4]); sb.append(hex[b & 0x0F]); } return sb.toString(); } 
+4
Jun 12 2018-11-11T00:
source share

Erm, understanding your question: are you sure that the signature method creates only SHA1 and encrypts it? GPG et al. Offer data compression / cleanup. Perhaps this java-signature-alg also creates a detachable / dockable signature.

+3
Feb 06 '09 at 16:56
source share

Taking @Mike Houston as a pointer, here is a complete code example that performs signature and hash and encryption.

 /** * @param args */ public static void main(String[] args) { try { boolean useBouncyCastleProvider = false; Provider provider = null; if (useBouncyCastleProvider) { provider = new BouncyCastleProvider(); Security.addProvider(provider); } String 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 String(Base64.encode(signature))); // Message Digest String 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 String(Base64.encode(encrypted))); } catch (Throwable e) { e.printStackTrace(); } } 

You can use the BouncyCastle provider or the default Sun provider.

+1
Dec 14 '15 at 16:23
source share

I have a similar problem, I tested adding code and found interesting results. With this code, I add that I can assume that depending on the "provider" used, the company may be different? (since the data included in the encryption is not always equal in all providers).

The results of my test.

Conclusion.- Signature Decipher = ??? (trash) + DigestInfo (if we know the value "trash", digital signatures will be equal)

IDE Eclipse OUTPUT ...

Input: This is a signed message.

Digest: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067

DigestInfo: 3021300906052b0e03021a0500041462b0a9ef15461c82766fb5bdaae9edbe4ac2e067

Decipher Text: 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(String[] 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(); String 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: " + bytes2String(hash)); System.out.println("Signature: " + bytes2String(signature)); System.out.println("Signature2: " + bytes2String(cipherText)); System.out.println("DigestInfo: " + bytes2String(digestInfo)); System.out.println("Signature Decipher: " + bytes2String(cipherText2)); } 
0
Mar 06 '13 at 4:58
source share



All Articles