c++ 和 node.js 之间的 RSA 加密
RSA encryption between c++ and node.js
我必须通过网络 (websocket) 发送一些加密数据
我使用以下 node.js 模块生成了密钥对:
https://github.com/juliangruber/keypair
我的 public 密钥如下所示:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAlUiMDQsBgj5P/T86w/eg9MXUj8M4WMVihP8YzmDxMqCFb7D+w4N/1XcxWxQT
....
Wo+SRCsr6npfp1ctDhMtkXIeNT4lKf3qUGhP5tbx/TreaNF/d8zCeinGR/KeBGadMwIDAQAB
-----END RSA PUBLIC KEY-----
在 C++ 代码中,我通过 char*
读取 public 密钥生成了一个 RSA class
const char rsaKey1[] = "-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAlUiMDQsBgj5P/T86w/eg9MXUj8M4WMVihP8YzmDxMqCFb7D+w4N/1XcxWxQT\n"
....
"Wo+SRCsr6npfp1ctDhMtkXIeNT4lKf3qUGhP5tbx/TreaNF/d8zCeinGR/KeBGadMwIDAQAB\n"
"-----END RSA PUBLIC KEY-----\n";
BIO* bio = BIO_new_mem_buf( rsaKey1, strlen(rsaKey1));
m_rsaPubKey = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL);
使用 m_rsaPubKey 签名,我可以用加密数据生成 std::vector 的无符号字符
std::vector<u8> Rsa::encrypt(std::string & msg)
{
std::vector<u8> encryptedData;
char *encrypt = new char[RSA_size(m_rsaPubKey)];
int encryptLen;
if (encryptLen = RSA_public_encrypt(msg.size() + 1, (unsigned
char*)msg.c_str(), (unsigned char*)encrypt, m_rsaPubKey,
RSA_PKCS1_OAEP_PADDING) == -1)
{
LogOutSys("error encoding string");
}
for (u32 i = 0; i < strlen(encrypt); i++)
{
encryptedData.push_back(encrypt[i]);
}
delete encrypt;
return encryptedData;
}
我在读取 public 密钥或加密我的数据时没有收到任何错误,所以我认为加密是正确的。
然后数据通过 websocket 并通过 node.js
接收
私钥是这样读的:
var rsa = new RSA(fs.readFileSync("./rsa-keys/sj_private_1.pem"),
{encryptionScheme :'pkcs8'})
和解码
var decrypted = rsa.decrypt(data)
其中数据是长度和内容相同的缓冲区(通过 websocket 发送时没有损坏)
c++ 方面:
encrypted len 256, first bytes 117 125 58 109
节点大小:
Buffer(256) [117, 125, 58, 109, 38, 229, 7, 189, …]
rsa.decrypt 生成异常:
TypeError: Cannot read property 'length' of null
我尝试了几个 encryptionScheme 选项(包括默认的,但总是得到相同的错误或不正确的密钥或数据
由于 OAEP 中的随机填充,解决加密问题有时会有些棘手。
要进一步排除故障,请使用以下清单来解决潜在问题:
- 确保您在两端使用相同的加密机制。在您的 C++ 代码中,您使用的是 RSA_PKCS1_OAEP_PADDING 但问题中的 JavaScript 行并未说明您在那里使用的机制。
- 确保机制在 C++ 和 Node 库中的实现方式相同。在两种实现中使用相同的散列方法和 MGF1(掩码生成函数)至关重要。这是我在职业生涯中见过的最典型的失败点之一。
- 由于您使用的是字节数组,请确保字节顺序没有任何问题。换句话说,确保两端在字节顺序方面使用相同的语言(自学:https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Data/endian.html)。
我必须通过网络 (websocket) 发送一些加密数据
我使用以下 node.js 模块生成了密钥对:
https://github.com/juliangruber/keypair
我的 public 密钥如下所示:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAlUiMDQsBgj5P/T86w/eg9MXUj8M4WMVihP8YzmDxMqCFb7D+w4N/1XcxWxQT
....
Wo+SRCsr6npfp1ctDhMtkXIeNT4lKf3qUGhP5tbx/TreaNF/d8zCeinGR/KeBGadMwIDAQAB
-----END RSA PUBLIC KEY-----
在 C++ 代码中,我通过 char*
读取 public 密钥生成了一个 RSA classconst char rsaKey1[] = "-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAlUiMDQsBgj5P/T86w/eg9MXUj8M4WMVihP8YzmDxMqCFb7D+w4N/1XcxWxQT\n"
....
"Wo+SRCsr6npfp1ctDhMtkXIeNT4lKf3qUGhP5tbx/TreaNF/d8zCeinGR/KeBGadMwIDAQAB\n"
"-----END RSA PUBLIC KEY-----\n";
BIO* bio = BIO_new_mem_buf( rsaKey1, strlen(rsaKey1));
m_rsaPubKey = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL);
使用 m_rsaPubKey 签名,我可以用加密数据生成 std::vector 的无符号字符
std::vector<u8> Rsa::encrypt(std::string & msg)
{
std::vector<u8> encryptedData;
char *encrypt = new char[RSA_size(m_rsaPubKey)];
int encryptLen;
if (encryptLen = RSA_public_encrypt(msg.size() + 1, (unsigned
char*)msg.c_str(), (unsigned char*)encrypt, m_rsaPubKey,
RSA_PKCS1_OAEP_PADDING) == -1)
{
LogOutSys("error encoding string");
}
for (u32 i = 0; i < strlen(encrypt); i++)
{
encryptedData.push_back(encrypt[i]);
}
delete encrypt;
return encryptedData;
}
我在读取 public 密钥或加密我的数据时没有收到任何错误,所以我认为加密是正确的。
然后数据通过 websocket 并通过 node.js
接收私钥是这样读的:
var rsa = new RSA(fs.readFileSync("./rsa-keys/sj_private_1.pem"),
{encryptionScheme :'pkcs8'})
和解码
var decrypted = rsa.decrypt(data)
其中数据是长度和内容相同的缓冲区(通过 websocket 发送时没有损坏)
c++ 方面:
encrypted len 256, first bytes 117 125 58 109
节点大小:
Buffer(256) [117, 125, 58, 109, 38, 229, 7, 189, …]
rsa.decrypt 生成异常:
TypeError: Cannot read property 'length' of null
我尝试了几个 encryptionScheme 选项(包括默认的,但总是得到相同的错误或不正确的密钥或数据
由于 OAEP 中的随机填充,解决加密问题有时会有些棘手。
要进一步排除故障,请使用以下清单来解决潜在问题:
- 确保您在两端使用相同的加密机制。在您的 C++ 代码中,您使用的是 RSA_PKCS1_OAEP_PADDING 但问题中的 JavaScript 行并未说明您在那里使用的机制。
- 确保机制在 C++ 和 Node 库中的实现方式相同。在两种实现中使用相同的散列方法和 MGF1(掩码生成函数)至关重要。这是我在职业生涯中见过的最典型的失败点之一。
- 由于您使用的是字节数组,请确保字节顺序没有任何问题。换句话说,确保两端在字节顺序方面使用相同的语言(自学:https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Data/endian.html)。