使用Node.js的C++ native扩展可以使用Node.js的高效性,而使用OpenSSL提供了安全加密通信的功能。在下面的攻略中,我将向您展示如何正确使用Node.js的C++模块将OpenSSL添加到您的项目中。
步骤
步骤1:设置OpenSSL
从OpenSSL官方网站下载和安装所需的软件包。请根据您的操作系统选择正确的软件包。
# Ubuntu
sudo apt-get install libssl-dev
# macOS
brew install openssl
步骤2:创建C++ 模块
在您的Node.js代码中添加C++模块的支持,我们需要创建一个新的C++模块。可以使用Node.js扩展工具进行这个操作。其中,binding.gyp
文件是配置扩展的
{
"targets": [
{
"target_name": "myaddon",
"sources": [ "myaddon.cc" ],
"include_dirs": [
"<!(node -e \"require('nan')\")",
"/usr/local/opt/openssl/include/"
],
"libraries": [ "-L/usr/local/opt/openssl/lib", "-lssl", "-lcrypto" ]
}
]
}
其中:
target_name
: 模块的名称sources
: 源代码include_dirs
: 头文件的所在目录libraries
: 链接的库,包括ssl和crypto库
步骤3:实现模块
在myaddon.cc
的文件中,执行如下操作实现模块
#include <nan.h>
#include <node.h>
#include <v8.h>
#include <string.h>
#include <openssl/crypto.h>
namespace demo {
NAN_METHOD(getOpenSSLVersionString) {
Nan::HandleScope scope;
const char* version = OpenSSL_version(OPENSSL_VERSION);
info.GetReturnValue().Set(Nan::New(version).ToLocalChecked());
}
NAN_MODULE_INIT(init) {
Nan::SetMethod(target, "getOpenSSLVersionString", getOpenSSLVersionString);
}
NODE_MODULE(addon, init)
}
该代码定义了一个名为getOpenSSLVersionString
的函数,可以使用Node.js
调用它并返回OPENSSL_VERSION
的字符串版本。 最后一行将模块导出为addon
。
步骤4:构建模块
在binding.gyp
所在的目录下运行以下命令
$ node-gyp configure
$ node-gyp build
或者,为了在Mac OSX上使用OpenSSL,请执行以下命令
$ node-gyp configure --openssl-include-dir=/usr/local/opt/openssl/include --openssl-lib-dir=/usr/local/opt/openssl/lib
$ node-gyp build
最后一行,将生成的二进制文件在终端中的输出位置。在Mac OS X上,默认情况下,它在“build/Release/”目录下。
步骤5:完成
现在,您已准备好在JavaScript中引用模块了。您可以通过以下方式引用模块。
const addon = require('./build/Release/addon.node');
console.log(addon.getOpenSSLVersionString());
示例
示例1: 实现加密/解密
在myaddon.cc
文件中增加对OpenSSL加密/解密功能的实现
#include <nan.h>
#include <node.h>
#include <v8.h>
#include <string.h>
#include <openssl/crypto.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
namespace demo {
NAN_METHOD(EncryptData) {
Nan::HandleScope scope;
auto cipher_algo = EVP_aes_128_cbc();
auto key = "key";
auto iv = "iv";
auto text = "hello world";
int len = strlen(text);
// -------------------- ENCRYPTION --------------------
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
// Set cipher and key length
EVP_EncryptInit_ex(ctx, cipher_algo, nullptr, (const unsigned char*)key, (const unsigned char*)iv);
BIO *bio_base = BIO_new(BIO_s_mem());
BIO *bio_cipher = BIO_new(BIO_f_cipher());
// Set cipher as BIO filter
BIO_set_cipher(bio_cipher, cipher_algo, key, iv, 1);
// Push BIO filter on top of memory BIO
BIO_push(bio_cipher, bio_base);
// Write data to memory BIO
BIO_write(bio_base, text, len);
// Flush memory BIO with data
BIO_flush(bio_base);
char *mem_buf = nullptr;
auto mem_len = BIO_get_mem_data(bio_base, &mem_buf);
// Clean up
BIO_free_all(bio_cipher);
EVP_CIPHER_CTX_free(ctx);
printf("Encrypted: %s\n", mem_buf);
info.GetReturnValue().Set(Nan::New(mem_buf).ToLocalChecked());
}
NAN_METHOD(DecryptData) {
Nan::HandleScope scope;
auto cipher_algo = EVP_aes_128_cbc();
auto key = "key";
auto iv = "iv";
auto cipher_text = "encrypted text";
// -------------------- DECRYPTION --------------------
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, cipher_algo, nullptr, (const unsigned char*)key, (const unsigned char*)iv);
BIO *bio_base = BIO_new(BIO_s_mem());
BIO *bio_cipher = BIO_new(BIO_f_cipher());
BIO_set_cipher(bio_cipher, cipher_algo, key, iv, 0);
BIO_push(bio_cipher, bio_base);
BIO_write(bio_base, cipher_text, strlen(cipher_text));
BIO_flush(bio_base);
char plain_buf[BUFSIZ] = {0};
auto plain_len = BIO_read(bio_base, plain_buf, BUFSIZ);
char *mem_buf = nullptr;
auto mem_len = BIO_get_mem_data(bio_base, &mem_buf);
EVP_CIPHER_CTX_free(ctx);
BIO_free_all(bio_cipher);
printf("Decrypted: %s\n", plain_buf);
info.GetReturnValue().Set(Nan::New(plain_buf).ToLocalChecked());
}
NAN_MODULE_INIT(init) {
Nan::SetMethod(target, "getOpenSSLVersionString", getOpenSSLVersionString);
Nan::SetMethod(target, "encryptData", EncryptData);
Nan::SetMethod(target, "decryptData", DecryptData);
}
NODE_MODULE(addon, init)
}
在node.js
中测试,代码如下
const addon = require('./build/Release/addon.node');
// Test encrypt / decrypt
var cipher_text = addon.encryptData();
console.log(cipher_text);
var plain_text = addon.decryptData(cipher_text);
console.log(plain_text);
示例2: 实现签名/验证
在myaddon.cc
文件中增加对OpenSSL签名/验证功能的实现
#include <nan.h>
#include <node.h>
#include <v8.h>
#include <string.h>
#include <openssl/crypto.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/md5.h>
#include <openssl/bn.h>
#include <openssl/safestack.h>
namespace demo {
char * private_key_= NULL;
char * public_key_= NULL;
EVP_PKEY * private_key = NULL;
EVP_PKEY * public_key = NULL;
// Example:
// openssl genrsa -out privkey.pem 2048
// openssl rsa -pubout -in privkey.pem -out pubkey.pem
int loadKeys() {
FILE *fp_pri = fopen("config/privkey.pem", "rb");
if (fp_pri == NULL) {
fprintf(stderr, "Failed to open privkey.pem\n");
return 0;
}
fseek(fp_pri, 0, SEEK_END);
int len_pri = ftell(fp_pri);
fseek(fp_pri, 0, SEEK_SET);
private_key_ = (char*)calloc(1, len_pri + 1);
fread(private_key_, 1, len_pri, fp_pri);
fclose(fp_pri);
FILE* fp_pub = fopen("config/pubkey.pem", "rb");
if (fp_pub == NULL) {
fprintf(stderr, "Failed to open pubkey.pem\n");
return 0;
}
fseek(fp_pub, 0, SEEK_END);
int len_pub = ftell(fp_pub);
fseek(fp_pub, 0, SEEK_SET);
public_key_ = (char*)calloc(1, len_pub + 1);
fread(public_key_, 1, len_pub, fp_pub);
fclose(fp_pub);
private_key = EVP_PKEY_new();
public_key = EVP_PKEY_new();
BIO* bp_private_key = BIO_new_mem_buf(private_key_, len_pri);
BIO_read(bp_private_key, private_key_, len_pri);
PEM_read_bio_PrivateKey(bp_private_key, &private_key, nullptr, nullptr);
BIO_free(bp_private_key);
BIO* bp_public_key = BIO_new_mem_buf(public_key_, len_pub);
BIO_read(bp_public_key, public_key_, len_pub);
PEM_read_bio_PUBKEY(bp_public_key, &public_key, nullptr, nullptr);
BIO_free(bp_public_key);
}
NAN_METHOD(getSign) {
Nan::HandleScope scope;
char *data = "Test message";
unsigned int l;
EVP_MD_CTX *mdctx;
const EVP_MD *md;
unsigned char signBuf[4096];
unsigned int signLen;
memset(signBuf, 0x00, sizeof(signBuf));
signLen = 0;
md = EVP_get_digestbyname("md5");
if (!md) {
printf("Unknown message digest\n");
return;
}
mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(mdctx, md, nullptr);
EVP_DigestUpdate(mdctx, data, strlen(data));
EVP_DigestSignInit(mdctx, nullptr, md, nullptr, private_key);
EVP_DigestSign(mdctx, signBuf, &signLen, (unsigned char *)data, strlen(data));
EVP_MD_CTX_destroy(mdctx);
char* encoded = new char[3 * signLen + 1];
for (unsigned i = 0; i < signLen; i++) {
sprintf(encoded + 2 * i, "%02x", signBuf[i]);
}
info.GetReturnValue().Set(Nan::New(encoded).ToLocalChecked());
}
NAN_METHOD(getVerify) {
Nan::HandleScope scope;
auto sign = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // getSign calculate it.
char *data = "Test message";
unsigned char signBuf[4096];
unsigned int signLen;
auto len = strlen(sign);
unsigned convData;
for (unsigned i = 0; i < len/2; i++) {
sscanf(sign + i * 2, "%2X", &convData);
signBuf[i] = convData;
}
signLen = len / 2;
EVP_MD_CTX *mdctx;
const EVP_MD *md;
md = EVP_get_digestbyname("md5");
if (!md) {
printf("Unknown message digest\n");
return;
}
mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(mdctx, md, nullptr);
EVP_DigestUpdate(mdctx, data, strlen(data));
EVP_DigestVerifyInit(mdctx, nullptr, md, nullptr, public_key);
EVP_DigestVerify(mdctx, signBuf, signLen, (unsigned char *)data, strlen(data));
EVP_MD_CTX_destroy(mdctx);
info.GetReturnValue().Set(Nan::New(true));
}
NAN_MODULE_INIT(init) {
Nan::SetMethod(target, "getOpenSSLVersionString", getOpenSSLVersionString);
Nan::SetMethod(target, "encryptData", EncryptData);
Nan::SetMethod(target, "decryptData", DecryptData);
Nan::SetMethod(target, "getSign", getSign);
Nan::SetMethod(target, "getVerify", getVerify);
// load keys once; or add function to reload keys.
loadKeys();
}
NODE_MODULE(addon, init)
}
在node.js
中测试,代码如下
const addon = require('./build/Release/addon.node');
// Test sign / verify
var sign = addon.getSign();
var ret = addon.getVerify(sign);
console.log(ret);
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:如何正确使用Nodejs 的 c++ module 链接到 OpenSSL - Python技术站