Accept Server'S Self-Signed Ssl Certificate in Java Client

Accept server's self-signed ssl certificate in Java client

You have basically two options here: add the self-signed certificate to your JVM truststore or configure your client to

Option 1

Export the certificate from your browser and import it in your JVM truststore (to establish a chain of trust):

<JAVA_HOME>\bin\keytool -import -v -trustcacerts
-alias server-alias -file server.cer
-keystore cacerts.jks -keypass changeit
-storepass changeit

Option 2

Disable Certificate Validation:

// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};

// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (GeneralSecurityException e) {
}
// Now you can access an https URL without having the certificate in the truststore
try {
URL url = new URL("https://hostname/index.html");
} catch (MalformedURLException e) {
}

Note that I do not recommend the Option #2 at all. Disabling the trust manager defeats some parts of SSL and makes you vulnerable to man in the middle attacks. Prefer Option #1 or, even better, have the server use a "real" certificate signed by a well known CA.

Java Rest Client using self-signed Certificate

first of all thank you all for your help. I finally got it to work following these steps:
1 - I determined my root CA Cert using command:

openssl s_client -showcerts -connect my.root.url.com:443

then I imported this certificate using Portecle.exe (https://sourceforge.net/projects/portecle/) but you can also import it using the normal keytool command, into my default Java Keystore (jre/lib/security/cacerts)
--> Make sure you assign the root URL as Alias (e.g. *.google.com if you would connect to a google API). This seems to be very important.

Then I used the following code:
First created the ServerSocketFactory:

private static SSLSocketFactory getSocketFactory() 
{
try
{
SSLContext context = SSLContext.getInstance("TLS");

// Create a key manager factory for our personal PKCS12 key file
KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = pk12Password.toCharArray(); // --> This is the Password for my P12 Client Certificate
keyStore.load(new FileInputStream(pk12filePath), keyStorePassword); // --> This is the path to my P12 Client Certificate
keyMgrFactory.init(keyStore, keyStorePassword);

// Create a trust manager factory for the trust store that contains certificate chains we need to trust
// our remote server (I have used the default jre/lib/security/cacerts path and password)
TrustManagerFactory trustStrFactory = TrustManagerFactory.getInstance("SunX509");
KeyStore trustStore = KeyStore.getInstance("JKS");
char[] trustStorePassword = jksTrustStorePassword.toCharArray(); // --> This is the Default password for the Java KEystore ("changeit")
trustStore.load(new FileInputStream(trustStorePath), trustStorePassword);
trustStrFactory.init(trustStore);

// Make our current SSL context use our customized factories
context.init(keyMgrFactory.getKeyManagers(),
trustStrFactory.getTrustManagers(), null);

return context.getSocketFactory();
}
catch (Exception e)
{
System.err.println("Failed to create a server socket factory...");
e.printStackTrace();
return null;
}
}

Then I created the connection using:

public static void launchApi() 
{
try
{
// Uncomment this if your server cert is not signed by a trusted CA
HostnameVerifier hv = new HostnameVerifier()
{
public boolean verify(String urlHostname, SSLSession session)
{
return true;
}};

HttpsURLConnection.setDefaultHostnameVerifier(hv);


URL url = new URL("https://myRootUrl.com/to/launch/api");

HttpsURLConnection.setDefaultSSLSocketFactory(getSocketFactory());
HttpsURLConnection urlConn = (HttpsURLConnection)url.openConnection();

String encoded = Base64.getEncoder().encodeToString((userName+":"+userPwd).getBytes(StandardCharsets.UTF_8)); //Acc User Credentials if needed to log in
urlConn.setRequestProperty ("Authorization", "Basic "+encoded);
urlConn.setRequestMethod("GET"); // Specify all needed Request Properties:
urlConn.setRequestProperty("Accept", "application/json;charset=UTF8");
urlConn.setRequestProperty("Cache-Control", "no-cache");

urlConn.connect();

/* Dump what we have found */
BufferedReader in =
new BufferedReader(
new InputStreamReader(urlConn.getInputStream()));
String inputLine = null;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}

This is what worked for me. Thank you all, and also thanks to:
this article that guided me on the right direction

Ciao

Java SSLServerSocket: Request client certificate allowing ANY client certificate

So now it's working.

First I was forcing handshake only on client side. It seems to be OK, but adding it in server side produces better error messages.

So I added on server side this last line after accepting connection (Groovy code):

ss.setNeedClientAuth(true)
SSLSocket socket = ss.accept() as SSLSocket
socket.startHandshake()

Then I got a more indicating error when a client arrives:

Exception in thread "main" javax.net.ssl.SSLException: Cannot read the array length because "caCerts" is null
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:133)
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.SSLSocketImpl.handleException(SSLSocketImpl.java:1702)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:465)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:421)
at javax.net.ssl.SSLSocket$startHandshake.call(Unknown Source)

