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...
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; ...
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:
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBALLA/Zk6+4JFJ+XdU6wmUkuEhGa8hLZ+m6J3puZbc9E+DSt7pW09
yMYwHF5MMICxE86cA6BrLjQLUUwvquNSK0ECAwEAAQJAcI/w4e3vdRABWNFvoCcd
iWpwSZWK6LR/YuZ/1e1e2DJw+NXyPXbilSrLvAdxnjlWTsTxUiEy1jFh36pSuvMk
AQIhAO4WtgysOoWkyvIOLIQwD0thWfdHxTpxqfd6flrBJ91hAiEAwDOQqHhnSeET
+N/hwUJQtCkHBJqvMF/kAi4Ry5G+OeECIEg1Exlc0pLdm781lUKx4LGX4NUiKyrC
di3cNJ4JnrGBAiEAi2gbYYbLbDO8F8TTayidfr9PXtCPhyfWKpqdv6i7cCECIH7A
6bh0tDCl6dOXQwbhgqF4hXiMsqe6DaHqIw8+XLnG
-----END RSA PRIVATE KEY-----
signature as base 64 (using your code):
YY6sur9gkHXH23cUbDMYjCJYqDdBK8GKp4XyRNl8H09cW8H/gKQI9Z6dkLMhNh7oPq1yABCRfTP8yRtfLVj7FA==
and in hexadecimals
618eacbabf609075c7db77146c33188c2258a837412bc18aa785f244d97c1f4f5c5bc1ff80a408f59e9d90b321361ee83ead720010917d33fcc91b5f2d58fb14
decrypted using RAW RSA (i.e. just modular exponentiation with the public exponent):
0001ffffffffffffffffffff003031300d0609608648016503040201050004202af565b95e5f4479492c520c430f07ae05d2bcff8923322e6f2ef6404d72ac64
This is a very clear example of a PKCS#1 signature, easily recognized by the FF
padding, followed by the ASN.1 structure (starting with 30
, SEQUENCE):
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 {joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) sha256(1)}
NULL
OCTET STRING(32 byte) 2AF565B95E5F4479492C520C430F07AE05D2BCFF8923322E6F2EF6404D72AC64
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.
$ sha256sum some_document.xml
2af565b95e5f4479492c520c430f07ae05d2bcff8923322e6f2ef6404d72ac64 some_document.xml
$ sha256sum some_document.xml
2af565b95e5f4479492c520c430f07ae05d2bcff8923322e6f2ef6404d72ac64 some_document.xml