javascript - RSA 登录 node.js 并在 C++ 中验证

标签 javascript c++ node.js rsa node-rsa

我正在研究一种方法来保护我从 node.js 服务器发送到 C++ 应用程序的消息。

从 node.js ,我创建了一个 key 对。

我正在使用 node-rsa 读取 node.js 端的公钥(https://github.com/rzcoder/node-rsa)

var rsa =  new nodeRSA(publicKeyBuffer  ,{encryptionScheme :'pkcs1'})

由于我的消息可能很长,我在调用 encrypt 之前计算了消息的加盐 sha256。

const hash = crypto.createHash('sha256').update(message + config.signSalt).digest('hex')

这部分工作正常,因为我能够在 C++ 端生成完全相同的哈希。

那我调用node-rsa的encrypt函数生成一个缓冲区

const signature = rsa.encrypt(hash)

我尝试了各种编码,但是由于数据是通过 websocket(+ MsgPack 打包)发送的,所以二进制格式是一个不错的选择

在 C++ 方面,我首先阅读 char[] 的 key

const char keyStr[] = "-----BEGIN RSA PRIVATE KEY-----\n" ..........

BIO* bio = BIO_new_mem_buf(keyStr, (int)strlen(keyStr)); // -1: assume string is null terminated

m_rsaPrivKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);

if (!m_rsaPrivKey)
        LogOutSys("ERROR: Could not load PRIVATE KEY! PEM_write_bio_RSAPrivateKey FAILED: %s\n", ERR_error_string(ERR_get_error(), NULL));

key 被正确读取,之后,我从消息中计算哈希值,从 unsigned char 缓冲区中生成一个 std::string

std::string hash = sha256(msg.c_str());

std::string signatureStr(signature.begin(), signature.end());

char *decrypt;

int decryptLen;

decrypt = new char[RSA_size(m_rsaPrivKey)];

decryptLen = RSA_private_decrypt((int)msg.size() + 1, (unsigned char*)msg.c_str(), (unsigned char*)decrypt, m_rsaPrivKey, RSA_NO_PADDING /* RSA_PKCS1_OAEP_PADDING */ );

if (decryptLen == -1)
{
    char errStr[130];
    ERR_error_string(ERR_get_error(), errStr);
    LogOutSys("Rsa::decrypt - Error decrypting string ssl error %s", errStr);
}

for (int i = 0; i < decryptLen; i++)
{
    decryptData.push_back(decrypt[i]);
}

delete decrypt;

解密失败并出现以下错误

Rsa::decrypt - 解密字符串时出错 ssl error error:0406506C:lib(4):func(101):reason(108)

我尝试了各种编码和填充模式,但总是出错。

最佳答案

(假设你想在nodejs端使用私钥签名,在openssl端使用公钥验证)

要使用示例私钥对字符串 Test 进行签名,请使用例如:

NodeRSA=require('node-rsa');
key = new NodeRSA(null, {signingScheme: 'pkcs1-sha256'});
key.importKey('-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI2GkqoVj1l59MQh\nI/ZswgdnNG3o5XSmyeGgNdmTgQQ6cWCJcscCS6d3+nWFl3Xe7SxzKxo7pHMHTeJU\nGTZpLzW7fk5Y/ISWIr2Qsswpm8JUVOAUQVU/qwZYPr0ACCDQLGLaVByKFgvKnf5p\npkdroM63AFakn5YlCP+WM4ASuZyvAgMBAAECgYAUsBNIYZZu0fEBqoaDQyqpwmBb\noKvJ/YeNP8ofX/yADbr9DZqFlMRSWqt1+m1FgazRzpQCZa2IUw0DhJ+a4I1R6E30\nw7ZVWdVvWtkA70YGMaqB618fMR6SpTmzVGUjzQqk7Zim+uQVugTXEimC6/7sa7em\nLtVdjXuvOFOCVEeXwQJBAO/bN2q2u8YTBy9q4A34KoeeM8NX8zV3bRVhrB4eVcT0\ngpNNCrvjo2g5qKQs1fmLmjylSBihus0RjjJZTwxsffkCQQCXDRhh83OCtLfDEztO\nObu5BvVFcli76VEdw4EqzJtrddG77B43ggYYyJFxOuJHz+33oM4GtnHEiHkV9sRS\n47nnAkBcu8qPLZsnl4+9m3qIrBv1Vwr4SXa0gznffGXJNz096rLZNH4j6nzw/Ong\nn50S4BB/xf871rucMV9iw/i1+vQxAkEAjDBDKOVhlzVSN2Jp8Df02cxzZnixkfUA\nq7b+8lHjDODUPqztfmbWcbn0Ajq8OBnqqaA8lk5NWDGw74mOu79OkQJARDwRop7c\nfkd39rY/+an50uj2L4UJv1Jb+Yal5c0u37ACRnp+0n6qlcL0pj98UfW9H+oYFqwT\nKXzq4lptzgfQIg==\n-----END PRIVATE KEY-----\n', 'pkcs8');
signature=key.sign('Test', 'hex');

