使用Java解密OpenSSL PEM编码的RSA私钥?

我有一个加密的私钥,我知道密码。

我需要使用Java库解密它。

我不想使用BouncyCastle,除非没有其他选择。 根据以往的经验,有太多的变化,没有足够的文档。

私钥是这种forms:

-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,56F3A98D9CFFA77A X5h7SUDStF1tL16lRM+AfZb1UBDQ0D1YbQ6vmIlXiK.... ..... /KK5CZmIGw== -----END RSA PRIVATE KEY----- 

我相信关键数据是Base64编码,因为我看到64个字符后的\r\n

我尝试了以下解密密钥:

 import java.security.Key; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; public String decrypt(String keyDataStr, String passwordStr){ // This key data start from "X5... to ==" char [] password=passwordStr.toCharArray(); byte [] keyDataBytes=com.sun.jersey.core.util.Base64.decode(keyDataStr); PBEKeySpec pbeSpec = new PBEKeySpec(password); EncryptedPrivateKeyInfo pkinfo = new EncryptedPrivateKeyInfo(keyDataBytes); SecretKeyFactory skf = SecretKeyFactory.getInstance(pkinfo.getAlgName()); Key secret = skf.generateSecret(pbeSpec); PKCS8EncodedKeySpec keySpec = pkinfo.getKeySpec(secret); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey pk=kf.generatePrivate(keySpec); return pk.toString(); } 

我得到了这个例外

 java.io.IOException: DerInputStream.getLength(): lengthTag=50, too big. at sun.security.util.DerInputStream.getLength(DerInputStream.java:561) at sun.security.util.DerValue.init(DerValue.java:365) at sun.security.util.DerValue.(DerValue.java:294) at javax.crypto.EncryptedPrivateKeyInfo. (EncryptedPrivateKeyInfo.java:84) 

我是否将正确的参数传递给EncryptedPrivateKeyInfo构造函数?

我怎样才能做到这一点?

我尝试了Ericsonn提出的建议,由于我正在使用Java 7,因此我无法使用Base64.getMimeCoder()而是使用了Base64.decode而我使用了Base64.decode而我得到了这个错误我得到了这样的错误输入长度必须是多个在com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750)用填充密码解密时的8

 static RSAPrivateKey decrypt(String keyDataStr, String ivHex, String password) throws GeneralSecurityException, UnsupportedEncodingException { byte[] pw = password.getBytes(StandardCharsets.UTF_8); byte[] iv = h2b(ivHex); SecretKey secret = opensslKDF(pw, iv); Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); byte [] keyBytes=Base64.decode(keyDataStr.getBytes("UTF-8")); byte[] pkcs1 = cipher.doFinal(keyBytes); /* See note for definition of "decodeRSAPrivatePKCS1" */ RSAPrivateCrtKeySpec spec = decodeRSAPrivatePKCS1(pkcs1); KeyFactory rsa = KeyFactory.getInstance("RSA"); return (RSAPrivateKey) rsa.generatePrivate(spec); } private static SecretKey opensslKDF(byte[] pw, byte[] iv) throws NoSuchAlgorithmException { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(pw); md5.update(iv); byte[] d0 = md5.digest(); md5.update(d0); md5.update(pw); md5.update(iv); byte[] d1 = md5.digest(); byte[] key = new byte[24]; System.arraycopy(d0, 0, key, 0, 16); System.arraycopy(d1, 0, key, 16, 8); return new SecretKeySpec(key, "DESede"); } private static byte[] h2b(CharSequence s) { int len = s.length(); byte[] b = new byte[len / 2]; for (int src = 0, dst = 0; src < len; ++dst) { int hi = Character.digit(s.charAt(src++), 16); int lo = Character.digit(s.charAt(src++), 16); b[dst] = (byte) (hi << 4 | lo); } return b; } static RSAPrivateCrtKeySpec decodeRSAPrivatePKCS1(byte[] encoded) { ByteBuffer input = ByteBuffer.wrap(encoded); if (der(input, 0x30) != input.remaining()) throw new IllegalArgumentException("Excess data"); if (!BigInteger.ZERO.equals(derint(input))) throw new IllegalArgumentException("Unsupported version"); BigInteger n = derint(input); BigInteger e = derint(input); BigInteger d = derint(input); BigInteger p = derint(input); BigInteger q = derint(input); BigInteger ep = derint(input); BigInteger eq = derint(input); BigInteger c = derint(input); return new RSAPrivateCrtKeySpec(n, e, d, p, q, ep, eq, c); } private static BigInteger derint(ByteBuffer input) { byte[] value = new byte[der(input, 0x02)]; input.get(value); return new BigInteger(+1, value); } private static int der(ByteBuffer input, int exp) { int tag = input.get() & 0xFF; if (tag != exp) throw new IllegalArgumentException("Unexpected tag"); int n = input.get() & 0xFF; if (n < 128) return n; n &= 0x7F; if ((n  2)) throw new IllegalArgumentException("Invalid length"); int len = 0; while (n-- > 0) { len <<= 8; len |= input.get() & 0xFF; } return len; } 

1640是keyDataStr.length(),1228是keyBytes.length

您需要使用非标准的OpenSSL方法来派生解密密钥。 然后用它来解密PKCS-#1编码的密钥 – 你正在使用的不是 PKCS#8信封。 您还需要标题中的IV作为这些过程的输入。

它看起来像这样:

  static RSAPrivateKey decrypt(String keyDataStr, String ivHex, String password) throws GeneralSecurityException { byte[] pw = password.getBytes(StandardCharsets.UTF_8); byte[] iv = h2b(ivHex); SecretKey secret = opensslKDF(pw, iv); Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); byte[] pkcs1 = cipher.doFinal(Base64.getMimeDecoder().decode(keyDataStr)); /* See note for definition of "decodeRSAPrivatePKCS1" */ RSAPrivateCrtKeySpec spec = decodeRSAPrivatePKCS1(pkcs1); KeyFactory rsa = KeyFactory.getInstance("RSA"); return (RSAPrivateKey) rsa.generatePrivate(spec); } private static SecretKey opensslKDF(byte[] pw, byte[] iv) throws NoSuchAlgorithmException { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(pw); md5.update(iv); byte[] d0 = md5.digest(); md5.update(d0); md5.update(pw); md5.update(iv); byte[] d1 = md5.digest(); byte[] key = new byte[24]; System.arraycopy(d0, 0, key, 0, 16); System.arraycopy(d1, 0, key, 16, 8); return new SecretKeySpec(key, "DESede"); } private static byte[] h2b(CharSequence s) { int len = s.length(); byte[] b = new byte[len / 2]; for (int src = 0, dst = 0; src < len; ++dst) { int hi = Character.digit(s.charAt(src++), 16); int lo = Character.digit(s.charAt(src++), 16); b[dst] = (byte) (hi << 4 | lo); } return b; } 

这已经是很多代码了,所以我将链接到另一个解决方案来定义decodeRSAPrivatePKCS1()方法。