Browse Source

crypto: add RSA encryption

Reviewed-By: Fedor Indutny <fedor@indutny.com>
v0.11.14-release
seishun 11 years ago
committed by Fedor Indutny
parent
commit
42bda05af8
  1. 16
      doc/api/crypto.markdown
  2. 10
      lib/crypto.js
  3. 146
      src/node_crypto.cc
  4. 29
      src/node_crypto.h
  5. 34
      test/simple/test-crypto.js

16
doc/api/crypto.markdown

@ -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

10
lib/crypto.js

@ -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;

146
src/node_crypto.cc

@ -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

29
src/node_crypto.h

@ -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() {

34
test/simple/test-crypto.js

@ -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…
Cancel
Save