package com.netflix.governator.guice.jetty;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import com.sun.jersey.core.util.Base64;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
public class JettySslModule extends AbstractModule {
// Generated with
// openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365000 -subj '/CN=localhost/' -nodes
private static final String CERTIFICATE =
"MIIC/TCCAeWgAwIBAgIJAICIMvFHSibpMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV" +
"BAMMCWxvY2FsaG9zdDAgFw0xNjA5MDYyMDM3NDJaGA8zMDE2MDEwODIwMzc0Mlow" +
"FDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB" +
"CgKCAQEAx22xi43/Y/DwPJczTXjs8fQFOdH5PmtNCteaJMAG14wb3LZd55EEstJ6" +
"lKy9LqTOywlsiIFMWDvOs5yUxFeak5OwJ8/84xfblCLyQZ7CHnkBcvXpx3jM934j" +
"rdgP+X2GeGj3QC8u/Qs3vlFaOoAB/k/sLm15VO3j9b4bp0E76joWOhwQieBe5/Qc" +
"ZvXUhfL+5DGoO9ROejP3+M9TM74L+ceQpN/8m71lUDiTOk13UhcqlFD7YSxWIzGq" +
"6maHa8z54L+Zuis6juHsgQHASZsYhN7nynW3+KBSNrs859x/WSPmy/zYwh08W8lm" +
"4kdjVE5TXw/NDjFnngwv16vtejh8gwIDAQABo1AwTjAdBgNVHQ4EFgQU4cWlr1ut" +
"ZZu4Qp3WVAzd7a1R5LAwHwYDVR0jBBgwFoAU4cWlr1utZZu4Qp3WVAzd7a1R5LAw" +
"DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAHsClc/3ftd9A/95Xj6H0" +
"g10hlqb0EAFRM97/DiDwt9ESdFoBnE51Zv7BmcPN0CBCyHMj/MnaB/BijuDR0N8/" +
"7h2R97rsXbNJkSFtLeV7cXBD/sXi+e8INX3rqADWQJ24Buv55miyR6M3CarY6yNl" +
"i+gnulM4jFRSq7jvfYUbzA9mi/fCqGI9F4poS2QKxMiyx5xcU57u3mHJYD+JbS+W" +
"UywJfv9PXkBkHxO9yEYJ3DG78CPUv45CsBCmoBRN7TDndpwDCqpXbXlxME2i8ljL" +
"yKg6WQUrig6xdE0yRcLoY8n3ibuNghDPBTEjhQ4UTs2JfISaBB/T2fZP8PQXqUgm" +
"Ag==";
private static final String PRIVATE_KEY =
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHbbGLjf9j8PA8" +
"lzNNeOzx9AU50fk+a00K15okwAbXjBvctl3nkQSy0nqUrL0upM7LCWyIgUxYO86z" +
"nJTEV5qTk7Anz/zjF9uUIvJBnsIeeQFy9enHeMz3fiOt2A/5fYZ4aPdALy79Cze+" +
"UVo6gAH+T+wubXlU7eP1vhunQTvqOhY6HBCJ4F7n9Bxm9dSF8v7kMag71E56M/f4" +
"z1Mzvgv5x5Ck3/ybvWVQOJM6TXdSFyqUUPthLFYjMarqZodrzPngv5m6KzqO4eyB" +
"AcBJmxiE3ufKdbf4oFI2uzzn3H9ZI+bL/NjCHTxbyWbiR2NUTlNfD80OMWeeDC/X" +
"q+16OHyDAgMBAAECggEBAKOXWA0ibl2NR4Rsg6kJiVTw11iW5d5OJuS997Qt0W7/" +
"f9uNvXo3e6M1BVjwWj/o8bmcAWv4pKe8Z9LunxpwwlxMyjPeaZPf/j+GazNpB9P3" +
"bzjegOcgMQLUdnAkzPXcAnLDqA7+pYztpsx374wNdZUn+pYbN2xzuIvdZtHMsVlw" +
"2ZSd/ZU/e3++jpDAqgdpul6TdMGcSxqjYyuwPWQhYx00qBIJNZig9phQKACxNYxR" +
"yyqdRv3t/csFhK17qnFgVf424nXFbg7ELoLpCujkwXkMQWkr8Z44DtKNJspJlST9" +
"zoVaTRhV5p7tXged/JUt8FG5nvgu4snlnMhn/K1n0AECgYEA7iK0sJaCwLtWnB46" +
"diUOkhANHrAe+XTXl1nwQQkD7vUHyrlukJDliEh1NhmJMRxPQbHf9RrUhLhgLBRg" +
"2ge67w6kGj2Agyx3WF2oblbD2OAHCe2Rrs5fF69mqMpvbqZZIWD+yFiBJp79bin5" +
"jN+uOhiABh1yfw/EjV0eU+XWyA8CgYEA1mOiaXWDVB7t495MJ8mi5I+BZcnMbvgI" +
"4hKWpZMdIGCsjEmvQJrjXzIXyATpznNOzrEup6tFjX0WmgWQOuQE5wSi9iGoaZLw" +
"YdGPgH3j2MwXlplSiWviibdZfIli28C4i3+FmGZlO5THHB/xK4uVtezDDMxpwFyQ" +
"SeDuL2ussE0CgYEA0uQLbwOsAfEmb5XZoj2JHNN4OwAwPi06rH/q5D2OrTV01BTK" +
"FN8tVzcMDoAo3kQ68GwNcWx0XqFGEmNtrkkARKuLqu1ifUiI3Mn82tKeGNe1hBZP" +
"WSbMUhZ07PByJOTOtF/I4zZ2EfTlbYVgymBhVHPUFRZJCru1DpgzvosiXgMCgYBV" +
"sAjwAan1607FrsnddTgIBlt/pYJyL+zM/wT7NKuFj14nzCOhvMZ3+/uJVH1mqKus" +
"7SBqn4fzHzXzZZnaD9ztwOqpWZaIa9RsJGgowShaNGiRJsLYbihjRscbgYXjs0mP" +
"Z+6rlPGNOM/EK/gmoWm7BuCGswTpf5WkEaThizXAWQKBgCgV3hc6wDbD84h6XGFF" +
"cNPJ1fiBdZflQ5P21QRGTe5tsIJPnZ3qj2JSsY4GODCfkzBOK+7VzGMCAi7fWlUt" +
"drUEPaOCDY7/9JkKgu8uPDYsUb22Q1BZi2vfbsfXytkna8bi4cXKJkeoNEKimJni" +
"jZIzPiAGS5/h4W+2PcfvygvD";
@Override
protected void configure() {
final KeyStore keyStore;
try {
keyStore = loadSelfSignedKeyStore();
} catch (Exception e) {
throw new RuntimeException(e);
}
bind(KeyStore.class).toInstance(keyStore);
Multibinder.newSetBinder(binder(), JettyConnectorProvider.class).addBinding()
.toInstance(new SslJettyConnectorProvider(keyStore));
}
private static class SslJettyConnectorProvider implements JettyConnectorProvider {
private final KeyStore keyStore;
private SslJettyConnectorProvider(KeyStore keyStore) {
this.keyStore = keyStore;
}
@Override
public Connector getConnector(Server server) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, "password".toCharArray());
sslContext.init(kmf.getKeyManagers(), null, null);
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setSslContext(sslContext);
ServerConnector serverConnector = new ServerConnector(server, sslContextFactory);
// In a real module, this would be configurable; for tests always use an ephemeral port
serverConnector.setPort(0);
return serverConnector;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private static KeyStore loadSelfSignedKeyStore() throws Exception {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
X509Certificate[] chain = new X509Certificate[1];
try (InputStream inputStream = new ByteArrayInputStream(Base64.decode(CERTIFICATE))) {
chain[0] = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(inputStream);
}
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(PRIVATE_KEY));
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(spec);
keyStore.setKeyEntry("1", privateKey, "password".toCharArray(), chain);
return keyStore;
}
}