I used the same when I needed to encrypt / decrypt data in Nodejs addon.
As I understand it, the classes from node_crypto.h are used to create my own bindings in Nodejs, I could not use them in my addon.
Then I tried to use OpenSSL from Nodejs, but could not do it, because OpenSSL is statically linked to the Nodejs executable.
After that, I tried calling JavaScript code from C ++ and finally got the following solution - calling Nodejs functions from C ++ code:
using namespace v8; // persistent handle for the crypto module static Persistent<Object> node_crypto; // Addon startup procedure void Init(Local<Object> exports, Local<Object> module) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); // get `require` function Local<Function> require = module->Get(String::NewFromUtf8(isolate, "require")).As<Function>(); // call require('crypto') Local<Value> args[] = { String::NewFromUtf8(isolate, "crypto") }; Local<Object> crypto = require->Call(module, 1, args).As<Object>(); // store crypto module in persistent handle for further use node_crypto.Reset(isolate, crypto); } NODE_MODULE(addon, Init); // must be invoked in then Node main thread since the function uses V8 API std::string encrypt(std::string const& key, std::string const& text) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); // get local handle from persistent Local<Object> crypto = Local<Object>::New(isolate, node_crypto); // get `createCipher` function from the crypto module Local<Function> createCipher = crypto->Get(String::NewFromUtf8(isolate, "createCipher")).As<Function>(); // call crypto.createCipher("aes256", key) Local<Value> create_args[] = { String::NewFromUtf8(isolate, "aes256"), String::NewFromUtf8(isolate, key.c_str()) }; Local<Object> cipher = createCipher->Call(crypto, 2, create_args).As<Object>(); // get update and final functions from the crypto module Local<Function> update = cipher->Get(String::NewFromUtf8(isolate, "update")).As<Function>(); Local<Function> final = cipher->Get(String::NewFromUtf8(isolate, "final")).As<Function>(); // buf1 = cipher.update(text), buf2 = cipher.final() Local<Value> update_args[] = { node::Buffer::New(isolate, text.data(), text.size()) }; Local<Value> buf1 = update->Call(cipher, 1, update_args); Local<Value> buf2 = final->Call(cipher, 0, nullptr); // concatenate update and final buffers into result string char const* const data1 = node::Buffer::Data(buf1); char const* const data2 = node::Buffer::Data(buf2); size_t const size1 = node::Buffer::Length(buf1); size_t const size2 = node::Buffer::Lenght(buf2); std::string result; result.reserve(size1 + size2); result.append(data1, size1); result.append(data2, size2); return result; } std::string decrypt(std::string const& key, std::string const& text) { // similar as in encrypt, use createDecipher instead }
As you can see, the C ++ code with the V8 API is pretty detailed. In a real project, I used the utility functions from my v8pp library to get object properties and call functions with data conversion into V8 handles.