从两个包(jsencrypt 和 node-rsa)获得相同的结果

Getting the same result from two packages (jsencrypt and node-rsa)

我们在应用程序的客户端使用 jsencrypt 来加密字符串(密码)并将其传递给处理第 3 方 API 授权的后端。

像这样:

const n = "_key"

export const encrypt2 = async(message: string): Promise < string > => {
  const JSEncrypt = (await
    import ('jsencrypt')).default;
  const jsencrypt = new JSEncrypt({
    default_key_size: '1024',
    default_public_exponent: "010001",
    log: false,
  });
  jsencrypt.setPublicKey(n);
  return jsencrypt.encrypt(message) || message;
};

由于产品变更,我们现在希望在后端(nodejs)进行加密。经过研究,我们发现 node-rsa 可以得到相同的结果。使用以下代码:

export const encrypt = async(message: string): Promise < string > => {
  return new NodeRSA().generateKeyPair(1024, '010001').importKey(n, 'public').encrypt(message).toString('base64')
};

鉴于 n 仍然相同,我们的第 3 方身份验证服务拒绝此实现。

问题与目标:

如何从这两种实现中获得有效的加密密码?

P.S,由于 window usage

,我无法在服务器端使用 jsencrypt

JSEncrypt 仅支持 PKCS#1 v1.5 填充(参见 here), while Node-RSA supports PKCS#1 v1.5 padding and OAEP, where OAEP is the default (see here)。不同的填充是两个代码不兼容的原因。

为了在 Node-RSA 代码中使用 PKCS#1 v1.5 填充,必须明确指定,例如在导入密钥时的第三个参数中:

const NodeRSA = require('node-rsa');

var x509 = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunF5aDa6HCfLMMI/MZLT
5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1E
bYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQw
KtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1x
H9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4
OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4i
GwIDAQAB
-----END PUBLIC KEY-----`;

var message = "The quick brown fox jumps over the lazy dog";
var privateKey = new NodeRSA(x509, 'public', { encryptionScheme: 'pkcs1' }); // specify PKCS#1 v1.5 padding

var ciphertext = privateKey.encrypt(message).toString('base64');
console.log("Ciphertext (Node-RSA): ", ciphertext);

可能的输出是:

Ciphertext (Node-RSA):  Lcfy7G0RYhDRCeWAvUXji4b4uFzrhpSsS9G158QcBiRy+Uae5UctQoA6MMgzZsjVWmf4Jlj50eZYTwxP94mGrTY0gMy9+FvFlMXDu0cMJ04qArOU4B+ZHnt3FElDjYvW0fnvbstYJFWd9OQjlLJc2mxSGPKEj0IQi3p8GQXK9qYfJx7MeONA+nfHdWYQgplicnz1crk+aiGf0zlJMAKdOoTodAtQuqVh9bNYyJcpPnzIt4eqUVW+wLEoXNqu14ibz1u5yMpCNX/+jBKug2fYaXv4ImsUnhqFEAok9sHDGVzZkihnYdDxnWaSocpG3db6UNhpGBoJSvwxdEuI86YgSQ==

测试:

由于 OAEP 和 PKCS#1v1.5 填充都不确定,因此无法通过比较生成的密文进行测试。相反,可以检查 Node-RSA 密文是否可以用该 JSEncrypt 代码解密,该 JSEncrypt 代码也可以解密 JSEncrypt 密文。如果是这种情况,加密代码在功能上是相同的:

var x509 = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunF5aDa6HCfLMMI/MZLT
5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1E
bYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQw
KtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1x
H9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4
OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4i
GwIDAQAB
-----END PUBLIC KEY-----`;

