Using the WSS4J Framework
Apache's
WSS4J is a Java implementation of the OASIS Web Services Security (WS-Security) specification. WSS4J is a framework that you can use to sign and verify SOAP messages with WS-Security information. WSS4J uses the Apache Axis and Apache XML-Security projects and is interoperable with JAX-RPC server/clients and .NET server/clients. WSS4J implements Username Token profile V1.0 and X.509 Token Profile V1.0. WSS4J can generate and process the following SOAP Bindings:
- XML Security
- XML Signature
- XML Encryption
- Tokens
- Username Tokens
- Timestamps
- SAML Tokens
WSS4J can secure Web services deployed in most Java Web services environments; however, it ships with specific support for the Axis Web services framework. You can use WSS4J in a standalone manner or in tandem with Axis to create and process WS-Security elements within a SOAP envelope. You can obtain the latest release of the WSS4J project by checking out the CVS module,
ws-wss4j, using the following CVS path with any CVS client.
:pserver:anoncvs@cvs.apache.org:/home/cvspublic.
The tools in the next sections demonstrate how to use WSS4J to generate XML that conforms to the latest WS-Security specification.
Initializing the WSS4J Framework
First, you must initialize the WSS4J frameworkin this case, to the default values. For example:
private static final WSSecurityEngine secEngine =
new WSSecurityEngine();
Next, create a crypto provider. The default factory
getInstance()method creates a provider according to the class name specified by the system property
org.apache.ws.security.crypto.provider. If the provider property is not set, the
getInstance() method creates a default class instance,
org.apache.ws.security.components.crypto.BouncyCastle.
The provider is initialized to the values specified in the
crypto.properties file found in the WSS4J
.jar file. As shipped, that file specifies
org.apache.ws.security.components.crypto.Merlin as the provider class.
private static final Crypto crypto =
CryptoFactory.getInstance();
You use the AxisClient as the context engine for messaging operations.
private AxisClient engine = null;
private MessageContext msgContext = null;
public WSSecuritySample()
{
engine = new AxisClient(new NullProvider());
msgContext = new MessageContext(engine);
}
Creating the Target SOAP Envelope
The following method creates and returns an Axis message from a SOAP envelope string.
private Message getAxisMessage(String unsignedEnvelope)
{
InputStream inStream =
new ByteArrayInputStream(
unsignedEnvelope.getBytes());
Message axisMessage = new Message(inStream);
axisMessage.setMessageContext(msgContext);
return axisMessage;
}
The SOAP envelope used in this article and passed to the
getAxisMessage method shown above is illustrated as follows:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=http://www.w3.org/2003/05/soap-envelope
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<sayHello xmlns=
"http://jeffhanson.com/services/helloworld">
<value xmlns="">
Hello world!
</value>
</sayHello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Signing a SOAP Message
The following method uses the WSSignEnvelope class to sign a SOAP envelope and adds the signature data to the envelope in compliance with WS-Security.
public Message signSOAPEnvelope(SOAPEnvelope
unsignedEnvelope) throws Exception
{
WSSignEnvelope signer = new WSSignEnvelope();
String alias = "16c73ab6-b892-458f-abf5-2f875f74882e";
String password = "security";
signer.setUserInfo(alias, password);
Document doc = unsignedEnvelope.getAsDocument();
The "build" method creates the signed SOAP envelope. It takes a SOAP Envelope as a W3C Document and adds a WSS Signature header to it. The signed elements depend on the signature parts specified by the
WSBaseMessage.setParts(java.util.Vector parts) method. By default, it signs the SOAP Body element.
The "crypto" parameter is the object that implements access to the keystore and handling of certificates.
WSS4J includes a default implementation,
org.apache.ws.security.components.crypto.Merlin.
Document signedDoc = signer.build(doc, crypto);
// Convert the signed document into a SOAP message.
Message signedSOAPMsg =
(org.apache.axis.Message)AxisUtil.toSOAPMessage(signedDoc);
return signedSOAPMsg;
}
Listing 1 shows a signed SOAP envelope as returned from the preceding method.
Adding Username Tokens to a SOAP Message
Listing 2 shows a WSS4J method that uses the WSEncryptBody class to add username tokens to a SOAP envelope in compliance with WS-Security.
The SOAP envelope contained within the Axis message returned from the method shown in
Listing 2 will look similar to
Listing 3:
Encrypting SOAP Messages
The following method uses the WSEncryptBody class to encrypt part of a SOAP envelope in compliance with WS-Security.
public Message encryptSOAPEnvelope(
SOAPEnvelope unsignedEnvelope, Message axisMessage)
throws Exception
{
WSEncryptBody encrypt = new WSEncryptBody();
encrypt.setUserInfo(
"16c73ab6-b892-458f-abf5-2f875f74882e");
// build the encrypted SOAP part
Document doc = unsignedEnvelope.getAsDocument();
Document encryptedDoc = encrypt.build(doc, crypto);
// Convert the document into a SOAP message
Message encryptedMsg =
(Message)AxisUtil.toSOAPMessage(encryptedDoc);
// Retrieve the desired SOAP part
String soapPart = encryptedMsg.getSOAPPartAsString();
((SOAPPart)axisMessage.getSOAPPart()).
setCurrentMessage(soapPart, SOAPPart.FORM_STRING);
encryptedDoc =
axisMessage.getSOAPEnvelope().getAsDocument();
// Convert the document into a SOAP message
Message encryptedSOAPMsg =
(Message)AxisUtil.toSOAPMessage(encryptedDoc);
return encryptedSOAPMsg;
}
The SOAP envelope contained within the Axis message returned from the preceding method will look like
Listing 4.
The "main" Method
Finally, a
main method drives the methods defined above to sign, add username tokens to, and encrypt a SOAP envelope.
public static void main(String[] args)
{
try
{
WSSecuritySample app = new WSSecuritySample();
Message axisMessage = app.getAxisMessage(soapMsg);
SOAPEnvelope unsignedEnvelope =
axisMessage.getSOAPEnvelope();
System.out.println(
"<<< Unsigned and Unencrypted >>>");
XMLUtils.PrettyElementToWriter(
unsignedEnvelope.getAsDOM(),
new PrintWriter(System.out));
Message samlMsg =
app.addUserTokens(unsignedEnvelope);
System.out.println("\n<<< User Tokens >>>");
XMLUtils.PrettyElementToWriter(
samlMsg.getSOAPEnvelope().getAsDOM(),
new PrintWriter(System.out));
Message encryptedMsg = app.encryptSOAPEnvelope(
unsignedEnvelope, axisMessage);
System.out.println("\n<<< Encrypted >>>");
XMLUtils.PrettyElementToWriter(
encryptedMsg.getSOAPEnvelope().getAsDOM(),
new PrintWriter(System.out));
Message signedMsg =
app.signSOAPEnvelope(unsignedEnvelope);
System.out.println("\n<<< Signed >>>");
XMLUtils.PrettyElementToWriter(
signedMsg.getSOAPEnvelope().getAsDOM(),
new PrintWriter(System.out));
}
catch (Exception e)
{
e.printStackTrace();
}
}
Although the process may initially seem complex, a method such as the
main method shown above simplifies the process considerably, breaking it down neatly into just a few steps: creating a SOAP envelope, and then signing, encrypting, and adding username tokens to it. I urge you to
download the sample code for this article and experiment with the process. The WSS4J framework provides the core methods you need to meet the WS-Security specifications.