根据私钥RSA解密报错

RSA decryption according to the private key reports an error

我的前端使用RSA的代码:

const rsa = new JSEncrypt();
rsa.setPublicKey(k);
const resultText = rsa.encrypt("violet");
console.log(resultText);

我的后台使用RSA的代码:

byte[] inputByte = org.apache.commons.codec.binary.Base64.decodeBase64(str.getBytes("UTF-8"));
byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(privateKey);
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new 
PKCS8EncodedKeySpec(decoded));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,priKey);
String outStr=new String(cipher.doFinal(inputByte));
return outStr;

像这样的公钥:

    -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA13gYdCmOjR9yqQD7ldzG
ZXabSon6SiLceCK6vRXf4NMbF+EQke0vRpqU3IZ/S1pFdvoQswQabsA4zf0WACVT
iaGIhWDlPu3mecri8rYtmOSfd8GCE0vEgFNvSD6IXRLPeLCB+i7WENBa4fCEtW8W
Hzdas96CLiESbjSAruRasQXP2OLqEA2GU83/069vh8uRKzui+yw0aAXZFyFyFRFa
lxYltFadVpz3+kBplvpzuj82t4fc3yCRbrpeRyTyX1sz0ULSxx/k3/p1OuJtIq9Y
9uN0G4gxhcDFJ4L41uXOln5CPapk7tlsYobhhvxYHw1rrweY+06hrQ7r0Hblv2nH
GQIDAQAB
-----END PUBLIC KEY-----

像这样的私钥:

    -----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA13gYdCmOjR9yqQD7ldzGZXabSon6SiLceCK6vRXf4NMbF+EQ
ke0vRpqU3IZ/S1pFdvoQswQabsA4zf0WACVTiaGIhWDlPu3mecri8rYtmOSfd8GC
E0vEgFNvSD6IXRLPeLCB+i7WENBa4fCEtW8WHzdas96CLiESbjSAruRasQXP2OLq
EA2GU83/069vh8uRKzui+yw0aAXZFyFyFRFalxYltFadVpz3+kBplvpzuj82t4fc
3yCRbrpeRyTyX1sz0ULSxx/k3/p1OuJtIq9Y9uN0G4gxhcDFJ4L41uXOln5CPapk
7tlsYobhhvxYHw1rrweY+06hrQ7r0Hblv2nHGQIDAQABAoIBAAyqFmXde294BblB
QYhRbafRDNeYvIlW+zZkdC1g98OzJMiGhf7NvhWcSFud3CWFrMeNcyXSe+s+iRgy
Y/SmPP6969RLGa5VNVK7RhOV+aAe7/COAyM3NNmGDehlJIaz8FXbqggWcKaUWIMn
K+WuHdK/4ijoTyZ+8yJfG6Er8tisryLQ9io9+op9g/ZWzaUKgu934/cDxUt70bfm
x+ZEPi6YfkJ1uOpXnnadDyw2RUDcvCM3fK3KF5fqM7SJAXY9b1pmLr+Ccn1qkT9G
I+QHidEsGfJciX5AoHnlIMLPMVIPKBbq4GwC/Ngb41LprNJWlPR38N2ySjky/Jyt
159XWHECgYEA9lx2KfFmyLyVjnkIF3JI50mSZAw4YPBBqB27UInacvxXbjfVURht
xK60GB9OkDbFdeNh89x86Gfwvm5bTq4W8YSH4Obd5Fg8XjTuaicTi03CSfF5SdJn
JLLOUmlqP75gkbEPNUoOfqhqq6IbyJVB3egyL90cd2/wCdJOVLEUly8CgYEA3+Y4
lNdl0xedkDNkWsCyyA4iPSUzVxsuch9hW/VGBwzga8rtiNllpKifVvctQOEu/KUe
vVQRF78ojZaMGT33l6TivnUL54z9Lo9uWghoG8TqMfnG34pFPe3R+zvGP87Hrozw
1EUhiMT198SlB/YHrgGGGlJbG+rlm5GIx3lEdDcCgYA4RSw0LlA0v2ZFkX14pYDj
WxmVwnjKI3ZLqObU4XfE1cA+i4AssrC3wNOfwt7V77ywTYxc/9qD1uHVDS3LzdWt
uoCyrOi3tDOtrNdb5asAIXWkIAR9CRLH/hNEHZHIF3rFLDT2DgE7iso6g59m9DiE
L/nulsleunGQPLnpfDzgvwKBgDRV5Q3tl3CTSZJGYQPRnTikDR7LzkdjJCUq7qAH
IhpNyTuJEKL3ZgnqHGzAlERhHpGRqzDIMMKjPUBzW0YfNPuuYA3y4Bh83UV/42SK
KIOtMK0D3JeuA2dparbWDw4lMIm9iiGkEyWcHH6Q6Z6FxN/InWcTrxZEfu0xRI6T
6wtbAoGAfl5dW9LNoaNfQbgIq+PJKZm9f1frza55mFTJgo3ravdb3GmzWVHN8xRf
nLKyKyNLqbkT35IGc39NkALJLxT5RibkAZLiUiwqdMF63sgODbA9AGTmhe+JHS+V
hBmFnCyp6UiN9E4ZAWcZQILa0rRMftMFngAJ3El0ZP+HziRnNzs=
-----END RSA PRIVATE KEY-----

