[英]RSA encryption with Java and decryption with JavaScript
我正在尝试使用 Java (使用javax.crypto.Cipher
)加密并使用 JavaScript (使用crypto.subtle
)解密。 我正在做的是,我让 JavaScript 端生成密钥对,然后通过以下方式将公钥发送到 Java 端:
$(window).on("load", function () {
const enc = new TextEncoder();
const dec = new TextDecoder();
crypto.subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 1024,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["encrypt", "decrypt"]
).then(function ({ privateKey, publicKey }) {
crypto.subtle.exportKey("spki", publicKey).then(function (spki) {
const strPublicKey = spkiToString(spki);
// SEND THE PUBLIC KEY TO THE SERVER (JAVA)
});
});
});
function spkiToString(keydata) {
var keydataS = arrayBufferToString(keydata);
return window.btoa(keydataS);
}
function arrayBufferToString(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return binary;
}
服务器使用公钥进行加密:
try {
String publicKey = ""// this will come from the JS side
String message = "encrypt me"//
byte[] publicBytes = Base64.getDecoder().decode(publicKey).getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"),
PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, pubKey, oaepParams);
return Base64.getEncoder().encode(cipher.doFinal(message));
} catch (Exception e) {
e.printStacktrace();
}
return new byte[0];
然后将结果值返回给 Javascript 端,因此它可以解密消息:
const encryptedToken = "" // this will be obtained from the server
crypto.subtle.decrypt({
name: "RSA-OAEP",
hash: { name: "SHA-256" }
},
privateKey,
enc.encode(atob(encryptedToken))
).then(function (result) {
console.log("decrypted", dec.decode(result))
}).catch(function (e) {
console.log(e);
})
当 Javascript 尝试解密时,它会抛出一个没有消息的DOMException
(检查附加图像)。
我做错了什么? 提前谢谢你。
问题是最后一个 JavaScript 代码片段的decrypt()
方法中的enc.encode(atob(encryptedToken))
行。 UTF-8 编码会破坏数据,从而阻止成功解密。 如果更改此设置,则解密工作如下所示。
下面的 JavaScript 代码与问题中的第一个代码段基本相同,只是以 PKCS8 格式导出私钥:
crypto.subtle.generateKey( { name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, true, ["encrypt", "decrypt"] ).then(function ({ privateKey, publicKey }) { crypto.subtle.exportKey("spki", publicKey).then(function (spki) { const strPublicKey = spkiToString(spki); console.log(strPublicKey.replace(/(.{56})/g,'$1\n')); crypto.subtle.exportKey("pkcs8", privateKey).then(function (pkcs8) { const strPrivateKey = spkiToString(pkcs8); console.log(strPrivateKey.replace(/(.{56})/g,'$1\n')); }); }); }); function spkiToString(keydata) { var keydataS = arrayBufferToString(keydata); return window.btoa(keydataS); } function arrayBufferToString(buffer) { var binary = ''; var bytes = new Uint8Array(buffer); var len = bytes.byteLength; for (var i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return binary; }
此代码用于生成例如以下 X.509/SPKI 格式的公钥:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1F8+EvG9XP8jSXItV89QtlYy/5Z+arMegvMwsasS5IIUdr4b4eE2FGoDalaqyAxWOg/pBkzfBWAQkhuKz3i14OsBYQl1QDDm3yfmI498SsE7tZyrENCfTGrPwoCrQmEwTWOCfIBCh+mGRAUTgcsQO/g8pIFglF3QTTzlu3n0KhQIDAQAB
和PKCS8格式的私钥:
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALUXz4S8b1c/yNJci1Xz1C2VjL/ln5qsx6C8zCxqxLkghR2vhvh4TYUagNqVqrIDFY6D+kGTN8FYBCSG4rPeLXg6wFhCXVAMObfJ+Yjj3xKwTu1nKsQ0J9Mas/CgKtCYTBNY4J8gEKH6YZEBROByxA7+DykgWCUXdBNPOW7efQqFAgMBAAECgYAK6oUFVNCHW15xI8f4ZerH1qh11tMgoUKlU0whb0wtdqLfj7mcl6/gkqDqzDPOaDYv8Y+vzT6CppoVU5YtznpCF4YRLuOfeAkY0kT9C7w62lu1C1aFMDS1Eydv0a10t001sp0W5U8J0LMgPpevPlksv2t9gZa08yGsBnVX9BIXjwJBAOrlsV6LsxNBnSKqXhZf1+uQe1vpPPzF3IXTvJzd4LhamcnImYayrg4Zjgj71+/0BFdWT9qGxtKGwJJGIjrMDG8CQQDFXLIrFMHVpdjrsAaQXvPWTSVIfVayi6Uib1HpXKiLJ53snebsBrBiShbAsJjrgWXzdurky6nGIlp5NV7i//pLAkEA4XaxRfe/XhdXtWNjxgQe41ueHH2GbXWZktbGrqcFwM4t2RHz0ueEy8HZpGPfQ9GrrQ0Kvs0o4AA5rO0mg9tBfwJBAJMWuaaH6spatyc4Yjv4uEuv5Sh4WUPp9WGLi4WbS/Whyf4N1It1lME8LGbhdqaWIrBnoTpxWw9SjREmqJgPZK8CQBAAI6IUkCeE+Lwub+akoBFuqyyIdIpIfXu4ntyxnZemmCNdotEfNL3yp0J3Rw6TpXyPDN/4uOrxt/aY2heXAKM=
在 Java 代码中应用了公钥,生成了下面的密文:
jCt9rD/6Q6OsjH+bd1XKB2FhDYTwzupQsFnwjKkrxulC3ztZx0j9/Zr6hBeCbFrdYFtxZi+j8lyyLJCHv0hpN0S5F/O6v/mhMIgCTWCmpWqcLqKC2zDWo180uL+dMysZm2JaBHzWA9VjnVTdVY3aRTWfu1fpEpTK6W9ESTVSS8Y=
下面的 JavaScript 代码与上面生成的私钥的导入扩展的第三个代码片段基本相同。 另外,应用上面生成的密文:
var pkcs8B64 = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALUXz4S8b1c/yNJci1Xz1C2VjL/ln5qsx6C8zCxqxLkghR2vhvh4TYUagNqVqrIDFY6D+kGTN8FYBCSG4rPeLXg6wFhCXVAMObfJ+Yjj3xKwTu1nKsQ0J9Mas/CgKtCYTBNY4J8gEKH6YZEBROByxA7+DykgWCUXdBNPOW7efQqFAgMBAAECgYAK6oUFVNCHW15xI8f4ZerH1qh11tMgoUKlU0whb0wtdqLfj7mcl6/gkqDqzDPOaDYv8Y+vzT6CppoVU5YtznpCF4YRLuOfeAkY0kT9C7w62lu1C1aFMDS1Eydv0a10t001sp0W5U8J0LMgPpevPlksv2t9gZa08yGsBnVX9BIXjwJBAOrlsV6LsxNBnSKqXhZf1+uQe1vpPPzF3IXTvJzd4LhamcnImYayrg4Zjgj71+/0BFdWT9qGxtKGwJJGIjrMDG8CQQDFXLIrFMHVpdjrsAaQXvPWTSVIfVayi6Uib1HpXKiLJ53snebsBrBiShbAsJjrgWXzdurky6nGIlp5NV7i//pLAkEA4XaxRfe/XhdXtWNjxgQe41ueHH2GbXWZktbGrqcFwM4t2RHz0ueEy8HZpGPfQ9GrrQ0Kvs0o4AA5rO0mg9tBfwJBAJMWuaaH6spatyc4Yjv4uEuv5Sh4WUPp9WGLi4WbS/Whyf4N1It1lME8LGbhdqaWIrBnoTpxWw9SjREmqJgPZK8CQBAAI6IUkCeE+Lwub+akoBFuqyyIdIpIfXu4ntyxnZemmCNdotEfNL3yp0J3Rw6TpXyPDN/4uOrxt/aY2heXAKM="; const pkcs8StrDER = atob(pkcs8B64); const pkcs8DER = str2ab(pkcs8StrDER); crypto.subtle.importKey( "pkcs8", pkcs8DER, { name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256", }, true, ["decrypt"] ).then(function(privateKey){ const dec = new TextDecoder(); const encryptedToken = "jCt9rD/6Q6OsjH+bd1XKB2FhDYTwzupQsFnwjKkrxulC3ztZx0j9/Zr6hBeCbFrdYFtxZi+j8lyyLJCHv0hpN0S5F/O6v/mhMIgCTWCmpWqcLqKC2zDWo180uL+dMysZm2JaBHzWA9VjnVTdVY3aRTWfu1fpEpTK6W9ESTVSS8Y="; crypto.subtle.decrypt( { name: "RSA-OAEP", }, privateKey, str2ab(atob(encryptedToken)) ).then(function (result) { console.log("decrypted:", dec.decode(result)) }).catch(function (e) { console.log(e); }); }); function str2ab(str) { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; }
代替损坏数据的 UTF-8 编码,使用 function str2ab()
将 Base64 解码数据转换为ArrayBuffer
。
我的评论中建议的替换_base64ToArrayBuffer(encryptedToken)
同样可能。 与str2ab()
不同, _base64ToArrayBuffer()
还执行 base64 解码。
运行代码会导致明文加密我的 Java 代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.