Using Axis2/Rampart on the Client side, we can always send the WS-Security Profiles e.g. UsernameToken,Timestamp, Signature and Encryption; but if the usage of Axis2/Rampart is not desired on the client side then we could still use the WSS4J/Apache WS Sec API to prepare and send the SOAP messages having WS-Security headers.
Apart from the above mentioned possible reason, there could possibly be one more reason for using reason for using WSS4J API to prepare and send the WS-Security headers, which is the B2B scenario where each Partner needs to understand other Partner's Security artifacts and also needs to Identify which Partner it is communicating with.
In that kind of real B2B scenario, where we one partner 'A' would wish to send documents/data to Multiple Business Partners B,C and so on. Then in that case, Partner 'A' must encrypt the same document using
- B’s Public Key to send to B
- C’s Public Key to send to C
- And so on.
So Partner 'A' must maintain the knowledge of the Public Keys of all the Business Partners, that means one common setting is not going to work, hence we need API level of control instead of configuration based.
Hence on the B, C and other partners they can then use their respective Private Key to decrypt the document/data. So in the scenario like above, using WSS4J API and specifying the Business Partner standard DUNS ID (Dun and Broadstreet) number, when using the crypto.properties file could make the Business Implementation more scalable and dynamic.
WS-Security describes enhancements to SOAP messaging to provide quality of protection through message integrity, message confidentiality, and single message authentication. WS-Security specification describes how to attach signatures and encryption headers to SOAP messages. In addition, it describes how to attach security tokens, including binary security tokens such as X.509 certificates and Kerberos tickets, to messages. WS-Security incorporates security features in the header of a SOAP message, working in the application layer. Thus it ensures end-to-end security. In this article we focus on UsernameToken(for UserId/Pwd WS Security Header), Timestamp(about the SOAP message creation and expiry), Signature(Data Non-Repudiation) and Encryption Profiles only.
UsernameToken
The UsernameToken profile specifies Username,Password and optional elements Naunce and Created (as Naunce and Created are also part of the Timestamp Profile).
<wsse:UsernameToken wsu:Id="Example-1"> <wsse:Username> ... </wsse:Username> <wsse:Password Type="..."> ... </wsse:Password> <wsse:Nonce EncodingType="..."> ... </wsse:Nonce> <wsu:Created>... </wsu:Created> |
where
/wsse:UsernameToken/wsse:Password/@Type
This optional URI attribute specifies the type of password being provided. The table below identifies the pre-defined types.
#PasswordText (default) The actual password for the username in plain text.
#PasswordDigest The digest of the password
Listing 1. Timestamp
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:Id="Timestamp-2">
<wsu:Created>2009-05-29T18:01:24.634Z</wsu:Created>
<wsu:Expires>2009-05-29T18:01:25.634Z</wsu:Expires>
</wsu:Timestamp>
|
Listing 2. Signature
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-secext-1.0.xsd">
</wsse:Security
soapenv:mustUnderstand="1">
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" />
<ds:Reference URI="#id-1">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>n6tb71NME+5VaehrBQ7EFLfYST0=/ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>KlKFpt3yoMgzspaXggq7E9ZDAytZ9Rg7RO3ltTQJdWKGD/YixzESzA==\
>/ds:SignatureValue>
<ds:KeyInfo Id="KeyId-1E2DC6C5B8285A4CC412435389661092">
<wsse:SecurityTokenReference xmlns:wsu="http://docs.oasis-open.org/wss
/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:Id="STRId-1E2DC6C5B8285A4CC412435389661093">
<wsse:KeyIdentifier
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-soap-message-security-1.0#Base64Binary"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-x509-token-profile-1.0#X509v3">
BASE64 Certificate Data
</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
|
Listing 3. Encryption
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/
2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
soapenv:mustUnderstand="1">
<xenc:EncryptedKey>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#
rsa-1_5" />
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference>
<wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/
2004/01/oasis-200401-wss-soap-message-security-1.0#
Base64Binary"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-x509-token-profile-1.0#X509v3">
MIICOjCCAaMCBEoe+AkwDQYJKoZIhvcNAQEEBQAwZDELMAkGA1UEBhMCVVMx
CzAJBgNVBAgTAklMMRIwEAYDVQQHEwlXb29kIERhbGUxDjAMBgN
VBAoTBURldnJ5MQ4wDAYDVQQLEwVEZXZyeTEUMBIGA1UEAxMLVmlrYXMgS3Vt
YXIwHhcNMDkwNTI4MjA0NjAxWhcNMDkwODI2MjA0NjAxWjBkMQ
swCQYDVQQGEwJVUzELMAkGA1UECBMCSUwxEjAQBgNVBAcTCVdvb2QgRGFs
ZTEOMAwGA1UEChMFRGV2cnkxDjAMBgNVBAsTBURldnJ5MRQwEgYDV
QQDEwtWaWthcyBLdW1hcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAoU
ui76B+dv0jw0WeurDFpv6uT2hZzEYmcY54S4Fz0ymXqCLjjz/7
m92ZyKZDUoh1jtMC0IqfAPKuEvCYtpSNjHtsDEfvPraiOPniOqtuPQuUkn8voI1UBL
tpC1ddoahDuSsYYi44071NK2kIYtwRc7Sm4KjNt4rWp4a
slNfszEcCAwEAATANBgkqhkiG9w0BAQQFAAOBgQB07CoQPs/y9Gh24zqIrLoKg
yXL5N7MdOKy2jsNHfwgoHxLMMf8ObVSuBf3mpBaJfqvY2H6s1
Gg2BytkHV94WvfztnAZfihuON8kV4ZnUVPnDweVZCGL9WPrU0xrJnIUextYy4FOh
5M16dcMx5NUE4vTrMB9hA7lBBm/UH0VYUpdA==
</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>
kv/Pk4fFopIQIcJQ4gfShSfyRfHhcB/1iLs18q+Fva9REcL9O8lordJD9P2CVOc6v4
cxnXaxrlVxN5xHumrBM9hSKnL/
pQwc4IqG+Q3qysCp2r0KN+slLu3cOcQKsBZm
u8gbe8sR00Lb9veuZuGvpeS/uAzC4EjW9uU7uwY3RPI=
</xenc:CipherValue>
</xenc:CipherData>
<xenc:ReferenceList>
<xenc:DataReference URI="#EncDataId-1" />
</xenc:ReferenceList>
</xenc:EncryptedKey>
</wsse:Security>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis
-200401-wss-wssecurity-secext-1.0.xsd"
soapenv:actor="test" soapenv:mustUnderstand="1" />
</soapenv:Header>
<soapenv:Body>
<xenc:EncryptedData Id="EncDataId-1" Type="http://www.w3.org/2001/04/
xmlenc#Content">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>
scJMCJnl7j9ZBF3eirVvklBqLmohZePrURT9j0x4dqPsSawqsjbvY1PZ0SSbgOs2Tybr51KiYxos
8GGuwdp6YXqidyDUFC8sc+h1JAuiUHqx6H7CseelgYCUJuBO8N4VR+0AtJFV7PcWrosmUtAItdYU
UZEO73YruYiTQh64rpPnzJgbZ9yQ9R+T3eOjbUeDlvKl11zSiQkA4ZsGzF14f6/wignGqisMPrNN
bEDSXDyAlOuq9/eV7CVUsmmp6/isCKOnctQyGZee4gFb/R0c3MCSBzSkdosBuLItN8O6XOvYylro
yufp2eB+WlltMcz0njOQ25IkiqXfhUssj1AYsxkb7yRzQNRZOrTFpMhs1p4MmiMUeyO/WgYo/qqG
WBUlyH2M18dr3rNiVRw+KvVcK4UEylEysJLhHoHHKiVq6QllVWnNVqr4kPTbnlSOKfa/JPL842cE
RBg9gZg5w5jYOA==
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</soapenv:Body>
</soapenv:Envelope>
|
Client Side WSS4J API Handling
UsernameToken Profile:In the code below, the password could be put in plain text or in the one way hash format(SHA1 by default), using WSConstants.PASSWORD_TEXT or WSConstants.PASSWORD_DIGEST respectively.
Listing 4. WS UsernameToken generation sample code
public SOAPMessage addUserTokensPlainPwd(SOAPEnvelope unsignedEnvelope)
throws Exception
{
// Get the message as document
Document doc = unsignedEnvelope.getOwnerDocument(); // //getAsDocument();
String username = "user";
String password = "pwd2009";
byte[] key = password.getBytes();
// Add the UserNameToken.
//WSSAddUsernameToken builder = new WSSAddUsernameToken("", false);
WSSecUsernameToken builder = new WSSecUsernameToken();
WSSecHeader wsSecHeader = new WSSecHeader();
//wsSecHeader.setActor("test"); // Actor Not Required,
// throws Security Header missing Exception
wsSecHeader.insertSecurityHeader(doc);
//builder.setPasswordType(WSConstants.PASSWORD_DIGEST); //PASSWORD_TEXT);
builder.setPasswordType(WSConstants.PASSWORD_TEXT);
builder.setUserInfo(username, password);
Document usernameTokenDoc = builder.build(doc,wsSecHeader);//
// Add an Id to it.
//Element usrEle =
// (Element)(doc.getElementById("UsernameToken")); //.item(0));
//(Element)(doc.getElementsByTagNameNS( WSConstants.WSSE_NS,
// "UsernameToken").item(0));
String idValue = "7654";
//usrEle.setAttribute("Id", idValue);
// Create a Reference to the UserNameToken.
Reference ref = new Reference(doc);
ref.setURI("#" + idValue);
ref.setValueType("UsernameToken");
SecurityTokenReference secRef = new SecurityTokenReference(doc);
secRef.setReference(ref);
// adding the namespace
WSSecurityUtil.setNamespace(secRef.getElement(),
WSConstants.WSSE_NS,
WSConstants.WSSE_PREFIX);
DOMSource src = new DOMSource(usernameTokenDoc);
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage soapMsg = mf.createMessage();
soapMsg.getSOAPPart().setContent(src);
System.out.println("User Token Message is "+soapMsg.
getSOAPHeader().toString());
return soapMsg;
}
|
In the code below, signer.setUserInfo(alias, password) will basically decide which Private key to use to Sign the Data, as Private Key is used for Signature generation.
Listing 5. SOAP Envelope Signature generation sample code
public SOAPMessage signSOAPEnvelope(SOAPEnvelope unsignedEnvelope)
throws Exception
{
// WSSignEnvelope signs a SOAP envelope according to the
// WS Specification (X509 profile) and adds the signature data
// to the envelope.
WSSignEnvelope signer = new WSSignEnvelope();
//WSSignEnvelope signer = new WSSignEnvelope();
String alias = "user"; //"16c73ab6-b892-458f-abf5-2f875f74882e";
String password = "pwd2009";
signer.setUserInfo(alias, password);
Document doc = unsignedEnvelope.getOwnerDocument();
// 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 that are specified by
// the WSBaseMessage.setParts(java.util.Vector parts)
// method. By default, SOAP Body is signed.
// The "crypto" parameter is the object that implements
// access to the keystore and handling of certificates.
// A default implementation is included:
// org.apache.ws.security.components.crypto.Merlin
signer.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
Document signedDoc = signer.build(doc, crypto);
DOMSource src = new DOMSource(signedDoc);
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage soapMsg = mf.createMessage();
soapMsg.getSOAPPart().setContent(src);
return soapMsg;
}
|
Listing 6. SOAP Envelope Ecryption sample code
public SOAPMessage encryptSOAPEnvelope(SOAPEnvelope unsignedEnvelope)
throws Exception
{
WSEncryptBody wsEncrypt = new WSEncryptBody();
// Get the message as document
Document doc = unsignedEnvelope.getOwnerDocument(); // //getAsDocument();
WSSecHeader wsSecHeader = new WSSecHeader();
String alias = "user"; //"16c73ab6-b892-458f-abf5-2f875f74882e";
String password = "password";
wsEncrypt.setUserInfo(alias, password); // To Identify the Key Alias
// On the Server side WS Impl, this same UserName
// (Here Password could be anything)
// should be used in the file ws_users.xml
// <sample_users>
// <user username="user" password="password"/>
// </sample_users>
// Axis2's Rampart would use below Call back
// <passwordCallbackClass>com.abcd.sample.is.ws.PWCBHandler
// </passwordCallbackClass>
// with below settings
// <items>Encrypt</items>
// <decryptionPropFile>cryptoServer.properties</decryptionPropFile>
//wsSecHeader.setActor("test");
wsSecHeader.insertSecurityHeader(doc);
//String idValue = "7654";
// Create a Reference to the UserNameToken.
Reference ref = new Reference(doc);
//ref.setURI("#" + idValue);
//ref.setValueType("UsernameToken");
SecurityTokenReference secRef = new SecurityTokenReference(doc);
secRef.setReference(ref);
// adding the namespace
WSSecurityUtil.setNamespace(secRef.getElement(),
WSConstants.WSSE_NS,
WSConstants.WSSE_PREFIX);
// Setting necessary parameters in WSEncryptBody.
wsEncrypt.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
//WSConstants.EMBED_SECURITY_TOKEN_REF);
//wsEncrypt.setSecurityTokenReference(secRef);
//wsEncrypt.setKey(key);
// Encrypt using the using the key
Document encDoc = wsEncrypt.build(doc, crypto);
DOMSource src = new DOMSource(encDoc);
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage soapMsg = mf.createMessage();
soapMsg.getSOAPPart().setContent(src);
return soapMsg;
}
|
Listing 7. Apache Aixs2's Services.xml file
<?xml version="1.0" encoding="UTF-8"?>
<serviceGroup>
<service name="ISService">
<messageReceivers>
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-out"
class="com.sample.is.ws.ISServiceMessageReceiverInOut"/>
</messageReceivers>
<parameter name="ServiceClass">com.sample.is.ws.ISServiceSkeleton</parameter>
<parameter name="useOriginalwsdl">true</parameter>
<parameter name="modifyUserWSDLPortAddress">true</parameter>
<operation name="addRequest" mep="http://www.w3.org/ns/wsdl/in-out"
namespace="http://ws.is.sample.com">
<actionMapping>\"\"</actionMapping>
<outputActionMapping>http://ws.is.sample.com/ISService/addRequestResponse
</outputActionMapping>
<faultActionMapping faultName="ISUDCGatewaySPMLError">http://ws.is.sample.com
/ISService/addRequest/Fault/ISUDCGatewaySPMLError
</faultActionMapping>
</operation>
<operation name="modifyRequest" mep="http://www.w3.org/ns/wsdl/in-out"
namespace="http://ws.is.sample.com">
<actionMapping>\"\"</actionMapping>
voutputActionMapping>http://ws.is.sample.com/ISService/modifyRequestResponse
</outputActionMapping>
<faultActionMapping faultName="ISUDCGatewaySPMLError">http://ws.is.sample.com/
ISService/modifyRequest/Fault/ISUDCGatewaySPMLError
</faultActionMapping>
</operation>
Note: Enable appropriate Profile here i.e. Signature or Encryption or UsernameToken.
It was disabled when testing several combinations, so depending upon which profile
you might want to use, remember to enable it.
<!--module ref="rampart" /-->
<!--parameter name="InflowSecurity"-->
<!--action-->
<!--items>Signature</items-->
<!--signaturePropFile>cryptoServer.properties</signaturePropFile-->
<!--signatureKeyIdentifier>DirectReference</signatureKeyIdentifier-->
<!--items>Encrypt</items-->
<!--decryptionPropFile>cryptoServer.properties</decryptionPropFile-->
<!--decryptionKeyIdentifier>DirectReference</decryptionKeyIdentifier-->
<!--items>Timestamp UsernameToken</items-->
<!--passwordCallbackClass>com.sample.is.ws.PWCBHandler<
/passwordCallbackClass-->
<!--items>UsernameToken</items-->
<!--passwordCallbackClass>com.sample.is.ws.PWCBHandler<
/passwordCallbackClass-->
<!--/action-->
<!--/parameter-->
</service>
</serviceGroup>
|
Listing 8. Password Callback Handler to be used in the Axis2/Rampart configuration
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.ws.security.WSPasswordCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.util.StringTokenizer;
public class PWCBHandler implements CallbackHandler {
Logger LOGGER = Logger.getLogger(ISServiceSkeleton.class);
public void handle(Callback[] callbacks)
throws IOException,UnsupportedCallbackException
{
XMLParserUtil xmlParserUtil = new XMLParserUtil();
String sampleISUserIdPwd = xmlParserUtil.parseXMLAndGetUserIdAndPwd("");
StringTokenizer st = new StringTokenizer(sampleISUserIdPwd,
CommonConstants.IS_UDCTODB_PROPS_KEY_DELIM_PIPE);
String userName = st.nextToken();
String password = st.nextToken();
// Here the WS Auth UserId/Passwords will be read from a XML file.
for (int i = 0; i lt callbacks.length; i++) {
WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
String id = pwcb.getIdentifer();
// pwcb.getPasswordType() http://docs.oasis-open.org/wss/2004/
// 01/oasis-200401-wss-username-token-profile-1.0#PasswordText
// pwcb.getPasswordType() http://docs.oasis-open.org/wss/
// 2004/01/oasis-200401-wss-username-token-profile-1.0
// #PasswordDigest
// WSSE UsernameToken Profile Reference: http://www.oasis-open.org/
// committees/wss/documents/WSS-Username-02-0223-merged.pdf
// WSSE spec states that, the PasswordDigest will always use SHA1
// as the Digest Algo.
if(userName.equals(id)) {
pwcb.setPassword(password);
}else {
throw new UnsupportedCallbackException(callbacks[i], "check failed");
}
}
}
}
|
Note:
- Any JDK be default doesnt allow low key strengths/algorithms to work properly, so you might have to download Unrestricted Security Ploicy Jars appropriately.
- The Keystore created above used RSA algorithm, as the default DSA algorithm used when creating the keystore was causing problems.
- In this particular implementation it is recommended to use the BouncyCastle Security Jar.
Listing 9. Crypto Server side properties
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.base64.encoded=false org.apache.ws.security.crypto.merlin.keystore.password=keystorepwd org.apache.ws.security.crypto.merlin.file=C:/server/partnersRSA.jks |
- Keytool can be used to generate the Keystore, please make sure that you use appropriate Unrestricted Security Policy Jars, as that might save lot of time as Security Errors are very confusing sometimes.
- Apache Axis2 1.4.* version along with Rampart 1.4, and wss4j-1.5.7
- JDK 1.4.* above
Learn
- Read
"Send secure/non-secure attachments over SOAP and HTTP", (developerWorks, Nov 2005) to learn how to send data securely over the Internet.
-
"Web
Services Security", (developerWorks, Mar 2004) provides additional details about the WS-Specifications.
- Read the step by step tutorial to use Rampart with
axis2 Web service for implementing
security.
- Check out
Rampart basic examples
and learn how you add WS-Security to axis2.
- View
Rampart samples.
Get products and technologies
- Download
IBM product evaluation versions
or explore
the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from
DB2®, Lotus®, Rational®, Tivoli®, and
WebSphere®.
Discuss
- Check out
developerWorks
blogs and
get involved in the
developerWorks community.

Vikas has extensive experience working with J2EE, WebSphere Application Server 5.1/6.0/7.0, WebSphere MQ 5.3/6.0, WebSphere ESB, Mule ESB, Oracle, ORM Tools, Spring for the past 10 years. Vikas was also the Sr Developer for WebSphere Partner Gateway Express 6.0/ Enterprise 6.0 at IBM, India. Interests include J2EE, Web Services Architectures including Security, TX, Address, WebSphere App Server/ESB/MQ, Mule ESB, and exploring more innovative ways for solving different things using languages/scripts like Java, Perl, C. Currently exploring the features of SAXON XSLT parser for it's Java extensibilities.





