Friday, November 1, 2013

Configure SSL in Weblogic

To test the SSL on weblogic, deploy a web application to the weblogic server. And from the weblogic admin console check the box "SSL Listen Port Enabled" in the "configuration" tab. By default, the SSL listen port on weblogic is 7002. In the following, the web application deployed is assumed to be myapp.

In the startWeblogic.cmd file that starts the weblogic server, you can add the following line that can generate some debug logs:

set JAVA_OPTIONS=%JAVA_OPTIONS% -Dssl.debug=ture -Dweblogic.StdoutDebugEnabled=true -Dweblogic.security.SSL.enforceConstraints=off -Dweblogic.security.SSL.verbose=true 

The Weblogic Default Configuration

By default, Weblogic uses its Demo identity and Demo trust store. More specifically, the following are used:
%WLHOME%\wlserver_10.3\server\lib\DemoIdentity.jks
%WLHOME%\wlserver_10.3\server\lib\DemoTrust.jks
Trust store password: DemoTrustKeyStorePassPhrase
Key store password: DemoIdentityKeyStorePassPhrase
Private key password: DemoIdentityPassPhrase

You can go to the weblogic admin console to look at the configuration. Go to the server and then check the "Keystore" tab and the "SSL" tab.

The following is the test program based on a sample from JSSE tutorial.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

/*
 * This example demostrates how to use a SSLSocket as client to
 * send a HTTP request and get response from an HTTPS server.
 * It assumes that the client is not behind a firewall.
 * In this example, one-way SSL is used. The server needs to be trusted.
 */

public class SSLSocketClient {

 public void test() {

  // if you are running this program using jrocket JVM, then the following lines that
  // set the trustStore and trustStorePassword are not needed.
  String trustStore = "C:\\bea1032\\jrockit_160_14_R27.6.5-32\\jre\\lib\\security\\cacerts";
  String trustStorePassword = "changeit";
  System.setProperty("javax.net.ssl.trustStore", trustStore);
  System.setProperty("javax.net.ssl.trustStorePassword",
    trustStorePassword);
    
  System.setProperty("javax.net.debug", "ssl");
  String host = "localhost"; 
  int port = 7002; 
  try {
   SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory
     .getDefault();
   SSLSocket socket = (SSLSocket) factory.createSocket(host, port);

   socket.startHandshake();

   PrintWriter out = new PrintWriter(new BufferedWriter(
     new OutputStreamWriter(socket.getOutputStream())));

   out.println("GET /myapp/ HTTP/1.0");
   out.println();
   out.flush();

   /*
    * Make sure there were no surprises
    */
   if (out.checkError())
    System.out
      .println("SSLSocketClient:  java.io.PrintWriter error");

   /* read response */
   BufferedReader in = new BufferedReader(new InputStreamReader(socket
     .getInputStream()));

   String inputLine;
   while ((inputLine = in.readLine()) != null)
    System.out.println(inputLine);

   in.close();
   out.close();
   socket.close();

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 public static void main(String[] args) throws Exception {
  SSLSocketClient p = new SSLSocketClient();
  p.test();
 }
}

Note that if you use jrocket as the JVM to run this client program, then you can remove those lines that set the trust store System properties. The jrocket JVM may have added the Weblogic Demo certificate into its trust store.

To Use Custom Identity and Custom Trust

Usually only one-way SSL is used. The client program that talks to the weblogic server will verify the identity certificate of the server to see if it can trust the server. If two-way SSL is enforced, then the client will need to supply its own identity to the weblogic server. More configurations are needed to make this whole thing successful. The following is an example about how to do all the necessary steps. The java keytool utility is used.

On the client side:

  1. Generate identity keystore.
          keytool -genkeypair -keystore myidentity.jks -storepass mytest
          
  2. Export a certificate that authenticates your public key. The following command will generate the file mycert.cer.
          keytool -exportcert -alias mykey -keystore myidentity.jks -storepass mytest -file mycert.cer
          
  3. In this case, we will still use the default jrocket JVM trust store which will trust the Demo certificate from weblogic server.

On the server side:

  1. Create the identity and trust store. In this case, we will still use DemoIdentity. So copy DemoIdentity.jks to C:\DemoIdentity.jks. Also copy DemoTrust.jks to C:\DemoTrust_mycert.jks
  2. Import the client certificate to the trust keystore so the server will trust the client.
        keytool -import -alias mykey  -file mycert.cer -keystore DemoTrust_mycert.jks 
        -storepass DemoTrustKeyStorePassPhrase 

On Weblogic admin console "KeyStores" tab.
For the "KeyStores", choose "Custom Identity and Custom Trust" from the drop-down list. And fill out the following.
--Identity--
Custom Identity Keystore: C:\DemoIdentity.jks
Custom Identity Keystore Type: JKS
Custom Identity Keystore Passphrase: DemoIdentityKeyStorePassPhrase
Confirm Custom Identity Keystore Passphrase: DemoIdentityKeyStorePassPhrase

--Trust--
Custom Trust Keystore: C:\DemoTrust_mycert.jks
Custom Trust Keystore: JKS
Custom Trust Keystore Passphrase: DemoTrustKeyStorePassPhrase
Confirm Trust Keystore Passphrase: DemoTrustKeyStorePassPhrase

On Weblogic admin console "SSL" tab.
Fill out the following.
--Identity--
Private Key Alias: demoidentity
Private Key Passphrase: DemoIdentityPassPhrase
Confirm Private Key Passphrase: DemoIdentityPassPhrase

In the "Advanced" section, choose "Client Certs requested and Enforced" for "Two Way Client Cert Behavior:".

After all these, you need to save the configuration and restart the weblogic server. Then run the following program. It should work.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

/*
 * This example demostrates how to use a SSLSocket as client to
 * send a HTTP request and get response from an HTTPS server.
 * It assumes that the client is not behind a firewall
 * In this example, two-way SSL is used. Client also needs to present its identity.
 */

public class SSLClient {

 public void testUsingSocket() {

  String keyStore = "C:/myidentity.jks";
  String keyStorePassword = "mytest";
  System.setProperty("javax.net.ssl.keyStore", keyStore);
  System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
  
  // if you are running this program using jrocket JVM, then the following lines that
  // set the trustStore and trustStorePassword are not needed.
  String trustStore = "C:\\bea1032\\jrockit_160_14_R27.6.5-32\\jre\\lib\\security\\cacerts";
  String trustStorePassword = "changeit";
  System.setProperty("javax.net.ssl.trustStore", trustStore);
  System.setProperty("javax.net.ssl.trustStorePassword",trustStorePassword);

  System.setProperty("javax.net.debug", "ssl");
  String host = "localhost"; 
  int port = 7002; 
  try {
   SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory
     .getDefault();
   SSLSocket socket = (SSLSocket) factory.createSocket(host, port);

   socket.startHandshake();

   PrintWriter out = new PrintWriter(new BufferedWriter(
     new OutputStreamWriter(socket.getOutputStream())));

   out.println("GET /myapp/ HTTP/1.0");
   out.println();
   out.flush();

   /*
    * Make sure there were no surprises
    */
   if (out.checkError())
    System.out
      .println("SSLSocketClient:  java.io.PrintWriter error");

   /* read response */
   BufferedReader in = new BufferedReader(new InputStreamReader(socket
     .getInputStream()));

   String inputLine;
   while ((inputLine = in.readLine()) != null)
    System.out.println(inputLine);

   in.close();
   out.close();
   socket.close();

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

public void testUsingURL() throws Exception {

  javax.net.ssl.HttpsURLConnection
    .setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() {

     public boolean verify(String hostname,
       javax.net.ssl.SSLSession sslSession) {
      if (hostname.equals("localhost")) {
       return true;
      }
      return false;
     }
    });

  String keyStore = "C:/bea1032/wlserver_10.3/server/lib/DemoIdentity.jks";
  String keyStorePassword = "DemoIdentityKeyStorePassPhrase";

  keyStore = "C:/workshop/java/security/myidentity.jks";
  keyStorePassword = "mytest";

  System.setProperty("javax.net.ssl.keyStore", keyStore);
  System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);

  URL myUrl = new URL("https://localhost:7002/myapp");
  BufferedReader in = new BufferedReader(new InputStreamReader(myUrl
    .openStream()));

  String inputLine;

  while ((inputLine = in.readLine()) != null)
   System.out.println(inputLine);

  in.close();
 }

 public static void main(String[] args) throws Exception {
  SSLClient p = new SSLClient();
  p.testUsingSocket();
  p.testUsingURL();
 }
}

Note that in the example above, the testUsingURL() method needs to set the HostnameVerifier to accept "localhost". Otherwise it will throw an exception. But the testUsingSocket() method does not need this. So there must be something different underneath in the Socket implementation.

References

  1. http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html

No comments:

Post a Comment