Portecle Packed

13.11.2011 § Napsat komentář

I found very nice, simple and easy-to-use tool Portecle, the GUI application helping with keystore management. To provide additional deploy option I created a package, containing a single-file executable with simple download page – see Portecle Pack. I decided so because the only option to launch the original application is via webstart, or to download a zip, which actually doesn’t contain any launcher. Just .jar file. Moreover, the project page is not clear. Simply stated, I missed the big download button at the top of the page… So here is it.

Java WebService over SSL

6.9.2011 § Komentáře: 3

I thought that customizing web service on https server (packed with JRE) and its client should be easy. But the lack of documentation make it quiet hard. Here are results of my findings. The topics covered are: Two-way SSL communication, custom SSLContext for webservice running on Java SDK embedded https server, dummy TrustManager, client certificate authentication. I’ll cover both the server and client side.

Suppose, you have created 2 certifikate keystore files, let’s say server.jks (containing server private key and client public certificate) and client.jks (symmetrically, the client private key and server public certificate).

The server side code follows, consider it as code for reference.

package com.example.echo;

import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsExchange;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;

import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.annotation.Resource;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.ws.Endpoint;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;

public class SSLWebService {
  
  public static void main(String[] args) throws Exception {
    new SSLWebService().runHttpsService();
  }
  
  public void runHttpsService() throws Exception {
    
    KeyStore ks = KeyStore.getInstance("JKS");
    FileInputStream keyStoreIn = new FileInputStream("server.jks");
    try {
      ks.load(keyStoreIn, "passphrase".toCharArray());
    } finally {
      keyStoreIn.close();
    }
    
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(ks, "passphrase".toCharArray());
    
    TrustManager[] trustManagers = new TrustManager[] {
    		new DummyTrustManager()};
    
    SSLContext sslCtx = SSLContext.getInstance("TLS");
    sslCtx.init(kmf.getKeyManagers(), trustManagers, null);
    
    HttpsConfigurator cfg = new HttpsConfigurator(sslCtx){
      public void configure(HttpsParameters params) {
            SSLParameters sslparams = getSSLContext().getDefaultSSLParameters();
            // Modify the default params: Will require client certificates
            sslparams.setNeedClientAuth(true);
            params.setSSLParameters(sslparams);
      }
    };

    ExecutorService httpThreadPool = Executors.newFixedThreadPool(10);
    
    HttpsServer httpsS = HttpsServer.create(new InetSocketAddress(8081), 50);
    httpsS.setHttpsConfigurator(cfg);
    httpsS.setExecutor(httpThreadPool);
    httpsS.start();
    
    HttpContext ctx = httpsS.createContext("/ws");
    
    ctx.setAuthenticator(new Authenticator(){
      @Override
      public Result authenticate(HttpExchange exch) {
        try {
          if(exch instanceof HttpsExchange) {
            HttpsExchange httpsExch = (HttpsExchange)exch;
            System.out.println("authen: " + httpsExch.getSSLSession().getPeerPrincipal().getName());
            // DO YOUR AUTHENTICATION HERE
            httpsExch.getSSLSession().putValue(
                "MY_PARAM_PEER_NAME",
                httpsExch.getSSLSession().getPeerPrincipal().getName());
            return new Authenticator.Success(exch.getPrincipal());
          }
        } catch (SSLPeerUnverifiedException e) {
          e.printStackTrace();
        }
        return new Authenticator.Failure(403);
      }
    });
    
    Endpoint endpoint = Endpoint.create(new Echo());
    endpoint.publish(ctx);
    
    // publish also on HTTP server, so we can access
    // wsdl through HTTP, just for our example.
    HttpServer httpS = HttpServer.create(new InetSocketAddress(8082), 50);
    httpS.start();
    Endpoint endpoint2 = Endpoint.create(new Echo());
    endpoint2.publish(httpS.createContext("/ws"));
  }
  
  @WebService(
      targetNamespace = "http://www.example.com/Echo")
  public static class Echo {
    
    @Resource
    protected WebServiceContext context;
    
