如何使用Java读取加密密码的密钥?

16
我有一个以PKCS8 DER格式保存并受密码保护的私钥文件,最简单的方法是什么?这是我用来加载未加密私钥的代码:
InputStream in = new FileInputStream(privateKeyFilename);
byte[] privateKeydata = new byte[in.available()];
in.read(privateKeydata);
in.close();
KeyFactory privateKeyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(privateKeydata);
PrivateKey privateKey = privateKeyFactory.generatePrivate(encodedKeySpec);

对于相同规范的未加密密钥,它可以正常工作。顺便说一下,我正在使用BouncyCastle。

我可以使用以下openssl命令查看此私钥

openssl pkcs8 -in ./privatekey.key -inform DER -passin pass:thisismypass

求助!!!

我在这个主题中发布了一些解决方案。但是我仍然有一个未回答的问题,希望有人可以帮助我使其在没有额外库的情况下正常工作,只使用BouncyCastle。

2个回答

9
我发现了解决方案!也许它不是那么优雅,但是......这里我将发布两个解决方案:
  1. 首选,但不起作用
  2. 有效的解决方法,但需要额外的库
第一个解决方案
我在这里找到了一种解决方案,但它会抛出异常。解决方法:
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;

import javax.crypto.*;
import javax.crypto.spec.*;

/*
 * This class demonstrates how to import an encrypted RSA private key as
 * generated by openssl. The input file is presumed to be in DER
 * format.
 */
public class ImportEncryptedPrivateKey
{
    public static byte[] readPK8FromFile(String fileName) throws IOException
    {
        File f = new File(fileName);
        DataInputStream dis = new DataInputStream(new FileInputStream(f));
        byte[] theData = new byte[(int) f.length()];
        dis.readFully(theData);
        return theData;
    }

    public static void main(String[] args) throws IOException,
            NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeySpecException, InvalidKeyException,
            InvalidAlgorithmParameterException
    {
        byte[] encryptedPKInfo = readPK8FromFile("rsapriv.pk8");
        EncryptedPrivateKeyInfo ePKInfo = new EncryptedPrivateKeyInfo(
                encryptedPKInfo);
        char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
        Cipher cipher = Cipher.getInstance(ePKInfo.getAlgName());
        PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
        // Now create the Key from the PBEKeySpec
        SecretKeyFactory skFac = SecretKeyFactory.getInstance(ePKInfo
                .getAlgName());
        Key pbeKey = skFac.generateSecret(pbeKeySpec);
        // Extract the iteration count and the salt
        AlgorithmParameters algParams = ePKInfo.getAlgParameters();
        cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
        // Decrypt the encryped private key into a PKCS8EncodedKeySpec
        KeySpec pkcs8KeySpec = ePKInfo.getKeySpec(cipher);
        // Now retrieve the RSA Public and private keys by using an
        // RSA keyfactory.
        KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
        // First get the private key
        RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) rsaKeyFac.generatePrivate(pkcs8KeySpec);
        // Now derive the RSA public key from the private key
        RSAPublicKeySpec rsaPubKeySpec = new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv.getPublicExponent());
        RSAPublicKey rsaPubKey = (RSAPublicKey) rsaKeyFac.generatePublic(rsaPubKeySpec);
    }

}

我的异常信息:

Exception in thread "main" java.security.NoSuchAlgorithmException: No such algorithm: 1.2.840.113549.1.5.13

第二个方案:

根据http://juliusdavies.ca/commons-ssl/pkcs8.html的内容,您可以了解第二个可行的解决方案。


+1 适用于 not-yet-commons-ssl。那时你甚至不需要 Bouncy Castle。 - Thilo
7
第二个解决方案,你可以将代码粘贴在这里吗?我无法打开那个链接。你使用了哪个库? - BRabbit27
2
第二个链接出现了404错误。有人解决了1.2.840.113549.1.5.13问题吗? - DevilCode
1
对于任何人来说:要解决第一个异常,您需要玩弄放入Cipher工厂方法和SecretKeyFactory工厂方法中的算法名称:您应该使用不同的算法,而不是AlgName,具体取决于您的密钥文件。 'PBEWithHmacSHA256AndAES_256'适合我。我发现这个(您可以尝试使用此构造)ePKInfo.getAlgParameters().toString()作为算法名称。 - CalmTechie
要阅读现在已经失效的链接,请参考 https://web.archive.org/web/20171113221445/http://juliusdavies.ca/commons-ssl/pkcs8.html。 - Jetto Martínez

2
这是我的代码,它能正常工作 :)
File f = new File(keyFile);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
EncryptedPrivateKeyInfo encryptPKInfo = new EncryptedPrivateKeyInfo(keyBytes);
Cipher cipher = Cipher.getInstance(encryptPKInfo.getAlgName());
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwd.toCharArray());
SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptPKInfo.getAlgName());
Key pbeKey = secFac.generateSecret(pbeKeySpec);
AlgorithmParameters algParams = encryptPKInfo.getAlgParameters();
cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
KeySpec pkcs8KeySpec = encryptPKInfo.getKeySpec(cipher);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(pkcs8KeySpec);

2
仅使用JDK(没有Bouncy Castle),我会得到“找不到支持1.2.840.113549.1.5.13”的任何提供程序”(这是PBEWithMD5AndDES的ID)。 - Thilo
有任何解决方案吗? - DevilCode
@Thilo:不是的,pbeWithMD5andDES是(rsadsi=1.2.840.113549).1.5.3(PKCS5v1中11种方案之一,现在重新命名为PBES1),而rsadsi.1.5.<b>13</b>是PBES2(PKCS5v2中的新通用方案)。 - dave_thompson_085

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接