diff options
author | seishun <vvnicholas@gmail.com> | 2014-05-05 17:35:28 +0300 |
---|---|---|
committer | Fedor Indutny <fedor@indutny.com> | 2014-08-11 22:00:34 +0400 |
commit | 42bda05af8d628db90f3ee64fa2dd89973cb1e03 (patch) | |
tree | f322c4605779c7055e407221e3d1ab25f5dd1015 /src/node_crypto.cc | |
parent | 93f3b640d06df5e123e7ede90e40df06258d9d47 (diff) | |
download | node-new-42bda05af8d628db90f3ee64fa2dd89973cb1e03.tar.gz |
crypto: add RSA encryption
Reviewed-By: Fedor Indutny <fedor@indutny.com>
Diffstat (limited to 'src/node_crypto.cc')
-rw-r--r-- | src/node_crypto.cc | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 4abc513dce..03c2d25b25 100644 --- a/src/node_crypto.cc +++ b/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 |