diff --git a/src/brs/web/server/AbstractServerConnectorBuilder.java b/src/brs/web/server/AbstractServerConnectorBuilder.java index a803f543c..b822ea7a5 100644 --- a/src/brs/web/server/AbstractServerConnectorBuilder.java +++ b/src/brs/web/server/AbstractServerConnectorBuilder.java @@ -15,6 +15,8 @@ import java.security.*; import java.security.cert.X509Certificate; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -23,6 +25,7 @@ public abstract class AbstractServerConnectorBuilder { private static final Logger logger = LoggerFactory.getLogger(AbstractServerConnectorBuilder.class); protected final WebServerContext context; + private static SslContextFactory.Server sslContextFactory; public AbstractServerConnectorBuilder(WebServerContext context) { this.context = context; @@ -30,52 +33,58 @@ public AbstractServerConnectorBuilder(WebServerContext context) { abstract ServerConnector build(Server server); - protected ServerConnector createSSLConnector(Server server) { - logger.info("Creating SSL Connector"); - HttpConfiguration httpsConfig = new HttpConfiguration(); - httpsConfig.setSecureScheme("https"); - httpsConfig.setSecurePort(context.getPropertyService().getInt(Props.API_PORT)); - httpsConfig.addCustomizer(new SecureRequestCustomizer()); - SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - - sslContextFactory.setKeyStorePath(context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PATH)); - sslContextFactory.setKeyStorePassword(context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PASSWORD)); - - // Handle optional Let's Encrypt Certificates... - String letsencryptPath = context.getPropertyService().getString(Props.API_SSL_LETSENCRYPT_PATH); - if (letsencryptPath != null && !letsencryptPath.isEmpty()) { - try { - loadLetsEncryptCertsAsPkcs12(letsencryptPath, context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PATH), context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PASSWORD)); - } catch (Exception e) { - logger.error(e.getMessage()); - } + private SslContextFactory.Server getSslContextFactory() { + if (sslContextFactory == null) { + sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PATH)); + sslContextFactory.setKeyStorePassword(context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PASSWORD)); - // Reload the certificate every week, in case it was renewed - ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); - Runnable reloadCert = () -> { + // Handle optional Let's Encrypt Certificates... + String letsencryptPath = context.getPropertyService().getString(Props.API_SSL_LETSENCRYPT_PATH); + if (letsencryptPath != null && !letsencryptPath.isEmpty()) { try { loadLetsEncryptCertsAsPkcs12(letsencryptPath, context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PATH), context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PASSWORD)); - sslContextFactory.reload(consumer -> logger.info("SSL keystore from letsencrypt reloaded.")); } catch (Exception e) { logger.error(e.getMessage()); } + + // Reload the certificate every week, in case it was renewed + ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + Runnable reloadCert = () -> { + try { + loadLetsEncryptCertsAsPkcs12(letsencryptPath, context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PATH), context.getPropertyService().getString(Props.API_SSL_KEY_STORE_PASSWORD)); + sslContextFactory.reload(consumer -> logger.info("SSL keystore from letsencrypt reloaded.")); + } catch (Exception e) { + logger.error(e.getMessage()); + } + }; + scheduler.scheduleWithFixedDelay(reloadCert, 7, 7, TimeUnit.DAYS); + } + + String[] strongCiphers = { + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + // Add more strong ciphers as needed }; - scheduler.scheduleWithFixedDelay(reloadCert, 7, 7, TimeUnit.DAYS); + sslContextFactory.setIncludeCipherSuites(strongCiphers); + sslContextFactory.setIncludeProtocols("TLSv1.2", "TLSv1.3"); + sslContextFactory.setExcludeProtocols("SSLv3"); } + return sslContextFactory; + } + + protected ServerConnector createSSLConnector(Server server) { + logger.info("Creating SSL Connector"); + HttpConfiguration httpsConfig = new HttpConfiguration(); + httpsConfig.setSecureScheme("https"); + httpsConfig.setSecurePort(context.getPropertyService().getInt(Props.API_PORT)); + httpsConfig.addCustomizer(new SecureRequestCustomizer()); - String[] strongCiphers = { - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - // Add more strong ciphers as needed - }; - sslContextFactory.setIncludeCipherSuites(strongCiphers); - sslContextFactory.setIncludeProtocols("TLSv1.2", "TLSv1.3"); - sslContextFactory.setExcludeProtocols("SSLv3"); - return new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), + return new ServerConnector(server, new SslConnectionFactory(getSslContextFactory(), "http/1.1"), new HttpConnectionFactory(httpsConfig)); } - public void loadLetsEncryptCertsAsPkcs12(String letsencryptPath, String p12Filename, String password) throws Exception { + public static void loadLetsEncryptCertsAsPkcs12(String letsencryptPath, String p12Filename, String password) throws Exception { logger.info("Converting Let's Encrypt Certificate to PKCS12..."); Security.addProvider(new BouncyCastleProvider()); @@ -92,16 +101,21 @@ public void loadLetsEncryptCertsAsPkcs12(String letsencryptPath, String p12Filen KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC"); PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded())); - X509CertificateHolder certObj = (X509CertificateHolder) certParser.readObject(); - JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter(); - certConverter.setProvider("BC"); - X509Certificate certificate = certConverter.getCertificate(certObj); - X509Certificate[] certificates = new X509Certificate[]{certificate}; + // Convert all certificates in fullchain.pem + JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter().setProvider("BC"); + List certList = new ArrayList<>(); + Object certObj; + while ((certObj = certParser.readObject()) != null) { + if (certObj instanceof X509CertificateHolder) { + certList.add(certConverter.getCertificate((X509CertificateHolder) certObj)); + } + } + X509Certificate[] certificates = certList.toArray(new X509Certificate[0]); // Add the private key and certificates to the keystore KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(null, null); - keyStore.setKeyEntry("SIGNUM_NODE_CERT", privateKey, password.toCharArray(), certificates); + keyStore.setKeyEntry("lets-encrypt", privateKey, password.toCharArray(), certificates); // Finally, save as PKCS12 file... try (OutputStream out = new FileOutputStream(p12Filename)) {