14.2.2 SSL Sockets
SSL client sockets are obtained from the SSLSocketFactory
class javax.net.ssl.SSLSocketFactory
, which extends the SocketFactory
class javax.net.SocketFactory
. The SSLSocketFactory
class overrides the getDefault
method to provide a factory that produces SSL sockets: public static SocketFactory getDefault
Obtain the default SSL socket factory for this implementation. That factory can be used to obtain SSL sockets. The default implementation is defined in the JREHOMElibsecurityjava.security file by
the property ssl.SocketFactory.provider
. If this is not set by default, it is not, a hardwired, internal implementation is used.
Like its server analogue, the ssl.SocketFactory.provider
property is ignored in the exportable version of JSSE. The default socket factory will handle both SSL 3.0 and TLS 1.0
protocols. Heres how we can write a simple client:
package javasec.samples.ch14; import java.io.;
import java.net.; import javax.net.;
import javax.net.ssl.; public class SSLSimpleClient {
public static void mainString[] args throws Exception { SocketFactory sf = SSLSocketFactory.getDefault ;
Socket s = sf.createSocketargs[0], Integer.parseIntargs[1]; BufferedReader br = new BufferedReader
new InputStreamReader s.getInputStream ;
PrintWriter pw = new PrintWriters.getOutputStream ; System.out.printlnWho is Sylvia?;
pw.printlnWho is Sylvia?; pw.flush ;
System.out.printlnbr.readLine ; s.close ;
} }
Note again that we used the SSLSocketFactory
class to obtain the socket factory; to use this client to connect to a non−SSL server, wed change the first line of the
main method to use the
SocketFactory class instead.
The socket returned from the SSL socket factory will be an instance of the SSLSocket
class javax.net.ssl.SSLSocket
, which extends the Socket
class. For the most part you can treat it like any other socket, but we will look at some advanced ways of handling the SSL socket a little later.
To run this program, we must supply the host and port that we want to contact. More importantly, the server will present its certificate to us, and we must have the root certificate of the servers CA in our truststore.
Using the truststore we created earlier, we can run the client with this command:
piccolo java −Djavax.net.ssl.trustStore=HOME.truststore \ javasec.samples.ch14.SSLSimpleClient localhost 9096
Who is Sylvia? What is she?
The first line of output is the string we sent to the server; the second line is the string returned by the server. At this point, weve developed a very simple server and client that communicate via SSL. Weve relied on the
SSL implementation to provide many of the details of this communication for us: the certificates used for verification, the cipher suites used, and so on. In the next few sections, well look at the SSL support classes
that allow us to modify these things.
14.3 SSL Sessions
When an SSL socket is connected, it joins an SSL session. Every SSL socket belongs to one SSL session. If there are many connections between a client and server they may share an SSL session, but there is no
programmatic way to determine which sockets are attached to which sessions. From our perspective, the SSL session is just an object that allows us to retrieve certain information about a particular SSL connection.
Specifically, the SSL session allows us to perform a necessary but often ignored step in the verification of an SSL peer.
In our first example, the server has sent its certificate to the client. If the client recognized the CA that issued the servers certificate, we allowed communication to proceed. However, as weve mentioned before, just
because someone has a valid certificate does not necessarily mean that they can be trusted. I can get a valid certificate for www.myevilenterprise.com and then spoof your DNS server so that when you attempt to
connect to shopping.oreilly.com, you get connected to me. Just because I present a certificate to you is no assurance that I am authorized to take your credit card information.
So when you make an SSL connection, you should verify that the server certificate contains the information you expect. Typically, this means that the name within the certificate should be the domain name of the
machine to which youre connecting. If your server requires clients to authenticate themselves, the server should follow this procedure with the clients certificate as well.
To verify this, you must use an object that implements the SSLSession
interface javax.net.ssl.SSLSession
. SSL session objects are retrieved from the SSLSocket
class using this method:
public SSLSession getSession Retrieve the SSL session associated with this SSL socket.
Because this method operates on an SSL socket and not a generic socket, well have to modify the protocol−independent technique we used in our first client in order to use it. Now when we get a socket back
from the SSLSocketFactory
, well have to cast it to an SSLSocket
object. Nonetheless, verifying the name in the servers certificate is important enough to warrant the protocol−specific code.
The SSLSession
interface contains the following methods: public byte[] getId
Return the arbitrary identifier assigned to this session. Chapter 14. SSL and HTTPS
public SSLSessionContext getSessionContext Return the session context associated with this session. The session context simply groups together
multiple SSL sessions; we wont use it in our examples.
public long getCreationTime Return the time at which this SSL session was created.
public long getLastAccessedTime Return the time at which this SSL session was last accessed i.e., when a new connection was last
added to the session.
public void invalidate Invalidate this session. Existing connections will not be affected by this call; new connections will
need to join a new session.
public void putValueString name, Object value Bind an object into this session, associated with the given name.
public Object getValueString name Return the bound object associated with the given name.
public void removeValueString name Remove the bound object associated with the given name.
public String[] getValueNames Retrieve the names of all bound objects.
public javax.security.cert.X509Certificate[] getPeerCertificateChain Retrieve the certificate chain that the peer sent in order to verify itself to us. This method throws an
SSLPeerUnverifiedException in two cases: if the server executes this method but does not
require the client to authenticate itself, and if the certificates presented by the peer are not recognized that is, if the root certificate does not correspond to a certificate held in the truststore.
public String getCipherSuite Return the name of the cipher suite that the peers negotiated.
public String getPeerHost Return the name of the host to which this socket is connected.
Unless youre writing your own implementation of JSSE, the last three methods are really the only ones that are useful. The SSL protocol defines a technique by which SSL connections can be reestablished without a
full time−consuming handshake, and SSL sessions can be used to provide that. However, that detail is 272
dependent on the JSSE implementation and is not something we can change programatically. We will use the last three methods to modify our original client. We can use the relevant methods of the SSL
session to retrieve information and complete the verification of the server. Changes to our original client are shown in bold:
package javasec.samples.ch14; import java.io.;
import java.net.; import javax.net.;
import javax.net.ssl.;
import javax.security.cert.;
public class SSLClientVerifier { public static void mainString[] args throws Exception {
SocketFactory sf = SSLSocketFactory.getDefault ;
SSLSocket s = SSLSocket sf.createSocket args[0], Integer.parseIntargs[1];
SSLSession sess = s.getSession ; String host = sess.getPeerHost ;
X509Certificate[] certs = sess.getPeerCertificateChain ; String dn = certs[0].getSubjectDN.getName ;
X500Name name = new X500Namedn; if host.equalsname.getCN
System.err.printlnWarning: Expected + host + and got + name.getCN ;
BufferedReader br = new BufferedReader new InputStreamReader
s.getInputStream ; PrintWriter pw = new PrintWriters.getOutputStream ;
System.out.printlnWho is Sylvia?; pw.printlnWho is Sylvia?;
pw.flush ; System.out.printlnbr.readLine ;
s.close ; }
}
We use the session object to retrieve the servers certificate. If the common name in the servers certificate is not equal to its hostname, we print out a warning message. It is conceivable that the name we used to
construct the socket may not exactly match the name embedded within the certificate, so we should obtain the IP addresses of both and ensure that they are the same. Thats the technique well use a little later when we
perform HTTP client verification. Note that this is the same thing a browser does when the name within a site certificate does not match the name within the URL.
If you run this with the test keystore created earlier youll see the warning message, but if you create another keystore with the common name equal to the hostname of your server, this code will execute without the
warning.
14.4 SSL Contexts and Key Managers