crypto: add RSA encryption
Reviewed-By: Fedor Indutny <fedor@indutny.com>
This commit is contained in:
parent
93f3b640d0
commit
42bda05af8
@ -593,6 +593,22 @@ Exports the encoded public key from the supplied SPKAC.
|
|||||||
|
|
||||||
Exports the encoded challenge associated with the SPKAC.
|
Exports the encoded challenge associated with the SPKAC.
|
||||||
|
|
||||||
|
## crypto.publicEncrypt(public_key, buffer)
|
||||||
|
|
||||||
|
Encrypts `buffer` with `public_key`. Only RSA is currently supported.
|
||||||
|
|
||||||
|
## crypto.privateDecrypt(private_key, buffer)
|
||||||
|
|
||||||
|
Decrypts `buffer` with `private_key`.
|
||||||
|
|
||||||
|
`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
|
||||||
|
|
||||||
## crypto.DEFAULT_ENCODING
|
## crypto.DEFAULT_ENCODING
|
||||||
|
|
||||||
The default encoding to use for functions that can take either strings
|
The default encoding to use for functions that can take either strings
|
||||||
|
@ -355,6 +355,16 @@ Verify.prototype.verify = function(object, signature, sigEncoding) {
|
|||||||
return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding));
|
return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.publicEncrypt = function(object, buffer) {
|
||||||
|
return binding.publicEncrypt(toBuf(object), buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.privateDecrypt = function(options, buffer) {
|
||||||
|
var key = options.key || options;
|
||||||
|
var passphrase = options.passphrase || null;
|
||||||
|
return binding.privateDecrypt(toBuf(key), buffer, passphrase);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exports.createDiffieHellman = exports.DiffieHellman = DiffieHellman;
|
exports.createDiffieHellman = exports.DiffieHellman = DiffieHellman;
|
||||||
|
@ -65,6 +65,9 @@ static const char PUBLIC_KEY_PFX[] = "-----BEGIN PUBLIC KEY-----";
|
|||||||
static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1;
|
static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1;
|
||||||
static const char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----";
|
static const char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----";
|
||||||
static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1;
|
static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1;
|
||||||
|
static const char CERTIFICATE_PFX[] = "-----BEGIN CERTIFICATE-----";
|
||||||
|
static const int CERTIFICATE_PFX_LEN = sizeof(CERTIFICATE_PFX) - 1;
|
||||||
|
|
||||||
static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
|
static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
|
||||||
| ASN1_STRFLGS_ESC_MSB
|
| ASN1_STRFLGS_ESC_MSB
|
||||||
| XN_FLAG_SEP_MULTILINE
|
| XN_FLAG_SEP_MULTILINE
|
||||||
@ -3544,6 +3547,139 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <PublicKeyCipher::Operation operation,
|
||||||
|
PublicKeyCipher::EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init,
|
||||||
|
PublicKeyCipher::EVP_PKEY_cipher_t EVP_PKEY_cipher>
|
||||||
|
bool PublicKeyCipher::Cipher(const char* key_pem,
|
||||||
|
int key_pem_len,
|
||||||
|
const char* passphrase,
|
||||||
|
const unsigned char* data,
|
||||||
|
int len,
|
||||||
|
unsigned char** out,
|
||||||
|
size_t* out_len) {
|
||||||
|
EVP_PKEY* pkey = NULL;
|
||||||
|
EVP_PKEY_CTX* ctx = NULL;
|
||||||
|
BIO* bp = NULL;
|
||||||
|
X509* x509 = NULL;
|
||||||
|
bool fatal = true;
|
||||||
|
|
||||||
|
bp = BIO_new(BIO_s_mem());
|
||||||
|
if (bp == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (!BIO_write(bp, key_pem, key_pem_len))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
// Check if this is a PKCS#8 or RSA public key before trying as X.509 and
|
||||||
|
// private key.
|
||||||
|
if (operation == kEncrypt &&
|
||||||
|
strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
|
||||||
|
pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
|
||||||
|
if (pkey == NULL)
|
||||||
|
goto exit;
|
||||||
|
} else if (operation == kEncrypt &&
|
||||||
|
strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
|
||||||
|
RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
|
||||||
|
if (rsa) {
|
||||||
|
pkey = EVP_PKEY_new();
|
||||||
|
if (pkey)
|
||||||
|
EVP_PKEY_set1_RSA(pkey, rsa);
|
||||||
|
RSA_free(rsa);
|
||||||
|
}
|
||||||
|
if (pkey == NULL)
|
||||||
|
goto exit;
|
||||||
|
} else if (operation == kEncrypt &&
|
||||||
|
strncmp(key_pem, CERTIFICATE_PFX, CERTIFICATE_PFX_LEN) == 0) {
|
||||||
|
x509 = PEM_read_bio_X509(bp, NULL, CryptoPemCallback, NULL);
|
||||||
|
if (x509 == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
pkey = X509_get_pubkey(x509);
|
||||||
|
if (pkey == NULL)
|
||||||
|
goto exit;
|
||||||
|
} else {
|
||||||
|
pkey = PEM_read_bio_PrivateKey(bp,
|
||||||
|
NULL,
|
||||||
|
CryptoPemCallback,
|
||||||
|
const_cast<char*>(passphrase));
|
||||||
|
if (pkey == NULL)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = EVP_PKEY_CTX_new(pkey, NULL);
|
||||||
|
if (!ctx)
|
||||||
|
goto exit;
|
||||||
|
if (EVP_PKEY_cipher_init(ctx) <= 0)
|
||||||
|
goto exit;
|
||||||
|
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
|
||||||
|
goto exit;
|
||||||
|
if (EVP_PKEY_cipher(ctx, NULL, out_len, data, len) <= 0)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
*out = new unsigned char[*out_len];
|
||||||
|
|
||||||
|
if (EVP_PKEY_cipher(ctx, *out, out_len, data, len) <= 0)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
fatal = false;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (pkey != NULL)
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
if (bp != NULL)
|
||||||
|
BIO_free_all(bp);
|
||||||
|
if (ctx != NULL)
|
||||||
|
EVP_PKEY_CTX_free(ctx);
|
||||||
|
|
||||||
|
return !fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <PublicKeyCipher::Operation operation,
|
||||||
|
PublicKeyCipher::EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init,
|
||||||
|
PublicKeyCipher::EVP_PKEY_cipher_t EVP_PKEY_cipher>
|
||||||
|
void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args.GetIsolate());
|
||||||
|
HandleScope scope(env->isolate());
|
||||||
|
|
||||||
|
ASSERT_IS_BUFFER(args[0]);
|
||||||
|
char* kbuf = Buffer::Data(args[0]);
|
||||||
|
ssize_t klen = Buffer::Length(args[0]);
|
||||||
|
|
||||||
|
ASSERT_IS_BUFFER(args[1]);
|
||||||
|
char* buf = Buffer::Data(args[1]);
|
||||||
|
ssize_t len = Buffer::Length(args[1]);
|
||||||
|
|
||||||
|
String::Utf8Value passphrase(args[2]);
|
||||||
|
|
||||||
|
unsigned char* out_value = NULL;
|
||||||
|
size_t out_len = -1;
|
||||||
|
|
||||||
|
bool r = Cipher<operation, EVP_PKEY_cipher_init, EVP_PKEY_cipher>(
|
||||||
|
kbuf,
|
||||||
|
klen,
|
||||||
|
args.Length() >= 3 && !args[2]->IsNull() ? *passphrase : NULL,
|
||||||
|
reinterpret_cast<const unsigned char*>(buf),
|
||||||
|
len,
|
||||||
|
&out_value,
|
||||||
|
&out_len);
|
||||||
|
|
||||||
|
if (out_len <= 0 || !r) {
|
||||||
|
delete[] out_value;
|
||||||
|
out_value = NULL;
|
||||||
|
out_len = 0;
|
||||||
|
if (!r) {
|
||||||
|
return ThrowCryptoError(env,
|
||||||
|
ERR_get_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(
|
||||||
|
Buffer::New(env, reinterpret_cast<char*>(out_value), out_len));
|
||||||
|
delete[] out_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DiffieHellman::Initialize(Environment* env, Handle<Object> target) {
|
void DiffieHellman::Initialize(Environment* env, Handle<Object> target) {
|
||||||
Local<FunctionTemplate> t = FunctionTemplate::New(env->isolate(), New);
|
Local<FunctionTemplate> t = FunctionTemplate::New(env->isolate(), New);
|
||||||
|
|
||||||
@ -4730,6 +4866,16 @@ void InitCrypto(Handle<Object> target,
|
|||||||
NODE_SET_METHOD(target, "getSSLCiphers", GetSSLCiphers);
|
NODE_SET_METHOD(target, "getSSLCiphers", GetSSLCiphers);
|
||||||
NODE_SET_METHOD(target, "getCiphers", GetCiphers);
|
NODE_SET_METHOD(target, "getCiphers", GetCiphers);
|
||||||
NODE_SET_METHOD(target, "getHashes", GetHashes);
|
NODE_SET_METHOD(target, "getHashes", GetHashes);
|
||||||
|
NODE_SET_METHOD(target,
|
||||||
|
"publicEncrypt",
|
||||||
|
PublicKeyCipher::Cipher<PublicKeyCipher::kEncrypt,
|
||||||
|
EVP_PKEY_encrypt_init,
|
||||||
|
EVP_PKEY_encrypt>);
|
||||||
|
NODE_SET_METHOD(target,
|
||||||
|
"privateDecrypt",
|
||||||
|
PublicKeyCipher::Cipher<PublicKeyCipher::kDecrypt,
|
||||||
|
EVP_PKEY_decrypt_init,
|
||||||
|
EVP_PKEY_decrypt>);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace crypto
|
} // namespace crypto
|
||||||
|
@ -559,6 +559,35 @@ class Verify : public SignBase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PublicKeyCipher {
|
||||||
|
public:
|
||||||
|
typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX *ctx);
|
||||||
|
typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX *ctx,
|
||||||
|
unsigned char *out, size_t *outlen,
|
||||||
|
const unsigned char *in, size_t inlen);
|
||||||
|
|
||||||
|
enum Operation {
|
||||||
|
kEncrypt,
|
||||||
|
kDecrypt
|
||||||
|
};
|
||||||
|
|
||||||
|
template <Operation operation,
|
||||||
|
EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init,
|
||||||
|
EVP_PKEY_cipher_t EVP_PKEY_cipher>
|
||||||
|
static bool Cipher(const char* key_pem,
|
||||||
|
int key_pem_len,
|
||||||
|
const char* passphrase,
|
||||||
|
const unsigned char* data,
|
||||||
|
int len,
|
||||||
|
unsigned char** out,
|
||||||
|
size_t* out_len);
|
||||||
|
|
||||||
|
template <Operation operation,
|
||||||
|
EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init,
|
||||||
|
EVP_PKEY_cipher_t EVP_PKEY_cipher>
|
||||||
|
static void Cipher(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
};
|
||||||
|
|
||||||
class DiffieHellman : public BaseObject {
|
class DiffieHellman : public BaseObject {
|
||||||
public:
|
public:
|
||||||
~DiffieHellman() {
|
~DiffieHellman() {
|
||||||
|
@ -823,6 +823,40 @@ var p = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' +
|
|||||||
var bad_dh = crypto.createDiffieHellman(p, 'hex');
|
var bad_dh = crypto.createDiffieHellman(p, 'hex');
|
||||||
assert.equal(bad_dh.verifyError, constants.DH_NOT_SUITABLE_GENERATOR);
|
assert.equal(bad_dh.verifyError, constants.DH_NOT_SUITABLE_GENERATOR);
|
||||||
|
|
||||||
|
// Test RSA encryption/decryption
|
||||||
|
(function() {
|
||||||
|
var input = 'I AM THE WALRUS';
|
||||||
|
var bufferToEncrypt = new Buffer(input);
|
||||||
|
|
||||||
|
var encryptedBuffer = crypto.publicEncrypt(rsaPubPem, bufferToEncrypt);
|
||||||
|
|
||||||
|
var decryptedBuffer = crypto.privateDecrypt(rsaKeyPem, encryptedBuffer);
|
||||||
|
assert.equal(input, decryptedBuffer.toString());
|
||||||
|
|
||||||
|
var decryptedBufferWithPassword = crypto.privateDecrypt({
|
||||||
|
key: rsaKeyPemEncrypted,
|
||||||
|
passphrase: 'password'
|
||||||
|
}, encryptedBuffer);
|
||||||
|
assert.equal(input, decryptedBufferWithPassword.toString());
|
||||||
|
|
||||||
|
encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt);
|
||||||
|
|
||||||
|
decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
|
||||||
|
assert.equal(input, decryptedBuffer.toString());
|
||||||
|
|
||||||
|
encryptedBuffer = crypto.publicEncrypt(keyPem, bufferToEncrypt);
|
||||||
|
|
||||||
|
decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
|
||||||
|
assert.equal(input, decryptedBuffer.toString());
|
||||||
|
|
||||||
|
assert.throws(function() {
|
||||||
|
crypto.privateDecrypt({
|
||||||
|
key: rsaKeyPemEncrypted,
|
||||||
|
passphrase: 'wrong'
|
||||||
|
}, encryptedBuffer);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
// Test RSA key signing/verification
|
// Test RSA key signing/verification
|
||||||
var rsaSign = crypto.createSign('RSA-SHA1');
|
var rsaSign = crypto.createSign('RSA-SHA1');
|
||||||
var rsaVerify = crypto.createVerify('RSA-SHA1');
|
var rsaVerify = crypto.createVerify('RSA-SHA1');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user