1111//** KeyManager
1212//******************************************************************************
1313/**
14- * Custom implementation of a X509KeyManager. This class is required to
15- * support keystores with multiple SSL certificates. By default, the standard
16- * Java X509KeyManager and the SunX509 implementation will pick the first
17- * aliases it finds for which there is a private key and a key of the right
18- * type for the chosen cipher suite (typically RSA). Instead, this class
19- * relies on a map of hostnames and their corresponding IP addresses. When a
20- * new SSL request is made, it checks the incoming IP address and finds the
21- * corresponding hostname. Then, it tries to find an alias in the keystore
22- * that corresponds to the hostname.
14+ * Custom implementation of a X509KeyManager. This class is used to support
15+ * keystores with multiple SSL certificates. By default, the standard Java
16+ * X509KeyManager and the SunX509 implementation will pick the first alias
17+ * it finds for which there is a private key and a key type that matches
18+ * the chosen cipher suite (typically RSA).
19+ *
20+ * Instead, this class tries to find an alias in the keystore that best
21+ * matches the requested hostname found in the SSL handshake. This assumes
22+ * that the keystore aliases contain hostnames (e.g. "www.acme.com") or top
23+ * level domain names (e.g. "acme.com").
24+ *
25+ * In addition, this class requires a mapping of aliases/hostnames to IP
26+ * addresses on the host server. This is required for the chooseServerAlias()
27+ * method which is called early in the SSL handshake process (well before
28+ * the hostname is known). When the chooseServerAlias() method is called, all
29+ * we have is a IP address to identify the alias so a hashmap is used to tie
30+ * a domain name to an IP address.
2331 *
2432 ******************************************************************************/
2533
2634public class KeyManager extends X509ExtendedKeyManager { //implements X509KeyManager
2735 private KeyStore keyStore ;
2836 private char [] password ;
29- private String alias ;
3037 private java .util .HashMap <InetAddress , String > aliases ;
3138
32- public KeyManager (KeyStore keystore , char [] password , String alias ) {
33- if (alias ==null ) throw new IllegalArgumentException ("Alias is null." );
34- this .keyStore = keystore ;
35- this .password = password ;
36- this .alias = alias ;
37- }
3839
40+ //**************************************************************************
41+ //** Constructor
42+ //**************************************************************************
3943 public KeyManager (KeyStore keystore , char [] password , java .util .HashMap <InetAddress , String > aliases ) {
4044 if (aliases ==null || aliases .isEmpty ()) throw new IllegalArgumentException ("Hosts is null or empty." );
4145 this .keyStore = keystore ;
4246 this .password = password ;
4347 this .aliases = aliases ;
4448 }
4549
50+
51+ //**************************************************************************
52+ //** chooseEngineServerAlias
53+ //**************************************************************************
54+ /** Returns an alias in the keystore that best matches the requested
55+ * hostname found in the SSL handshake
56+ * @param keyType Not used
57+ * @param issuers Not used
58+ * @param engine SSLEngine with a handshake session
59+ */
4660 public String chooseEngineServerAlias (String keyType , Principal [] issuers , SSLEngine engine ) {
47- if (alias !=null ) return alias ;
48- else {
49- try {
50-
51- //Get hostname from SSL handshake
52- ExtendedSSLSession session = (ExtendedSSLSession ) engine .getHandshakeSession ();
53- String hostname = null ;
54- for (SNIServerName name : session .getRequestedServerNames ()) {
55- if (name .getType () == StandardConstants .SNI_HOST_NAME ) {
56- hostname = ((SNIHostName ) name ).getAsciiName ();
57- break ;
58- }
61+ try {
62+
63+ //Get hostname from SSL handshake (www.acme.com)
64+ String hostname = null ;
65+ ExtendedSSLSession session = (ExtendedSSLSession ) engine .getHandshakeSession ();
66+ for (SNIServerName name : session .getRequestedServerNames ()) {
67+ if (name .getType () == StandardConstants .SNI_HOST_NAME ) {
68+ hostname = ((SNIHostName ) name ).getAsciiName ();
69+ break ;
5970 }
71+ }
72+ if (hostname ==null ) return null ;
73+ else hostname = hostname .toLowerCase ();
6074
6175
62- String [] arr = hostname .split ("\\ ." );
63- hostname = arr [arr .length -2 ] + "." + arr [arr .length -1 ];
76+ //Get top-level domain name (acme.com)
77+ String [] arr = hostname .split ("\\ ." );
78+ String domainName = arr [arr .length -2 ] + "." + arr [arr .length -1 ];
6479
65- //System.out.println("hostname: " + hostname);
66- //System.out.println(InetAddress.getByName(hostname));
67- //System.out.println(aliases.get(InetAddress.getByName(hostname)));
6880
6981
70- return aliases .get (InetAddress .getByName (hostname ));
71- }
72- catch (Exception e ){
73- return null ;
82+ //Special case for keystores with wildcard certs and top-level domain
83+ //certs. When creating aliases for wildcard certs, I use the top-level
84+ //domain name as the alias (e.g. "acme.com" alias for a "*.acme.com"
85+ //wildcard cert). However, in some cases we also want a cert for the
86+ //top-level domain (e.g. "acme.com"). So for top-level domain certs,
87+ //I use an underscore prefix (e.g. "_acme.com" alias for a "acme.com"
88+ //cert). The following code will search for an alias with an
89+ //underscore prefix whenever a top-level domain name is requested.
90+ if (domainName .equals (hostname )){
91+ java .util .Enumeration enumeration = keyStore .aliases ();
92+ while (enumeration .hasMoreElements ()) {
93+ String alias = (String ) enumeration .nextElement ();
94+ if (alias .equals ("_" +domainName )) return alias ;
95+ }
7496 }
97+
98+
99+
100+ //Return the alias associated with the IP address of the top-level
101+ //domain name.
102+ return aliases .get (InetAddress .getByName (domainName ));
103+
104+ }
105+ catch (Exception e ){
106+ return null ;
75107 }
76108 }
77109
110+
111+ //**************************************************************************
112+ //** chooseEngineServerAlias
113+ //**************************************************************************
114+ /** Returns an alias that best matches the given HTTP socket.
115+ * @param keyType Not used
116+ * @param issuers Not used
117+ * @param socket HTTP socket
118+ */
78119 public String chooseServerAlias (String keyType , Principal [] issuers , Socket socket ) {
79- if ( alias != null ) return alias ;
80- else return aliases .get (socket .getLocalAddress ());
120+ //System.out.println("chooseServerAlias: " + socket.getLocalAddress()) ;
121+ return aliases .get (socket .getLocalAddress ());
81122 }
82123
124+
125+ //**************************************************************************
126+ //** getPrivateKey
127+ //**************************************************************************
128+ /** Returns the private key from the keystore for a given alias.
129+ */
83130 public PrivateKey getPrivateKey (String alias ) {
84131 try {
85132 return (PrivateKey ) keyStore .getKey (alias , password );
@@ -89,6 +136,12 @@ public PrivateKey getPrivateKey(String alias) {
89136 }
90137 }
91138
139+
140+ //**************************************************************************
141+ //** getPrivateKey
142+ //**************************************************************************
143+ /** Returns the x509 certificate chain from the keystore for a given alias.
144+ */
92145 public X509Certificate [] getCertificateChain (String alias ) {
93146 try {
94147 java .security .cert .Certificate [] certs = keyStore .getCertificateChain (alias );
@@ -105,18 +158,18 @@ public X509Certificate[] getCertificateChain(String alias) {
105158 }
106159
107160 public String [] getServerAliases (String keyType , Principal [] issuers ) {
108- throw new UnsupportedOperationException ("Method getServerAliases() not yet implemented." );
161+ throw new UnsupportedOperationException ("Method getServerAliases() not implemented." );
109162 }
110163
111164 public String [] getClientAliases (String keyType , Principal [] issuers ) {
112- throw new UnsupportedOperationException ("Method getClientAliases() not yet implemented." );
165+ throw new UnsupportedOperationException ("Method getClientAliases() not implemented." );
113166 }
114167
115168 public String chooseClientAlias (String keyTypes [], Principal [] issuers , Socket socket ) {
116- throw new UnsupportedOperationException ("Method chooseClientAlias() not yet implemented." );
169+ throw new UnsupportedOperationException ("Method chooseClientAlias() not implemented." );
117170 }
118171
119172 public String chooseEngineClientAlias (String [] strings , Principal [] prncpls , SSLEngine ssle ) {
120- throw new UnsupportedOperationException ("Method chooseEngineClientAlias() not yet implemented." );
173+ throw new UnsupportedOperationException ("Method chooseEngineClientAlias() not implemented." );
121174 }
122175}
0 commit comments