问题描述
我可以使用 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());