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.
|
||||
|
||||
## 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
|
||||
|
||||
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));
|
||||
};
|
||||
|
||||
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;
|
||||
|
@ -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 char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----";
|
||||
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
|
||||
| ASN1_STRFLGS_ESC_MSB
|
||||
| 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) {
|
||||
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, "getCiphers", GetCiphers);
|
||||
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
|
||||
|
@ -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 {
|
||||
public:
|
||||
~DiffieHellman() {
|
||||
|
@ -823,6 +823,40 @@ var p = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' +
|
||||
var bad_dh = crypto.createDiffieHellman(p, 'hex');
|
||||
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
|
||||
var rsaSign = crypto.createSign('RSA-SHA1');
|
||||
var rsaVerify = crypto.createVerify('RSA-SHA1');
|
||||
|
Loading…
x
Reference in New Issue
Block a user