package peergos.server;
import com.sun.net.httpserver.*;
import peergos.server.mutable.*;
import peergos.shared.corenode.*;
import peergos.shared.mutable.*;
import peergos.shared.storage.ContentAddressedStorage;
import peergos.server.corenode.HttpCoreNodeServer;
import peergos.server.net.*;
import peergos.shared.util.*;
import javax.net.ssl.*;
import java.io.*;
import java.net.*;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.Logger;
public class UserService
{
public static final String DHT_URL = "/api/v0/";
public static final String SIGNUP_URL = "/signup/";
public static final String ACTIVATION_URL = "/activation/";
public static final String UI_URL = "/";
public static final int HANDLER_THREADS = 100;
public static final int CONNECTION_BACKLOG = 100;
static {
// disable weak algorithms
System.out.println("\nInitial security properties:");
printSecurityProperties();
// The ECDH and RSA ket exchange algorithms are disabled because they don't provide forward secrecy
Security.setProperty("jdk.tls.disabledAlgorithms",
"SSLv3, RC4, MD2, MD4, MD5, SHA1, DSA, DH, RSA keySize < 2048, EC keySize < 160, " +
"TLS_RSA_WITH_AES_256_GCM_SHA384, " +
"TLS_RSA_WITH_AES_256_CBC_SHA256, " +
"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, " +
"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, " +
"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, " +
"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, " +
"TLS_RSA_WITH_AES_128_CBC_SHA256, " +
"TLS_RSA_WITH_AES_128_GCM_SHA256");
Security.setProperty("jdk.certpath.disabledAlgorithms",
"RC4, MD2, MD4, MD5, SHA1, DSA, RSA keySize < 2048, EC keySize < 160");
Security.setProperty("jdk.tls.rejectClientInitializedRenegotiation", "true");
System.out.println("\nUpdated security properties:");
printSecurityProperties();
Security.setProperty("jdk.tls.ephemeralDHKeySize", "2048");
}
static void printSecurityProperties() {
System.out.println("jdk.tls.disabledAlgorithms: " + Security.getProperty("jdk.tls.disabledAlgorithms"));
System.out.println("jdk.certpath.disabledAlgorithms: " + Security.getProperty("jdk.certpath.disabledAlgorithms"));
System.out.println("jdk.tls.rejectClientInitializedRenegotiation: "+Security.getProperty("jdk.tls.rejectClientInitializedRenegotiation"));
}
private final Logger LOGGER;
private final InetSocketAddress local;
private final CoreNode coreNode;
private final MutablePointers mutable;
private HttpServer server;
public UserService(InetSocketAddress local, Logger LOGGER, ContentAddressedStorage dht, CoreNode coreNode, MutablePointers mutable, Args args) throws IOException
{
this.LOGGER = LOGGER;
this.local = local;
this.coreNode = coreNode;
this.mutable = mutable;
init(dht, args);
}
public boolean init(ContentAddressedStorage dht, Args args) throws IOException {
boolean isLocal = this.local.getHostName().contains("local");
if (!isLocal)
try {
HttpServer httpServer = HttpServer.create();
httpServer.createContext("/", new RedirectHandler("https://" + local.getHostName() + ":" + local.getPort() + "/"));
httpServer.bind(new InetSocketAddress(InetAddress.getLocalHost(), 80), CONNECTION_BACKLOG);
httpServer.start();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Couldn't start http redirect to https for user server!");
}
System.out.println("Starting user API server at: " + local.getHostName() + ":" + local.getPort());
if (isLocal) {
System.out.println("Starting user server on localhost:"+local.getPort()+" only.");
server = HttpServer.create(local, CONNECTION_BACKLOG);
} else if (args.hasArg("publicserver")) {
System.out.println("Starting user server on all interfaces.");
server = HttpsServer.create(new InetSocketAddress(InetAddress.getByName("::"), local.getPort()), CONNECTION_BACKLOG);
} else
server = HttpsServer.create(local, CONNECTION_BACKLOG);
if (!isLocal) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
char[] password = "storage".toCharArray();
KeyStore ks = getKeyStore("storage.p12", password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
// TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
// tmf.init(SSL.getTrustedKeyStore());
// setup the HTTPS context and parameters
sslContext.init(kmf.getKeyManagers(), null, null);
sslContext.getSupportedSSLParameters().setUseCipherSuitesOrder(true);
// set up perfect forward secrecy
sslContext.getSupportedSSLParameters().setCipherSuites(new String[]{
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"
});
SSLContext.setDefault(sslContext);
((HttpsServer)server).setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure(HttpsParameters params) {
try {
// initialise the SSL context
SSLContext c = SSLContext.getDefault();
SSLEngine engine = c.createSSLEngine();
params.setNeedClientAuth(false);
params.setCipherSuites(engine.getEnabledCipherSuites());
params.setProtocols(engine.getEnabledProtocols());
// get the default parameters
SSLParameters defaultSSLParameters = c.getDefaultSSLParameters();
params.setSSLParameters(defaultSSLParameters);
} catch (Exception ex) {
System.err.println("Failed to create HTTPS port");
ex.printStackTrace(System.err);
}
}
});
}
catch (NoSuchAlgorithmException | InvalidKeyException | KeyStoreException | CertificateException |
NoSuchProviderException | SignatureException |
UnrecoverableKeyException | KeyManagementException ex)
{
System.err.println("Failed to create HTTPS port");
ex.printStackTrace(System.err);
return false;
}
}
Function<HttpHandler, HttpHandler> wrap = h -> !isLocal ? new HSTSHandler(h) : h;
server.createContext(DHT_URL, wrap.apply(new DHTHandler(dht)));
server.createContext(SIGNUP_URL, wrap.apply(new InverseProxyHandler("demo.peergos.net", isLocal)));
server.createContext(ACTIVATION_URL, wrap.apply(new InverseProxyHandler("demo.peergos.net", isLocal)));
//define web-root static-handler
StaticHandler handler;
try {
String webroot = args.getArg("webroot");
System.out.println("Using webroot from local file system: " + webroot);
handler = new FileHandler(Paths.get(webroot), true);
} catch (IllegalStateException ile) {
System.out.println("Using webroot from jar");
handler = new JarHandler(true, Paths.get("webroot"));
}
boolean webcache = args.getBoolean("webcache", true);
if (webcache) {
System.out.println("Caching web-resources");
handler = handler.withCache();
}
server.createContext(UI_URL, wrap.apply(handler));
server.createContext("/" + HttpCoreNodeServer.CORE_URL, wrap.apply(new HttpCoreNodeServer.CoreNodeHandler(coreNode)));
server.createContext("/" + HttpMutablePointerServer.MUTABLE_POINTERS_URL, wrap.apply(new HttpMutablePointerServer.MutationHandler(mutable)));
server.setExecutor(Executors.newFixedThreadPool(HANDLER_THREADS));
server.start();
return true;
}
public static KeyStore getKeyStore(String filename, char[] password)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, InvalidKeyException,
NoSuchProviderException, SignatureException
{
KeyStore ks = KeyStore.getInstance("PKCS12");
if (new File(filename).exists())
{
ks.load(new FileInputStream(filename), password);
return ks;
}
throw new IllegalStateException("SSL keystore file doesn't exist: "+filename);
}
}