Skip to content

Commit 8ea2547

Browse files
authored
bael-9167 - Using Different Certificates on Specific Connections in Java (#18646)
* bael-9167 - ready for review * bael-9167 review 1 * fixing newlines * related to !18657 https://team.baeldung.com/browse/JAVA-47895
1 parent 5d9d19e commit 8ea2547

File tree

7 files changed

+496
-1
lines changed

7 files changed

+496
-1
lines changed

core-java-modules/core-java-networking-6/pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
<artifactId>commons-net</artifactId>
6666
<version>${commons-net.version}</version>
6767
</dependency>
68+
<dependency>
69+
<groupId>org.wiremock</groupId>
70+
<artifactId>wiremock</artifactId>
71+
<version>${wiremock.version}</version>
72+
<scope>test</scope>
73+
</dependency>
6874
</dependencies>
6975

7076
<build>
@@ -82,6 +88,7 @@
8288
<okhttp.version>4.12.0</okhttp.version>
8389
<webflux.version>3.4.3</webflux.version>
8490
<commons-net.version>3.8.0</commons-net.version>
91+
<wiremock.version>3.13.0</wiremock.version>
8592
</properties>
8693

87-
</project>
94+
</project>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.baeldung.multiplecerts;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.nio.file.Files;
6+
import java.nio.file.Path;
7+
import java.security.KeyStore;
8+
import java.security.KeyStoreException;
9+
import java.security.NoSuchAlgorithmException;
10+
import java.security.UnrecoverableKeyException;
11+
import java.security.cert.CertificateException;
12+
import java.util.stream.Stream;
13+
14+
import javax.net.ssl.KeyManagerFactory;
15+
import javax.net.ssl.TrustManagerFactory;
16+
import javax.net.ssl.X509KeyManager;
17+
import javax.net.ssl.X509TrustManager;
18+
19+
public class CertUtils {
20+
21+
private CertUtils() {
22+
}
23+
24+
private static KeyStore loadKeyStore(Path path, String password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
25+
KeyStore store = KeyStore.getInstance(path.toFile(), password.toCharArray());
26+
try (InputStream stream = Files.newInputStream(path)) {
27+
store.load(stream, password.toCharArray());
28+
}
29+
return store;
30+
}
31+
32+
public static X509KeyManager loadKeyManager(Path path, String password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
33+
KeyStore store = loadKeyStore(path, password);
34+
35+
KeyManagerFactory factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
36+
factory.init(store, password.toCharArray());
37+
38+
return (X509KeyManager) Stream.of(factory.getKeyManagers())
39+
.filter(X509KeyManager.class::isInstance)
40+
.findAny()
41+
.orElseThrow(() -> new IllegalStateException("no appropriate manager found"));
42+
}
43+
44+
public static X509TrustManager loadTrustManager(Path path, String password) throws IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException {
45+
KeyStore store = loadKeyStore(path, password);
46+
47+
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
48+
factory.init(store);
49+
50+
return (X509TrustManager) Stream.of(factory.getTrustManagers())
51+
.filter(X509TrustManager.class::isInstance)
52+
.findAny()
53+
.orElseThrow(() -> new IllegalStateException("no appropriate manager found"));
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.baeldung.multiplecerts;
2+
3+
import java.net.Socket;
4+
import java.security.Principal;
5+
import java.security.PrivateKey;
6+
import java.security.cert.X509Certificate;
7+
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
import javax.net.ssl.SSLEngine;
14+
import javax.net.ssl.SSLSession;
15+
import javax.net.ssl.SSLSocket;
16+
import javax.net.ssl.X509ExtendedKeyManager;
17+
import javax.net.ssl.X509KeyManager;
18+
19+
public class RoutingKeyManager extends X509ExtendedKeyManager {
20+
21+
private final Map<String, X509KeyManager> hostMap = new HashMap<>();
22+
23+
public void put(String host, X509KeyManager manager) {
24+
hostMap.put(host, manager);
25+
}
26+
27+
private X509KeyManager select(String host) {
28+
X509KeyManager manager = hostMap.get(host);
29+
if (manager == null)
30+
throw new IllegalArgumentException("key manager not found for " + host);
31+
32+
return manager;
33+
}
34+
35+
@Override
36+
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
37+
if (socket instanceof SSLSocket sslSocket) {
38+
String host = host(socket, sslSocket);
39+
return select(host).chooseClientAlias(keyType, issuers, socket);
40+
}
41+
42+
throw new UnsupportedOperationException("unsupported socket");
43+
}
44+
45+
@Override
46+
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
47+
String host = engine.getPeerHost();
48+
return select(host).chooseClientAlias(keyType, issuers, (Socket) null);
49+
}
50+
51+
@Override
52+
public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
53+
String host = engine.getPeerHost();
54+
return select(host).chooseServerAlias(keyType, issuers, (Socket) null);
55+
}
56+
57+
@Override
58+
public X509Certificate[] getCertificateChain(String alias) {
59+
return select(alias).getCertificateChain(alias);
60+
}
61+
62+
@Override
63+
public PrivateKey getPrivateKey(String alias) {
64+
return select(alias).getPrivateKey(alias);
65+
}
66+
67+
@Override
68+
public String[] getClientAliases(String keyType, Principal[] issuers) {
69+
List<String> aliases = new ArrayList<>();
70+
71+
hostMap.forEach((host, km) -> aliases.addAll(Arrays.asList(km.getClientAliases(keyType, issuers))));
72+
return aliases.toArray(new String[] {});
73+
}
74+
75+
@Override
76+
public String[] getServerAliases(String keyType, Principal[] issuers) {
77+
List<String> list = new ArrayList<>();
78+
79+
hostMap.forEach((host, km) -> list.addAll(Arrays.asList(km.getServerAliases(keyType, issuers))));
80+
return list.toArray(new String[] {});
81+
}
82+
83+
@Override
84+
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
85+
if (socket instanceof SSLSocket sslSocket) {
86+
String host = host(socket, sslSocket);
87+
return select(host).chooseServerAlias(keyType, issuers, socket);
88+
}
89+
90+
throw new UnsupportedOperationException("unsupported socket");
91+
}
92+
93+
private String host(Socket socket, SSLSocket sslSocket) {
94+
SSLSession session = sslSocket.getHandshakeSession();
95+
return session != null ? session.getPeerHost()
96+
: socket.getInetAddress()
97+
.getHostName();
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.baeldung.multiplecerts;
2+
3+
import java.io.IOException;
4+
import java.nio.file.Paths;
5+
import java.security.KeyManagementException;
6+
import java.security.KeyStoreException;
7+
import java.security.NoSuchAlgorithmException;
8+
import java.security.UnrecoverableKeyException;
9+
import java.security.cert.CertificateException;
10+
11+
import javax.net.ssl.KeyManager;
12+
import javax.net.ssl.SSLContext;
13+
import javax.net.ssl.TrustManager;
14+
15+
public class RoutingSslContextBuilder {
16+
17+
private final RoutingKeyManager routingKeyManager;
18+
private final RoutingTrustManager routingTrustManager;
19+
20+
public RoutingSslContextBuilder() {
21+
routingKeyManager = new RoutingKeyManager();
22+
routingTrustManager = new RoutingTrustManager();
23+
}
24+
25+
public static RoutingSslContextBuilder create() {
26+
return new RoutingSslContextBuilder();
27+
}
28+
29+
public RoutingSslContextBuilder trust(String host, String certsDir, String password) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
30+
routingTrustManager.put(host, CertUtils.loadTrustManager(Paths.get(certsDir, "trust." + host + ".p12"), password));
31+
routingKeyManager.put(host, CertUtils.loadKeyManager(Paths.get(certsDir, "client." + host + ".p12"), password));
32+
return this;
33+
}
34+
35+
public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException {
36+
SSLContext context = SSLContext.getInstance("TLS");
37+
context.init(new KeyManager[] { routingKeyManager }, new TrustManager[] { routingTrustManager }, null);
38+
39+
return context;
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.baeldung.multiplecerts;
2+
3+
import java.net.Socket;
4+
import java.security.cert.CertificateException;
5+
import java.security.cert.X509Certificate;
6+
import java.util.ArrayList;
7+
import java.util.Arrays;
8+
import java.util.HashMap;
9+
import java.util.List;
10+
import java.util.Map;
11+
12+
import javax.net.ssl.SSLEngine;
13+
import javax.net.ssl.SSLSession;
14+
import javax.net.ssl.SSLSocket;
15+
import javax.net.ssl.X509ExtendedTrustManager;
16+
import javax.net.ssl.X509TrustManager;
17+
18+
public class RoutingTrustManager extends X509ExtendedTrustManager {
19+
20+
private final Map<String, X509TrustManager> hostMap = new HashMap<>();
21+
22+
public void put(String host, X509TrustManager manager) {
23+
hostMap.put(host, manager);
24+
}
25+
26+
private X509TrustManager select(String host) {
27+
X509TrustManager manager = hostMap.get(host);
28+
if (manager == null)
29+
throw new IllegalArgumentException("trust manager not found for " + host);
30+
31+
return manager;
32+
}
33+
34+
@Override
35+
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
36+
String host = host(socket);
37+
select(host).checkServerTrusted(chain, authType);
38+
}
39+
40+
@Override
41+
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
42+
String host = engine.getPeerHost();
43+
select(host).checkServerTrusted(chain, authType);
44+
}
45+
46+
@Override
47+
public X509Certificate[] getAcceptedIssuers() {
48+
List<X509Certificate> list = new ArrayList<>();
49+
50+
hostMap.forEach((host, km) -> list.addAll(Arrays.asList(km.getAcceptedIssuers())));
51+
return list.toArray(new X509Certificate[] {});
52+
}
53+
54+
@Override
55+
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
56+
String host = host(socket);
57+
select(host).checkClientTrusted(chain, authType);
58+
}
59+
60+
@Override
61+
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
62+
String host = engine.getPeerHost();
63+
select(host).checkClientTrusted(chain, authType);
64+
}
65+
66+
@Override
67+
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
68+
throw new UnsupportedOperationException("socket is required");
69+
}
70+
71+
@Override
72+
public void checkClientTrusted(X509Certificate[] chain, String authType) {
73+
throw new UnsupportedOperationException("socket is required");
74+
}
75+
76+
private String host(Socket socket) {
77+
if (socket instanceof SSLSocket sslSocket) {
78+
SSLSession session = sslSocket.getHandshakeSession();
79+
return session != null ? session.getPeerHost() : null;
80+
}
81+
return null;
82+
}
83+
}

0 commit comments

Comments
 (0)