Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'javax.xml.ws.Endpoint' and 2 ways SSL

I tried to deploy a web service with 2 ways SSL in java using the class ‘javax.xml.ws.Endpoint’. My SSL setup is very restrictive. I have to set a specific set of options and settings. That’s a requirement I cannot discuss.

In order to setup SSL, I need to provide a Server Context object. After doing some search I end up using the class ‘com.sun.net.httpserver.HttpsServer’ (and some others related classes also in package ‘com.sun’). It works perfectly on a Windows JVM and on the HPUX JVM.

However, I know (I should say, I believe) that classes from package ‘com.sun’ should not be used because they are not part of the standard runtime environment. Those classes could be moved/modified/removed without any prior notice and are JVM implementation dependant.

My actual code is:

private static HttpsServer createHttpsServer() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException, NoSuchProviderException {

    final String keyStoreType = "...";
    final String keyStoreFile = "...";
    final String keyStorePassword = "...";
    final String trustStoreType = "...";
    final String trustStoreFile = "...";
    final String trustStorePassword = "...";
    final String hostName = "...";
    final int portNumber = "...;
    final String sslContextName = "TLSv1.2";

    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(new FileInputStream(keyStoreFile), keyStorePassword.toCharArray());

    KeyStore trustStore = KeyStore.getInstance(trustStoreType);
    trustStore.load(new FileInputStream(trustStoreFile), trustStorePassword.toCharArray());

    KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyFactory.init(keyStore, keyStorePassword.toCharArray());

    TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustFactory.init(trustStore);

    SSLContext sslContext = SSLContext.getInstance(sslContextName);
    sslContext.init(keyFactory.getKeyManagers(), trustFactory.getTrustManagers(), getSecureRandom(pConfiguration));

    HttpsServer httpsServer = HttpsServer.create(new InetSocketAddress(hostName, portNumber), portNumber);
    HttpsConfigurator configurator = getHttpsConfigurator(pConfiguration, sslContext);
    httpsServer.setHttpsConfigurator(configurator);

    httpsServer.start();

    return httpsServer;
}

private static Endpoint publishSsl(final HttpsServer pHttpsServer, final String pPath, final Object implementationObject) {
    LOGGER.entering(LOGGER_SOURCE_CLASS, "publishSsl");

    HttpContext httpContext = pHttpsServer.createContext(pPath);
    Endpoint endPoint = Endpoint.create(implementationObject);
    endPoint.publish(httpContext);
    return endPoint;
}

private static HttpsConfigurator getHttpsConfigurator(final MyProperties pConfiguration, SSLContext pSslContext) {
    EnforcingHttpsConfigurator configurator = new EnforcingHttpsConfigurator(pSslContext);

    // Those are hidden properties to override the SSL configuration if needed.
    final String ciphers = pConfiguration.getProperty("overrideSslConfiguration.ciphers", "");
    final boolean needClientAuth = pConfiguration.getPropertyAsBoolean("overrideSslConfiguration.needClientAuth", true);
    final String protocols = pConfiguration.getProperty("overrideSslConfiguration.protocols", "");

    if (!ciphers.isEmpty()) {
        configurator.setCiphers(ciphers);
    }

    configurator.setNeedClientAuth(needClientAuth);

    if (!protocols.isEmpty()) {
        configurator.setProtocols(protocols);
    }

    return configurator;
}

public class EnforcingHttpsConfigurator extends HttpsConfigurator {
private static final Logger LOGGER = Logger.getLogger(EnforcingHttpsConfigurator.class.getCanonicalName());
private static final String LOGGER_SOURCE_CLASS = EnforcingHttpsConfigurator.class.getName();

private String mProtocols = "TLSv1.2";
private String mCiphers = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_GCM_SHA256";
private boolean mNeedClientAuth = true;

public EnforcingHttpsConfigurator(SSLContext pSslContext) {
    super(pSslContext);
}

public String getProtocols() {
    return mProtocols;
}

public void setProtocols(String pProtocols) {
    LOGGER.warning("Override SSL configuration, Set protocols '" + pProtocols + "'. This is potentially unsafe.");
    mProtocols = pProtocols;
}

public String getCiphers() {
    return mCiphers;
}

public void setCiphers(String pCiphers) {
    LOGGER.warning("Override SSL configuration, Set ciphers '" + pCiphers + "'. This is potentially unsafe.");
    mCiphers = pCiphers;
}

public boolean isNeedClientAuth() {
    return mNeedClientAuth;
}

public void setNeedClientAuth(boolean pNeedClientAuth) {
    if (!pNeedClientAuth) {
        LOGGER.warning("Override SSL configuration, no client authentication required. This is potentially unsafe.");
    }
    mNeedClientAuth = pNeedClientAuth;
}

@Override
public void configure(HttpsParameters params) {
    LOGGER.entering(LOGGER_SOURCE_CLASS, "configure");

    final SSLContext context = getSSLContext();
    final SSLParameters sslParams = context.getDefaultSSLParameters();

    // Override current values
    sslParams.setCipherSuites(mCiphers.split(","));
    sslParams.setProtocols(mProtocols.split(","));
    sslParams.setNeedClientAuth(mNeedClientAuth);

    params.setSSLParameters(sslParams);

    LOGGER.exiting(LOGGER_SOURCE_CLASS, "configure");
}

}

Question 1: Is the statement ‘should not use classes in com.sun’ valid? For the reason I explained? From my search (e.g. What is inside com.sun package?), I found out it seems to have a difference between package ‘sun.’ and ‘com.sun.’. Still no definitive (documented) answer. Please, give reference for your answer.

Question 2: If I should not use the class ‘com.sun.net.httpserver.HttpsServer’, what could/should I use?

NOTE: I don't want to use a container (like Tomcat, Jetty, ...). I won't explain the reason. That's off topic.

like image 795
Algiz Avatar asked May 03 '17 13:05

Algiz


1 Answers

There's no issue with using the com.sun.net package HTTP server other than it's not part of the JDK spec, it's just more code that Oracle bundle into their distribution. You wont find those classes in OpenJDK, but it's no different from say tomcat or jetty. The issue with using sun or com.sun packages has always been that they are not part of the JDK spec, they are their code that implements various JDK components or just stuff they provide, because they are good guys/gals. See this SO question and this FAQ from Oracle for details about sun. and com.sun

Personally I would avoid it because there are better options. You can package your Endpoint up as a WAR file and deploy to a servlet engine or use Spring Boot/Dropwizard to bundle the servlet engine into a big jar file.

I'd look at the servlet engines that use battle tested non blocking IO and have much better management and operational control. Already mentioned are Jetty and Tomcat which are both very good, there's also JBoss Wildfly and a bunch of other commercial options (WebLogic, Websphere, probably thousands of others)

All of these will allow you to do 2-way SSL, and many will allow you to re-use your existing KeyStore and TrustStore code.

Spring Boot has a nice SOAP example and you will find the same approach works for many other servlet engines.

like image 174
stringy05 Avatar answered Nov 02 '22 10:11

stringy05