【问题标题】:How to read PKCS8 file with empty password using BouncyCastle如何使用 BouncyCastle 读取密码为空的 PKCS8 文件
【发布时间】:2021-06-15 02:46:50
【问题描述】:

我可以使用 OpenSSL 生成一个密码为空的 PKCS#8 加密文件:

~ $ openssl pkcs8 -topk8 -in ca_private.pem 
Enter pass phrase for ca_private.pem:
Enter Encryption Password: <ENTER>
Verifying - Enter Encryption Password: <ENTER>
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIBvTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIsWq90VBNFMwCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCFtKOCdDUeRohccAqQZaDIBIIB
YG+ohLBKQ766BTCXXZ7wyAP1l0grcQPgnzI2XmEj33rBIMogS6l3oAN3Faos2I6n
PcUY+aNLQtDSbvPzF4ozd0oWYBTa60iYGboQQ2RolhRRTzNW6K2tWBWUB35v2rLV
VYu7xJMX+dr/PxzhEgaQ4Nerb7v7/EAn4fLv3zcW9f/tPbljKUAiKc/YYP+GjRjA
GyJThdVpyeK6Jflobc3V8gqL8Gk0MgeHmXuUR1+SthA6ia5havH7D/FMLvXxZtRK
CpWOQ8mJp7g7dbUf+qWTLX+dMPQFPZDEofdkCY2/J4dSkgNnPgp+1oxSVpEAAR9v
gWsRezU2KfFUEMIljYOT+s4ZhkeAGtA8qa8qnr0yv9uz1OkzFtrleNf0WV8wRqI7
azo/7ff9TbecseYlTRgR40nd2l3Z9RLMVhsS09vPffYDw3jt+Zqf3m7iEri6eSug
5bMcZTszaQsVT0HOfCcpQ1Q=
-----END ENCRYPTED PRIVATE KEY-----

使用PEMParser 读取此内容时,它会返回PKCS8EncryptedPrivateKeyInfo 的实例(如预期的那样)。

尝试使用 JceOpenSSLPKCS8DecryptorProviderBuilder 解密它,但是失败了

org.bouncycastle.pkcs.PKCSException:无法读取加密数据:密码为空

在创建解密密钥时对空密码进行硬编码检查。有没有解决的办法? OpenSSL 肯定可以很好地读取 PKCS#8 回...

示例代码:

import com.excelfore.api.lib.util.ApiCrypto;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;

import java.io.FileReader;

class Scratch {
    public static void main(String[] args) throws Exception {

        try (FileReader fr = new FileReader(args[0])) {

            Object whatIsIt = new PEMParser(fr).readObject();
            if (whatIsIt instanceof PKCS8EncryptedPrivateKeyInfo) {
                PKCS8EncryptedPrivateKeyInfo pInfo = (PKCS8EncryptedPrivateKeyInfo) whatIsIt;
                InputDecryptorProvider provider =
                        new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider(ApiCrypto.bouncyCastleProvider).build(args[1].toCharArray());
                pInfo.decryptPrivateKeyInfo(provider);
                System.out.println("ok");
            } else {
                System.out.println("I don't want "+whatIsIt.getClass().getName());
            }
        }
    }
}

【问题讨论】:

  • 您找到解决方案了吗?
  • @JúlioFalbo Dave Thompson 的答案有效,但它依赖于加载提供程序的精确顺序,我发现这是不可行的(我根本不希望有弹性的提供程序被注册,它会弄乱更大的框架) ,所以我只要求 PKCS#8 密钥有密码,否则将使用 PKCS#1 密钥。
  • 奇怪,因为它不适合我。

标签: java bouncycastle pkcs#8


【解决方案1】:

不确定是否符合您的预期(请勿使用空密码进行测试)

    public static KeyPair parseKeyPair(Path pemFile, String passPhrase) {
        try(PEMParser pemParser = new PEMParser(Files.newBufferedReader(pemFile))) {
            Object object = pemParser.readObject();
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BouncyCastleProviderHolder.BC_PROVIDER);
            if(object instanceof PEMEncryptedKeyPair) {
                if(passPhrase == null)
                    throw new IllegalArgumentException("Pass phrase required for parsing RSA private key");

                PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(passPhrase.toCharArray());
                return converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
            }
            if(object instanceof PEMKeyPair) {
                return converter.getKeyPair((PEMKeyPair) object);
            }
        } catch(Exception e) {
            throw new PicApplicationException("Couldn't parse pem to keypair", e);
        }

        throw new PicApplicationException("Couldn't parse pem to keypair");
    }

您可以向此行传递空密码:

PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build("".toCharArray());

【讨论】:

  • 该代码适用于 OpenSSL traditional aka legacy PEM 文件,而不是为此 Q 指定的 PKCS8。另外,即使对于 trad 它也不起作用对于空密码:您可以构建 DecryptorProvider 但使​​用 解密失败。尽管 OpenSSL 命令行不会像 pkcs8 那样使用空密码短语写入 trad 文件这一事实在一定程度上抵消了这一点;你必须编写一个程序来做到这一点。
【解决方案2】:

BouncyCastle 提供程序不会使用空密码进行 PKCS8 解密(实际上是任何 PBE)。 SunJCE 提供程序将,但您不能明确指定它作为要使用的提供程序,因为它处理 PBES2 的 OID 密码的算法参数;相反,您必须让 DecryptorProvider 使用默认的 JCA 搜索,并在该搜索 after SunJCE 中有 Bouncy 提供程序;最简单的方法就是在末尾加上Security.addProvider

    Security.addProvider(new BouncyCastleProvider());
    try( Reader r = new FileReader(args[0]); PEMParser p = new PEMParser(r) ) {
        JceOpenSSLPKCS8DecryptorProviderBuilder b = new JceOpenSSLPKCS8DecryptorProviderBuilder();
        //DON'T:if( args.length>1 ) b.setProvider(args[1]);
        InputDecryptorProvider d = b.build(new char[0]);
        PrivateKeyInfo k0 = ((PKCS8EncryptedPrivateKeyInfo)p.readObject()).decryptPrivateKeyInfo(d);
        PrivateKey k1 = new JcaPEMKeyConverter().getPrivateKey(k0);
        System.out.println (k1.getAlgorithm());
    }

【讨论】:

  • 我认为提供者的顺序并不重要。要将密码短语转换为密钥,使用javax.crypto.SecretKeyFactory,它会遍历所有已注册的提供程序,直到找到成功返回密钥的提供程序。虽然条件可能适用,并且它依赖于 JDK 实现。
  • 对我不起作用
猜你喜欢
  • 2018-12-24
  • 2018-09-30
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
  • 2018-12-27
  • 1970-01-01
  • 2014-05-20
  • 1970-01-01
相关资源
最近更新 更多