I am connecting to a service through my Java code. Let's call FQDN or hostname of service as: abc.xyz.com
.
My Java implementation returns exception:
java.net.UnknownHostException: dfour6gtsg5a.streaming.us-phoenix-1.oci.oraclecloud.com
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:567)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
at java.base/java.net.Socket.connect(Socket.java:633)
at java.base/sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:304)
at java.base/sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:174)
at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:183)
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:532)
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:637)
at java.base/sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:266)
at java.base/sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:380)
at
Now, when I include entry of FQDN of service and IPAddress in /etc/hosts
as below:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
...
100.70.125.57 abc.xyz.com
Same Java implementation works correctly as service FQDN get resolved to IP address correctly.
If I try to run Java implementation with IPAddress, I get below error:
javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address 100.70.125.57 found
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:371)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:314)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:309)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396)
In production environment, I will not have access to /etc/hosts
for editing.
How can I resolve the Service FQDN to the IPAddress inside my Java implementation?
The simplest way I found is to provide value true
to HttpsURLConnection.setHostnameVerifier()
You need to override the method public boolean verify(String hostname, SSLSession session)
for returning true
everytime.
Below is an example:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.Exception;
import java.net.URL;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
public class SkipVerification implements Runnable {
private static final String TAG = SkipVerification.class.toString();
private String server_port;
private String serverIP;
public SkipVerification(String serverIP, String server_port){
this.serverIP = serverIP;
this.server_port = server_port;
}
public void run() {
try {
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
URL url = new URL("https://" + serverIP + ":" + server_port + "/json");
InputStream inStream = null;
try {
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setHostnameVerifier(hostnameVerifier);
inStream = urlConnection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
} catch (Exception e) {
Log.e(TAG, "error fetching data from server", e);
} finally {
if (inStream != null) {
inStream.close();
}
}
} catch (Exception e) {
Log.e(TAG, "error initializing SkipVerificationn thread", e);
}
}
}
This works when using HttpsURLConnection
class. For other implementation, please refer to comment to question by @Elliott Frisch.