So after a while I found something suspicious in the piece of code to accept connections from ANY certificate:

def x509TrustAllCerts = new X509TrustManager() {
X509Certificate[] getAcceptedIssuers() {
return null // <--- SUSPICIOUS!!
}
void checkClientTrusted(X509Certificate[] certs, String authType) { }
void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
TrustManager[] trustAllCerts = new TrustManager[] { x509TrustAllCerts }

So I made the simplest fix changing null to empty array:

def x509TrustAllCerts = new X509TrustManager() {
X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0] // <-- FIX
}
void checkClientTrusted(X509Certificate[] certs, String authType) { }
void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
TrustManager[] trustAllCerts = new TrustManager[] { x509TrustAllCerts }

Now it's working the way I intended.

Java SSL connection with self-signed certificate without copying complete keystore to client

You shouldn't be generating a public-private key pair, but rather import the certificate of the server into your (the client's) Java truststore. The certificate is not a secret, and thus does not provide a security risk on the client side. See the -import option for keytool. Here's in an example.

Self-signed Certificate and Client Keystore for SSL Authentication

Not an answer yet, but getting too complicated for comments so I'll give a start and edit later.

Should (SSL/TLS) server(s) and client(s) share a key (and cert)? Okay for development and maybe test, varies for production. As a general rule every independent system that needs to be authenticated should have its own privatekey and a certificate for that key (or sometimes but rarely multiple certs). If both (or more generally all) the systems involved and the communication between them will be under control of the same administrator, it doesn't really hurt to relax this rule and (re)use one key+cert. Since the key+cert(s) used should always be (re)configurable, this is a decision you can change later, even change several times if that turns out to be desirable or necessary.

Should the cert(s) be self-signed? Usually good for development and test, varies for production. Again if all relevant systems are under control of one admin, or at least a limited group of people who know each other (such as divisions or offices of one organization) there's no real security need for a CA to verify identities and determine trust. One consideration is that the server cert must encode the domain-name(s) (or IP address(es) if you use that option) in the cert, so changing them, which is often needed in development and test, means going back to the CA for a new cert, at least a minor nuisance and sometimes a delay.

At the moment you have one key and selfsigned cert you are trying to share for both client and server. While you may well want to change these later, I would recommend focussing on getting something working first. Changing several interrelated things at the same time tends to cause confusion.

"Extract" a cert? For the current setup, with one (shared) key+cert (selfsigned), you already have the key+cert in a JKS, the same key+cert in a P12, and the same cert in a file (cert.crt) so you don't need to extract anything. If you (later) generate for the Java (client) side a new key and selfsigned cert using keytool, yes you would need to extract that cert.
If you obtain a CA-signed cert for either the existing key or a new key, you might need to extract the root/anchor, but you might already have it, see below.

What cert(s) is needed to trust? The client always (with minor exceptions not relevant here) needs in its truststore the anchor for the server's cert, and when client authentication (also called mutual or two-way) is used as in your case the server needs in its truststore the anchor for the client's cert. In your current case the client and server use the same cert, so both sides need the same anchor.

  • For a self-signed cert the anchor must be the cert itself -- which must be changed, usually manually, any time the partner cert changes.

  • For a CA-issued cert the anchor can be and (always?) should be the root cert for that CA; the CA root is usually long-lived (like 20 years and more) and does not need to change when a partner/entity cert does.

  • If you use a well-known CA like Verisign or GoDaddy, as opposed to a CA run by yourself or maybe by "Joe's Dicount Liquor Store and Certificate Authority" in your local red-light district, in some systems the roots for those well-known CAs may be already installed in which case you don't need to do anything.

How did the client work to trust the server? Your Java client apparently used the client.jks file as both keystore and truststore. This can easily work because Java uses one file format that can contain both kinds of data, and further treats the cert in an own-key entry as also a trusted cert (anchor).

What about the server truststore? On the other hand, software that uses pkcs12 format for a keystore, or at least to import a keystore entry, sometimes (I'd say often) does not use that same format for its truststore, and certainly not the same file(s). The fact that it did reject client use of the same cert it already has as its own probably means it doesn't treat the pkcs12 you gave it as truststore data, although in principle it could be disliking something else such as the lack of an ExtendedKeyUsage extension or the unavailability of revocation/status data.

You don't identify or describe the device which is your server, so I can only guess at the myriad of ways that SSL/TLS servers, especially embedded ones, can work. But somewhere in your device there is probably a way to add certs to its truststore. Also it should have some kind of error log somewhere that may contain additional information about whether it just needs the anchor (here the selfsigned cert) in the truststore, or something else -- assuming you can see that log without SSL/TLS authentication!

If you can give us (a link to) doc on your server/device, or tell us in sufficient detail what it shows, offers, and/or accepts, I can probably be more specific.



Related Topics



Leave a reply



Submit