但是,我在java密码解密的时候报这样的错误:

java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=111, too big.

我该如何解决这个问题?

1.你解码错误。 PEM 格式有一个 dash-BEGIN 行标识数据类型,一个 base64 编码数据块,和一个 dash-END 行。 BEGIN 和 END 行是格式的一部分,但它们不包含 base64 编码的数据;只有中间的行包含 base64 编码的数据。您显然将整个内容(包括 BEGIN 和 END 行)传递给 commons.codec.Base64,这会导致在实际数据前后解码一堆垃圾。该垃圾不是有效的 ASN.1 DER,因此当 Java 尝试将其解析为 DER 时失败。

2。另外,您的数据不是 PKCS8 明文私钥。 PEM 类型 'RSA PRIVATE KEY' 是包含 'traditional' 或 'legacy' 格式的 OpenSSL-defined 格式,即私钥的 PKCS1 表示。这不是 PKCS8,它是 Java 原生支持的唯一密钥格式;这就是规范 class 被命名为 PKCS8EncodedKeySpec 的原因,因为它是一个编码为 PKCS8 的关键规范,更具体地说是 PKCS8-clear。如果通过在 base64 解码之前删除 BEGIN 和 END 行来解决上述问题,Java 可以将结果解析为 DER,但不能解析为 PKCS8 明文密钥;你得到一个关于 'algid parse error, not a sequence' 的不同异常。要解决此问题,有 5 种方法:

  • 更改您最初用于生成密钥对的任何过程,以便它生成 PKCS8,而不是 OpenSSL-legacy PKCS1。特别是因为您无论如何都需要通过发布来替换您破坏的密钥对,如 207421 所说。你不知道那个过程是什么或曾经是什么,所以我不能提供任何细节。

  • 将生成的私钥或副本转换为 PKCS8-clear。这不是编程或开发和 offtopic,但如果您拥有或获得 OpenSSL(在相同或任何可访问且安全的系统上),您可以

    openssl pkey -in oldfile -out newfile   # 1.0.0 up only, but older is now rare
    # or
    openssl pkcs8 -topk8 -nocrypt -in oldfile -out newfile   # even ancient versions
    

一旦你有了一个 PKCS8-clear 文件,只需删除 BEGIN 和 END 行并对剩下的内容进行 base64 解码,然后像你已经做的那样将其作为 PKCS8EncodedKeySpec 传递给 KeyFactory

  • 使用https://www.bouncycastle.org。 'bcpkix' jar 包含 (Java) 代码以读取大量 OpenSSL-supported PEM 格式,包括您拥有的 RSA-PKCS1 私钥格式。关于这个有很多现有的问题;只需搜索 PEMParser 和 JcaPEMKeyConverter。

  • 自己转换。在删除 BEGIN 和 END 行后,对您拥有的文件主体进行解码,以获取 PKCS1 密钥,然后为该密钥构建 PKCS8 格式,然后将其作为 PKCS8EncodedKeySpec 传递给 KeyFactory。请参阅 Noa Resare 和 Jean-Alexis Aufauvre 在 Getting RSA private key from PEM BASE64 Encoded private key file or mine in Java: Convert DKIM private key from RSA to DER for JavaMail 上的回答。

  • 完全自己做。解码没有 BEGIN/END 的文件以获得 PCKS1,将其解析为 DER,例如RFC8447,并构建 RSAPrivateCrtKeySpec。我在上面链接的 Q 上的其他一些人就是这样做的。但是,这需要:使用未记录的内部 sun.* classes,它曾经在 Java 中工作(因此存在现有答案)但是 'modular' Java 版本(9向上)自 2017 年以来,难度不断增加或变得不可能;使用 BouncyCastle,它记录了对 ASN.1 的(和良好的)支持——但是如上所述,使用 bcpkix 完成整个工作会更容易;或编写您自己的 ASN.1 解析,这是一项很好的工作。

PS:用RSA加密文本通常是一个糟糕的设计;它不适合那个。但这并不是真正的编程问题,也不属于这里。