使用Java中的Jedis,如果您的密钥库中有多个密钥,它如何为SSL选择正确的别名?

问题描述 投票:0回答:1

我正在尝试使用 TLS 连接到 Redis,对于内部只有一个证书的密钥库来说它工作得很好。

问题是,如果我将多个证书导入到我的密钥库中,它如何知道选择正确的别名来提取正确的密钥?

我实现了自己的 X509KeyManager 来看看它是如何工作的,以及

chooseClientAlias(String[] strings, Principal[] prncpls, Socket socket)
方法似乎传递了一个 prncples 的空数组,我想这就是它如何知道要使用哪个证书的方法。

但由于它是空的,它只是返回与字符串输入中指定的密钥类型(又名 RSA)匹配的第一个别名,并且第一个别名可能不是正确的(最终会选择错误的密钥,并且 ssl 连接失败)。

我是否误解了如何为连接选择正确的别名,例如我是否需要为我接口的每个 SSL 应用程序创建不同的 SSL 套接字工厂和 KeyManager,并显式指定别名使用?抱歉,我不太熟悉 TLS 和 java。谢谢。


我用来生成证书的命令(运行两次以创建真正的测试证书,以及我在真实证书之后导入的随机假证书,以测试它是否会选择正确的别名):

Create CA:
===
"C:\Program Files\Git\mingw64\bin\openssl.exe" genrsa -out ca.key 2048
"C:\Program Files\Git\mingw64\bin\openssl.exe" req -new -x509 -sha256 -key ca.key -out ca.crt

Create Redis Server Cert:
===
"C:\Program Files\Git\mingw64\bin\openssl.exe" genrsa -out redis.key
"C:\Program Files\Git\mingw64\bin\openssl.exe" req -new -sha256 -key redis.key -out redis.csr
"C:\Program Files\Git\mingw64\bin\openssl.exe" x509 -req -in redis.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out redis.crt -days 1000 -sha256

Create Client:
===
"C:\Program Files\Git\mingw64\bin\openssl.exe" genrsa -out client1.key 2048
"C:\Program Files\Git\mingw64\bin\openssl.exe" req -new -sha256 -key client1.key -out client1.csr
"C:\Program Files\Git\mingw64\bin\openssl.exe" x509 -req -in client1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client1.crt -days 1000 -sha256

我用来将证书导入密钥库的命令:

Add ca to truststore:
=====
keytool -import -alias redisCA -keystore keystore.jks -file ca.crt

generate pkcs12:
=====
openssl pkcs12 -export -in client1.crt -inkey client1.key -out keystore.p12 -name my_cert

Import pkcs12 cert/key to keystore:
=====
keytool -importkeystore -destkeystore keystore.jks -srckeystore keystore.p12 -srcstoretype PKCS12 -alias my_cert

我用来与 Redis 交互的代码(基本上直接从他们的网站示例中获取):

public void testWithTls() throws IOException, GeneralSecurityException {
        HostAndPort address = new HostAndPort("localhost", 6379);
        
        SSLSocketFactory sslFactory = createSslSocketFactory(
                "D:\\tmp\\keystore.jks",
                "123456",
                "D:\\tmp\\keystore.jks",
                "123456"
        );

        JedisClientConfig config = DefaultJedisClientConfig.builder()
                .ssl(true).sslSocketFactory(sslFactory)
                .build();

        JedisPooled jedis = new JedisPooled(address, config);
        jedis.set("foo", "bar");
        System.out.println(jedis.get("foo")); // prints bar
}
   
private static SSLSocketFactory createSslSocketFactory(
            String caCertPath, String caCertPassword, String userCertPath, String userCertPassword)
            throws IOException, GeneralSecurityException {

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(new FileInputStream(userCertPath), userCertPassword.toCharArray());

        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(new FileInputStream(caCertPath), caCertPassword.toCharArray());

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
        trustManagerFactory.init(trustStore);

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, userCertPassword.toCharArray());

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

        return sslContext.getSocketFactory();
}

信息:

Jedis version: 4.4.3
Redis Docker container version: redis:7.0.10
Redis Docker container run command: `redis-server --tls-port 6379 --port 0 --tls-cert-file /tls/redis.crt --tls-key-file /tls/redis.key --tls-ca-cert-file /tls/ca.crt --loglevel warning`
Why am I using a jks store and not the p12: Because thats what the company I work at uses
java ssl jedis
1个回答
0
投票

JDK 中没有针对此类用例的内置解决方案。我建议创建 2 个 SSLContext。对于每个密钥库 1 并使用它来调用 Jedis 服务器。

如果您愿意使用图书馆,那么这个选项是可以的。我创建了这种可以解决问题的选项。请参阅下面的代码片段:

import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.CertificateUtils;

import javax.net.ssl.SSLSocketFactory;
import java.nio.file.Paths;
import java.security.cert.Certificate;
import java.util.List;

public class App {
    public static void main(String[] args) {
        String caCertPath = "D:\\tmp\\ca-certs.crt";
        List<Certificate> certificates = CertificateUtils.loadCertificate(Paths.get(caCertPath));

        SSLFactory sslFactory = SSLFactory.builder()
                .withIdentityMaterial(Paths.get("D:\\tmp\\keystore-one.jks"), "123456".toCharArray())
                .withIdentityMaterial(Paths.get("D:\\tmp\\keystore-two.jks"), "123456".toCharArray())
                .withIdentityRoute("client-alias-one", "https://localhost:6379/", "https://localhost:6380/")
                .withIdentityRoute("client-alias-two", "https://localhost:6381/", "https://localhost:6382/")
                .withTrustMaterial(certificates)
                .build();

        SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();

        JedisClientConfig config = DefaultJedisClientConfig.builder()
                .ssl(true)
                .sslSocketFactory(sslSocketFactory)
                .build();
    }
    
}

在这种情况下,您需要指定在密钥库文件中为密钥命名的别名,并将其映射到特定主机和端口作为身份路由(也称为密钥材料路由)。这样,它将使用目标服务器列表的特定密钥。该库可以在这里找到:sslcontext-kickstart

© www.soinside.com 2019 - 2024. All rights reserved.