    @WebMethod(operationName = "echo")
    @WebResult(name = "message")
    public String echo(@WebParam(name = "msg") String msg) {
      String user = "unknown";
      MessageContext msgCtx = context.getMessageContext();
      Object httpExchange = msgCtx.get("com.sun.xml.ws.http.exchange");
      if(httpExchange instanceof HttpsExchange) {
        HttpsExchange httpsExch = (HttpsExchange)httpExchange;
        user = (String)httpsExch.getSSLSession().getValue("MY_PARAM_PEER_NAME");
      }
      return "user '" + user + "' said: " + msg;
    }
  }
  
  public static class DummyTrustManager implements X509TrustManager {
    public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
    public void checkClientTrusted(X509Certificate[] certs, String authType)
        throws CertificateException {
      // DO YOUR VERIFICATION
    }
    public void checkServerTrusted(X509Certificate[] certs, String authType)
        throws CertificateException {
      // DO YOUR VERIFICATION
    }
  }
}

Obtain wsdl and generate client classes, from commandline:

wget -O echo.wsdl http://localhost:8082/ws?wsdl
wsimport -Xnocompile http://localhost:8082/ws?wsdl

And finally write a client:

package com.example.echo;

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

public class SSLWebClient {

  public void callEcho(String wsUrl, String msg) throws Exception {
    SSLContext sslCtx = SSLContext.getInstance("SSL");
    TrustManager[] trustManagers = new TrustManager[] {
      new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
          return new X509Certificate[0];
        }
        public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
        }
        public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
        }
      }
    };
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    KeyStore ks = KeyStore.getInstance("JKS");
    FileInputStream keyStoreIn = new FileInputStream("client.jks");
    try {
      ks.load(keyStoreIn, "passphrase".toCharArray());
    } finally {
      keyStoreIn.close();
    }
    kmf.init(ks, "passphrase".toCharArray());
    sslCtx.init(kmf.getKeyManagers(), trustManagers, null);
    
    // This is a key point: We must not initialize service with target URL, because then
    // the WSDL would be downloaded from the HTTPS server, before we initialize our
    // context properties. So we initialize the EchoService with previously downloaded WSDL
    // and then use it to call the real service through HTTPS.
    EchoService service = new EchoService(
        getClass().getResource("echo.wsdl"),
        new QName("http://www.example.com/Echo", "EchoService"));
    Echo port = service.getEchoPort();

    Map<String, Object> ctxt = ((BindingProvider)port).getRequestContext();
    ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, wsUrl);
    
    // Sets dummy hostname verifier, so that server certificate CN doesn't
    // have to match its hostname
    HostnameVerifier hnv = new HostnameVerifier() {
      @Override
      public boolean verify(String hostname, SSLSession sslSession) {
        return true;
      }
    };
    ctxt.put("com.sun.xml.internal.ws.transport.https.client.hostname.verifier", hnv);
    ctxt.put("com.sun.xml.ws.transport.https.client.hostname.verifier", hnv);
    
    // Sets socket factory from our ssl context,
    // which has dummy Trust manager. If used, server certificate doesn't need to be
    // in our trust keystore nor in Java "cacerts" keystore.
    ctxt.put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", sslCtx.getSocketFactory());
    ctxt.put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", sslCtx.getSocketFactory());
    
    String response = port.echo(msg);
    System.out.println("Response: " + response);
  }

  public static void main(String[] args) throws Exception {
    new SSLWebClient().callEcho("https://localhost:8081/ws/Echo", "Hello");
  } 
}

Taskblocks

29.6.2011 § Napsat komentář

Prvním příspěvkem bych rád zviditelnil svůj hobby projekt Taskblocks. Jde o jednoduchou aplikaci pomáhající při plánování projektů malých rozměrů. U nás ve firmě to už používá pár lidí při rozvrhování práce a hlavně pro jeho export/update do Bugzilly, kterou používáme jako hlavní repozitář plánových úkolů.

Prográmek toho neumí moc, ale tak to chci taky nechat, v nejbližší době se chci soustředit spíš na vytvoření takových věcí, aby se dal integrovat s jinými nástroji. Např. teď dokončuji načítání a ukládání projektu z/do webu (http), od čehož si slibuji možnost použít Taskblocks pro editaci projektů z webových stránek (spouštění s Java Webstart).

taskblocks screenshot

Taskblocks

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: