@@ -129,6 +129,8 @@ public class SimpleHttpServer extends PluginService implements Authenticator {
129
129
int websocketPort = DEFAULT_WEBSOCKET_PORT ;
130
130
private String bindHostname = "localhost" ;
131
131
private boolean httpsEnabled = DEFAULT_HTTPS_ENABLED ;
132
+ private SslContext context ;
133
+ private Provider <SSLEngine > engineProvider ;
132
134
133
135
@ Inject
134
136
public SimpleHttpServer (Topics t , Kernel kernel , DeviceConfiguration deviceConfiguration ) {
@@ -183,68 +185,10 @@ public void postInject() {
183
185
@ SuppressWarnings ("UseSpecificCatch" )
184
186
@ Override
185
187
public void startup () throws InterruptedException {
186
- SslContext context = null ;
187
- Provider < SSLEngine > engineProvider = null ;
188
+ context = null ;
189
+ engineProvider = null ;
188
190
if (httpsEnabled ) {
189
- Path workPath ;
190
- KeyStore ks ;
191
- try {
192
- workPath = kernel .getNucleusPaths ().workPath (getServiceName ());
193
- ks = KeyStore .getInstance ("JKS" );
194
- } catch (IOException | KeyStoreException e ) {
195
- serviceErrored (e );
196
- return ;
197
- }
198
-
199
- // Get passphrase or generate a new one to use for the keystore password
200
- char [] passphrase = Coerce .toString (getRuntimeConfig ().lookup ("keystorePassphrase" )
201
- .dflt (Utils .generateRandomString (24 ))).toCharArray ();
202
-
203
- // Either load cert/key from keystore or create and then save to keystore for later
204
- Path keyStorePath = workPath .resolve ("keystore.jks" );
205
- try {
206
- if (Files .exists (keyStorePath )) {
207
- try (InputStream is = Files .newInputStream (keyStorePath )) {
208
- ks .load (is , passphrase );
209
- }
210
- } else {
211
- // Initialize keystore as empty
212
- ks .load (null , passphrase );
213
-
214
- // Generate keys and certificate
215
- KeyPairGenerator keyGen = KeyPairGenerator .getInstance ("RSA" );
216
- keyGen .initialize (4096 , new SecureRandom ());
217
- KeyPair keyPair = keyGen .generateKeyPair ();
218
- X509Certificate cert = selfSign (keyPair , bindHostname );
219
-
220
- ks .setCertificateEntry (CERT_NAME , cert );
221
- ks .setKeyEntry (PRIVATE_KEY_NAME , keyPair .getPrivate (), new char [0 ], new Certificate []{cert });
222
- try (OutputStream os = Files .newOutputStream (keyStorePath )) {
223
- ks .store (os , passphrase );
224
- }
225
- }
226
- } catch (IOException | NoSuchAlgorithmException | CertificateException
227
- | KeyStoreException | OperatorCreationException e ) {
228
- serviceErrored (e );
229
- return ;
230
- }
231
-
232
- try {
233
- // Grab key and cert for SSL setup
234
- PrivateKey privateKey = (PrivateKey ) ks .getKey (PRIVATE_KEY_NAME , new char [0 ]);
235
- X509Certificate cert = (X509Certificate ) ks .getCertificate (CERT_NAME );
236
- context = SslContextBuilder .forServer (privateKey , cert ).build ();
237
- SslContext finalContext = context ;
238
- engineProvider = () -> finalContext .newEngine (ByteBufAllocator .DEFAULT );
239
-
240
- // Save certificate fingerprint as space separated hex bytes
241
- String fingerprint = fingerprintCert (cert , SHA_1_ALGORITHM );
242
- config .getRoot ().lookup (CERT_FINGERPRINT_NAMESPACE , SHA_1_ALGORITHM ).withValue (fingerprint );
243
- fingerprint = fingerprintCert (cert , SHA_256_ALGORITHM );
244
- config .getRoot ().lookup (CERT_FINGERPRINT_NAMESPACE , SHA_256_ALGORITHM ).withValue (fingerprint );
245
- } catch (NoSuchAlgorithmException | CertificateEncodingException
246
- | KeyStoreException | UnrecoverableKeyException | SSLException e ) {
247
- serviceErrored (e );
191
+ if (!initializeHttps ()) {
248
192
return ;
249
193
}
250
194
}
@@ -277,6 +221,86 @@ public void startup() throws InterruptedException {
277
221
reportState (State .RUNNING );
278
222
}
279
223
224
+ boolean initializeHttps () {
225
+ Path workPath ;
226
+ KeyStore ks ;
227
+ try {
228
+ workPath = kernel .getNucleusPaths ().workPath (getServiceName ());
229
+ ks = KeyStore .getInstance ("JKS" );
230
+ } catch (IOException | KeyStoreException e ) {
231
+ serviceErrored (e );
232
+ return true ;
233
+ }
234
+
235
+ // Get passphrase or generate a new one to use for the keystore password
236
+ char [] passphrase = Coerce .toString (getRuntimeConfig ().lookup ("keystorePassphrase" )
237
+ .dflt (Utils .generateRandomString (24 ))).toCharArray ();
238
+
239
+ // Either load cert/key from keystore or create and then save to keystore for later
240
+ Path keyStorePath = workPath .resolve ("keystore.jks" );
241
+ try {
242
+ if (Files .exists (keyStorePath )) {
243
+ try (InputStream is = Files .newInputStream (keyStorePath )) {
244
+ ks .load (is , passphrase );
245
+ } catch (IOException e ) {
246
+ // If the password is wrong for whatever reason, delete the existing keystore and
247
+ // reinitialize it
248
+ if (e .getCause () instanceof UnrecoverableKeyException ) {
249
+ Files .deleteIfExists (keyStorePath );
250
+ initializeKeyStore (ks , passphrase , keyStorePath );
251
+ } else {
252
+ throw e ;
253
+ }
254
+ }
255
+ } else {
256
+ initializeKeyStore (ks , passphrase , keyStorePath );
257
+ }
258
+ } catch (IOException | NoSuchAlgorithmException | CertificateException
259
+ | KeyStoreException | OperatorCreationException e ) {
260
+ serviceErrored (e );
261
+ return false ;
262
+ }
263
+
264
+ try {
265
+ // Grab key and cert for SSL setup
266
+ PrivateKey privateKey = (PrivateKey ) ks .getKey (PRIVATE_KEY_NAME , new char [0 ]);
267
+ X509Certificate cert = (X509Certificate ) ks .getCertificate (CERT_NAME );
268
+ context = SslContextBuilder .forServer (privateKey , cert ).build ();
269
+ SslContext finalContext = context ;
270
+ engineProvider = () -> finalContext .newEngine (ByteBufAllocator .DEFAULT );
271
+
272
+ // Save certificate fingerprint as space separated hex bytes
273
+ String fingerprint = fingerprintCert (cert , SHA_1_ALGORITHM );
274
+ config .getRoot ().lookup (CERT_FINGERPRINT_NAMESPACE , SHA_1_ALGORITHM ).withValue (fingerprint );
275
+ fingerprint = fingerprintCert (cert , SHA_256_ALGORITHM );
276
+ config .getRoot ().lookup (CERT_FINGERPRINT_NAMESPACE , SHA_256_ALGORITHM ).withValue (fingerprint );
277
+ } catch (NoSuchAlgorithmException | CertificateEncodingException
278
+ | KeyStoreException | UnrecoverableKeyException | SSLException e ) {
279
+ serviceErrored (e );
280
+ return false ;
281
+ }
282
+ return true ;
283
+ }
284
+
285
+ private void initializeKeyStore (KeyStore ks , char [] passphrase , Path keyStorePath )
286
+ throws IOException , NoSuchAlgorithmException , CertificateException , OperatorCreationException ,
287
+ KeyStoreException {
288
+ // Initialize keystore as empty
289
+ ks .load (null , passphrase );
290
+
291
+ // Generate keys and certificate
292
+ KeyPairGenerator keyGen = KeyPairGenerator .getInstance ("RSA" );
293
+ keyGen .initialize (4096 , new SecureRandom ());
294
+ KeyPair keyPair = keyGen .generateKeyPair ();
295
+ X509Certificate cert = selfSign (keyPair , bindHostname );
296
+
297
+ ks .setCertificateEntry (CERT_NAME , cert );
298
+ ks .setKeyEntry (PRIVATE_KEY_NAME , keyPair .getPrivate (), new char [0 ], new Certificate []{cert });
299
+ try (OutputStream os = Files .newOutputStream (keyStorePath )) {
300
+ ks .store (os , passphrase );
301
+ }
302
+ }
303
+
280
304
static String fingerprintCert (X509Certificate cert , String algorithm )
281
305
throws NoSuchAlgorithmException , CertificateEncodingException {
282
306
StringBuilder sb = new StringBuilder ();
0 commit comments