Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Digital Signatures: Encrypting the Hash vs Signing the Hash?

I'm trying to implement SHA256-RSA digital signatures and I'm confused with the terminology and implementation in C#.

AFAIK, "signing a file" is to generate the hash of a file, and then encrypt that hash. I've also heard the phrase "signing the hash". Is this the same thing? Or is this hashing a hash and then encrypting hash'?

Here's the code in question:

public void SignatureTest(byte[] data, X509Certificate2 cert)
{
    var sha256 = new SHA256CryptoServiceProvider();
    var rsa = (RSACryptoServiceProvider)cert.PrivateKey;

    var hashOfData = sha256.ComputeHash(data);

    var encryptedHash = rsa.Encrypt(hashOfData, false);
    var encryptedHashOAEP = rsa.Encrypt(hashOfData, true);            
    var signedHash = rsa.SignHash(hashOfData, "SHA256");

    //Shouldn't one of these be true?
    var false1 = CompareAsBase64Str(encryptedHash, signedHash);
    var false2 = CompareAsBase64Str(encryptedHashOAEP, signedHash);

    //This is the one that actually matches
    var true1 = CompareAsBase64Str(signedHash, rsa.SignData(data, sha256));
}

public bool CompareAsBase64Str(byte[] b1, byte[] b2)
{
    return (Convert.ToBase64String(b1) == Convert.ToBase64String(b2));
}

Here's what MSDN says on RSACryptoServiceProvider:

SignHash() Computes the signature for the specified hash value by encrypting it with the private key.

Encrypt() Encrypts data with the RSA algorithm.

Shouldnt SignHash(hash) and Encrypt(hash) be the same?

like image 782
nr-91 Avatar asked Dec 10 '16 04:12

nr-91


2 Answers

You need to separate concerns, this will help you understand the terminology.

Any arbitrary blob of data can be hashed and/or encrypted in any combination. Hash means: use a cryptographic algorithm to generate a value that is irreversible (that is, simply by knowing algorithm and hash you are unable to reconstitute original data) and consistent (that is, given the same data and algorithm, the value of the hash produced is always the same).

Encrypt means: use a cryptographic algorithm to encipher data (altogether or in blocks) with a given key (a key can be symmetric or asymmetric).

Sign means: Hash the data and Encrypt the hash with a given key. Then, given the pair (for asymmetric) or same (for symmetric) key, a consumer can validate that:

  1. hash is matching, that means the data has not been altered in transit
  2. hash did come from the source that at least has the pair key (for asymmetric) or same key (for symmetric)
like image 91
zaitsman Avatar answered Oct 18 '22 01:10

zaitsman


The answer given by zaitsman is a good explanation of the topics related to your questions and I think should be the accepted answer, but just to help tie it back to your specific question of why encrypting the hash doesn't give you the same result as signing the hash (the rsa.SignHash(hashOfData, "SHA256") in your code):

Signing a hash doesn't just encrypt the hash data -- it also encrypts the name (or some identifier) of the hashing algorithm used to generate the hash along with it. Without that, the receiver wouldn't know what algorithm to use when computing their own hash (of the message being sent) to compare with the one they just decrypted in order to verify the authenticity of the message (which, of course, is the whole point).

When you encrypted the hash yourself (with rsa.Encrypt(hashOfData, false) and rsa.Encrypt(hashOfData, true)), you only encrypted the hash data and not the combination of hash data and algorithm identifier ("SHA256" in your code). In other words, you encrypted different data, so you got different (encrypted) results.

The reason the return value of that SignHash call does match the value returned by rsa.SignData(data, sha256) is that the latter method does the same thing, except it does the hashing and hash signing as one operation, so you don't have to compute the hash as a separate step if you don't need it for any purpose other than signing it.

From RSACryptoServiceProvider.SignData Method on MSDN:

Computes the hash value of the specified data and signs it.


Also see: Why does SignHash need to know what hash algorithm was used?

like image 32
John K Avatar answered Oct 18 '22 00:10

John K