给出一个十六进制编码的签名:

'45f0c0672c8c07ecfe318b8ffa425c169ed2458ac6f0e4f1ffe0bcebec38cc3e59311858ed443d45f0dc81935ee0c490fa452b2f427d59a2c43fdd69f71f2b46d9e39072cb517b4afba5d5c66b26e14ca8a2900650b923fd77271deba84103d42c1f81619825d4987eeabc05401b0bc35bee08c59843aa94a535d2fb032b681b'

要使用 openssl 验证此签名,请使用例如:

#include <stdlib.h>
#include <stdio.h>

#include <openssl/pem.h>
#include <openssl/evp.h>

int main(void) {

    // Read public key from memory
    EVP_PKEY *pubKey=NULL;
    {
        BIO * pubKeyPemBio = BIO_new_mem_buf("-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCNhpKqFY9ZefTEISP2bMIHZzRt\n6OV0psnhoDXZk4EEOnFgiXLHAkund/p1hZd13u0scysaO6RzB03iVBk2aS81u35O\nWPyEliK9kLLMKZvCVFTgFEFVP6sGWD69AAgg0Cxi2lQcihYLyp3+aaZHa6DOtwBW\npJ+WJQj/ljOAErmcrwIDAQAB\n-----END PUBLIC KEY-----", -1);
        if(pubKeyPemBio==NULL) { printf("Error at line %i\n", __LINE__); return 0; }
        pubKey=PEM_read_bio_PUBKEY(pubKeyPemBio, &pubKey, NULL, NULL);
        BIO_free(pubKeyPemBio);
        if(pubKey==NULL) { printf("Error at line %i\n", __LINE__); return 0; }
    }

    // Verify signature
    {
        EVP_MD_CTX *ctx = EVP_MD_CTX_create();
        if(ctx==NULL) { printf("Error at line %i\n", __LINE__); return 0; }

        int ret=EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubKey);
        if(ret!=1) { printf("Error at line %i\n", __LINE__); return 0; }

        ret=EVP_DigestVerifyUpdate(ctx, "Test", 4);
        if(ret!=1) { printf("Error at line %i\n", __LINE__); return 0; }

        ret=EVP_DigestVerifyFinal(ctx, "\x45\xf0\xc0\x67\x2c\x8c\x07\xec\xfe\x31\x8b\x8f\xfa\x42\x5c\x16\x9e\xd2\x45\x8a\xc6\xf0\xe4\xf1\xff\xe0\xbc\xeb\xec\x38\xcc\x3e\x59\x31\x18\x58\xed\x44\x3d\x45\xf0\xdc\x81\x93\x5e\xe0\xc4\x90\xfa\x45\x2b\x2f\x42\x7d\x59\xa2\xc4\x3f\xdd\x69\xf7\x1f\x2b\x46\xd9\xe3\x90\x72\xcb\x51\x7b\x4a\xfb\xa5\xd5\xc6\x6b\x26\xe1\x4c\xa8\xa2\x90\x06\x50\xb9\x23\xfd\x77\x27\x1d\xeb\xa8\x41\x03\xd4\x2c\x1f\x81\x61\x98\x25\xd4\x98\x7e\xea\xbc\x05\x40\x1b\x0b\xc3\x5b\xee\x08\xc5\x98\x43\xaa\x94\xa5\x35\xd2\xfb\x03\x2b\x68\x1b", 128);
        if(ret!=1) { printf("Error at line %i\n", __LINE__); return 0; }
        EVP_MD_CTX_destroy(ctx);
    }
    EVP_PKEY_free(pubKey);

    printf("All OK!\n");
    return EXIT_SUCCESS;
}

