### sign.sign(private_key, [output_format])
Calculates the signature on all the updated data passed through the
-sign. `private_key` is a string containing the PEM encoded private
-key for signing.
+sign.
+
+`private_key` can be an object or a string. If `private_key` is a string, it is
+treated as the key with no passphrase.
+
+`private_key`:
+
+* `key` : A string holding the PEM encoded private key
+* `passphrase` : A string of passphrase for the private key
Returns the signature in `output_format` which can be `'binary'`,
`'hex'` or `'base64'`. If no encoding is provided, then a buffer is
Sign.prototype.update = Hash.prototype.update;
-Sign.prototype.sign = function(key, encoding) {
- encoding = encoding || exports.DEFAULT_ENCODING;
- var ret = this._binding.sign(toBuf(key));
+Sign.prototype.sign = function(options, encoding) {
+ if (!options)
+ throw new Error('No key provided to sign');
+
+ var key = options.key || options;
+ var passphrase = options.passphrase || null;
+ var ret = this._binding.sign(toBuf(key), null, passphrase);
+ encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
ret = ret.toString(encoding);
}
+static int CryptoPemCallback(char *buf, int size, int rwflag, void *u) {
+ if (u) {
+ size_t buflen = static_cast<size_t>(size);
+ size_t len = strlen(static_cast<const char*>(u));
+ len = len > buflen ? buflen : len;
+ memcpy(buf, u, len);
+ return len;
+ }
+
+ return 0;
+}
+
+
void ThrowCryptoErrorHelper(unsigned long err, bool is_type_error) {
HandleScope scope(node_isolate);
char errmsg[128];
if (!bio)
return NULL;
- X509 * x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ X509 * x509 = PEM_read_bio_X509(bio, NULL, CryptoPemCallback, NULL);
if (!x509) {
BIO_free_all(bio);
return NULL;
String::Utf8Value passphrase(args[1]);
- EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, NULL, NULL,
+ EVP_PKEY* key = PEM_read_bio_PrivateKey(bio,
+ NULL,
+ CryptoPemCallback,
len == 1 ? NULL : *passphrase);
if (!key) {
int ret = 0;
X509 *x = NULL;
- x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
+ x = PEM_read_bio_X509_AUX(in, NULL, CryptoPemCallback, NULL);
if (x == NULL) {
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
ctx->extra_certs = NULL;
}
- while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
+ while ((ca = PEM_read_bio_X509(in, NULL, CryptoPemCallback, NULL))) {
r = SSL_CTX_add_extra_chain_cert(ctx, ca);
if (!r) {
if (!bio)
return;
- X509_CRL *x509 = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
+ X509_CRL *x509 = PEM_read_bio_X509_CRL(bio, NULL, CryptoPemCallback, NULL);
if (x509 == NULL) {
BIO_free_all(bio);
return;
}
- X509 *x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
+ X509 *x509 = PEM_read_bio_X509(bp, NULL, CryptoPemCallback, NULL);
if (x509 == NULL) {
BIO_free_all(bp);
}
-bool Sign::SignFinal(unsigned char** md_value,
- unsigned int *md_len,
- const char* key_pem,
- int key_pem_len) {
- if (!initialised_)
+bool Sign::SignFinal(const char* key_pem,
+ int key_pem_len,
+ const char* passphrase,
+ unsigned char** sig,
+ unsigned int *sig_len) {
+ if (!initialised_) {
+ ThrowError("Sign not initalised");
return false;
+ }
BIO* bp = NULL;
EVP_PKEY* pkey = NULL;
+ bool fatal = true;
+
bp = BIO_new(BIO_s_mem());
+ if (bp == NULL)
+ goto exit;
+
if (!BIO_write(bp, key_pem, key_pem_len))
- return false;
+ goto exit;
- pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
+ pkey = PEM_read_bio_PrivateKey(bp,
+ NULL,
+ CryptoPemCallback,
+ const_cast<char*>(passphrase));
if (pkey == NULL)
- return 0;
+ goto exit;
+
+ if (EVP_SignFinal(&mdctx_, *sig, sig_len, pkey))
+ fatal = false;
- EVP_SignFinal(&mdctx_, *md_value, md_len, pkey);
- EVP_MD_CTX_cleanup(&mdctx_);
initialised_ = false;
- EVP_PKEY_free(pkey);
- BIO_free_all(bp);
+
+ exit:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ if (bp != NULL)
+ BIO_free_all(bp);
+
+ EVP_MD_CTX_cleanup(&mdctx_);
+
+ if (fatal) {
+ unsigned long err = ERR_get_error();
+ if (err) {
+ ThrowCryptoError(err);
+ } else {
+ ThrowError("PEM_read_bio_PrivateKey");
+ }
+ return false;
+ }
+
return true;
}
unsigned char* md_value;
unsigned int md_len;
+ unsigned int len = args.Length();
enum encoding encoding = BUFFER;
- if (args.Length() >= 2) {
+ if (len >= 2 && args[1]->IsString()) {
encoding = ParseEncoding(args[1]->ToString(), BUFFER);
}
+ String::Utf8Value passphrase(args[2]);
+
ASSERT_IS_BUFFER(args[0]);
- ssize_t len = Buffer::Length(args[0]);
+ size_t buf_len = Buffer::Length(args[0]);
char* buf = Buffer::Data(args[0]);
md_len = 8192; // Maximum key size is 8192 bits
md_value = new unsigned char[md_len];
- bool r = sign->SignFinal(&md_value, &md_len, buf, len);
+ bool r = sign->SignFinal(buf,
+ buf_len,
+ len >= 3 && !args[2]->IsNull() ? *passphrase : NULL,
+ &md_value,
+ &md_len);
if (!r) {
delete[] md_value;
md_value = NULL;
// Split this out into a separate function once we have more than one
// consumer of public keys.
if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
- pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
+ pkey = PEM_read_bio_PUBKEY(bp, NULL, CryptoPemCallback, NULL);
if (pkey == NULL)
goto exit;
} else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
- RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
+ RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, CryptoPemCallback, NULL);
if (rsa) {
pkey = EVP_PKEY_new();
if (pkey)
goto exit;
} else {
// X.509 fallback
- x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
+ x509 = PEM_read_bio_X509(bp, NULL, CryptoPemCallback, NULL);
if (x509 == NULL)
goto exit;
void SignInit(const char* sign_type);
bool SignUpdate(const char* data, int len);
- bool SignFinal(unsigned char** md_value,
- unsigned int *md_len,
- const char* key_pem,
- int key_pem_len);
+ bool SignFinal(const char* key_pem,
+ int key_pem_len,
+ const char* passphrase,
+ unsigned char** sig,
+ unsigned int *sig_len);
protected:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
--- /dev/null
+-----BEGIN DSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,2E8DE7F5BD338C4118488E8D640FC695
+
+7jnL7kBITpnfjHc4DiRF+9d0M9QKdQmiL9N7Bj52XC+L0jZTwfNld3xi6fQ1GNle
+RKrrgSgEYXxf+RJ9Nz/BOttYWnIyAWSswFIjm1vGjpoYTH6H/wFg1QSoZUfINO2I
+3p4Y+cYVWOgSAYegzT5sdWTKDJrRUUfYFThmdQk0uO3s8B7urQuVlEtHr02OuPAj
+hRiWaBtxkttBWA+x8dgpgAbjHlZIWv1fj0EAKaaVehaNzEyK+nyS1a816ssDV3t8
+YMOZdCdKgzbBr5T3Zf83hdzpmhagBNZve1P0kJEgGdydiRWSyTxotOt5AGsRSvza
+A9PVk8V/U6U1B18hACxGV4wiKCMQDAsHfo+BrVoZBvVBlpW4dfcsIEtQqwu18x2b
+wIW5Qc2zXFFL6P+eqfdZ0ZRdfsClX6/GYOO5Z4oy8iAQSuD1UdaG6Psy84U7LU8g
+++OcbEcw8UnKjKVJU+zBC4QmhxUSUiOQwcgeFQuMIEMtprUKztgm/oPClAMTY/pm
+FGxLZS59owVWkrN9Oc4ccw+6Zt6mDxH9cnHv+nkGlcK9pcD+gU1MVXUfuby+DNbI
+4iYqUoYZdb9gpWQ/VrXMX63NydXzE+vMB9BxOlgfw3b6BrFCUAuyH1FiIAlGeAjP
+LZa06WiOayeu6Lm8rzeu/Cjbe1pYzK9cyX3JxSGJxipPeO4URZ5+hyqBMyCCCUq8
+EVFcfwgkdQaeVeBUdxJyRXfuBQmlgJF0Ixlkw29StpI2dAbNrtcSAIwbsxDInK4b
+6ItdadW+0nCRAxdVbGt6oQIPqpjbmtVkqj+m1yAic1xYc7Kd2xngGdtOMefKefcw
++7d7E82ljPycHDG2SNENsFV9TNENdNlaP1A1HQy+f/1YkLZHfNLQrUf1+BRR7oHI
+N0ACLF6jgZ9MFelB64774veUTLvcrmYKIX7TnV25kw28ZIQ8StmIt9YJ+Mq+x6DC
+32JbRBbwbHm598fCrfr471xw/SM1/OnPefVhJSQ6223IfjuSWG0Snvjo7mHbaduz
+xWW6ApT7/iilanZs8uKBuPaEtwu3CmJcdgj0tTUuXb5ivY3M0dD/ZLSQliqb49iU
+64LX0/kRvkUZ6nJqPA4nlKPfGebo6H0V4oX7XF/gm74=
+-----END DSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,9D916E00476DFF9E70FA4BA9E3A6CB0E
+
+oj0VC35ShSEqlfJ0rLGgkqJCyIK+mXSsa/X/xAur+lI/RVOVTWd7oQQGTdI/0rLX
+PdQR02Na3X9Rptezh6J04PfMGeFysxdT6RpC+rkHRPVbN0F4TqxSNNXzkwK70+EF
+dSuDMyVKv9YN4wWDf0g6VKe4ShAH/sqICQBrVyzWyYLvH/hwZmZZ1QEab6ylIKtb
+EJunwu9BxVVA04bbuATKkKjJOqDn0fG8hb4bYbyD02dJwgLePzzn36F31kcBCEHI
+tESlD3RsS+EtfpfgPkplXNOhqYzkD9auDb7Zy+ZwL20fjnJb75OSGu8gOg3KTljt
+mApZOg0nJ5Jk9ATAdyzyVSFOM1Hhcw12ws06Dq9KRnXgO6bbuadLTFRDdvSYDFvD
+ijUb+97UolQfYIXQMqXli3EIvHr7CTWe/3mpoDgK1mtr0+923Bm97XgE7KSr0L46
+n5QpNjCZf1vbXldNmW+TRifiJMgtVdS7x0N4vqDPNEe+FelVv3U4Pz3HIOtFuWLr
+ZCxlgVxJY4IsyYlV0ItQjIv8fJiAyemZdO2lA9K6h0eEF+9Apr3i79JGWUi74p5D
+Ooak4le0Va9O34f6FxCGn/a54A6bhKu24Ub/0gr/e4WRa7693euEdgIAZXhtMu2Z
+taU5SKjjXPzjmRCM2kINHTCENlaU4oFzTmj3TYY/jdKyNP1bHa07NhlomladkIHK
+GD6HaYkcbuwvh8hOPsopSwuS+NqjnGPq9Vv4ecBC+9veDEmpIE1iR6FK9Hjrre88
+kLoMQNmA+vuc8jG4/FIHM3SauQiR1ZJ6+zkz97kcmOf+X7LRaS4j6lfFR6qHiJ6y
+-----END RSA PRIVATE KEY-----
'ascii');
var rsaKeyPem = fs.readFileSync(common.fixturesDir + '/test_rsa_privkey.pem',
'ascii');
+var rsaKeyPemEncrypted = fs.readFileSync(
+ common.fixturesDir + '/test_rsa_privkey_encrypted.pem', 'ascii');
+var dsaPubPem = fs.readFileSync(common.fixturesDir + '/test_dsa_pubkey.pem',
+ 'ascii');
+var dsaKeyPem = fs.readFileSync(common.fixturesDir + '/test_dsa_privkey.pem',
+ 'ascii');
+var dsaKeyPemEncrypted = fs.readFileSync(
+ common.fixturesDir + '/test_dsa_privkey_encrypted.pem', 'ascii');
+
try {
var credentials = crypto.createCredentials(
rsaVerify.update(rsaPubPem);
assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
+// Test RSA key signing/verification with encrypted key
+rsaSign = crypto.createSign('RSA-SHA1');
+rsaSign.update(rsaPubPem);
+assert.doesNotThrow(function() {
+ var signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' };
+ rsaSignature = rsaSign.sign(signOptions, 'hex');
+});
+assert.equal(rsaSignature,
+ '5c50e3145c4e2497aadb0eabc83b342d0b0021ece0d4c4a064b7c' +
+ '8f020d7e2688b122bfb54c724ac9ee169f83f66d2fe90abeb95e8' +
+ 'e1290e7e177152a4de3d944cf7d4883114a20ed0f78e70e25ef0f' +
+ '60f06b858e6af42a2f276ede95bbc6bc9a9bbdda15bd663186a6f' +
+ '40819a7af19e577bb2efa5e579a1f5ce8a0d4ca8b8f6');
+
+rsaVerify = crypto.createVerify('RSA-SHA1');
+rsaVerify.update(rsaPubPem);
+assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
+
+rsaSign = crypto.createSign('RSA-SHA1');
+rsaSign.update(rsaPubPem);
+assert.throws(function() {
+ var signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' };
+ rsaSign.sign(signOptions, 'hex');
+});
//
// Test RSA signing and verification
// Test DSA signing and verification
//
(function() {
- var privateKey = fs.readFileSync(
- common.fixturesDir + '/test_dsa_privkey.pem');
+ var input = 'I AM THE WALRUS';
+
+ // DSA signatures vary across runs so there is no static string to verify
+ // against
+ var sign = crypto.createSign('DSS1');
+ sign.update(input);
+ var signature = sign.sign(dsaKeyPem, 'hex');
+
+ var verify = crypto.createVerify('DSS1');
+ verify.update(input);
+
+ assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
+})();
- var publicKey = fs.readFileSync(
- common.fixturesDir + '/test_dsa_pubkey.pem');
+//
+// Test DSA signing and verification with encrypted key
+//
+(function() {
var input = 'I AM THE WALRUS';
+ var sign = crypto.createSign('DSS1');
+ sign.update(input);
+ assert.throws(function() {
+ sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex');
+ });
+
// DSA signatures vary across runs so there is no static string to verify
// against
var sign = crypto.createSign('DSS1');
sign.update(input);
- var signature = sign.sign(privateKey, 'hex');
+
+ var signature;
+ assert.doesNotThrow(function() {
+ var signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' };
+ signature = sign.sign(signOptions, 'hex');
+ });
var verify = crypto.createVerify('DSS1');
verify.update(input);
- assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true);
+ assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
})();