I play with digital signatures using node.js. For test purpose, I created a digital signature of some XML data, first using only SHA256, then using RSA-SHA256.
The thing that puzzles me is that both methods of signing create exactly the same signature. Both signatures are identical. If they're identical, then why two different methods (SHA256 vs. RSA-SHA256)?
I include code below:
var crypto = require('crypto'),
path = require('path'),
fs = require('fs'),
pkey_path = path.normalize('private_key.pem'),
pkey = '';
function testSignature(pkey) {
var sign1 = crypto.createSign('RSA-SHA256'),
sign2 = crypto.createSign('SHA256');
fs.ReadStream('some_document.xml')
.on('data', function (d) {
sign1.update(d);
sign2.update(d);
})
.on('end', function () {
var s1 = sign1.sign(pkey, "base64"),
s2 = sign2.sign(pkey, "base64");
console.log(s1);
console.log(s2);
});
}
// You need to read private key into a string and pass it to crypto module.
// If the key is password protected, program execution will stop and
// a prompt will appear in console, awaiting input of password.
testSignature(fs.readFileSync(pkey_path));
The code above outputs some string, which is the signature, and then again exactly the same string, which is also a signature of the same data, but created with - supposedly - different algorithm, yet it's identical with previous one...
What you are looking at is two times a PKCS#1 v1.5 signature. This is a deterministic scheme for signatures, so it always returns the same result (compare this to the PSS scheme, which is randomized, providing better security properties). RSA PKCS#1 v1.5 signature generation and PSS signature generation is defined within RFC 3447 (also known as the RSA v2.1 specifications).
If you use your code with RSA 512 bits (testing purposes only, use a key of 2048 bits or over) then you will get the following result:
Private key:
signature as base 64 (using your code):
and in hexadecimals
decrypted using RAW RSA (i.e. just modular exponentiation with the public exponent):
This is a very clear example of a PKCS#1 signature, easily recognized by the
FF
padding, followed by the ASN.1 structure (starting with30
, SEQUENCE):So that thing in the end is the hash, in this case over just
Test 123\n
as I didn't want to type out any XML today.A signature cannot be created by SHA256 alone.
SHA256 is a hashing algorithm; i.e. an algorithm creating a short fingerprint number representing an arbitrary large amount of data. To produce a signature, this fingerprint still has to be treated somehow to allow identification of the holder of some private signature key. One such treatment is to encrypt the fingerprint using the private key of a rsa key pair allowing others to decrypt the result using the associated public key and so verify that the keeper of the private key indeed must have been the signer.
In the context of your crypto API that RSA encryption scheme either is the default treatment when the treatment is not explicitly named, or the kind of treatment is deduced from the private key you use as parameter in the
sign
call --- if it is a RSA private key, it uses RSA; if it is a DSA key, it uses DSA; ...