一些(随机)笔记:

  • 上面的代码只是 nodejs 和 openssl 之间 RSA 签名/验证的工作概念验证。请不要期望它是一个安全的实现,具有正确的错误处理、安全的 key 长度、防止定时/侧信道攻击等。

  • 签名的字符串/缓冲区可以任意长,只要由 node-rsa

  • 执行散列
  • 编码的公钥/私钥可以在nodejs中使用例如key.exportKey('pkcs8-public')/key.exportKey('pkcs8')

  • 选择适合您的编码(我使用字符串和十六进制字符串,因为它们清晰易读)。您可能想使用缓冲区

祝你好运!

免责声明:我不是加密专家,所以请验证我的想法。


使用的私钥:

-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI2GkqoVj1l59MQh
I/ZswgdnNG3o5XSmyeGgNdmTgQQ6cWCJcscCS6d3+nWFl3Xe7SxzKxo7pHMHTeJU
GTZpLzW7fk5Y/ISWIr2Qsswpm8JUVOAUQVU/qwZYPr0ACCDQLGLaVByKFgvKnf5p
pkdroM63AFakn5YlCP+WM4ASuZyvAgMBAAECgYAUsBNIYZZu0fEBqoaDQyqpwmBb
oKvJ/YeNP8ofX/yADbr9DZqFlMRSWqt1+m1FgazRzpQCZa2IUw0DhJ+a4I1R6E30
w7ZVWdVvWtkA70YGMaqB618fMR6SpTmzVGUjzQqk7Zim+uQVugTXEimC6/7sa7em
LtVdjXuvOFOCVEeXwQJBAO/bN2q2u8YTBy9q4A34KoeeM8NX8zV3bRVhrB4eVcT0
gpNNCrvjo2g5qKQs1fmLmjylSBihus0RjjJZTwxsffkCQQCXDRhh83OCtLfDEztO
Obu5BvVFcli76VEdw4EqzJtrddG77B43ggYYyJFxOuJHz+33oM4GtnHEiHkV9sRS
47nnAkBcu8qPLZsnl4+9m3qIrBv1Vwr4SXa0gznffGXJNz096rLZNH4j6nzw/Ong
n50S4BB/xf871rucMV9iw/i1+vQxAkEAjDBDKOVhlzVSN2Jp8Df02cxzZnixkfUA
q7b+8lHjDODUPqztfmbWcbn0Ajq8OBnqqaA8lk5NWDGw74mOu79OkQJARDwRop7c
fkd39rY/+an50uj2L4UJv1Jb+Yal5c0u37ACRnp+0n6qlcL0pj98UfW9H+oYFqwT
KXzq4lptzgfQIg==
-----END PRIVATE KEY-----

对应的公钥:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCNhpKqFY9ZefTEISP2bMIHZzRt
6OV0psnhoDXZk4EEOnFgiXLHAkund/p1hZd13u0scysaO6RzB03iVBk2aS81u35O
WPyEliK9kLLMKZvCVFTgFEFVP6sGWD69AAgg0Cxi2lQcihYLyp3+aaZHa6DOtwBW
pJ+WJQj/ljOAErmcrwIDAQAB
-----END PUBLIC KEY-----

关于javascript - RSA 登录 node.js 并在 C++ 中验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46522770/

相关文章:

C++在没有sqrt函数循环故障的情况下找到平方根

javascript - 使用 ORM Sequelize/node.js 操作数据

node.js - 为 Let's encrypt 设置 certbot 时要选择哪个软件和系统?

c++ - 中途反转/交换

c++ - Qt: QHostAddress: 没有这样的文件或目录

javascript - 我有一个 .log 对象文件,如何将它们转换为 JSON 对象以便在 Javascript 中进行迭代?

javascript - 添加或删除渐进增强类的最佳方法是什么?

javascript - 使用 Express 和 NodeJS 构建 REST API 的最佳实践

javascript - 如何将 "reverse engineer"一个简单的 react 组件转换为纯 jquery

javascript - 页面没有在 Angularjs 中重定向