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

yizhihongxing

使用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日

相关文章

  • Node.js+Express配置入门教程

    对于“Node.js+Express配置入门教程”的详细讲解,我将分为以下几个部分: Node.js简介 Express框架介绍 Node.js+Express项目搭建 配置路由及返回数据 示例说明1:返回JSON格式数据 示例说明2:返回静态HTML页面 接下来,我将对每个部分进行详细的讲解。 1. Node.js简介 Node.js是一个基于Chrome…

    node js 2023年6月8日
    00
  • Node.js安装详细步骤教程(Windows版)详解

    Node.js安装详细步骤教程(Windows版)详解 介绍 Node.js是一款基于Chrome V8 JavaScript引擎的JavaScript运行环境,可以在服务器端运行JavaScript,也可以用来开发桌面应用程序。下面是Node.js在Windows系统上安装的详细步骤。 步骤 1. 下载安装包 打开Node.js的官方网站 https://…

    node js 2023年6月7日
    00
  • node.js事件循环机制及与js区别详解

    Node.js事件循环机制及与JS区别详解 事件循环机制 事件循环是 Node.js 的重要组成部分,它是 Node.js 实现非阻塞 I/O 的核心。Node.js 中的事件循环采用的是基于 libuv 库的事件循环,它由以下几个部分组成: Timers(定时器阶段):处理 setTimeout() 和 setInterval() 的回调函数(这些回调函数…

    node js 2023年6月8日
    00
  • 如何用node优雅地打印全链路日志

    下面是详细的攻略。 1. 需求分析 在开发过程中,我们需要记录应用程序的全链路日志以便于排查问题和进行性能优化。要实现全链路日志,需要收集每个请求的相关信息,如请求方法、请求参数、响应状态码、响应时间、错误类型等信息。这些信息需要保留到一个日志文件中。 2. 策略设计 要优雅地打印全链路日志,我们需要使用以下策略: 定义一个格式化文本日志中间件,将收集的日志…

    node js 2023年6月8日
    00
  • Babel 入门教程学习笔记

    Babel 入门教程学习笔记 什么是 Babel Babel 是一个广泛使用的 JavaScript 编译器,它能将 ECMAScript 2015+ 代码转换为向后兼容的 JavaScript 代码。这意味着,我们可以使用最新的 JavaScript 语言特性开发项目,同时也可以保证代码在现有的浏览器和环境中执行正确。 安装 Babel 安装 Babel …

    node js 2023年6月8日
    00
  • node.js require() 源码解读

    当使用Node.js编写JavaScript应用程序时,要使用模块化编程是非常重要的。在 Node.js 中,要使用模块化编程,我们需要用到 require() 函数。本文将解读 require() 的源代码,理解 require() 的实现原理。 理解 Node.js 中的 Require() 函数 Node.js 中的 require() 函数用于引入模…

    node js 2023年6月8日
    00
  • Node.js中path.join()优势例举分析

    “Node.js中path.join()优势例举分析”攻略: 什么是path.join()? 在Node.js中,path模块是对文件路径进行操作的模块,常用的操作有:路径拼接、解析、返回绝对路径、返回相对路径等。其中,path.join()是将路径片段通过特定的分隔符连接起来形成路径的方法。 语法格式: path.join([…paths]) …p…

    node js 2023年6月8日
    00
  • 多版本node的安装和切换详细操作步骤

    下面是多版本node的安装和切换详细操作步骤的完整攻略: 安装nvm nvm是管理node版本的工具,我们需要先安装它。以下步骤适用于MacOS和Linux系统,对于Windows系统请自行查找对应的安装方法。 打开终端或命令行界面,输入以下命令下载nvm安装脚本: curl -o- https://raw.githubusercontent.com/nvm…

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