var pkcs8 = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6cXloNrocJ8sw
wj8xktPmEOTfTgJT7KkUwWIGOjBB1QxApgdn5+SUHsakvEJq3Fgn+FnuuN8cfcqW
rbT9jURtgNGinJNJ+StPM/PCxfhSv+XbkK11CV2EcJMyDB/8S/9u4E2ht/N1kT4O
F2/mVDAq2MjjeUMq8CLmQR63ZMXB8lwmsGJEl4Rwt9WBZNcZFCfuCeBYrKRS7mtL
zd4BTXEf0UuiNB/KJrz38TKSI47v/dRbB34wBNn0cuNLHb8t/eDaOvzV6J8SZgOW
uL85mng6Fm77QGjUteWgJN76+YhDZgJfsRq1Q67JAy3ZXDHi5X538DcM/o+0wYEq
kXxK3iIbAgMBAAECggEASlJj0ExIomKmmBhG8q8SM1s2sWG6gdQMjs6MEeluRT/1
c2v79cq2Dum5y/+UBl8x8TUKPKSLpCLs+GXkiVKgHXrFlqoN+OYQArG2EUWzuODw
czdYPhhupBXwR3oX4g41k/BsYfQfZBVzBFEJdWrIDLyAUFWNlfdGIj2BTiAoySfy
qmamvmW8bsvc8coiGlZ28UC85/Xqx9wOzjeGoRkCH7PcTMlc9F7SxSthwX/k1VBX
mNOHa+HzGOgO/W3k1LDqJbq2wKjZTW3iVEg2VodjxgBLMm0MueSGoI6IuaZSPMyF
EM3gGvC2+cDBI2SL/amhiTUa/VDlTVw/IKbSuar9uQKBgQDd76M0Po5Lqh8ZhQ3o
bhFqkfO5EBXy7HUL15cw51kVtwF6Gf/J2HNHjwsg9Nb0eJETTS6bbuVd9bn884Jo
RS986nVTFNZ4dnjEgKjjQ8GjfzdkpbUxsRLWiIxuOQSpIUZGdMi2ctTTtspvMsDs
jRRYdYIQCe/SDsdHGT3vcUCybwKBgQDXDz6iVnY84Fh5iDDVrQOR4lYoxCL/ikCD
JjC6y1mjR0eVFdBPQ4j1dDSPU9lahBLby0VyagQCDp/kxQOl0z2zBLRI4I8jUtz9
/9KW6ze7U7dQJ7OTfumd5I97OyQOG9XZwKUkRgfyb/PAMBSUSLgosi38f+OC3IN3
qlvHFzvxFQKBgQCITpUDEmSczih5qQGIvolN1cRF5j5Ey7t7gXbnXz+Umah7kJpM
IvdyfMVOAXJABgi8PQwiBLM0ySXo2LpARjXLV8ilNUggBktYDNktc8DrJMgltaya
j3HNd2IglD5rjfc2cKWRgOd7/GlKcHaTEnbreYhfR2sWrWLxJOyoMfuVWwKBgFal
CbMV6qU0LfEo8aPlBN8ttVDPVNpntP4h0NgxPXgPK8Pg+gA1UWSy4MouGg/hzkdH
aj9ifyLlCX598a5JoT4S0x/ZeVHd/LNI8mtjcRzD6cMde7gdFbpLb5NSjIAyrsIA
X4hxvpnqiOYRePkVIz0iLGziiaMbfMwlkrxvm/LRAoGBALPRbtSbE2pPgvOHKHTG
Pr7gKbmsWVbOcQA8rG801T38W/UPe1XtynMEjzzQ29OaVeQwvUN9+DxFXJ6Yvwj6
ih4Wdq109i7Oo1fDnMczOQN9DKch2eNAHrNSOMyLDCBm++wbyHAsS2T0VO8+gzLA
BviZm5AFCQWfke4LZo5mOS10
-----END PRIVATE KEY-----`;

var message = "The quick brown fox jumps over the lazy dog";

var publicKey = new JSEncrypt();
publicKey.setPublicKey(x509);
var ciphertext = publicKey.encrypt(message);
console.log("Ciphertext (JSEncrypt): ", ciphertext);
 
var privateKey = new JSEncrypt();   
privateKey.setPrivateKey(pkcs8);
var decrypted = privateKey.decrypt(ciphertext);
console.log("Decrypted (JSEncrypt): ", decrypted);

var ciphertextFromNodeRSA = `Lcfy7G0RYhDRCeWAvUXji4b4uFzrhpSsS9G158QcBiRy+Uae5UctQoA6MMgzZsjVWmf4Jlj50eZYTwxP94mGrTY0gMy9+FvFlMXDu0cMJ04qArOU4B+ZHnt3FElDjYvW0fnvbstYJFWd9OQjlLJc2mxSGPKEj0IQi3p8GQXK9qYfJx7MeONA+nfHdWYQgplicnz1crk+aiGf0zlJMAKdOoTodAtQuqVh9bNYyJcpPnzIt4eqUVW+wLEoXNqu14ibz1u5yMpCNX/+jBKug2fYaXv4ImsUnhqFEAok9sHDGVzZkihnYdDxnWaSocpG3db6UNhpGBoJSvwxdEuI86YgSQ==`;

decrypted = privateKey.decrypt(ciphertextFromNodeRSA);
console.log("Decrypted (Node-RSA): ", decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/3.2.1/jsencrypt.min.js"></script>

可能的输出:

Ciphertext (JSEncrypt):  WWcnfnlAB7Quf/jMQIyD9AgOpewJQdPgA/UQFqIu9PvoMhMUgjo+4LnxeKjnFGqhG9mgctqmVKW1EGjEsnJiThbgiC6VNIwufYloXgJcqTrT78p7UiTO9+k7z1H8iCy4HkgXtiOkoBpGI094zr/iMnTyiTMfMKjopygM969pgDPsaXqWeiE2MX4ZjxFcNP9QpYoZpdS7PGxDBj31kbXvttOB/1Oc57IqOepF2N8n0jp9KzIBnAf+CDjzUFbUh8t23feuktoL/o/MYqqySmY/hkvkkmtNd4LDLtTA0NjcU7Es99ue90xNcuYKCEybzaZ20pp769PQjZHu/lU+T7zlrg== 
Decrypted (JSEncrypt):  The quick brown fox jumps over the lazy dog 
Decrypted (Node-RSA):  The quick brown fox jumps over the lazy dog

证明两个加密代码在功能上是等价的。