无法将 WebCrypto 密钥对转换为 Java RSA 密钥
Posted
技术标签:
【中文标题】无法将 WebCrypto 密钥对转换为 Java RSA 密钥【英文标题】:Unable to convert a WebCrypto key pair to Java RSA keys 【发布时间】:2018-07-16 06:38:19 【问题描述】:我有公钥和私钥作为字符串,它们是从 Webcrypto API RSA-OAEP 算法。我想加密和解密一些 通过使用这些纯文本并在尝试转换时出现异常 字符串转字节数组
Java 代码:
package mailsend;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
public class RsaOaep
public static void main(String[] args) throws Exception
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
//Encryption
byte[] input = "\"userid\":\"1242\",\"appid\":\"1234\",\"tenentid\":\"4567\",\"sessionid\":\"session1234567\"".getBytes();
Cipher cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC");
SecureRandom random = new SecureRandom();
String publicKey="\n" +
" \"alg\": \"RSA-OAEP-256\",\n" +
" \"e\": \"AQAB\",\n" +
" \"ext\": true,\n" +
" \"key_ops\": [\n" +
" \"encrypt\"\n" +
" ],\n" +
" \"kty\": \"RSA\",\n" +
" \"n\": \"uChD48P6OBCynflLVsuP51hExmS7JIJmSPe6g_RYeb-O8rbaJA6mJE7QsMBeCrBUeyjhFYOFkGV9tpu3xIMkuRmTiyqy_mP2DKo8x9RB0gGVRvDuFjyOb3qpTAEA1SGyo_jGwN3RmVAVzVOTAAHqgQpRHtUsFEpQImUJgOUGVAfyFYpyyJKypcRv-RIU2JxOg3w7-i53erZPMHq_eWzEEXOgAU49UPjlTV18bXklBKSy3sbhKDpvHvOh_rEufhtD1Jl5RAQz3o8GGWPmcSf_3h5xa5ppqcgx6cfHDY9KKgxgCScwtjzTqU_QJO_zRnf3kYFU4dIFkfpXOJDDJc6RwQ\"\n" +
"";
byte[] keyBytes = Base64.getDecoder().decode(publicKey);
PublicKey publicKeyNew = KeyFactory.getInstance("RSA", "BC").generatePublic(new X509EncodedKeySpec(keyBytes));
cipher.init(Cipher.ENCRYPT_MODE, publicKeyNew, random);
byte[] cipherText = cipher.doFinal(input);
System.out.println("cipher: " + new String(cipherText));
//Decryption
String privateKey="\n" +
" \"alg\": \"RSA-OAEP-256\",\n" +
" \"d\": \"FZO8Lp_r_a0xLHLE6cjElePg_QjY54Ry1RpXi3Xx9uPjrRsREJf5zffBGnCTpDd4Uozd4I4uNFa75c01eSwvfZOaVrw8SDOwpNe-cuBzDNbcJXl9v_O88aFi3DGi5hYCbxVrPjZPRGIeh9YCu4W98vwhOJZcCY2SeZEyjZxoAyjOmsvvylpUVN2ZVJ22lDOaBJMerPdwyXFv9wsUserFleZByk-M8WLz5JiT3MIg6NuMiMryx3-lhcHCBXgHJMeuxrdnPc_acer3WNkgWf5Q4LkTu9TT_Uz4kULtPvdbccv3-JyE0x4ZjCfiAmT65vUM5WvmCE6MZseR3WONIxmh0Q\",\n" +
" \"dp\": \"FdzMUisoeG6Kk4C7Ol7k3QqR6QKa18LsMVIQXruQ7Vxm4bJ0lo8WyHWhLy4n11JLs7OGFENyXu7NtXw0ezmolfKIJVlItxWofICm8wm3Rhljrxe9KJnp1V4L1ibCXLOXOLsqhUwesWOhjAkfTHWJf3-wslSxVtFbif3fjeaKHlk\",\n" +
" \"dq\": \"YTfHIEIOYiJ9AW_r3R8ou69ApwyzFNqczSeVooMKvKTYrpdj4ms6MeW5uL8Pq9HKFr8AFA2FWtOvrHrxShw_1V9IuvNChmPEF11RIO7CSc6xeK98zFtgqzbpJ81SKlcKh4icpaSjX3SQa7qbasgk9MBxK5gmpc5XZZNICDKfZ-E\",\n" +
" \"e\": \"AQAB\",\n" +
" \"ext\": true,\n" +
" \"key_ops\": [\n" +
" \"decrypt\"\n" +
" ],\n" +
" \"kty\": \"RSA\",\n" +
" \"n\": \"uChD48P6OBCynflLVsuP51hExmS7JIJmSPe6g_RYeb-O8rbaJA6mJE7QsMBeCrBUeyjhFYOFkGV9tpu3xIMkuRmTiyqy_mP2DKo8x9RB0gGVRvDuFjyOb3qpTAEA1SGyo_jGwN3RmVAVzVOTAAHqgQpRHtUsFEpQImUJgOUGVAfyFYpyyJKypcRv-RIU2JxOg3w7-i53erZPMHq_eWzEEXOgAU49UPjlTV18bXklBKSy3sbhKDpvHvOh_rEufhtD1Jl5RAQz3o8GGWPmcSf_3h5xa5ppqcgx6cfHDY9KKgxgCScwtjzTqU_QJO_zRnf3kYFU4dIFkfpXOJDDJc6RwQ\",\n" +
" \"p\": \"_8_ViZjqi4qU0jdt4dbrv81-FYFjJ0A3CpHhOwDxvIHRBMTS188_SEe-CZFwF91myv3AF9ZA64gYscr6t765F3-R7ydygryQWOedE-meRZhUHw3E3y-w_Khvtv0k3DW_NBO1-i3MDi8HV-xoSED9_ZYP6B0N05sBeoLhr76MuVk\",\n" +
" \"q\": \"uErwgmSi7_t90oqKEED4Da8csEZVBN6_7n9xbZEk366lPf5rPETPbr8DKG-rA4kXq3l10ZksC-oo0RKdMpnqoHiYjmFPex1uLXJOAR_sg8ENtYtH-U6tNBjpoLufnbtg39O4bT7jr0HXx3MQmNy4rumfO97XypqIdKkrAODtJqk\",\n" +
" \"qi\": \"brnAuBOvNKKdkwsI836lPNxmKqAMc-Jbtn7YmOu3ofq6PtiT5blJSGUJizMd1YDRHTLlQzWt1eqFZr_AjidGZ0QvYSj1jZT1hdpkIGn1M6oDDwh73mDC_Wqg5qtyFp6YudzWpSt5TG7m94pyPFx79wm1WedW53RVNQVdx3yOcBI\"\n" +
"";
byte[] keyBytesNew = Base64.getDecoder().decode(privateKey);
PrivateKey privateKeyNew = KeyFactory.getInstance("RSA", "BC").generatePrivate(new X509EncodedKeySpec(keyBytesNew));
cipher.init(Cipher.DECRYPT_MODE, privateKeyNew);
byte[] plainText = cipher.doFinal(cipherText);
System.out.println("plain : " + new String(plainText));
例外:
线程“主”java.lang.IllegalArgumentException 中的异常: 非法 base64 字符 7b 在 java.util.Base64$Decoder.decode0(Base64.java:714) 在 java.util.Base64$Decoder.decode(Base64.java:526) 在 java.util.Base64$Decoder.decode(Base64.java:549) 在 mailsend.RsaOaep.main(RsaOaep.java:30)
【问题讨论】:
这可能会有所帮助 ***.com/questions/28584080/…> 你的publicKey
字符串不在Base64中
我认为密钥被格式化为 JSON 网络令牌。找到合适的库来解析它们。
实际上我正在从 webcryptoAPI 获取导入的公钥和私钥,我无法在 java 公钥中映射 webcrypto 导入的公钥来解密一些纯文本
【参考方案1】:
webcrypto 导出的密钥是 JWK (JSON Web Key) 格式。 Java不直接支持这种格式,所以必须先处理RSA密钥对的组成部分
正确生成密钥后,您可以检查您的加密代码
1.解码 JSON Web Key:解析 JSON 字符串并提取公钥和私钥的组件
我建议使用像 Jackson 这样的 JSON 库。看到这个sample
public class JWK
String n;
String e;
//getters and setters
ObjectMapper mapper = new ObjectMapper();
JWK jwk = mapper.readValue(publicKeyJson, JWK.class);
2。将关键组件转换为 BigInteger:模数、公共指数、私有指数等被编码为 base64url 字符串。将这些字符串解码为 byte[] 并使用它们构建 BigInteger,以便它们可以被 Java Key builder 加载
byte[] publicExponentBytes = Base64.getUrlDecoder().decode(jwkPublic.e);
byte[] privateExponentBytes = Base64.getUrlDecoder().decode(jwkPrivate.d);
byte[] modulusBytes = Base64.getUrlDecoder().decode(jwkPublic.n);
BigInteger publicExponent = new BigInteger(1, publicExponentBytes );
BigInteger privateExponent = new BigInteger(1, privateExponentBytes);
BigInteger modulus = new BigInteger(1, modulusBytes);
3.使用模数 (n) 和公共指数 (e) 构建公钥
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent );
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(spec);
4.使用模数 (n) 和私有指数 (d) 构建私钥
RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(modulus, privateExponent);
KeyFactory factory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = factory.generatePrivate(privateKeySpec);
【讨论】:
我遵循了您建议的方法,谢谢。我在解密数据时遇到问题。我在这里发布了我的代码更改和异常 更正答案。私有指数是“d”,而不是“dP”以上是关于无法将 WebCrypto 密钥对转换为 Java RSA 密钥的主要内容,如果未能解决你的问题,请参考以下文章
Node.js 和 webcrypto 之间的 RSA 加密
Nodejs AES-256-GCM 通过 webcrypto api 解密加密的客户端消息
如何将使用 PuTTYgen (Windows) 生成的 SSH 密钥对转换为 ssh-agent 和 Keychain (Linux) 使用的密钥对