如何使用 BouncyCastle

问题描述

我可以使用 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());
            }
        }
    }
}

解决方法

BouncyCastle 提供程序不会使用空密码进行 PKCS8 解密(实际上是任何 PBE)。 SunJCE 提供程序将,但您不能将其明确指定为要使用的提供程序,因为它通过 PBES2 的 OID 处理密码的算法参数;相反,您必须让 DecryptorProvider 使用默认的 JCA 搜索并在该搜索中使用 Bouncy 提供程序 after SunJCE;最简单的方法是把它放在最后 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());
    }
,

不确定是否有您所期望的(不要使用空密码进行测试)

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...