29
29
import javax .net .ssl .KeyManagerFactory ;
30
30
import javax .net .ssl .TrustManager ;
31
31
import java .io .IOException ;
32
- import java .io .InputStream ;
33
- import java .nio .file .Files ;
34
32
import java .nio .file .Paths ;
35
33
import java .security .GeneralSecurityException ;
36
34
import java .security .KeyStore ;
37
35
import java .text .MessageFormat ;
36
+ import java .util .Timer ;
38
37
39
38
/**
40
39
* {@link KeyStoresFactory} implementation that reads the certificates from
41
40
* keystore files.
42
41
* <p>
43
- * if the trust certificates keystore file changes, the {@link TrustManager}
44
- * is refreshed with the new trust certificate entries (using a
45
- * {@link ReloadingX509TrustManager} trustmanager).
42
+ * If either the truststore or the keystore certificates file changes, it
43
+ * would be refreshed under the corresponding wrapper implementation -
44
+ * {@link ReloadingX509KeystoreManager} or {@link ReloadingX509TrustManager}.
45
+ * </p>
46
46
*/
47
47
@ InterfaceAudience .Private
48
48
@ InterfaceStability .Evolving
@@ -51,6 +51,13 @@ public class FileBasedKeyStoresFactory implements KeyStoresFactory {
51
51
private static final Logger LOG =
52
52
LoggerFactory .getLogger (FileBasedKeyStoresFactory .class );
53
53
54
+ /**
55
+ * The refresh interval used to check if either of the truststore or keystore
56
+ * certificate file has changed.
57
+ */
58
+ public static final String SSL_STORES_RELOAD_INTERVAL_TPL_KEY =
59
+ "ssl.{0}.stores.reload.interval" ;
60
+
54
61
public static final String SSL_KEYSTORE_LOCATION_TPL_KEY =
55
62
"ssl.{0}.keystore.location" ;
56
63
public static final String SSL_KEYSTORE_PASSWORD_TPL_KEY =
@@ -77,14 +84,119 @@ public class FileBasedKeyStoresFactory implements KeyStoresFactory {
77
84
public static final String DEFAULT_KEYSTORE_TYPE = "jks" ;
78
85
79
86
/**
80
- * Reload interval in milliseconds.
87
+ * The default time interval in milliseconds used to check if either
88
+ * of the truststore or keystore certificates file has changed and needs reloading.
81
89
*/
82
- public static final int DEFAULT_SSL_TRUSTSTORE_RELOAD_INTERVAL = 10000 ;
90
+ public static final int DEFAULT_SSL_STORES_RELOAD_INTERVAL = 10000 ;
83
91
84
92
private Configuration conf ;
85
93
private KeyManager [] keyManagers ;
86
94
private TrustManager [] trustManagers ;
87
95
private ReloadingX509TrustManager trustManager ;
96
+ private Timer fileMonitoringTimer ;
97
+
98
+
99
+ private void createTrustManagersFromConfiguration (SSLFactory .Mode mode ,
100
+ String truststoreType ,
101
+ String truststoreLocation ,
102
+ long storesReloadInterval )
103
+ throws IOException , GeneralSecurityException {
104
+ String passwordProperty = resolvePropertyName (mode ,
105
+ SSL_TRUSTSTORE_PASSWORD_TPL_KEY );
106
+ String truststorePassword = getPassword (conf , passwordProperty , "" );
107
+ if (truststorePassword .isEmpty ()) {
108
+ // An empty trust store password is legal; the trust store password
109
+ // is only required when writing to a trust store. Otherwise it's
110
+ // an optional integrity check.
111
+ truststorePassword = null ;
112
+ }
113
+
114
+ // Check if obsolete truststore specific reload interval is present for backward compatible
115
+ long truststoreReloadInterval =
116
+ conf .getLong (
117
+ resolvePropertyName (mode , SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY ),
118
+ storesReloadInterval );
119
+
120
+ if (LOG .isDebugEnabled ()) {
121
+ LOG .debug (mode .toString () + " TrustStore: " + truststoreLocation +
122
+ ", reloading at " + truststoreReloadInterval + " millis." );
123
+ }
124
+
125
+ trustManager = new ReloadingX509TrustManager (
126
+ truststoreType ,
127
+ truststoreLocation ,
128
+ truststorePassword );
129
+
130
+ if (truststoreReloadInterval > 0 ) {
131
+ fileMonitoringTimer .schedule (
132
+ new FileMonitoringTimerTask (
133
+ Paths .get (truststoreLocation ),
134
+ path -> trustManager .loadFrom (path ),
135
+ exception -> LOG .error (ReloadingX509TrustManager .RELOAD_ERROR_MESSAGE , exception )),
136
+ truststoreReloadInterval ,
137
+ truststoreReloadInterval );
138
+ }
139
+
140
+ if (LOG .isDebugEnabled ()) {
141
+ LOG .debug (mode .toString () + " Loaded TrustStore: " + truststoreLocation );
142
+ }
143
+ trustManagers = new TrustManager []{trustManager };
144
+ }
145
+
146
+ /**
147
+ * Implements logic of initializing the KeyManagers with the options
148
+ * to reload keystores.
149
+ * @param mode client or server
150
+ * @param keystoreType The keystore type.
151
+ * @param storesReloadInterval The interval to check if the keystore certificates
152
+ * file has changed.
153
+ */
154
+ private void createKeyManagersFromConfiguration (SSLFactory .Mode mode ,
155
+ String keystoreType , long storesReloadInterval )
156
+ throws GeneralSecurityException , IOException {
157
+ String locationProperty =
158
+ resolvePropertyName (mode , SSL_KEYSTORE_LOCATION_TPL_KEY );
159
+ String keystoreLocation = conf .get (locationProperty , "" );
160
+ if (keystoreLocation .isEmpty ()) {
161
+ throw new GeneralSecurityException ("The property '" + locationProperty +
162
+ "' has not been set in the ssl configuration file." );
163
+ }
164
+ String passwordProperty =
165
+ resolvePropertyName (mode , SSL_KEYSTORE_PASSWORD_TPL_KEY );
166
+ String keystorePassword = getPassword (conf , passwordProperty , "" );
167
+ if (keystorePassword .isEmpty ()) {
168
+ throw new GeneralSecurityException ("The property '" + passwordProperty +
169
+ "' has not been set in the ssl configuration file." );
170
+ }
171
+ String keyPasswordProperty =
172
+ resolvePropertyName (mode , SSL_KEYSTORE_KEYPASSWORD_TPL_KEY );
173
+ // Key password defaults to the same value as store password for
174
+ // compatibility with legacy configurations that did not use a separate
175
+ // configuration property for key password.
176
+ String keystoreKeyPassword = getPassword (
177
+ conf , keyPasswordProperty , keystorePassword );
178
+ if (LOG .isDebugEnabled ()) {
179
+ LOG .debug (mode .toString () + " KeyStore: " + keystoreLocation );
180
+ }
181
+
182
+ ReloadingX509KeystoreManager keystoreManager = new ReloadingX509KeystoreManager (
183
+ keystoreType ,
184
+ keystoreLocation ,
185
+ keystorePassword ,
186
+ keystoreKeyPassword );
187
+
188
+ if (storesReloadInterval > 0 ) {
189
+ fileMonitoringTimer .schedule (
190
+ new FileMonitoringTimerTask (
191
+ Paths .get (keystoreLocation ),
192
+ path -> keystoreManager .loadFrom (path ),
193
+ exception -> LOG .error (ReloadingX509KeystoreManager .RELOAD_ERROR_MESSAGE , exception )),
194
+ storesReloadInterval ,
195
+ storesReloadInterval );
196
+ }
197
+
198
+ keyManagers = new KeyManager [] { keystoreManager };
199
+ }
88
200
89
201
/**
90
202
* Resolves a property name to its client/server version if applicable.
@@ -139,56 +251,28 @@ public void init(SSLFactory.Mode mode)
139
251
conf .getBoolean (SSLFactory .SSL_REQUIRE_CLIENT_CERT_KEY ,
140
252
SSLFactory .SSL_REQUIRE_CLIENT_CERT_DEFAULT );
141
253
254
+ long storesReloadInterval = conf .getLong (
255
+ resolvePropertyName (mode , SSL_STORES_RELOAD_INTERVAL_TPL_KEY ),
256
+ DEFAULT_SSL_STORES_RELOAD_INTERVAL );
257
+
258
+ fileMonitoringTimer = new Timer ("SSL Certificates Store Monitor" , true );
259
+
142
260
// certificate store
143
261
String keystoreType =
144
- conf .get (resolvePropertyName (mode , SSL_KEYSTORE_TYPE_TPL_KEY ),
145
- DEFAULT_KEYSTORE_TYPE );
146
- KeyStore keystore = KeyStore .getInstance (keystoreType );
147
- String keystoreKeyPassword = null ;
148
- if (requireClientCert || mode == SSLFactory .Mode .SERVER ) {
149
- String locationProperty =
150
- resolvePropertyName (mode , SSL_KEYSTORE_LOCATION_TPL_KEY );
151
- String keystoreLocation = conf .get (locationProperty , "" );
152
- if (keystoreLocation .isEmpty ()) {
153
- throw new GeneralSecurityException ("The property '" + locationProperty +
154
- "' has not been set in the ssl configuration file." );
155
- }
156
- String passwordProperty =
157
- resolvePropertyName (mode , SSL_KEYSTORE_PASSWORD_TPL_KEY );
158
- String keystorePassword = getPassword (conf , passwordProperty , "" );
159
- if (keystorePassword .isEmpty ()) {
160
- throw new GeneralSecurityException ("The property '" + passwordProperty +
161
- "' has not been set in the ssl configuration file." );
162
- }
163
- String keyPasswordProperty =
164
- resolvePropertyName (mode , SSL_KEYSTORE_KEYPASSWORD_TPL_KEY );
165
- // Key password defaults to the same value as store password for
166
- // compatibility with legacy configurations that did not use a separate
167
- // configuration property for key password.
168
- keystoreKeyPassword = getPassword (
169
- conf , keyPasswordProperty , keystorePassword );
170
- if (LOG .isDebugEnabled ()) {
171
- LOG .debug (mode .toString () + " KeyStore: " + keystoreLocation );
172
- }
262
+ conf .get (resolvePropertyName (mode , SSL_KEYSTORE_TYPE_TPL_KEY ),
263
+ DEFAULT_KEYSTORE_TYPE );
173
264
174
- InputStream is = Files .newInputStream (Paths .get (keystoreLocation ));
175
- try {
176
- keystore .load (is , keystorePassword .toCharArray ());
177
- } finally {
178
- is .close ();
179
- }
180
- if (LOG .isDebugEnabled ()) {
181
- LOG .debug (mode .toString () + " Loaded KeyStore: " + keystoreLocation );
182
- }
265
+ if (requireClientCert || mode == SSLFactory .Mode .SERVER ) {
266
+ createKeyManagersFromConfiguration (mode , keystoreType , storesReloadInterval );
183
267
} else {
268
+ KeyStore keystore = KeyStore .getInstance (keystoreType );
184
269
keystore .load (null , null );
270
+ KeyManagerFactory keyMgrFactory = KeyManagerFactory
271
+ .getInstance (SSLFactory .SSLCERTIFICATE );
272
+
273
+ keyMgrFactory .init (keystore , null );
274
+ keyManagers = keyMgrFactory .getKeyManagers ();
185
275
}
186
- KeyManagerFactory keyMgrFactory = KeyManagerFactory
187
- .getInstance (SSLFactory .SSLCERTIFICATE );
188
-
189
- keyMgrFactory .init (keystore , (keystoreKeyPassword != null ) ?
190
- keystoreKeyPassword .toCharArray () : null );
191
- keyManagers = keyMgrFactory .getKeyManagers ();
192
276
193
277
//trust store
194
278
String truststoreType =
@@ -199,33 +283,7 @@ public void init(SSLFactory.Mode mode)
199
283
resolvePropertyName (mode , SSL_TRUSTSTORE_LOCATION_TPL_KEY );
200
284
String truststoreLocation = conf .get (locationProperty , "" );
201
285
if (!truststoreLocation .isEmpty ()) {
202
- String passwordProperty = resolvePropertyName (mode ,
203
- SSL_TRUSTSTORE_PASSWORD_TPL_KEY );
204
- String truststorePassword = getPassword (conf , passwordProperty , "" );
205
- if (truststorePassword .isEmpty ()) {
206
- // An empty trust store password is legal; the trust store password
207
- // is only required when writing to a trust store. Otherwise it's
208
- // an optional integrity check.
209
- truststorePassword = null ;
210
- }
211
- long truststoreReloadInterval =
212
- conf .getLong (
213
- resolvePropertyName (mode , SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY ),
214
- DEFAULT_SSL_TRUSTSTORE_RELOAD_INTERVAL );
215
-
216
- if (LOG .isDebugEnabled ()) {
217
- LOG .debug (mode .toString () + " TrustStore: " + truststoreLocation );
218
- }
219
-
220
- trustManager = new ReloadingX509TrustManager (truststoreType ,
221
- truststoreLocation ,
222
- truststorePassword ,
223
- truststoreReloadInterval );
224
- trustManager .init ();
225
- if (LOG .isDebugEnabled ()) {
226
- LOG .debug (mode .toString () + " Loaded TrustStore: " + truststoreLocation );
227
- }
228
- trustManagers = new TrustManager []{trustManager };
286
+ createTrustManagersFromConfiguration (mode , truststoreType , truststoreLocation , storesReloadInterval );
229
287
} else {
230
288
if (LOG .isDebugEnabled ()) {
231
289
LOG .debug ("The property '" + locationProperty + "' has not been set, " +
@@ -256,7 +314,7 @@ String getPassword(Configuration conf, String alias, String defaultPass) {
256
314
@ Override
257
315
public synchronized void destroy () {
258
316
if (trustManager != null ) {
259
- trustManager . destroy ();
317
+ fileMonitoringTimer . cancel ();
260
318
trustManager = null ;
261
319
keyManagers = null ;
262
320
trustManagers = null ;
0 commit comments