如何正确使用Nodejs 的 c++ module 链接到 OpenSSL

使用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技术站

(0)
上一篇 2023年6月8日
下一篇 2023年6月8日

相关文章

  • JavaScript使用正则表达式获取全部分组内容的方法示例

    首先,我们需要先了解什么是正则表达式,正则表达式是一种用来匹配字符串文本的特殊模式,利用这种模式,我们可以通过匹配和搜索来进行字符串处理。 下面是使用正则表达式获取全部分组内容的方法示例,具体步骤如下: 1. 创建正则表达式对象 首先,我们需要创建一个正则表达式对象,用于匹配和搜索字符串。 let reg = /正则表达式/; 上述代码中的正则表达式可以根据…

    node js 2023年6月8日
    00
  • 详解如何实现一个简单的Node.js脚手架

    详解如何实现一个简单的Node.js脚手架 什么是脚手架 脚手架是指为了快速搭建一个项目框架或是基础代码而提供的一套工具链、库和模板的集合。它可以帮助开发者快速创建出项目模板或基础代码,让开发者只需关注业务代码的实现,而不用花费时间来搭建项目框架。 实现一个简单的Node.js脚手架 第一步:创建项目 首先需要创建一个名为simple-node-cli的文件…

    node js 2023年6月8日
    00
  • 前端必会的nodejs知识工具模块使用示例详解

    前端必会的nodejs知识工具模块使用示例详解 什么是Node.js Node.js是一个基于Chrome V8引擎的JavaScript运行环境。Node.js使得JavaScript可以脱离浏览器在服务器端运行。它拥有丰富的API和生态系统,可以帮助我们轻松地开发Web应用程序、命令行工具和后端服务。 NPM:Node Package Manager N…

    node js 2023年6月7日
    00
  • NodeJs下的测试框架Mocha的简单介绍

    下面我就为你详细讲解NodeJs下的测试框架Mocha的简单介绍。 Mocha简介 Mocha是一个基于Node.js的JavaScript测试框架,可以在服务器端运行测试脚本,也可以在浏览器中使用。它提供了丰富的方法和API来进行测试,包括测试用例的编写、测试覆盖率的分析、异步代码的测试等。Mocha最重要的特点是其灵活性,可以搭配各种断言库(Assert…

    node js 2023年6月8日
    00
  • JavaScript异步队列进行try catch时的问题解决

    JavaScript中的异步操作很常见,例如通过ajax请求获取数据,或者使用setTimeout等函数延时执行代码。在异步操作中,代码不会按照原来的顺序依次执行,而是先执行后续的代码,异步操作完成后再回来执行该操作后面的代码。这种机制带来了很多便利,同时也带来了不少问题,其中包括try catch无法捕获异步代码中的错误。 以下是解决该问题的完整攻略: 1…

    node js 2023年6月8日
    00
  • NodeJS与Mysql的交互示例代码

    下面我就来详细讲解NodeJS与Mysql的交互示例代码的完整攻略。 前置知识 在学习NodeJS与Mysql的交互之前,需要先掌握以下知识: NodeJS基础知识:包括NodeJS的安装、常用API、事件循环等知识。 Mysql基础知识:包括Mysql的安装、数据库创建、数据表创建等知识。 Mysql NodeJS驱动模块:NodeJS可以使用第三方模块来…

    node js 2023年6月8日
    00
  • JavaScript图片处理与合成总结

    当涉及到JavaScript图片处理与合成时,我们可以使用许多工具和库,但是本文将介绍如何使用原生JavaScript来完成这个任务。 步骤一:加载图片 首先,我们需要加载所有需要处理的图片。我们一般使用Image对象来完成这个任务。 在以下示例中,我们加载两个图片: const image1 = new Image(); const image2 = ne…

    node js 2023年6月8日
    00
  • 简述pm2常用命令集合及配置文件说明

    下面我给你详细讲解“简述PM2常用命令集合及配置文件说明”的完整攻略。 一、PM2常用命令集合 在使用PM2时,经常需要用到一些常用命令,以下是一些常见命令: 1. pm2 start 启动一个进程启动文件。示例: pm2 start index.js 2. pm2 list 显示所有已经启动的进程列表,示例: pm2 list 3. pm2 restart…

    node js 2023年6月8日
    00
合作推广
合作推广
分享本页
返回顶部