密钥库证书链长度错误-如何以编程方式将整个证书链加载到Java密钥库中,以便使用SSL身份验证连接到Kafka



我正试图为使用SSL身份验证的Kafka使用者设置密钥库,但我一直遇到身份验证问题。我有一个Python示例,它在不使用密钥库的情况下工作。所以,我认为我填充密钥库的方式有问题。(注意:我不能只在命令行上执行,因为证书需要根据轮换时间表进行更改,所以我需要使用Java代码自动执行。(

以下是我如何填充密钥库:

String cert = "-----BEGIN CERTIFICATE-----n" +
"MIIEmTCCAoGgAwIBAgIUew1ANL9cTyhxLIo1ZpWLdKT4nOwwDQYJKoZIhvcNAQELn" +
...
"fjv+XLmCfL1IKqcsEYmEPmyf5Knwk0mO7gtw1fg=n" +
"-----END CERTIFICATE-----n" +
"-----BEGIN CERTIFICATE-----n" +
"MIIFtTCCA52gAwIBAgIUe6eKhfms7ldZ78MxKiMzwFQNhsYwDQYJKoZIhvcNAQELn" +
...
"jyDgNvJnm3g5eP6KUm9NNo7Le6lZoZhC3g==n" +
"-----END CERTIFICATE-----n" +
"-----BEGIN CERTIFICATE-----n" +
"MIIG5DCCBMygAwIBAgITKgAABSItHRkNBBF47gAAAAAFIjANBgkqhkiG9w0BAQsFn" +
...
"CF2TF5vdlOAUtvrJdnYgqNlSQHPAPeBP1runuwCV9ziZBTlra03cFw==n" +
"-----END CERTIFICATE-----n" +
"-----BEGIN CERTIFICATE-----n" +
"MIIF3TCCA8WgAwIBAgITXAAAAALRq61XLgYZigAAAAAAAjANBgkqhkiG9w0BAQ0Fn" +
...
"7ulMCI8RqFm3p32fs//+8o0=n" +
"-----END CERTIFICATE-----n" +
"-----BEGIN CERTIFICATE-----n" +
"MIIFEzCCAvugAwIBAgIQORoOm2GoxqBGK3xSM9br+zANBgkqhkiG9w0BAQ0FADAcn" +
...
"mBSMIdx3Iw==n" +
"-----END CERTIFICATE-----n";
String privateKey = "-----BEGIN RSA PRIVATE KEY-----n" +
"MIIEogIBAAKCAQEAstlhsvxwbG8fVawH++HXq7mrqy9xfjIWwD45JAJSlstBBoBEn" +
...
"TovqdueB5W7DR0FVYoxmLj3vcG6fy/j9f+O9fb/mo94Ma39Px3I=n" +
"-----END RSA PRIVATE KEY-----";
String rootCA = "-----BEGIN CERTIFICATE-----n" +
"MIIFEzCCAvugAwIBAgIQORoOm2GoxqBGK3xSM9br+zANBgkqhkiG9w0BAQ0FADAcn" +
...
"mBSMIdx3Iw==n" +
"-----END CERTIFICATE-----";
final PEMParser rootCaParser = new PEMParser(new StringReader(rootCA));
final PEMParser certParser = new PEMParser(new StringReader(certificate));
final PEMParser keyParser = new PEMParser(new StringReader(privateKey));
final X509Certificate rootCa = new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider()).getCertificate((X509CertificateHolder) rootCaParser.readObject());
final X509Certificate cert = new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider()).getCertificate((X509CertificateHolder) certParser.readObject());
final PrivateKey key = new JcaPEMKeyConverter().setProvider(new BouncyCastleProvider()).getKeyPair((PEMKeyPair) keyParser.readObject()).getPrivate();
KeyStore keystore = KeyStore.getInstance("jks");
char[] keyStorePass = config.getKeystorePassword().toCharArray();
char[] trustStorePass = config.getTruststorePassword().toCharArray();
keystore.load(null);
final Certificate[] chain = { cert };
keystore.setKeyEntry("privateKey", key, keyStorePass, chain); 
keystore.setCertificateEntry("CARoot",rootCa);
keystore.setCertificateEntry("localhost", cert);
try (FileOutputStream out = new FileOutputStream(config.getKeystoreLocation())) {
keystore.store(out, keyStorePass);
}

在测试密钥库时,我注意到私钥(别名privatekey(显示的证书链长度为1。这是不对的,因为实际的证书链的长度应该是5。因此,我认为链被截断了,就像代码没有正确地将证书链解析为5个证书,而只解析为一个证书一样。

如果我通过运行将证书(带链(附加到私钥

cat pk cert > pkWithChain.pem

并设置PKCS12记录,如:

openssl pkcs12 -export -in pkWithChain.pem -inkey pk -name pkWithChain > pkWithChain.p12

然后将其导入我的密钥库,如:

keytool -importkeystore -srckeystore pkWithChain.p12 -destkeystore keystore.jks -srcstoretype pkcs12 -alias pkwithchain

当我像这样检查密钥库中的密钥时:

keytool -list -v -keystore keystore.jks

我手动放入的私钥的证书长度为5,但我通过Java放入的私钥证书长度为1。

如何以编程方式将正确的(整个(证书链放入密钥库?(此外,我还需要做什么来为Kafka正确设置密钥库吗?我的Kafka配置/设置已经验证为正确。密钥库就是问题所在。(

首先,在修复明显的密文后,您的代码不会编译。它的String cert =final X509Certificate cert =在同一范围内,并且引用了未定义的certificate。我假设第一个CCD_ 9应该是CCD_。假设:

PEMParser.readObject()读取一个PEM块。这就是为什么它以单数"Object"命名的原因,"Object"是一个单独的,而不是复数、复数或多个。

要读取多个证书(在这种情况下形成一个链(,请使用循环,例如:

/*final*/ PEMParser certParser = new PEMParser(new StringReader(certificate)); // as already
List<Certificate> certlist = new ArrayList<Certificate>();
for( X509CertificateHolder tmp; (tmp = (X509CertificateHolder) certParser.readObject()) != null; )
certlist.add( new JcaX509CertificateConverter().getCertificate(tmp) ); // don't really need BCprov for this
// add { } if your coding style calls for it
Certificate[] chain = certlist.toArray(new Certificate[certlist.length()]);

此外,您的rootCA似乎是您链中的最后一个证书。您可以直接使用chain[chain.length-1],而不是使用冗余副本。

或者,您可以使用JCA标准CertificateFactory,而不是Bouncy的PEM解析,它已经处理了多个证书:

Collection<? extends Certificate> certcoll = CertificateFactory.getInstance("X.509")
.generateCertificates( new ByteArrayInputStream(certificate.getBytes()) );
// note Certificates with an s meaning plural, multiple, not limited to one
// String.getBytes(/*nocharset*/) is often dangerous, but okay FOR PEM
Certificate[] chain = certcoll.toArray(new Certificate[certcoll.length()]);

附言:有一些方法可以在计划的基础上运行"命令行"程序,完全或部分自动,但这对SO来说是不主题的。也可以从Java运行和控制"命令行(commandline("程序,但你没有要求。

最新更新