} protected void engineInitKeyStore ks, char[] pw {
this.ks = ks; this.pw = new char[pw.length];
System.arraycopypw, 0, this.pw, 0, pw.length; }
}
The key manager factory expects a keystore and a password but not an alias, so weve had to specify the alias via a system property. In general, if you need other initialization fields in your own key manager, this is the
way to get them.
This engine class requires a security provider, of course; well use the provider from Chapter 8, which contains this mapping:
putKeyManagerFactory.XYZ, javasec.samples.ch14.SSLKeyManagerFactory;
Finally, heres how we use the key manager within our server:
package javasec.samples.ch14; import java.io.;
import java.net.; import java.security.;
import javax.net.; import javax.net.ssl.;
import javax.security.cert.; import com.sun.net.ssl.;
import javasec.sample.ch08.XYZProvider; public class SSLServerKeyManager {
public static void mainString[] args throws Exception { Security.addProvidernew XYZProvider ;
SSLContext sc = SSLContext.getInstanceTLS; KeyStore ks = KeyStore.getInstancejceks;
char[] password = args[1].toCharArray ; ks.loadnew FileInputStreamargs[0], null;
KeyManagerFactory kmf = KeyManagerFactory.getInstanceXYZ; kmf.initks, password;
sc.initkmf.getKeyManagers , null, null; ServerSocketFactory ssf = sc.getServerSocketFactory ;
ServerSocket ss = ssf.createServerSocket9096; while true {
new SSLSimpleServerss.accept.start ; }
} }
The only code change here is that weve installed the security provider and used the XYZ algorithm to provide the key manager factory. However, when we run this version of the server, we must remember to set the
xyz.aliasName property to an entry in the keystore that were loading:
piccolo java −Dxyz.aliasName=sdo javasec.samples.ch14.SSLServerKeyManager \
HOME.keystore
14.4.2 Working with Trust Managers
There is less need to provide a custom trust manager than to provide a custom key manager. Unlike the key manager, the trust manager is never asked to provide a specific alias or single certificate: it is asked to provide
all the certificates it can use to validate a peer, and it is asked to validate peers. Hence, you might provide a custom trust manager if you want to limit the certificates from a truststore that you use to validate peers or if
you want to do some sort of special validation of a peer such as checking an external certificate−revocation list.
Trust managers generally are provided from trust manager factories. The TrustManagerFactory
class com.sun.net.ssl.TrustManagerFactory
is completely analogous to the KeyManagerFactory
class: it contains a static getDefaultAlgorithm
method that returns the sun.ssl.trustmanager.type
property defined in the JREHOMElibsecurityjava.security file. By default that property is not defined, and the algorithm defaults to SunX509.
The static getInstance
methods of the TrustManagerFactory
class provide specific trust manager factories, and the
getTrustManager method returns an array of trust managers that a
specific factory defines. Similarity to the
KeyManagerFactory interface extends to the definition of the trust manager itself. The
TrustManager interface
com.sun.net.ssl.TrustManager is an empty interface, and Suns
implementation of the SSLContext
class requires that the trust manager actually implement the X509TrustManager
interface com.sun.net.ssl.X509TrustManager
. Well briefly discuss the
X509TrustManager interface here. Its a simple enough interface that we wont
show a complete example that uses it. The interface contains the following three methods: public boolean isClientTrustedX509Certificate[] chain
Check that the chain of certificates provided by the peer can be validated by root certificates in the truststore and that the validation is trusted for client−side SSL that is, your server should trust the
connecting client.
public boolean isServerTrustedX509Certificate[] chain Check that the chain of certificates provided by the peer can be validated by root certificates in the
truststore and that the validation is trusted for server−side SSL that is, whether your client should trust the server to which you are connecting.
public X509Certificate[] getAcceptedIssuers Return the list of certificates you will use to validate the peer. This is normally all the root certificates
in the truststore.
14.5 Miscellaneous SSL Issues
Finally, there are a number of miscellaneous SSL issues that the SSL socket API is designed to handle, including SSL proxies, client−side authentication, choosing a cipher suite, SSL handshaking, and JSSE
permissions. Chapter 14. SSL and HTTPS
14.5.1 SSL Proxies
SSL clients often need to make connections through a proxy server; this enables them to make requests through a firewall. If you need to make a connection through a proxy server, use this method of the
SSLSocketFactory class:
public abstract Socket createSocketSocket s, String host, int port, boolean autoClose Create an SSL socket to the given host and port that uses the existing socket as its proxy. The existing
socket is a standard plain socket that has been connected to the appropriate proxy host and proxy port. If
autoClose is
true , the underlying socket will be closed when this socket is closed. If the
socket cannot be created, an IOException
is thrown. If youre using your own protocol, its up to you to define what data should flow between your program and
the proxy server before layering the sockets with this call. If youre using HTTPS, you must send a connect string and read the headers from the proxy server on the underlying socket before you create the SSL socket.
JSSE comes with a set of sample code that shows how this can be accomplished. However, if youre using HTTPS as your protocol, its far easier to use the HTTPS protocol handler, which handles all these details for
you see Section 14.6 later in this chapter.
14.5.2 Client−Side Authentication
As weve mentioned, in most SSL conversations the server presents credentials to the client and the client verifies those credentials; the client is then assured of the servers identity. The client does not normally
present its certificate credentials to the server; in fact, the client is not even required to possess such credentials.
SSL servers can require the client authenticate itself as well, however. To do this, the server uses the setNeedClientAuth
method on its server socket as follows:
SSLContext sc = ... set up context as before ... SSLServerSocketFactory sssf =
SSLServerSocketFactory sc.getServerSocketFactory ; SSLServerSocket sss = SSLServerSocket sssf.createServerSocket9096;
sss.setNeedClientAuthtrue;
Note that this leads us further from the path of protocol−independent socket factories. Although the createServerSocket
method returns an instance of the ServerSocket
class, we must cast that object to an
SSLServerSocket in order to call the
setNeedClientAuth method.
Remember that if you put this code into your server, your server will need a valid truststore that contains the root certificate of the clients certificate chain. Be sure to set the appropriate
trustStore property when
you run a server with this code.
14.5.3 Choosing an SSL Cipher Suite
When an SSL conversation begins, the client and server negotiate between themselves as to which SSL cipher suite they will use. The suite is chosen based upon the credentials that each side possesses and the suites that
each side supports. For example, a server cant support an RSA cipher suite unless it has an available RSA private key. The client and server must support at least one common cipher suite in order to communicate; if
they both support multiple ciphers, the strongest available suite will be chosen.
SSL sockets in Suns implementation of JSSE support 15 different cipher suites, as listed in Table 14−1. These strings are part of the SSL specification and are defined as
SSL_key exchange 282
algorithm_WITH_encryption algorithm_hash algorithm . When a number appears in
the encryption algorithm, it refers to the key strength of the encryption: higher numbers are more secure though the highest numbers used to be subject to export control, and many other SSL implementations may
not support them.
Table 14−1. SSL Cipher Suites Supported by Suns JSSE Implementation
Prefix Key Exchange
Encryption Hash
SSL_ DH_anon_EXPORT_
WITH_DES40_CBC_ SHA
SSL_ DH_anon_EXPORT_
WITH_RC4_40_ MD5
SSL_ DH_anon_
WITH_3DES_EDE_CBC_ SHA
SSL_ DH_anon_
WITH_DES_CBC_ SHA
SSL_ DH_anon_
WITH_RC4_128_ MD5
SSL_ DHE_DSS_EXPORT_
WITH_DES40_CBC_ SHA
SSL_ DHE_DSS_
WITH_3DES_EDE_CBC_ SHA
SSL_ DHE_DSS_
WITH_DES_CBC_ SHA
SSL_ RSA_EXPORT_
WITH_RC4_40_ MD5
SSL_ RSA_
WITH_3DES_EDE_CBC_ SHA
SSL_ RSA_
WITH_DES_CBC_ SHA
SSL_ RSA_
WITH_NULL_ MD5
SSL_ RSA_
WITH_NULL_ SHA
SSL_ RSA_
WITH_RC4_128_ MD5
SSL_ RSA_
WITH_RC4_128_ SHA
Even though these 15 suites are all supported, by default just 8 of them are enabled; those suites are listed in bold in the table. The other suites will not be used unless you enable them explicitly.
To see and modify the suites that an SSL socket is using, use the following methods which are defined for both the
SSLSocket and
SSLServerSocket classes:
public abstract String[] getEnabledCipherSuites Return an array containing all the cipher suites that are enabled on the socket. Unless different suites
have been enabled, this will return an array containing the eight bold items in Table 14−1. public abstract void setEnabledCipherSuitesString[] suites
Set which cipher suites are enabled on the particular socket. The list completely overwrites the old set of enabled suites; if you want to add support for a new suite, you must construct an array containing
all the presently enabled suites plus the new suite you want to support. Each string in the given array must have been listed in the array returned by the
getSupportedCipherSuites method.
public abstract String[] getSupportedCipherSuites Return an array containing all the cipher suites that this implementation supports.
To add support for anonymous, exportable Diffie−Hellman key exchange using 40−bit DES encryption and an Chapter 14. SSL and HTTPS
SHA hash to a server socket, execute this code:
SSLServerSocket sss = ... initialize from socket factory ...; String[] enabled = sss.getEnabledCipherSuites ;
String[] newEnabled = new String[enabled.length + 1]; System.arraycopyenabled, 0, newEnabled, 0, enabled.length;
newEnabled[enabled.length] = SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA; sss.setEnabledCipherSuitesnewEnabled;
If youre writing the client, you must supply similar code for the SSLSocket
.
14.5.4 SSL Handshaking
When the SSL client and server negotiate which cipher suite to use, they engage in an SSL handshake. If you want to keep track of the handshake, you can use various handshaking event classes.
A handshake event is like any other event. To receive a handshake event, register a class that implements the HandshakeCompletedListener
interface javax.net.ssl.HandshakeCompletedListener
by calling the addHandshake−CompletedListener
method of the SSL socket. For SSL servers, call this method on the socket returned from the
accept method of the SSL server socket; it is defined
only in the SSLSocket
class. The listener can be removed with the removeHandshakeCompletedListener
method. When the handshake is complete, the
handshakeCompleted method of the handshake listener is
called and it is passed a HandshakeCompletedEvent
object javax.net.ssl.HandshakeCompletedEvent
. This event allows you to retrieve the cipher suite that was negotiated, the certificate chain the peer presented for authentication if applicable, the SSL session
that is in use, and the socket on which the event occurred. Note that all of this information is available directly from the SSL socket as well.
14.5.5 JSSE Permissions
To use some features of JSSE in an environment in which a security manager has been installed, untrusted code will need the following permissions:
permission com.sun.net.ssl.SSLPermission setHostnameVerifier; permission com.sun.net.ssl.SSLPermission setDefaultAuthenticator;
permission com.sun.net.ssl.SSLPermission getSSLSessionContext;
The setHostnameVerifier
permission is needed to call the setDefault−HostnameVerifier
method of the HttpsURLConnection
class. It is not needed to call the setHostnameVerifier
method of that class; setting the global verifier is the only option considered unsafe. The setDefaultAuthenticator
permission is needed to call the setDefaultAuthenticator
method of the HttpsURLConnection
class. The getSSLSessionContext
permission is needed to call the
getSessionContext method of the
SSLSession class.
14.6 The HTTPS Protocol Handler
SSL is often used as the underlying communication protocol of HTTPS. If youre talking to an HTTPS server you can write the SSL−level code yourself, but its generally easier to use the standard URL class to talk to the
server and install a protocol handler that implements the HTTPS protocol. JSSE comes with such a protocol handler.
As an example, heres a simple URL−based client that can retrieve arbitrary URLs:
package javasec.samples.ch14; import java.io.;
import java.net.; public class URLClient {
public static void mainString[] args throws Exception { URL u = new URLargs[0];
URLConnection uc = u.openConnection ; BufferedReader br = new BufferedReader
new InputStreamReaderuc.getInputStream ; String s = br.readLine ;
while s = null { System.out.printlns;
s = br.readLine ; }
} }
You can run this code with an HTTP−based URL as follows:
piccolo java javasec.samples.ch14.URLClient http:www.sun.com
... lots of output from sun.com ...
Similarly, by specifying the appropriate property for the HTTPS protocol handler, you can connect to an HTTPS−based URL:
piccolo java \ −Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol \
javasec.samples.ch14.URLClient https:www.sun.com
As always, the server sun.com in this case will present its certificate to the client, which must verify it using its truststore. In this case, weve used the default truststore JREHOMElibsecuritycacerts, which contains
the root certificate of many CAs including the one used by Sun. If you connect to your own server, you may need to specify the appropriate
trustStore property.
14.6.1 Verifying HTTPS Hosts
When we wrote our own SSL client socket code, we had to extract the name from the servers certificate and make sure that it represented the host to which we expected to connect. The HTTPS protocol handler will do
that for us automatically, and if the hostnames dont match, an IOException
will be thrown when you attempt to get the input or output stream.
There are times when this verification is insufficient. If you connect to https:192.18.297.41, the default verification will fail. The certificate presented by that site has an embedded name of www.sun.com, and even
though 192.18.297.41 is the correct IP address, the protocol handler will do a string comparison of 192.18.297.41 and www.sun.com and will fail. In cases such as this you may want to look up the IP address of
the name in the certificate and see if it matches your target. You may also want to ask the user if its okay to proceed regardless of whether the names match.
To handle such situations, you can implement a hostname verifier in order to perform extended hostname verification. Extended hostname verification is used only if the name in the certificate and the hostname in the
URL dont match; if the names match, the HTTPS protocol handler does not call the hostname verifier. Hence, a hostname verifier cannot be used to prevent any arbitrary connection.