繁体   English   中英

使用 Java 进行 RSA 加密,使用 JavaScript 进行解密

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM