Home > Web Services > How to develop a standalone SSL web service

How to develop a standalone SSL web service

How to develop a standalone SSL web service

Hi! Today, I´m gonna show you how to develop a simple SSL web service. Please note that this is not a “enterprise” way of developing a secure web service, but this may be very useful for testing purposes.

Constraints

To develop this service, you need to use a Sun/Oracle JVM (I tested with 1.6 VM, but should work with 1.5, too). This is not gonna work in any other kind of VM. Also, you need to have JAX-WS in your classpath.

The code

The code begins with the development of the web service itself. I used a standard JAX-WS service as an example, so my service looks like this:


@WebService
public class SOAPService {

	public String test() {
		return "Hello, SSL world!";
	}	
}

Next, we may use Java´s Endpoint class to create our object as an web service. The code is like

Endpoint endpoint = Endpoint.create(new SOAPService());

At this point, you need to create the server, and to create the server, you need a .jks (Java Keystore) file. You may create this file using JDK´s (or JRE´s) tooling, but I prefer using a GUI to do so. Personally, I like Lazgo´s KeyStore Explorer (available here). I won´t get in details here on how to create the JKS file; if you don´t know how to do it, you may want to have a look here.

Note: be aware that the CN attribute of your certificate must be equal to your host´s name!

After creating the JKS file, we are ready to create our HTTPS server. I won´t explain this code very much, as it should be pretty self-explanatory:


public static void main(String[] args) throws Exception {
		Endpoint endpoint = Endpoint.create(new SOAPService());
		SSLContext ssl =  SSLContext.getInstance("SSLv3");
		
		
		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
		KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());


		//Load the JKS file (located, in this case, at D:\keystore.jks, with password 'test'
		store.load(new FileInputStream("D:\\keystore.jks"), "test".toCharArray()); 

		//init the key store, along with the password 'test'
		kmf.init(store, "test".toCharArray());
		KeyManager[] keyManagers = new KeyManager[1];
		keyManagers = kmf.getKeyManagers();
		
		

		//Init the trust manager factory
		TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

		//It will reference the same key store as the key managers
		tmf.init(store);
		
		TrustManager[] trustManagers = tmf.getTrustManagers();
		
		
		ssl.init(keyManagers, trustManagers, new SecureRandom());

		//Init a configuration with our SSL context
		HttpsConfigurator configurator = new HttpsConfigurator(ssl);
		

		//Create a server on localhost, port 443 (https port)
		HttpsServer httpsServer = HttpsServer.create(new InetSocketAddress("localhost", 443), 443);
		httpsServer.setHttpsConfigurator(configurator);
		
		
		//Create a context so our service will be available under this context
		HttpContext context = httpsServer.createContext("/test");
		httpsServer.start();
		

		//Finally, use the created context to publish the service
		endpoint.publish(context);
		
		

	}


And voilà! That should be enough to your service be available under SSL. With this code, the wsdl of the service should be available under https://localhost:443/test?wsdl.

Please note that, at the time that I developed this service, I had some trouble with the generated WSDL, specifically with the port address and references to schema files. A reasonable work around for this problem (if you have it too) is to download the files (WSDL, schemas, and so on) and fix it by hand.

See ya!

