The aim of this blog post is to:
- Provide an example of creating a JAX-WS Web Service secured with WS-SecureConversation
- Provide an example of creating a standalone JAX-WS Java client for invoking the Web Service
- Provide an example of a message exchange for WS-SecureConversation
The scope of this post has intentionally been kept to what can be done directly from WebLogic server rather than implementing what may be required for a more thorough, production style service.
WebLogic ships with a number of pre-defined policies that can be used to "decorate" a web service with particular behaviours, normally related to security. For the purposes of demonstrating WS-SecureConversation we want, unsurprisingly, to use a WS-SecureConversation Policy. The pre-defined WS-SecireConversation policies are documented at:
http://download.oracle.com/docs/cd/E21764_01/web.1111/e13713/message.htm#i243913
The policy we are going to use is:
Wssp1.2-2007-Wssc1.3-Bootstrap-Wss1.1.xml.
This policy is described as:
WS-SecureConversation handshake is protected by WS-Security 1.1. The application messages are signed and encrypted with DerivedKeys. The soap:Body of the RequestSecurityToken and RequestSecurityTokenResponseCollection messages are both signed and encrypted. The WS-Addressing headers are signed. Signature and encryption use derived keys from an encrypted key.
The selected policy implements WS-SecureConversation 1.3 and WS-SecureConversation 2005/2.
By default when you deploy a web service to WebLogic that uses a WS-Trust based policy (WS-SecureConversation extends WS-Trust) WebLogic generates a co-located secure token service (STS). This STS can be used to generate a Token that can be used to access the secured web service. The Token can be used for subsequent requests.
It should be possible to use an external STS instead of the co-located STS but this is beyond the scope of this post.
Creating the Certificates and Keys for Message Level Security
The policy chosen for this example dictates encrypting and signing the RequestSecurityToken (RST) message using client/server certificates to initialize (bootstrap) the secure conversation. Therefore we need to create a pair of certifcates and keys that will be used by the server and client for encrypting and signing these messages. These certificates are only used for encrypting and signing the messages for Web Service (message-level security) and are not used for Secure Socket Layer (SSL) transport-level security.
First we create a directory to hold the certificate/key pairs that we are going to generate. This should be a well-known path as we should generate these once and may be used for other examples. As such we shall create a directory under the domain directory and will issue any further commands from that directory.
mkdir ${domain.dir}/examples/security
We then use the CertGen utility provided with WebLogic to generate the basic certificates and keys needed for this example. The CertGen utility creates certificates that are issued by the DemoCA that is (by default) trusted by the DemoTrust. This significantly simplifies the configuration that is required for this demonstration. A real-world scenario where DemoTrust is not being used will require some further configuration.
# Create the client certificate (public key) and private key pair:
java utils.CertGen -cn soademo_client -certfile soademo_client.cer -keyfile soademo_client.key -keyfilepass password
# Create the server certificate (public key) and private key pair:
java utils.CertGen -cn soademo_server -certfile soademo_server.cer -keyfile soademo_server.key -keyfilepass password
This will create a number of files.
- soademo_client.cer.der - The client public key (certificate) in DER format
- soademo_client.cer.pem - The client public key (certificate) in PEM format
- soademo_client.key.der - The client private key in DER format
- soademo_client.key.pem - The client private key in PEM format
- soademo_server.cer.der - The server public key (certificate) in DER format
- soademo_server.cer.pem - The server public key (certificate) in PEM format
- soademo_server.key.der - The server private key in DER format
- soademo_server.key.pem - The server private key in PEM format
Next we copy the DER encoded demo CA certificate from the WebLogic server installation into the example security directory. This is safer than using the certificate directy from the product installation.
copy ${wl.home}/server/lib/CertGenCA.der .
Next we convert the demo CA certifcate from binary DER format to PEM format. The PEM format is required to allow us to complete the certificate chain in the next step. PEM format files can simply be concatenated together whereas DER format cannot.
java utils.der2pem CertGenCA.der
Next we create the certificate chains for generated certificates. To achieve this the demo CA certificate (in PEM format) is concatenated to the generated certificates (also in PEM format).
type CertGenCA.pem >> soademo_client.cer.pem
type CertGenCA.pem >> soademo_server.cer.pem
Finally we create a pair of Java KeyStores (JKS) that contain the client and server public (certificate) and private key pairs:
java utils.ImportPrivateKey -keystore soademo_client.jks -storepass password -storetype JKS -keypass password -alias soademo_client -certfile soademo_client.cer.pem -keyfile soademo_client.key.pem password
java utils.ImportPrivateKey -keystore soademo_server.jks -storepass password -storetype JKS -keypass password -alias soademo_server -certfile soademo_server.cer.pem -keyfile soademo_server.key.pem password
If you dont fancy doing all that by hand then you can run the Ant script in the setup directory.
Ant Script Target:
build.cmd generateKeys
Configuring WebLogic WebServices Security
There are several configuration steps that need to be completed in order for WebLogic Server to support this example. Luckily there is a sample WLST script to do this packaged with the WebLogic Server examples. Assuming that the WebLogic Server examples were installed when WebLogic was installed then the sample WLST script can be found at:
${wl_home}/samples/server/examples/src/examples/webservices/wsrm_security/configWss_Service.py
If the WebLogic Server examples were not installed initially these can be installed be re-running the WebLogic installer and selecting the examples pack. For the purposes of providing a complete example this sample WLST script has also been packaged with the files that accompany this blog post.
Run the Ant target:
build.cmd configWss
This WLST script performs the following:
1. Enables certificate type (X.509) in the default identity asserter
2. Creates the default "default_wss" web service security context
3. Creates the Secure Conversation Token (sct) credential provider in the default wss context
4. Creates the Derived Key (dk) credential provider in the default wss context
5. Creates the Certificate (x509) credential provider in teh default wss context
6. Creates the confidentiality XML keystore (using the keystore generated earlier)
7. Creates the integrity XML keystore (using the keystore that were generated earlier)
8. Creates the X509 token handler
Building the Web Service
For this example we are going to use Oracle Enterprise Pack for Eclipse (OEPE) as the tooling for generating the web service and the client. These instructions should be easily adapted for use in JDeveloper if that is your development weapon of choice.
For this example we are going to produce the service implementation first. Ideally we should create it interface (WSDL) first but it is far easier to reference the policy using an annotation in the implementation and therefore simplifies the example.
1. Create a new Web Service Project
Name: ExampleWSSC
2. Create a new WebLogic Web Service
Source folder: ExampleWSSC/src
Package: com.oracle.uk.ocs.examples.wssc
Name: ExampleWSSC
3. Add the following code:
...
@WebService
@Policies ({
@Policy(uri = "policy:Wssp1.2-2007-Wssc1.3-Bootstrap-Wss1.1.xml")
})
public class ExampleWSSC {
@WebMethod
public String sayHello(String name) {
return "Hello " + name + "!";
}
}
The important bit here is the @Policy annotation.
4. Generate the WSDL
Parent Directory: WebContent
5. Deploy the Web Service
Check that the service has successfully deployed. Access the WSDL at the following URL:
http://localhost:7001/ExampleWSSC/ExampleWSSCService?WSDL
Building the Java Client
The client is a fairly standard JAX-WS standalone Java client. The majority of the code is concerned with setting up a Binary Security Token which requires setting up the certificate stores which will be used for the message exchange encryption.
1. Generate the Client Library
Create a new directory in the Web Service project under the WebContent folder named clientlib
Right-click the WSDL that was exported earler and select the WebLogic WebServices -> Generate Web Service Client
Location: WebContent/clientlibs
2. Create a new Java Project
Name: ExampleWSSCClient
Classpath:
3. Add the client-code
package com.oracle.uk.ocs.examples.wssc.client;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.ws.BindingProvider;
import weblogic.wsee.security.bst.ClientBSTCredentialProvider;
import weblogic.wsee.security.util.CertUtils;
import weblogic.xml.crypto.wss.WSSecurityContext;
import weblogic.xml.crypto.wss.provider.CredentialProvider;
import com.oracle.uk.ocs.examples.wssc.ExampleWSSC;
import com.oracle.uk.ocs.examples.wssc.ExampleWSSCService;
public class ExampleWSSCClient {
ExampleWSSCService service;
ExampleWSSC port;
public ExampleWSSCClient() {
System.out.println("Initialising client (service and port)");
service = new ExampleWSSCService();
port = service.getExampleWSSCPort();
}
public MapgetRequestContext() {
return ((BindingProvider)port).getRequestContext();
}
public void addBinarySecureTokenCredProvider(String keystore, String keystorePassphrase, String keyAlias, String keyPassword, String serverCertFile) throws Exception{
System.out.println("Adding a BST credential provider");
List credProviders;
if (getRequestContext().containsKey(WSSecurityContext.CREDENTIAL_PROVIDER_LIST)) {
credProviders = (List) getRequestContext().get(WSSecurityContext.CREDENTIAL_PROVIDER_LIST);
} else {
credProviders = new ArrayList();
}
// Create a certificate from the PEM file
X509Certificate serverCert =
(X509Certificate)CertUtils.getCertificate(serverCertFile);
serverCert.checkValidity();
// Create the X509 Client Credential Provider
CredentialProvider cp = new ClientBSTCredentialProvider(
keystore,
keystorePassphrase,
keyAlias,
keyPassword,
"JKS",
serverCert);
credProviders.add(cp);
getRequestContext().put(WSSecurityContext.CREDENTIAL_PROVIDER_LIST, credProviders);
System.out.println("Successfully added credential provider");
}
public void setEndpoint(String endpoint) {
System.out.println("Endpoint set to " + endpoint);
getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);
}
public String sayHello(String name) {
System.out.println("Calling operation sayHello: name=" + name);
return port.sayHello(name);
}
public static void main(String[] args) throws Exception {
ExampleWSSCClient client = new ExampleWSSCClient();
client.addBinarySecureTokenCredProvider(
"d:/projects/oracle/osbdev/domains/osbdev/examples/security/soademo_client.jks", //keystore
"password", //keystorePassphrase
"soademo_client", //keyAlias
"password", //keyPassword,
"d:/projects/oracle/osbdev/domains/osbdev/examples/security/soademo_server.cer.pem" //serverCertFile
);
client.setEndpoint("http://localhost:7001/ExampleWSSC/ExampleWSSCService");
System.out.println("Starting...");
System.out.println(client.sayHello("Request 1"));
System.out.println(client.sayHello("Request 2"));
System.out.println("Success!");
}
}
Observing the Message Exchange
Right so we now have a service and client that are communicating. We can observe the messages going between the client and the server by introducing a proxy listening on a different port. Eclipse (OEPE) has such a proxy, simply right-click on the server and select "Monitoring -> Monitor port 7001 (http)" from the context menu. You will need to update your client endpoint to goto the proxy rather than going directly to the server.
SCT = Security Context Token (basis of a secure conversation)
RST = Request Secuirty Token
RSTR = Request Security Token Response
1. Client sends a RST/SCT request
2. Server responds with a RSTR/SCT
3. Client sends Request 1 (with the SCT from 2)
4. Server responds with the service response, secured with the derived key exchanged during 1&2
5. Client sends Request 2 (with the SCT from 2)
6. Server reponds with the service response, secured with the derived key exchanged during 1&2
Next Steps
So what can we do to build on from this? The following are a list of activities that would build on from the work performed in this blog and hopefully form the basis of some future follow-up blog posts.
Adapt the Wssp1.2-2007-Wssc1.3-Bootstrap-Https-UNT.xml policy to use HTTP rather than HTTPS so that the Request Security Token request can be more readily observed. This would also allow us to build a SoapUI client more easily. Use a secure token service (STS) from a third-party to provide the SCT rather than the one co-located on the server.