繁体   English   中英

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

[英]Keystore cert chain wrong length - How to load entire cert chain into Java keystore programmatically for connecting to Kafka with SSL auth?

我正在尝试为使用 SSL 身份验证的 Kafka 消费者设置密钥库,但我不断遇到身份验证问题。 我有一个 Python 示例,它可以在不使用密钥库的情况下工作。 所以,我相信我在填充密钥库的方式上做错了。 (注意:我不能只在命令行上执行此操作,因为证书需要按轮换计划进行更改,因此我需要使用 Java 代码自动执行此操作。)

这是我填充密钥库的方式:

String cert = "-----BEGIN CERTIFICATE-----\n" +
        "MIIEmTCCAoGgAwIBAgIUew1ANL9cTyhxLIo1ZpWLdKT4nOwwDQYJKoZIhvcNAQEL\n" +
        ...
        "fjv+XLmCfL1IKqcsEYmEPmyf5Knwk0mO7gtw1fg=\n" +
        "-----END CERTIFICATE-----\n" +
        "-----BEGIN CERTIFICATE-----\n" +
        "MIIFtTCCA52gAwIBAgIUe6eKhfms7ldZ78MxKiMzwFQNhsYwDQYJKoZIhvcNAQEL\n" +
        ...
        "jyDgNvJnm3g5eP6KUm9NNo7Le6lZoZhC3g==\n" +
        "-----END CERTIFICATE-----\n" +
        "-----BEGIN CERTIFICATE-----\n" +
        "MIIG5DCCBMygAwIBAgITKgAABSItHRkNBBF47gAAAAAFIjANBgkqhkiG9w0BAQsF\n" +
        ...
        "CF2TF5vdlOAUtvrJdnYgqNlSQHPAPeBP1runuwCV9ziZBTlra03cFw==\n" +
        "-----END CERTIFICATE-----\n" +
        "-----BEGIN CERTIFICATE-----\n" +
        "MIIF3TCCA8WgAwIBAgITXAAAAALRq61XLgYZigAAAAAAAjANBgkqhkiG9w0BAQ0F\n" +
        ...
        "7ulMCI8RqFm3p32fs//+8o0=\n" +
        "-----END CERTIFICATE-----\n" +
        "-----BEGIN CERTIFICATE-----\n" +
        "MIIFEzCCAvugAwIBAgIQORoOm2GoxqBGK3xSM9br+zANBgkqhkiG9w0BAQ0FADAc\n" +
        ...
        "mBSMIdx3Iw==\n" +
        "-----END CERTIFICATE-----\n";

String privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
        "MIIEogIBAAKCAQEAstlhsvxwbG8fVawH++HXq7mrqy9xfjIWwD45JAJSlstBBoBE\n" +
        ...
        "TovqdueB5W7DR0FVYoxmLj3vcG6fy/j9f+O9fb/mo94Ma39Px3I=\n" +
        "-----END RSA PRIVATE KEY-----";

String rootCA = "-----BEGIN CERTIFICATE-----\n" +
        "MIIFEzCCAvugAwIBAgIQORoOm2GoxqBGK3xSM9br+zANBgkqhkiG9w0BAQ0FADAc\n" +
        ...
        "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 个证书,但只解析为一个。

如果我通过运行 append 证书(带链)到私钥:

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 配置/设置已被验证为正确。密钥库是问题所在。)

首先,您的代码在修复明显的修订后无法编译。 它在同一个 scope 中具有String cert =final X509Certificate cert = ,并且对未定义的certificate的引用。 我假设第一个cert应该是certificate 鉴于:

PEMParser.readObject()读取一个 PEM-block 这就是为什么它以单数“对象”命名的原因,它是一个,单独的,单独的,而不是复数,多个或多个。

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

/*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()]);

PS:有一些方法可以完全或部分自动地按计划运行“命令行”程序,但这对于 SO 来说是题外话。 也可以从 Java 运行和控制“命令行”程序,但你没有问这个。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM