Reputation: 51
I have a spring-boot application and I am trying to open a secure websocket connection to a server, but I keep getting an error due to: No subject alternative names present.
Here is what I have tried:
StandardWebSocketClient client = new StandardWebSocketClient();
SSLContext sslContext = this.getSslContext("path/to/truststore", "path/to/keystore.p12", "password");
client.getUserProperties().put(SSL_CONTEXT_PROPERTY, sslContext);
WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
headers.add("Sec-WebSocket-Key", "SGVsbG8sIHdvcmxkIQ==");
headers.add("Origin", "https://192.168.1.132:8445");
headers.add("Sec-WebSocket-Version", "13");
String url = "wss://192.168.1.132:8445/websocket";
URI uri = new URI(url);
ListenableFuture<WebSocketSession> future = client.doHandshake(new AbstractWebSocketHandler() {
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
receivedMessages += message.getPayload();
System.out.println(message.getPayload());
}
}, headers, uri);
WebSocketSession socketSession = future.get();
And here is the getSslContext method:
private SSLContext getSslContext(String trustStoreFile, String keystoreFile, String password)
throws GeneralSecurityException, IOException {
KeyStore keystore = KeyStore.getInstance("JKS");
try (InputStream in = new FileInputStream(keystoreFile)) {
keystore.load(in, password.toCharArray());
}
try (InputStream in = new FileInputStream(trustStoreFile)) {
keystore.load(in, password.toCharArray());
}
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keystore, password.toCharArray());
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keystore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(
keyManagerFactory.getKeyManagers(),
trustManagerFactory.getTrustManagers(),
new SecureRandom());
return sslContext;
}
Given this code, I am always getting the following error:
Caused by: java.security.cert.CertificateException: No subject alternative names present
at java.base/sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:141)
at java.base/sun.security.util.HostnameChecker.match(HostnameChecker.java:100)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:455)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:429)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:283)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:141)
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1302)
I have also tried to replicate the request using the following curl command:
curl --include --no-buffer --header "Connection: Upgrade" --header "Upgrade: websocket" --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" --header "Origin: https://192.168.1.132:8445" --header "Sec-WebSocket-Version: 13" -Ss --cacert myCA.pem --cert SSLCert.pem:`cat SSLCert.pass` https://192.168.1.132:8445/websocket
The handshake seems to be successful:
HTTP/1.1 101
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
X-Application-Context: panel-command-server:default,postgres,cassandra,kafka:8080
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: qGEgH3En71di5rrssAZTmtRTyFk=
Any ideas on what might be the reason behind the No subject alternative names present error from the spring application?
Upvotes: 1
Views: 1799
Reputation: 51
Managed to fix the issue by overriding some methods from the trust manager to bypass the checks like in this answer: How to bypass ssl certificate checking in java.
This is the final version of the getSslContext I used:
private SSLContext getSslContext(String trustStoreFile, String keystoreFile, String password)
throws GeneralSecurityException, IOException {
KeyStore keystore = KeyStore.getInstance("JKS");
try (InputStream in = new FileInputStream(keystoreFile)) {
keystore.load(in, password.toCharArray());
}
try (InputStream in = new FileInputStream(trustStoreFile)) {
keystore.load(in, password.toCharArray());
}
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keystore, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);
// Get hold of the default trust manager
X509TrustManager x509Tm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
x509Tm = (X509TrustManager) tm;
break;
}
}
// Wrap it in your own class.
final X509TrustManager finalTm = x509Tm;
X509ExtendedTrustManager customTm = new X509ExtendedTrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(
keyManagerFactory.getKeyManagers(),
new TrustManager[]{customTm},
new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
return sslContext;
}
Upvotes: 3