Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Support for signature verification with RSA/DSA public keys #1166

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions doc/api/crypto.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,12 @@ This is the mirror of the signing object above.
Updates the verifier object with data.
This can be called many times with new data as it is streamed.

### verifier.verify(cert, signature, signature_format='binary')
### verifier.verify(object, signature, signature_format='binary')

Verifies the signed data by using the `cert` which is a string containing
the PEM encoded certificate, and `signature`, which is the previously calculated
signature for the data, in the `signature_format` which can be `'binary'`, `'hex'` or `'base64'`.
Verifies the signed data by using the `object` and `signature`. `object` is a
string containing a PEM encoded object, which can be one of RSA public key,
DSA public key, or X.509 certificate. `signature` is the previously calculated
signature for the data, in the `signature_format` which can be `'binary'`,
`'hex'` or `'base64'`.

Returns true or false depending on the validity of the signature for the data and public key.
76 changes: 55 additions & 21 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
return ThrowException(Exception::TypeError(String::New("Not a string or buffer"))); \
}

static const char *RSA_PUB_KEY_PFX = "-----BEGIN RSA PUBLIC KEY-----";
static const char *DSA_PUB_KEY_PFX = "-----BEGIN PUBLIC KEY-----";
static const int RSA_PUB_KEY_PFX_LEN = strlen(RSA_PUB_KEY_PFX);
static const int DSA_PUB_KEY_PFX_LEN = strlen(DSA_PUB_KEY_PFX);

namespace node {
namespace crypto {

Expand Down Expand Up @@ -1497,7 +1502,7 @@ class Cipher : public ObjectWrap {

static Handle<Value> CipherInitIv(const Arguments& args) {
Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());

HandleScope scope;

cipher->incomplete_base64=NULL;
Expand Down Expand Up @@ -1532,7 +1537,7 @@ class Cipher : public ObjectWrap {
assert(iv_written == iv_len);

String::Utf8Value cipherType(args[0]->ToString());

bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);

delete [] key_buf;
Expand Down Expand Up @@ -1826,7 +1831,7 @@ class Decipher : public ObjectWrap {

static Handle<Value> DecipherInit(const Arguments& args) {
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());

HandleScope scope;

cipher->incomplete_utf8=NULL;
Expand All @@ -1850,7 +1855,7 @@ class Decipher : public ObjectWrap {
assert(key_written == key_len);

String::Utf8Value cipherType(args[0]->ToString());

bool r = cipher->DecipherInit(*cipherType, key_buf,key_len);

delete [] key_buf;
Expand All @@ -1864,7 +1869,7 @@ class Decipher : public ObjectWrap {

static Handle<Value> DecipherInitIv(const Arguments& args) {
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());

HandleScope scope;

cipher->incomplete_utf8=NULL;
Expand Down Expand Up @@ -1900,7 +1905,7 @@ class Decipher : public ObjectWrap {
assert(iv_written == iv_len);

String::Utf8Value cipherType(args[0]->ToString());

bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);

delete [] key_buf;
Expand Down Expand Up @@ -2265,7 +2270,7 @@ class Hmac : public ObjectWrap {
}

int r;

if( Buffer::HasInstance(args[0])) {
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
Expand Down Expand Up @@ -2756,29 +2761,58 @@ class Verify : public ObjectWrap {
int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) {
if (!initialised_) return 0;

EVP_PKEY* pkey = NULL;
BIO *bp = NULL;
EVP_PKEY* pkey;
X509 *x509;
X509 *x509 = NULL;
int r = 0;

bp = BIO_new(BIO_s_mem());
if(!BIO_write(bp, key_pem, key_pemLen)) return 0;

x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL );
if (x509==NULL) return 0;
if (bp == NULL) {
ERR_print_errors_fp(stderr);
return 0;
}
if(!BIO_write(bp, key_pem, key_pemLen)) {
ERR_print_errors_fp(stderr);
return 0;
}

pkey=X509_get_pubkey(x509);
if (pkey==NULL) return 0;
// Check if this is an RSA or DSA "raw" public key before trying
// X.509
if (strncmp(key_pem, RSA_PUB_KEY_PFX, RSA_PUB_KEY_PFX_LEN) == 0 ||
strncmp(key_pem, DSA_PUB_KEY_PFX, DSA_PUB_KEY_PFX_LEN) == 0) {
pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
if (pkey == NULL) {
ERR_print_errors_fp(stderr);
return 0;
}
} else {
// X.509 fallback
x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
if (x509 == NULL) {
ERR_print_errors_fp(stderr);
return 0;
}

int r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey);
EVP_PKEY_free (pkey);
pkey = X509_get_pubkey(x509);
if (pkey == NULL) {
ERR_print_errors_fp(stderr);
return 0;
}
}

if (r != 1) {
r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey);
if (r != 1)
ERR_print_errors_fp (stderr);
}
X509_free(x509);
BIO_free(bp);

if(pkey != NULL)
EVP_PKEY_free (pkey);
if (x509 != NULL)
X509_free(x509);
if (bp != NULL)
BIO_free(bp);
EVP_MD_CTX_cleanup(&mdctx);
initialised_ = false;

return r;
}

Expand Down
15 changes: 15 additions & 0 deletions test/fixtures/test_rsa_privkey.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB
AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA
QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK
kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u
412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc
mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7
kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA==
-----END RSA PRIVATE KEY-----
6 changes: 6 additions & 0 deletions test/fixtures/test_rsa_pubkey.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
oYi+1hqp1fIekaxsyQIDAQAB
-----END PUBLIC KEY-----
16 changes: 16 additions & 0 deletions test/simple/test-crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ var path = require('path');
var caPem = fs.readFileSync(common.fixturesDir + '/test_ca.pem', 'ascii');
var certPem = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii');
var keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii');
var rsaPubPem = fs.readFileSync(common.fixturesDir + '/test_rsa_pubkey.pem', 'ascii');
var rsaKeyPem = fs.readFileSync(common.fixturesDir + '/test_rsa_privkey.pem', 'ascii');

try {
var credentials = crypto.createCredentials(
Expand Down Expand Up @@ -144,3 +146,17 @@ assert.equal(txt, plaintext, 'encryption and decryption with key and iv');
assert.throws(function() {
crypto.createHash('sha1').update({foo: 'bar'});
}, /string or buffer/);


// Test RSA key signing/verification
var rsaSign = crypto.createSign('RSA-SHA1');
var rsaVerify = crypto.createVerify('RSA-SHA1');
assert.ok(rsaSign);
assert.ok(rsaVerify);

rsaSign.update(rsaPubPem);
var rsaSignature = rsaSign.sign(rsaKeyPem, 'hex');
assert.equal(rsaSignature, '5c50e3145c4e2497aadb0eabc83b342d0b0021ece0d4c4a064b7c8f020d7e2688b122bfb54c724ac9ee169f83f66d2fe90abeb95e8e1290e7e177152a4de3d944cf7d4883114a20ed0f78e70e25ef0f60f06b858e6af42a2f276ede95bbc6bc9a9bbdda15bd663186a6f40819a7af19e577bb2efa5e579a1f5ce8a0d4ca8b8f6');

rsaVerify.update(rsaPubPem);
assert.equal(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), 1);