Advertisements
  1. jason
    02/10/2011 at 4:42 PM

    Just wondering if there was anyway to get the certificate the client uses to connect to the ssl enabled server? I’ve tried (or so it seems) everything possible but still with no luck?

    For example, server has keystore and truststore (they can be the same or not). When client connects to ssl server, the server first checks to see if it has clients certificate in truststore. If it does then connection proceeds, if the clients certificate is *not* in the server’s truststore, then connection is aborted by the server.

    I’ve got all of that working fine, but everytime I try to find out which client has connected to which @webservice, I get a null pointer exception. Any ideas? Suggestions?

    -Jason

  2. jason
    02/10/2011 at 5:29 PM

    nvm, found it from

    MessageContext context = wsContext.getMessageContext();
    HttpsExchange hse = (HttpsExchange)wsContext.getMessageContext().get(“com.sun.xml.internal.ws.http.exchange”);
    hse.getSSLSession().getPeerPrincipal().getName();

    Guess I was looking in the wrong place 🙂

    • 02/12/2011 at 1:22 PM

      Hi, Jason! It´s really nice that you figured it out on your own, congratulations! I just would like to reinforce the piece of code your shared, by letting readers know that there are two ways of getting the Web Services context. One of them is by injection:

      @Resource
      private WebServiceContext context;

      And the other one is as shown in “How to intercept web services ingoing/outgoing messages.

      Once again, congratulations!

  3. heniu
    05/02/2011 at 6:38 PM

    This is quite easy to figure out (because of handy-dandy map interface of MessageContext), but I’ll mention that the key name of HttpsExchange can also be:
    com.sun.xml.ws.http.exchange

  4. tom
    09/06/2011 at 9:43 PM

    Hi there,

    I wrote a web service server using JAX-WS just like this here. But I am having trouble with the client program that would access the soap port via HTTPS and SSL. Here is what my client code looks like:

    System.setProperty(“javax.net.ssl.trustStore”, “certificate.p12”);
    System.setProperty(“javax.net.ssl.trustStorePassword”, “password”);
    System.setProperty(“javax.net.ssl.trustStoreType”, “PKCS12”);
    System.setProperty(“javax.net.ssl.TrustManagerFactory.algorithm”, “SunX509”);

    System.setProperty(“javax.net.ssl.keyStore”, “certificate.p12”);
    System.setProperty(“javax.net.ssl.keyStorePassword”, “password”);
    System.setProperty(“javax.net.ssl.keyStoreType”, “PKCS12”);
    System.setProperty(“javax.net.ssl.KeyManagerFactory.algorithm”, “SunX509”);

    URL theURL = new URL(“https://192.28.1.1:8888/test/TestSoap?wsdl”);
    QName theQName = new QName(“http://TestSoap.test”, “TestSoapPortType”);
    Service theService = Service.create(theURL, theQName);
    TestSoapPortType handle = theService.getPort(TestSoapPortType.class);

    handle.soapMethod(null);

    But it’s throwing an exception at the Service.create invocation:
    javax.net.ssl.SSLHandshakeException:
    sun.security.validator.ValidatorException: PKIX path building failed:
    sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

    Can you please help?

    • 09/19/2011 at 8:59 AM

      Hi, Tom! Usually, this kind of exception happens when the system can’t find the path for the certificate. What happens if set it with an absolute path?

      Regards,
      Alexandre

      • tom
        09/19/2011 at 4:22 PM

        Hi, Alexandre,

        Thank you for following up on this. I was able to resolve the problem last week. You are absolutely right. It was because the path for the trust store was not correct. After that was fixed, I was able to get the SSL handshake to work.

        I did run into another problem, however. It seems like after soap operation calls from the client are returned from my web service server, the socket is kept open. For some existing client programs, they expect to have the socket closed in order to return from their read operation.

        The web service is published using Sun’s built-in HttpServer package, and the Endpoint class. Is there a way to force the server to close the socket connection after it returns a soap call? I tried setting the system property http.keepAlive to false to no avail.

        Currently I have a way around this problem, but it’s not very elegant. Essentially I get the HttpHandler object from the HttpContext after it’s been created by the Endpoint.publish operation. And I call its handle() method from another HttpHandler class I wrote, which follows it up by sending a HttpHeader with “Connection” set to “close”. Something like this:

        HttpServer server = HttpServer.create(myInetSocketAddress, 5);
        HttpContext context = server.createContext(mySoapPath);
        Endpoint endpoint = Endpoint.create(mySoapImpl);
        endpoint.publish(context);
        MyHandler handler = new MyHandler(context.getHandler());
        server.removeContext(mySoapPath);
        server.createContext(mySoapPath, handler);
        server.start();

        private class MyHandler implements HttpHandler {
        private HttpHandler h;
        public MyHandler(HttpHandler in) {
        h = in;
        }
        public void handle(HttpExchange t) throws IOException {
        h.handle(t);
        Headers closing = t.getResponseHeaders();
        closing.set(“Connection”, “close”);
        t.sendResponseHeaders(200, 0);
        t.close();
        }
        }

        It works for my current needs, but it just seems like there’s got to be a better way…

        Thanks again

      • 09/20/2011 at 8:49 AM

        Hi, tom!

        Sorry to disappoint you, but I don’t know anything about this problem… it sounds like a really serious bug on the API, but anyway, I strongly recommend you to use this version only to do testing, not in the real environment. So, you really should try putting this on an application server or anything like that if you want to move this to production, OK?

        Once again, sorry.

  5. edulib
    01/31/2013 at 12:20 PM

    If you want to replace keytool with a GUI tool then you can also use CERTivity.
    http://www.edulib.com/products/keystores-manager/

    It can handle different types of keystores (JKS, JCEKS, PKCS12, BKS, UBER, Windows) and digital signatures.

    • 02/03/2013 at 9:40 PM

      edulib, it looks like a great tool (“looks” because I didn’t have time to test it, sorry). But what I like most in KeyStore Explorer is that it’s free (although it’s not open source). I saw your tool is paid (and, for a Brazilian like me, well paid – around R$ 150,00 today). Is there anything your tool does that is really awesome, considering the features of KeyStore Explorer’s?

      Thanks in advance.

  6. edulib
    02/04/2013 at 2:46 PM

    Hi Alexandre,

    Here are several features that can help you do various tasks easier and faster. CERTivity aims at making it easier to manage digital security-related assets from any source.

    – Microsoft Windows keystores: It can open both the Root keystore and the User keystore on MS Windows systems.
    – Installed JREs: It can detect all the JRE CA keystores not just the current one.
    – Expiring self-signed key pairs: It can extend the validity of self-signed key pair entries. You only need to specify the new expiry date instead of creating a new key pair from scratch.
    – Import SSL certificates: It can import certificates directly into the active keystore after retrieving them from SSL.
    – Test SSL protocols: It can use SSL certificates against a text protocol over SSL to send requests and receive responses (e.g. a web service, SMTP, direct XML over TCP/IP) – both server side and client side certificates/key pairs can be configured.
    – Get revocation status: It can get the revocation status of a certificate.
    – Signing: It can also sign PDF and XML files using a key pair from one of your keystores.
    – Verifying signatures: It can check the digital signatures of PDF, XML and JAR files and import the embedded certificates directly into the active keystore.

    CERTivity is also actively developed and a new version is due in about one month. If any of these features sounds interesting or if there is any feature that you need, we’d be happy to talk to you about them. Just drop us a line at support AT edulib DOT ro.

    Best regards,
    The CERTivity Team

  7. Maddy
    06/16/2014 at 3:10 PM

    Hello,

    I tried the above method and it does work if it has only one operation defined. What if the Webservice has more than one web method? How do we handle that?

    Thanks,
    Maddy

    • 06/16/2014 at 7:35 PM

      Hi, Maddy

      The number of operations should be of no difference at all. The scenario I can think about that could provoke changes are the mechanisms of addressing of the operations (i.e. rpc, document literal, document literal wrapped, etc.).

      Could you provide an example, so I can take a look?

      Regards,
      Alexandre

      • Maddy
        06/20/2014 at 8:10 PM

        Hi Alexandre,

        I am doing contract first approach. So, I created HelloWorld.wsdl with two operations and my interface looks like below.

        @WebService
        public interface HelloWorld {

        @WebMethod
        String operationA(String A, String B);

        @WebMethod
        String operationB(String C);
        }

        My HelloWorldImpl.java provide implementation for above methods.

        @WebService
        public class HelloWorldImpl implements HelloWorld {

        @Override
        public String OperationA(String A, String B) {
        ……
        }

        @Override
        public String OperationB(String C) {
        ……
        }
        }

        My HelloWorldPublisher.java is similar to the code you have above except that you have “/test” in the http_context where as I have “/HelloWorld” as the http_context.

        Therfore, when I try to open the wsdl from the browser as https://ws.abc.com:443/HelloWorld?wsdl it opens the wsdl. When I submit a soap request from SOAPUI for operationA or operationB, I get an error context not found. So, literally Endpoint class doesn’t know where to forward the request to.

        Thanks,
        Maddy

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: