javascript - 使用 Java 进行 RSA 加密,使用 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 代码片段enc.encode(atob(encryptedToken))
的方法中。decrypt()
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 编码,str2ab()
使用该函数将 Base64 解码的数据转换为ArrayBuffer
.
我的评论中建议的替换_base64ToArrayBuffer(encryptedToken)
同样可能。与, 不同str2ab()
,_base64ToArrayBuffer()
也执行 base64 解码。
运行代码会导致纯文本加密我的 Java 代码。
推荐阅读
- python - Pyqt gui,带有放置在背景图像上的按钮和数字指示器
- python - git clone 的 Python 进度条
- tfs - TFS 自动构建:Nuget Pack 失败并显示代码 (1)
- database - 需要帮助编写代码以在 Excel 中将特定数据从一张工作表移动到另一张工作表
- intershop - 以应用程序级别定义的货币获取产品价格
- python-2.7 - 错误:save() 得到了一个意外的关键字参数“格式”
- firebase - Firebase 身份验证与 Firebase 管理员
- python - 有没有比 idxmax 更快的替代方法?
- java - FusedLocationClient 间隔在手机锁定时减速
- bash - BASH - $@ 中包含的选项