用Java解密OpenSSL PEM编码的RSA私钥? [英] Decrypting an OpenSSL PEM Encoded RSA private key with Java?

查看:143
本文介绍了用Java解密OpenSSL PEM编码的RSA私钥?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我需要使用Java库解密它。



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



私钥是这样的:

  ----- BEGIN RSA PRIVATE KEY ----- 
Proc-Type:4,ENCRYPTED
DEK-Info:DES-EDE3-CBC ,56F3A98D9CFFA77A

X5h7SUDStF1tL16lRM + AfZb1UBDQ0D1YbQ6vmIlXiK ....
.....
/ KK5CZmIGw ==
----- END RSA PRIVATE KEY --- -

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



我尝试以下方式解密密钥:

  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){
//这个密钥数据从X5 ...到==开始
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());
密钥秘密= 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,太大。 
at sun.security.util.DerInputStream.getLength(DerInputStream.java:561)
at sun.security.util.DerValue.init(DerValue.java:365)
at sun.security .util.DerValue。< init>(DerValue.java:294)
在javax.crypto.EncryptedPrivateKeyInfo。< init> (EncryptedPrivateKeyInfo.java:84)

我将正确的参数传递给 EncryptedPrivateKeyInfo



我如何使这项工作?建议,自从我工作Java 7以来,一个小小的变化,我不能使用Base64.getMimeCoder(),而是使用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.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);
/ *有关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);
返回新的SecretKeySpec(键,DESede);
}

private static byte [] h2b(CharSequence s)
{
int len = s.length();
byte [] b = new byte [len / 2]; (int src = 0,dst = 0; src 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);
返回新的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);
返回新的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 <1)||(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 keyDataStr,String ivHex,String password)
throws GeneralSecurityException
{
byte [] pw = password.getBytes(StandardCharsets.UTF_8);
byte [] iv = h2b(ivHex);
SecretKey secret = opensslKDF(pw,iv);
密码密码= Cipher.getInstance(DESede / CBC / PKCS5Padding);
cipher.init(Cipher.DECRYPT_MODE,secret,new IvParameterSpec(iv));
byte [] pkcs1 = cipher.doFinal(Base64.getMimeDecoder()。decode(keyDataStr));
/ *有关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);
返回新的SecretKeySpec(键,DESede);
}

private static byte [] h2b(CharSequence s)
{
int len = s.length();
byte [] b = new byte [len / 2]; (int src = 0,dst = 0; src 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()方法的定义


I have an encrypted private key and I know the password.

I need to decrypt it using a Java library.

I'd prefer not to use BouncyCastle though, unless there is no other option. Based on previous experience, there is too much change and not enough documentation.

The private key is in this form:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,56F3A98D9CFFA77A

X5h7SUDStF1tL16lRM+AfZb1UBDQ0D1YbQ6vmIlXiK....
.....
/KK5CZmIGw==
-----END RSA PRIVATE KEY-----

I believe the key data is Base64 encoded since I see \r\n after 64 characters.

I tried the following to decrypt the key:

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();
}

I get this Exception

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.<init>(DerValue.java:294)
    at javax.crypto.EncryptedPrivateKeyInfo.<init> (EncryptedPrivateKeyInfo.java:84)

Am I passing the right parameter to EncryptedPrivateKeyInfo constructor?

How can I make this work?

I tried what Ericsonn suggested, with one small change since I am working Java 7, I could not use Base64.getMimeCoder() instead I used Base64.decode and I am getting this error I am getting an error like this Input length must be multiple of 8 when decrypting with padded cipher at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750)

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 < 1) || (n > 2))
              throw new IllegalArgumentException("Invalid length");
            int len = 0;
            while (n-- > 0) {
              len <<= 8;
              len |= input.get() & 0xFF;
            }
            return len;
          }

1640 is keyDataStr.length() and 1228 is keyBytes.length

解决方案

You need to use a non-standard, OpenSSL method for deriving the decryption key. Then use that to decrypt the PKCS-#1–encoded key—what you are working with is not a PKCS #8 envelope. You'll also need the IV from the header as input to these processes.

It looks something like this:

  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;
  }

This is already a lot of code, so I will link to another answer for the definition of the decodeRSAPrivatePKCS1() method.

这篇关于用Java解密OpenSSL PEM编码的RSA私钥?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