我正在研究一种方法来保护我从 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/