Using X.509 Certificates for Identity Propagation with Web Service Security in WebLogic Server

This was originally posted on my dev2dev blog April 29th, 2008.

One of my customers is considering a web services interface for exchanging information with their clients.  Security is a very important consideration in their design, so they are considering using best practices for authentication, integrity and confidentiality.  In this post I'll explain some detail around my experiences setting up a simple prototype using Web Service Security hands-on.

Introduction

Currently they plan on using HTTPS for the transport, but SSL may be terminated by SSL hardware accelerators before it reaches the application server, so transport level security is not enough in this instance, we'll also need some message level security.  The newer WS-Security 1.1 specification is available in the latest releases of WebLogic Server, but WS-Security 1.0 has maximum interoperability with other web services stacks.  So it you are not in control of web service stack's that your clients are using, it's best to use the older standard.  SAML, Username, and X.509 Token Profiles can be used for authentication.  In this instance since certificates are already required for SSL, it will be straightforward to also use them to authenticate identity.  This is a similar use-case to a previous dev2dev article posted on securing web services in WLS 9.2, but in this case we are going to use the X.509 certificates for the identity propagation instead of Username Token.  The formal name for this is the OASIS WS-Security X.509 Token Profile.  The identity in the certificate will map to an LDAP user.

Note that WebLogic Server ships with examples that perform User-name Token authentication located at <BEA_HOME>\wlserver_10.0\samples\server\examples\src\examples\webservices\wss1.1

I was not able to find a X.509 Token Profile example, so I wrote it up here and hopefully you can benefit from my experiences by getting this running and understand the steps in under an hour.

Client Diagram

ws-secClient

Server-side Diagram

ws-secServer

Basic Steps

  • Client Certificate Setup (10 minutes)
  • Server Configuration (20 minutes)
  • Server-side JWS Programming (15 minutes)
  • Client-side Web Service Programming (15 minutes)

    Client Certificate Setup

    First, set up a java key store and certificate identifying a user that we can map to an LDAP user.  In this example I use only the CN attribute.  This CN attribute will map to a user in WebLogic's embedded LDAP, but you can use another enterprise LDAP (Active Directory, eDirectory, OpenLDAP) just as easily.  We will use self-signed certificates in this simple example, but in production I would highly encourage you to use certificates issued by a Certificate Authority.

    keytool -genkey -v -keyalg RSA -sigalg SHA1withRSA -keystore D:/bea102/wlserver_10.0/samples/domains/wl_server/keystores/Identity.jks -alias testalias -keysize 1024 -keypass weblogic -storepass weblogic -dname CN=secuser'

    Now we need to export the pubic key to a .pem file so we can import it on the server side.


    keytool -export -alias testalias -file testalias.pem -sigalg SHA1withRSA -keystore D:/bea102/wlserver_10.0/samples/domains/wl_server/keystores/Identity.jks -storepass weblogic -rfc

     

    Server Configuration

    Some administrative configuration is required in the console to load the certificate into the trusted certificate chain and to tell the web services security stack to do identity propagation when X.509 tokens are found.

    WebLogic ships with a DemoTrust keystore that can be used for testing purposes.  Again, for production domains, you would want to not use the DemoTrust keystore, but it works well for prototyping the configuration steps.  Here is how I imported the certificate into the keystore.


    keytool -v -import -trustcacerts -alias testalias -file testalias.pem -keystore D:/bea102/wlserver_10.0/server/lib/DemoTrust.jks -keypass DemoTrustKeyStorePassPhrase -noprompt

    Web Service Security Configuration for the domain is also required.  Follow the console configuration instructions here.  Then follow the instructions here to configure the identity propagation with a Token Handler.  Note that one of the steps will specify that we are using the CN attribute to map to the user name in LDAP.

    If you have not already done so, create a user.

    users [2]

    Server-side JWS Programming

    In order to require authentication on a web service, you need only specify a policy via an annotation in the JWS file.  WLS ships with a policy inside the weblogic.jar file known as Auth.xml (as well as Sign.xml and Encrypt.xml which are explained in the earlier article I mentioned).  Here is a simple JWS file that shows how to attach that policy.  This will require all users calling the time operation to be authenticated.  Use the context object to retrieve the identity principal used to validate that we are indeed using an authenticated user.


    package com.bea.sample.webservice; 

    import java.util.Date;

    import javax.jws.WebMethod;
    import javax.jws.WebService;

    import weblogic.jws.Context;
    import weblogic.jws.Policies;
    import weblogic.jws.Policy;
    import weblogic.wsee.jws.JwsContext;

    @WebService
    public class Auth {

    @Context
    private JwsContext ctx;
    @WebMethod
    @Policies({
    @Policy(uri = "policy:Auth.xml", direction=Policy.Direction.inbound )})
    public String time()
    {
    return "User " + ctx.getCallerPrincipal() + " " + (new Date().toString());
    }
    }


    Client-side JAX-RPC Programming

    The normal way to generate JAX-RPC clients provided by BEA is to use clientgen.  In this case we need to add a special parameter generatePolicyMethods="true" to indicate that policy is being used and generate extra methods on the stubs that are created.  Here is the snippet from the build.xml file for ant that generates the client code.


    <target name="run-clientgen" depends="clean">
    <taskdef name="clientgen" classname="weblogic.wsee.tools.anttasks.ClientGenTask" classpathref=""compile.classpath" />

    <mkdir dir="${build.dir}"/>

    <clientgen
    wsdl="${wsdl.dir}/${project.name}.wsdl"
    packageName="${client.package.prefix}.security.http.x509certbst.standalone.stub"
    destDir="${build.dir}"
    generatePolicyMethods="true"
    />

    <javac
    srcdir="${build.dir}"
    destdir="${build.dir}"
    source="1.5"
    >
    <include name="test/**/*.java"/>
    <classpath>
    <path refid="compile.classpath"/>
    </classpath>
    </javac>

    <delete file="${lib.dir}/${client.jar.name}Client.jar"/>
    <mkdir dir="${lib.dir}"/>

    <jar destfile="${lib.dir}/${client.jar.name}Client.jar">
    <fileset dir="${build.dir}">
    <include name="**/*.*"/>
    <exclude name="**/*.java"/>
    </fileset>
    </jar>
    </target>


    The JAX-RPC client code specifies the policy that indicates which certificate to use for x509 identity propagation.  It is different from just using the policy attached to the WSDL on the server-side because we need to give some guidance to the web service stack on the client as to how to embed the identity.  Notice how we specify the CN=secuser in the TokenIssuer element.  I have not included the policy element to sign the body to lower the overhead of the web service invocation.  It is a trade-off of additional security versus performance.


    <wsp:Policy wsu:Id="WSSEX509CertificateTokenPolicy.xml"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:wssp="http://www.bea.com/wls90/security/policy"
    >
    <wssp:Identity>
    <wssp:SupportedTokens>
    <wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
    </wssp:SupportedTokens>
    </wssp:Identity>
    <wssp:Integrity xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part">
    <wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <wssp:CanonicalizationAlgorithm URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    <wssp:Target>
    <wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <wssp:MessageParts Dialect="http://www.bea.com/wls90/security/policy/wsee#part"
    >wls:SecurityHeader(wsu:Timestamp)</wssp:MessageParts>
    </wssp:Target>
    <wssp:SupportedTokens>
    <wssp:SecurityToken IncludeInMessage="true" TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
    <wssp:TokenIssuer>CN=secuser</wssp:TokenIssuer>
    </wssp:SecurityToken>
    </wssp:SupportedTokens>
    </wssp:Integrity>
    <wssp:MessageAge Age="300"/>
    </wsp:Policy>

    Inside the Client.java file that makes use of the generated JAX-RPC classes from clientgen, simply specify the policy file in the constructor for the port.


    AuthService_Impl service = new AuthService_Impl();

    Auth port = service.getAuthSoapPort(
    Thread.currentThread().getContextClassLoader().getResourceAsStream(_properties.getProperty("helloworld.http.x509certbst.WSPolicyFile")),
    true,
    false
    );


    There is also some special code required to set up reading from the keystore.  I've commented out some code that illustrates how we would inject a Username token if we were using that approach instead.


    List<CredentialProvider> credProviders = new ArrayList<CredentialProvider>();

    CredentialProvider cp = new ClientBSTCredentialProvider(
    _properties.getProperty("helloworld.http.x509certbst.IdentityKeyStore"),
    _properties.getProperty("helloworld.http.x509certbst.IdentityKeyStorePassword"),
    _properties.getProperty("helloworld.http.x509certbst.IdentityKeyAlias"),
    _properties.getProperty("helloworld.http.x509certbst.IdentityKeyPassword")
    );

    credProviders.add(cp);

    /*
    CredentialProvider cp = new ClientUNTCredentialProvider(
    _properties.getProperty("helloworld.http.passwordunt.username").getBytes(),
    _properties.getProperty("helloworld.http.passwordunt.password").getBytes()
    );
    credProviders.add(cp);
    */
    authStub._setProperty(
    WSSecurityContext.CREDENTIAL_PROVIDER_LIST,
    credProviders
    );

    authStub._setProperty(
    WSSecurityContext.TRUST_MANAGER,
    new TrustManager()
    {
    public boolean certificateCallback(X509Certificate[] chain, int validateErr)
    {
    return true;
    }
    }
    );


    When you run the client, you should see output like this if everything is working correctly.


    [Client.Client()]: Attempting to load .properties file from JAXRPCClients.properties
    [Client.Client(String)]: JAXRPCClients.properties was loaded successfully.
    [Client.time()]: response.getStringParameter()=User secuser Mon Apr 28 17:26:26 CDT 2008
    [Client.example()]: Elapsed milliseconds: 1141

    If you encounter problems, then you can enable verbose debugging on both the client and server side by specifying the JVM argument -Dweblogic.wsee.verbose=* which will output to the standard out all the details over the web service requests and responses.

    I have attached my Eclipse-based Workshop Studio projects from the WebLogic Portal 10.2 download to the site if you would like to try it out yourself.  You may have to adjust some of the project settings to adjust the location of webservicesclient.jar and add the weblogic.jar to the ant classpath to generate the client.  Hopefully my experiences with this simple prototype will benefit you.


  • Comments:

    Nice entry on web service security configuration. This type of content is sorely missing from WebLogic documentation. Anyways, to my question. In this example, we needed to create a TokenHandler. Do you have more insight on the property/value pairs that can be assigned? Also, are there any more blog entries that are related to message-level security?

    Posted by brian on August 18, 2008 at 06:50 PM PDT #

    Post a Comment:
    Comments are closed for this entry.
    About

    James Bayer Image
    I was formerly a Product Manager on the WebLogic Server team based out of Oracle HQ. You can find my new blog at http://iamjambay.com.
    Follow Me on Twitter
    Oracle WebLogic

    Search

    Archives
    « April 2014
    SunMonTueWedThuFriSat
      
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
       
           
    Today