Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

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

Closed
wants to merge 1 commit into from

2 participants

@mcavage

Hey Ryan,

Turns out node didn't support signature verification with anything except for X.509 certificates. This patch takes a guess at inferring whether or not the PEM file passed into crypto.verify() is a public key or certificate, and does the right switch based on that. Minor point I realized after I committed is that sometimes RSA public key headers are just -----BEGIN PUBLIC KEY-----, like DSA, so it's not technically accurate to have the #define C string DSA_KEY_PFX, but, meh.

Thanks,
m

@mcavage

Oh, and we probably want this in both v0.4 and master. Thoughts?

@mcavage

Lastly, I left this code as-is from crypto:

if (r != 1)
ERR_print_errors_fp (stderr);

It would probably make sense to kill that off, since it's not really an error if the signature doesn't match. I can either reprepare a pull request, or if you want to just change it as part of the merge. Let me know.

@ry
ry commented

thanks mark - landed in v0.4

please do a new pull for removing error printing.

@ry ry closed this pull request from a commit
Mark Cavage Support for signature verification with RSA/DSA public keys
Fixes #1166.
88552c5
@ry ry closed this in 88552c5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 9, 2011
This page is out of date. Refresh to see the latest.
View
10 doc/api/crypto.markdown
@@ -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.
View
76 src/node_crypto.cc
@@ -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 {
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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);
@@ -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;
}
View
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-----
View
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-----
View
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 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(
@@ -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);
Something went wrong with that request. Please try again.