12

I have this code to connect the server with a client using SSL, and now I want to add client-side authentication:

(I have a server keystore (JCEKS type) and a client keystore (JKS type), the server uses a truststore (cacerts) where I imported both certificates because I also want to use this truststore for client authentication)

Client code:

System.setProperty("javax.net.ssl.trustStore", cerServer);
System.setProperty("javax.net.ssl.trustStoreType","JCEKS");
System.setProperty("javax.net.ssl.trustStorePassword", pwdCacerts);

SSLSocketFactory sslsocketfactory = (SSLSocketFactory)  SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("localhost", port);

Server Code:

KeyStore ks = LoadKeyStore(new File(serverKeyStore), pwdKeyStore, "JCEKS");
KeyManagerFactory kmf; 
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, pwdKeyStore.toCharArray());

SSLContext sc = SSLContext.getInstance("SSL");
sc.init(kmf.getKeyManagers(),null, null);   

SSLServerSocketFactory ssf = sc.getServerSocketFactory(); 
sslserversocket = (SSLServerSocket) ssf.createServerSocket(port);

thanks in advance for any help.

edit: I add this code in the server side:

System.setProperty("javax.net.ssl.trustStore", cacerts);
System.setProperty("javax.net.ssl.trustStoreType","JKS");
System.setProperty("javax.net.ssl.trustStorePassword", pwdCacerts);

but if I delete the client certificate in cacerts, the connection doesn't give me error and for that I think it's wrong that way

naxo
  • 153
  • 2
  • 2
  • 18
  • See also [Java HTTPS client certificate authentication](https://stackoverflow.com/questions/1666052/java-https-client-certificate-authentication) – Vadzim Dec 18 '18 at 13:17

1 Answers1

18

If you want your system to use client-certificate authentication, you'll need

  • the server to request (or require) a client certificate. This is done by setting setWantClientAuth(true) on the server socket (or setNeedClientAuth, respectively). You'll also need the server to advertise the CA it accepts, which is normally done by using a truststore on the server that contains the CA by which the client-certificate chain was issued (this seems to be what you've done by setting javax.net.ssl.trustStore* on the server).

  • the client to be configured with a keystore containing the client certificate (possible the chain if there are intermediate CAs) and its private key. This can be done by setting the javax.net.ssl.keyStore* (which may affect other connections) or by using a KeyManagerFactory in the same way as you've done it on the server side.

If you use setWantClientAuth(true), you might still not get an error, since the server will accept connections that don't have a client-certificate (the server would then check the SSLSession's peer certificates to see whether there was a cert or not). setNeedClientAuth(true) would break the connection when the client doesn't present a certificate.

user207421
  • 305,947
  • 44
  • 307
  • 483
Bruno
  • 119,590
  • 31
  • 270
  • 376
  • tyvm! but I dont understand why if I check sslsocket.getNeedClientAuth() it return false, then I put sslsocket.setNeedClientAuth(true) before the server reads the inputstream and it dont return error (and I deleted the client certificate in cacerts) – naxo Mar 17 '14 at 18:58
  • (`getNeedClientAuth` is just for reading the current setting). Are you talking about your server or client socket here? – Bruno Mar 17 '14 at 19:18
  • about my server socket, I want the server requires client autenticathion in a specific situation then I include there: "sslsocket.setNeedClientAuth(true)" ... but the connection inst lost as I expected (there inst client certificate in cacerts, because I deleted it before for testing) and the server continue reading the stream – naxo Mar 17 '14 at 19:43
  • 2
    Just to be clear, `setNeedClientAuth` is a *server* side setting only. I'll assume you're talking about the accepted socket here. You should be able to trigger a re-negotiation by calling `getSSLSession().invalidate()`. – Bruno Mar 17 '14 at 21:07
  • 2
    @Bruno I corrected your 2nd bullet point, please check. NB If he sets `wantClientAuth` or `needClientAuth` before he does any I/O he hasn't done the handshake yet anyway so he doesn't need to invalidate anything. – user207421 Mar 18 '14 at 01:28
  • 1
    @EJP, ah, of course, thank you! Just a typo, but I hope naxo wasn't too confused by this. You're also right about not needing to invalidate, I just wasn't sure what was meant by "*I want the server requires client autenticathion in a specific situation then I include there: "sslsocket.setNeedClientAuth(true)" ...*". – Bruno Mar 18 '14 at 02:27
  • yes, I think that´s the way, I'm talking about accepted socket in the server side so I need a re-negotiation ... my server requires client authentication depending on the client request: 1) the server first accept connections, 2) when the client connects, the server read the stream (in.readUTF), 3) if the read data are equals "private" then the server requires client authentication – naxo Mar 18 '14 at 11:09
  • @Bruno, @EJP, I'm trying whit this code: `if (in.readUTF().equalsIgnoreCase("private")) { SSLSession sslSession = sslsocket.getSession(); sslSession.invalidate(); sslsocket.setNeedClientAuth(true); [continue reading...] ` but I dont know how validate the session again after that. – naxo Mar 18 '14 at 11:17
  • 1
    You can call `startHandshake()` or even `getSession()` again to trigger a handshake (see `SSLSocket` documentation introduction). – Bruno Mar 18 '14 at 11:23
  • sorry if I didn't explain it well (Im a new developer and my English is bad) but I dont understand why the server can "continue reading" after above code without errors, I dont want it continue reading ultil the sever check the client certificate inside cacerts (and the certificate there isn´t there because I delete it for testing and get that error) – naxo Mar 18 '14 at 11:26
  • 1
    There might still be something in the input stream buffer, if it's already been sent by the client anyway. This might have to do with some of your custom protocol design instead. You more or less need to "synchronise" the TLS re-renegotiation with your protocol's actions if you want it all to happen in a certain order, otherwise, you're not quite sure when the handshake may happen with respect to your application protocol. In HTTP for example, the server tends to re-negotiate after receiving the client's request and blocking before sending the response. – Bruno Mar 18 '14 at 11:34
  • @Bruno, thanks now I understand, thanks for putting me on the right path, now I will read documentation about this and I will try finish it myself – naxo Mar 18 '14 at 11:47
  • 2
    After you invalidate the session, any further I/O will cause a new handshake: you don't actually need to call startHandshake() at all. If you want to check the certificate immediately at that point, without allowing any further I/O, just call SSLSocket.getSession() and get the peer certificate(a) from that. – user207421 Mar 18 '14 at 18:36
  • @EJP ty (I just read ur post), I've tried forcing the handshake (using the 3 ways described here: [link](http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLSocket.html)) and I only get errors and more errors... now Im trying this code at that point: `SSLSession sslSession = sslsocket.getSession(); sslsocket.setNeedClientAuth(true); sslSession.invalidate(); sslsocket.getSession().getPeerCertificates();` but get `peer not authenticated at sun.security.ssl.SSLSessionImpl.getPeerCertificates(Unknown Source)` – naxo Mar 19 '14 at 17:24
  • if I also setNeedClientAuth(true) in the initial handshake (when the socket is created) I dont get that error so I think the certificate its fine into cacerts. @Bruno forgive me for also notify you, but I´m stuck, I´m reading the oracle ssl documentation and trying it by myself but I only get errors – naxo Mar 19 '14 at 17:43