I'm trying to connect to Redis using TLS, and it works fine for a keystore that has only a single cert inside of it.
The problem is, if I have multiple certs imported to my keystore, how does it know to choose the correct alias to pull the correct key?
I implemented my own X509KeyManager to see how it works, and the chooseClientAlias(String[] strings, Principal[] prncpls, Socket socket)
method appears to be passed an empty array for prncples, which I'd presume would be how it could tell what cert to use.
But since that is empty, it simply returns whatever the first alias is that matches the keytype specified in the strings input, aka RSA, and that first alias might not be the correct one (which then ends up with it picking the incorrect key, and the ssl connection fails).
Is there something I'm misunderstanding about how this should be working to choose the correct alias for the connection, like do I need to be creating a different SSL Socket Factory & KeyManager for every SSL application I interface with, and explicitly specify the alias to use? Sorry, I'm not super well versed in TLS with java. Thanks.
Commands I used to generate the certs (ran this twice to create the real test cert, and a random fake cert which I imported after the real one to test if it would pick the right alias):
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
Commands I used to import the certs to a keystore:
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
Code I used to interface with Redis (taken basically straight off their websites example):
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();
}
Information:
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