Browse Source

Support for signature verification with RSA/DSA public keys

Fixes #1166.
v0.7.4-release
Mark Cavage 14 years ago
committed by Ryan Dahl
parent
commit
88552c51ae
  1. 10
      doc/api/crypto.markdown
  2. 76
      src/node_crypto.cc
  3. 15
      test/fixtures/test_rsa_privkey.pem
  4. 6
      test/fixtures/test_rsa_pubkey.pem
  5. 16
      test/simple/test-crypto.js

10
doc/api/crypto.markdown

@ -139,10 +139,12 @@ This is the mirror of the signing object above.
Updates the verifier object with data. Updates the verifier object with data.
This can be called many times with new data as it is streamed. 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 Verifies the signed data by using the `object` and `signature`. `object` is a
the PEM encoded certificate, and `signature`, which is the previously calculated string containing a PEM encoded object, which can be one of RSA public key,
signature for the data, in the `signature_format` which can be `'binary'`, `'hex'` or `'base64'`. 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. Returns true or false depending on the validity of the signature for the data and public key.

76
src/node_crypto.cc

@ -42,6 +42,11 @@
return ThrowException(Exception::TypeError(String::New("Not a string or buffer"))); \ 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 node {
namespace crypto { namespace crypto {
@ -1497,7 +1502,7 @@ class Cipher : public ObjectWrap {
static Handle<Value> CipherInitIv(const Arguments& args) { static Handle<Value> CipherInitIv(const Arguments& args) {
Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This()); Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
HandleScope scope; HandleScope scope;
cipher->incomplete_base64=NULL; cipher->incomplete_base64=NULL;
@ -1532,7 +1537,7 @@ class Cipher : public ObjectWrap {
assert(iv_written == iv_len); assert(iv_written == iv_len);
String::Utf8Value cipherType(args[0]->ToString()); String::Utf8Value cipherType(args[0]->ToString());
bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len); bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
delete [] key_buf; delete [] key_buf;
@ -1826,7 +1831,7 @@ class Decipher : public ObjectWrap {
static Handle<Value> DecipherInit(const Arguments& args) { static Handle<Value> DecipherInit(const Arguments& args) {
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This()); Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
HandleScope scope; HandleScope scope;
cipher->incomplete_utf8=NULL; cipher->incomplete_utf8=NULL;
@ -1850,7 +1855,7 @@ class Decipher : public ObjectWrap {
assert(key_written == key_len); assert(key_written == key_len);
String::Utf8Value cipherType(args[0]->ToString()); String::Utf8Value cipherType(args[0]->ToString());
bool r = cipher->DecipherInit(*cipherType, key_buf,key_len); bool r = cipher->DecipherInit(*cipherType, key_buf,key_len);
delete [] key_buf; delete [] key_buf;
@ -1864,7 +1869,7 @@ class Decipher : public ObjectWrap {
static Handle<Value> DecipherInitIv(const Arguments& args) { static Handle<Value> DecipherInitIv(const Arguments& args) {
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This()); Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
HandleScope scope; HandleScope scope;
cipher->incomplete_utf8=NULL; cipher->incomplete_utf8=NULL;
@ -1900,7 +1905,7 @@ class Decipher : public ObjectWrap {
assert(iv_written == iv_len); assert(iv_written == iv_len);
String::Utf8Value cipherType(args[0]->ToString()); String::Utf8Value cipherType(args[0]->ToString());
bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len); bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
delete [] key_buf; delete [] key_buf;
@ -2265,7 +2270,7 @@ class Hmac : public ObjectWrap {
} }
int r; int r;
if( Buffer::HasInstance(args[0])) { if( Buffer::HasInstance(args[0])) {
Local<Object> buffer_obj = args[0]->ToObject(); Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj); char *buffer_data = Buffer::Data(buffer_obj);
@ -2756,29 +2761,58 @@ class Verify : public ObjectWrap {
int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) { int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) {
if (!initialised_) return 0; if (!initialised_) return 0;
EVP_PKEY* pkey = NULL;
BIO *bp = NULL; BIO *bp = NULL;
EVP_PKEY* pkey; X509 *x509 = NULL;
X509 *x509; int r = 0;
bp = BIO_new(BIO_s_mem()); bp = BIO_new(BIO_s_mem());
if(!BIO_write(bp, key_pem, key_pemLen)) return 0; if (bp == NULL) {
ERR_print_errors_fp(stderr);
x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL ); return 0;
if (x509==NULL) return 0; }
if(!BIO_write(bp, key_pem, key_pemLen)) {
ERR_print_errors_fp(stderr);
return 0;
}
pkey=X509_get_pubkey(x509); // Check if this is an RSA or DSA "raw" public key before trying
if (pkey==NULL) return 0; // 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); pkey = X509_get_pubkey(x509);
EVP_PKEY_free (pkey); 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); ERR_print_errors_fp (stderr);
}
X509_free(x509); if(pkey != NULL)
BIO_free(bp); EVP_PKEY_free (pkey);
if (x509 != NULL)
X509_free(x509);
if (bp != NULL)
BIO_free(bp);
EVP_MD_CTX_cleanup(&mdctx); EVP_MD_CTX_cleanup(&mdctx);
initialised_ = false; initialised_ = false;
return r; return r;
} }

15
test/fixtures/test_rsa_privkey.pem

@ -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
test/fixtures/test_rsa_pubkey.pem

@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
oYi+1hqp1fIekaxsyQIDAQAB
-----END PUBLIC KEY-----

16
test/simple/test-crypto.js

@ -36,6 +36,8 @@ var path = require('path');
var caPem = fs.readFileSync(common.fixturesDir + '/test_ca.pem', 'ascii'); var caPem = fs.readFileSync(common.fixturesDir + '/test_ca.pem', 'ascii');
var certPem = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii'); var certPem = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii');
var keyPem = fs.readFileSync(common.fixturesDir + '/test_key.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 { try {
var credentials = crypto.createCredentials( var credentials = crypto.createCredentials(
@ -144,3 +146,17 @@ assert.equal(txt, plaintext, 'encryption and decryption with key and iv');
assert.throws(function() { assert.throws(function() {
crypto.createHash('sha1').update({foo: 'bar'}); crypto.createHash('sha1').update({foo: 'bar'});
}, /string or buffer/); }, /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);

Loading…
Cancel
Save