|
21 | 21 | import akka.actor.Props;
|
22 | 22 | import akka.actor.Terminated;
|
23 | 23 | import akka.dispatch.Dispatcher;
|
| 24 | +import akka.http.javadsl.ConnectionContext; |
24 | 25 | import akka.http.javadsl.Http;
|
| 26 | +import akka.http.javadsl.HttpsConnectionContext; |
25 | 27 | import akka.stream.Materializer;
|
26 | 28 | import ch.qos.logback.classic.LoggerContext;
|
27 | 29 | import com.arpnetworking.commons.builder.Builder;
|
|
51 | 53 | import com.arpnetworking.utility.Launchable;
|
52 | 54 | import com.fasterxml.jackson.databind.JsonNode;
|
53 | 55 | import com.fasterxml.jackson.databind.ObjectMapper;
|
| 56 | +import com.google.common.base.Charsets; |
54 | 57 | import com.google.common.collect.ImmutableList;
|
55 | 58 | import com.google.common.collect.Maps;
|
56 | 59 | import com.google.common.collect.Sets;
|
|
61 | 64 | import com.typesafe.config.Config;
|
62 | 65 | import com.typesafe.config.ConfigFactory;
|
63 | 66 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
| 67 | +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; |
| 68 | +import org.bouncycastle.jce.provider.BouncyCastleProvider; |
| 69 | +import org.bouncycastle.openssl.PEMParser; |
| 70 | +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; |
| 71 | +import org.bouncycastle.util.io.pem.PemObject; |
64 | 72 | import scala.concurrent.Await;
|
65 | 73 | import scala.concurrent.ExecutionContextExecutor;
|
66 | 74 | import scala.concurrent.Future;
|
67 | 75 | import scala.concurrent.duration.Duration;
|
68 | 76 |
|
| 77 | +import java.io.ByteArrayInputStream; |
69 | 78 | import java.io.File;
|
| 79 | +import java.io.FileInputStream; |
| 80 | +import java.io.FileNotFoundException; |
| 81 | +import java.io.IOException; |
| 82 | +import java.io.InputStreamReader; |
70 | 83 | import java.net.URI;
|
| 84 | +import java.security.KeyManagementException; |
| 85 | +import java.security.KeyStore; |
| 86 | +import java.security.KeyStoreException; |
| 87 | +import java.security.NoSuchAlgorithmException; |
| 88 | +import java.security.PrivateKey; |
| 89 | +import java.security.SecureRandom; |
| 90 | +import java.security.Security; |
| 91 | +import java.security.UnrecoverableKeyException; |
| 92 | +import java.security.cert.Certificate; |
| 93 | +import java.security.cert.CertificateException; |
| 94 | +import java.security.cert.CertificateFactory; |
| 95 | +import java.security.cert.X509Certificate; |
71 | 96 | import java.util.ArrayList;
|
72 | 97 | import java.util.List;
|
73 | 98 | import java.util.Locale;
|
|
81 | 106 | import java.util.concurrent.Semaphore;
|
82 | 107 | import java.util.concurrent.TimeUnit;
|
83 | 108 | import javax.annotation.Nullable;
|
| 109 | +import javax.net.ssl.KeyManagerFactory; |
| 110 | +import javax.net.ssl.SSLContext; |
| 111 | +import javax.net.ssl.TrustManagerFactory; |
84 | 112 |
|
85 | 113 | /**
|
86 | 114 | * Class containing entry point for Metrics Data Aggregator (MAD).
|
@@ -248,7 +276,73 @@ private void launchActors(final Injector injector) {
|
248 | 276 | supplementalHttpRoutes.build());
|
249 | 277 | final Http http = Http.get(actorSystem);
|
250 | 278 |
|
251 |
| - http.newServerAt(_configuration.getHttpHost(), _configuration.getHttpPort()).bind(routes); |
| 279 | + http.newServerAt(_configuration.getHttpHost(), _configuration.getHttpPort()).withMaterializer(materializer).bind(routes); |
| 280 | + if (_configuration.getEnableHttps()) { |
| 281 | + Security.addProvider(new BouncyCastleProvider()); |
| 282 | + final HttpsConnectionContext httpsContext = createHttpsContext(); |
| 283 | + http.newServerAt(_configuration.getHttpsHost(), _configuration.getHttpsPort()) |
| 284 | + .withMaterializer(materializer).enableHttps(httpsContext) |
| 285 | + .bind(routes); |
| 286 | + } |
| 287 | + } |
| 288 | + |
| 289 | + private HttpsConnectionContext createHttpsContext() { |
| 290 | + final PrivateKey privateKey; |
| 291 | + try (PEMParser keyReader = new PEMParser( |
| 292 | + new InputStreamReader( |
| 293 | + new FileInputStream(_configuration.getHttpsKeyPath()), Charsets.UTF_8))) { |
| 294 | + final Object keyObject = keyReader.readObject(); |
| 295 | + |
| 296 | + if (keyObject instanceof PrivateKeyInfo) { |
| 297 | + final PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) keyObject; |
| 298 | + privateKey = new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo); |
| 299 | + } else { |
| 300 | + throw new RuntimeException( |
| 301 | + String.format("Invalid private key format, expected PEM private key, got %s", keyObject.getClass().getName())); |
| 302 | + } |
| 303 | + } catch (final FileNotFoundException e) { |
| 304 | + throw new RuntimeException(String.format("Could not find https key file: %s", _configuration.getHttpsKeyPath()), e); |
| 305 | + } catch (final IOException e) { |
| 306 | + throw new RuntimeException(String.format("Error reading https key file: %s", _configuration.getHttpsKeyPath()), e); |
| 307 | + } |
| 308 | + |
| 309 | + final X509Certificate cert; |
| 310 | + try (PEMParser certReader = new PEMParser( |
| 311 | + new InputStreamReader( |
| 312 | + new FileInputStream(_configuration.getHttpsCertificatePath()), Charsets.UTF_8))) { |
| 313 | + final PemObject certObject = certReader.readPemObject(); |
| 314 | + final CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
| 315 | + cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certObject.getContent())); |
| 316 | + } catch (final FileNotFoundException e) { |
| 317 | + throw new RuntimeException( |
| 318 | + String.format("Could not find https certificate file: %s", _configuration.getHttpsCertificatePath()), e); |
| 319 | + } catch (final IOException e) { |
| 320 | + throw new RuntimeException( |
| 321 | + String.format("Error reading https certificate file: %s", _configuration.getHttpsCertificatePath()), e); |
| 322 | + } catch (final CertificateException e) { |
| 323 | + throw new RuntimeException("Error building X509 certificate", e); |
| 324 | + } |
| 325 | + |
| 326 | + try { |
| 327 | + final KeyStore ks = KeyStore.getInstance("JKS"); |
| 328 | + ks.load(null, null); |
| 329 | + ks.setCertificateEntry("cert", cert); |
| 330 | + |
| 331 | + ks.setKeyEntry("key", privateKey, new char[0], new Certificate[]{cert}); |
| 332 | + final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); |
| 333 | + keyManagerFactory.init(ks, new char[0]); |
| 334 | + |
| 335 | + final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); |
| 336 | + tmf.init(ks); |
| 337 | + |
| 338 | + final SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); |
| 339 | + sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); |
| 340 | + |
| 341 | + return ConnectionContext.httpsServer(sslContext); |
| 342 | + } catch (final UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | IOException |
| 343 | + | KeyStoreException | CertificateException e) { |
| 344 | + throw new RuntimeException("Could not create SSLContext for https configuration", e); |
| 345 | + } |
252 | 346 | }
|
253 | 347 |
|
254 | 348 | @SuppressWarnings("deprecation")
|
|
0 commit comments