1
- package javaxt .express ;
2
- import java .net .InetAddress ;
3
- import java .net .Socket ;
4
- import java .security .KeyStore ;
5
- import java .security .Principal ;
6
- import java .security .PrivateKey ;
7
- import java .security .cert .X509Certificate ;
8
- import javax .net .ssl .*;
9
-
10
- //******************************************************************************
11
- //** KeyManager
12
- //******************************************************************************
13
- /**
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.
31
- *
32
- ******************************************************************************/
33
-
34
- public class KeyManager extends X509ExtendedKeyManager { //implements X509KeyManager
35
- private KeyStore keyStore ;
36
- private char [] password ;
37
- private java .util .HashMap <InetAddress , String > aliases ;
38
-
39
-
40
- //**************************************************************************
41
- //** Constructor
42
- //**************************************************************************
43
- public KeyManager (KeyStore keystore , char [] password , java .util .HashMap <InetAddress , String > aliases ) {
44
- if (aliases ==null || aliases .isEmpty ()) throw new IllegalArgumentException ("Hosts is null or empty." );
45
- this .keyStore = keystore ;
46
- this .password = password ;
47
- this .aliases = aliases ;
48
- }
49
-
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
- */
60
- public String chooseEngineServerAlias (String keyType , Principal [] issuers , SSLEngine engine ) {
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 ;
70
- }
71
- }
72
- if (hostname ==null ) return null ;
73
- else hostname = hostname .toLowerCase ();
74
-
75
-
76
- //Get top-level domain name (acme.com)
77
- String [] arr = hostname .split ("\\ ." );
78
- String domainName = arr [arr .length -2 ] + "." + arr [arr .length -1 ];
79
-
80
-
81
-
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
- }
96
- }
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 ;
107
- }
108
- }
109
-
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
- */
119
- public String chooseServerAlias (String keyType , Principal [] issuers , Socket socket ) {
120
- //System.out.println("chooseServerAlias: " + socket.getLocalAddress());
121
- return aliases .get (socket .getLocalAddress ());
122
- }
123
-
124
-
125
- //**************************************************************************
126
- //** getPrivateKey
127
- //**************************************************************************
128
- /** Returns the private key from the keystore for a given alias.
129
- */
130
- public PrivateKey getPrivateKey (String alias ) {
131
- try {
132
- return (PrivateKey ) keyStore .getKey (alias , password );
133
- }
134
- catch (Exception e ) {
135
- return null ;
136
- }
137
- }
138
-
139
-
140
- //**************************************************************************
141
- //** getPrivateKey
142
- //**************************************************************************
143
- /** Returns the x509 certificate chain from the keystore for a given alias.
144
- */
145
- public X509Certificate [] getCertificateChain (String alias ) {
146
- try {
147
- java .security .cert .Certificate [] certs = keyStore .getCertificateChain (alias );
148
- if (certs == null || certs .length == 0 ) return null ;
149
- X509Certificate [] x509 = new X509Certificate [certs .length ];
150
- for (int i = 0 ; i < certs .length ; i ++){
151
- x509 [i ] = (X509Certificate )certs [i ];
152
- }
153
- return x509 ;
154
- }
155
- catch (Exception e ) {
156
- return null ;
157
- }
158
- }
159
-
160
- public String [] getServerAliases (String keyType , Principal [] issuers ) {
161
- throw new UnsupportedOperationException ("Method getServerAliases() not implemented." );
162
- }
163
-
164
- public String [] getClientAliases (String keyType , Principal [] issuers ) {
165
- throw new UnsupportedOperationException ("Method getClientAliases() not implemented." );
166
- }
167
-
168
- public String chooseClientAlias (String keyTypes [], Principal [] issuers , Socket socket ) {
169
- throw new UnsupportedOperationException ("Method chooseClientAlias() not implemented." );
170
- }
171
-
172
- public String chooseEngineClientAlias (String [] strings , Principal [] prncpls , SSLEngine ssle ) {
173
- throw new UnsupportedOperationException ("Method chooseEngineClientAlias() not implemented." );
174
- }
1
+ package javaxt .express ;
2
+ import java .util .*;
3
+ import javax .net .ssl .*;
4
+ import java .net .Socket ;
5
+ import java .net .InetAddress ;
6
+ import java .security .KeyStore ;
7
+ import java .security .Principal ;
8
+ import java .security .PrivateKey ;
9
+ import java .security .cert .X509Certificate ;
10
+
11
+ //******************************************************************************
12
+ //** KeyManager
13
+ //******************************************************************************
14
+ /**
15
+ * Custom implementation of a X509KeyManager. This class is used to support
16
+ * keystores with multiple SSL certificates. By default, the standard Java
17
+ * X509KeyManager and the SunX509 implementation will pick the first alias
18
+ * it finds for which there is a private key and a key type that matches
19
+ * the chosen cipher suite (typically RSA).
20
+ *
21
+ * Instead, this class tries to find an alias in the keystore that best
22
+ * matches the requested hostname found in the SSL handshake. This assumes
23
+ * that the keystore aliases contain hostnames (e.g. "www.acme.com") or top
24
+ * level domain names (e.g. "acme.com").
25
+ *
26
+ * In addition, this class requires a mapping of aliases/hostnames to IP
27
+ * addresses on the host server. This is required for the chooseServerAlias()
28
+ * method which is called early in the SSL handshake process (well before
29
+ * the hostname is known). When the chooseServerAlias() method is called, all
30
+ * we have is a IP address to identify the alias so a hashmap is used to tie
31
+ * a domain name to an IP address.
32
+ *
33
+ ******************************************************************************/
34
+
35
+ public class KeyManager extends X509ExtendedKeyManager { //implements X509KeyManager
36
+ private KeyStore keyStore ;
37
+ private char [] password ;
38
+ private HashMap <InetAddress , String > aliases ;
39
+
40
+
41
+ //**************************************************************************
42
+ //** Constructor
43
+ //**************************************************************************
44
+ public KeyManager (KeyStore keystore , char [] password , HashMap <InetAddress , String > aliases ) {
45
+ if (aliases ==null || aliases .isEmpty ()) throw new IllegalArgumentException ("Hosts is null or empty." );
46
+ this .keyStore = keystore ;
47
+ this .password = password ;
48
+ this .aliases = aliases ;
49
+ }
50
+
51
+
52
+ //**************************************************************************
53
+ //** chooseEngineServerAlias
54
+ //**************************************************************************
55
+ /** Returns an alias in the keystore that best matches the requested
56
+ * hostname found in the SSL handshake
57
+ * @param keyType Not used
58
+ * @param issuers Not used
59
+ * @param engine SSLEngine with a handshake session
60
+ */
61
+ public String chooseEngineServerAlias (String keyType , Principal [] issuers , SSLEngine engine ) {
62
+ try {
63
+
64
+ //Get hostname from SSL handshake (www.acme.com)
65
+ String hostname = null ;
66
+ ExtendedSSLSession session = (ExtendedSSLSession ) engine .getHandshakeSession ();
67
+ for (SNIServerName name : session .getRequestedServerNames ()) {
68
+ if (name .getType () == StandardConstants .SNI_HOST_NAME ) {
69
+ hostname = ((SNIHostName ) name ).getAsciiName ();
70
+ break ;
71
+ }
72
+ }
73
+ if (hostname ==null ) return null ;
74
+ else hostname = hostname .toLowerCase ();
75
+
76
+
77
+ //Get top-level domain name (acme.com)
78
+ String [] arr = hostname .split ("\\ ." );
79
+ String domainName = arr [arr .length -2 ] + "." + arr [arr .length -1 ];
80
+
81
+
82
+
83
+ //Special case for keystores with wildcard certs and top-level domain
84
+ //certs. When creating aliases for wildcard certs, I use the top-level
85
+ //domain name as the alias (e.g. "acme.com" alias for a "*.acme.com"
86
+ //wildcard cert). However, in some cases we also want a cert for the
87
+ //top-level domain (e.g. "acme.com"). So for top-level domain certs,
88
+ //I use an underscore prefix (e.g. "_acme.com" alias for a "acme.com"
89
+ //cert). The following code will search for an alias with an
90
+ //underscore prefix whenever a top-level domain name is requested.
91
+ if (domainName .equals (hostname )){
92
+ Enumeration enumeration = keyStore .aliases ();
93
+ while (enumeration .hasMoreElements ()) {
94
+ String alias = (String ) enumeration .nextElement ();
95
+ if (alias .equals ("_" +domainName )) return alias ;
96
+ }
97
+ }
98
+
99
+
100
+
101
+ //Return the alias associated with the IP address of the top-level
102
+ //domain name.
103
+ return aliases .get (InetAddress .getByName (domainName ));
104
+
105
+ }
106
+ catch (Exception e ){
107
+ return null ;
108
+ }
109
+ }
110
+
111
+
112
+ //**************************************************************************
113
+ //** chooseEngineServerAlias
114
+ //**************************************************************************
115
+ /** Returns an alias that best matches the given HTTP socket.
116
+ * @param keyType Not used
117
+ * @param issuers Not used
118
+ * @param socket HTTP socket
119
+ */
120
+ public String chooseServerAlias (String keyType , Principal [] issuers , Socket socket ) {
121
+ //System.out.println("chooseServerAlias: " + socket.getLocalAddress());
122
+ return aliases .get (socket .getLocalAddress ());
123
+ }
124
+
125
+
126
+ //**************************************************************************
127
+ //** getPrivateKey
128
+ //**************************************************************************
129
+ /** Returns the private key from the keystore for a given alias.
130
+ */
131
+ public PrivateKey getPrivateKey (String alias ) {
132
+ try {
133
+ return (PrivateKey ) keyStore .getKey (alias , password );
134
+ }
135
+ catch (Exception e ) {
136
+ return null ;
137
+ }
138
+ }
139
+
140
+
141
+ //**************************************************************************
142
+ //** getPrivateKey
143
+ //**************************************************************************
144
+ /** Returns the x509 certificate chain from the keystore for a given alias.
145
+ */
146
+ public X509Certificate [] getCertificateChain (String alias ) {
147
+ try {
148
+ java .security .cert .Certificate [] certs = keyStore .getCertificateChain (alias );
149
+ if (certs == null || certs .length == 0 ) return null ;
150
+ X509Certificate [] x509 = new X509Certificate [certs .length ];
151
+ for (int i = 0 ; i < certs .length ; i ++){
152
+ x509 [i ] = (X509Certificate )certs [i ];
153
+ }
154
+ return x509 ;
155
+ }
156
+ catch (Exception e ) {
157
+ return null ;
158
+ }
159
+ }
160
+
161
+ public String [] getServerAliases (String keyType , Principal [] issuers ) {
162
+ throw new UnsupportedOperationException ("Method getServerAliases() not implemented." );
163
+ }
164
+
165
+ public String [] getClientAliases (String keyType , Principal [] issuers ) {
166
+ throw new UnsupportedOperationException ("Method getClientAliases() not implemented." );
167
+ }
168
+
169
+ public String chooseClientAlias (String keyTypes [], Principal [] issuers , Socket socket ) {
170
+ throw new UnsupportedOperationException ("Method chooseClientAlias() not implemented." );
171
+ }
172
+
173
+ public String chooseEngineClientAlias (String [] strings , Principal [] prncpls , SSLEngine ssle ) {
174
+ throw new UnsupportedOperationException ("Method chooseEngineClientAlias() not implemented." );
175
+ }
175
176
}
0 commit comments