Web Services Security for Java
October 28, 2003
My new WebServices.XML.com column, which focuses on web services security, will demonstrate practical aspects of using various security standards for web services along with specific server side technologies and programming languages. In the first few articles of the column, I will demonstrate the use of web services security (WSS) in Java applications, and I will outline what is required for their implementation. This first column presents a simplified high-level API that offers Java programmers an easy interface to produce and consume WSS messages.
In this column I discuss security tokens, token references, XML encryption (for convenience I use the namespace prefix xenc), and XML digital signatures (prefix ds) that I have already covered in my recent four part series (Part 1, Part 2, Part 3, and Part 4).
WSS Use Cases: Client Side
A good place to start is a review of various web services security authoring use cases:
-
A client application wants to add a security token to a SOAP message for authentication. I discussed several types of security tokens (certificate, username, SAML assertion, etc.) in the second, third, and fourth parts of my Web Services Security series.
The client authors the complete SOAP Body, passes the SOAP Envelope along with a security token to WSS4J, and asks WSS4J to attach the security token to the SOAP message.
The client application does not have the capability of generating the WSS structure for the token. So in order to author the token the client application will rely on WSS4J. For example, if the token is an X509 certificate, the client application will simply pass the X509 certificate byte array to WSS4J. WSS4J will author the complete WSS compliant XML structure of the security token, wrap the security token inside a WSS header, and return the completed WSS message back to the client application.
-
A client application wants to sign a portion of a SOAP message using a private cryptographic key associated with an X509 certificate. The client application has already added the certificate to the SOAP message inside a WSS header as a security token. The client application asks WSS4J to sign a particular portion of the WSS message using the private key associated with the certificate. There are two ways of identifying the particular portion that needs to be signed: either a fragment identifier or an XPath expression.
If you want to use fragment identifiers, the Web Services Utility (
wsu
) namespace can help you.wsu
is a general purpose namespace (http://schemas.xmlsoap.org/ws/2003/06/utility
) which defines utility data structures (such as identifiers, timestamps, etc.) for use by other web services specifications. One of the attributes defined by the wsu namespace iswsu:Id
, which is used to hold identifiers for different element nodes.WSS clients can use
wsu:Id
attributes as fragment identifiers by adding awsu:Id
attribute value to the SOAP message element that it wants to sign. For example, in Listing 1 thewsu:Id
attribute value of the SOAP message body is "MyMessageBody". So if the client wants to sign the SOAP message body of Listing 1, it will simply ask WSS4J to sign the element with
attribute value "MyMessageBody".wsu:Id
Instead of using the
wsu:Id
attribute, WSS clients can also use XPath to identify a particular element node of a WSS message. In this case, the client will author an XPath expression to identify the element to be signed. Refer to Resources to learn how to write an XPath expression to identify a particular XML element.Whether the client uses
wsu:Id
or an XPath expression to identify the element to be singed, WSS4J will produce the signature and return the signed WSS message to the client application. -
A client application wants to encrypt a particular portion of a SOAP message. The client application first adds the certificate of the intended recipient of the message as a WSS token. It then provides the fragment identifier or an XPath expression to WSS4J and asks the application to produce XML encrypted version of the message. WSS4J produces the encrypted portion of the message and returns the completed message back to the requesting client application.
The client application can use these use cases in any order and any number of times. For example, a client application can first add a security token to a SOAP message, then sign a portion of the message, then encrypt a part of a message, and so on. Listing 1 is an example WSS message that contains two tokens, a signature, and an encrypted data structure.
Server Side
These three use cases represent the client or producer usage model of WSS4J. Now let's consider the server or consumer usage model.
-
A server application has received a WSS message with a number of security tokens. The XML firewall needs to extract a list of all tokens from the WSS message. So it asks WSS4J to extract the tokens from the WSS message. WSS4J extracts all the tokens and returns them to the requesting server application.
-
The server will need to provide a secret key to WSS4J for decryption of encrypted portions of a WSS message. If the author of the WSS message used a public key to encrypt a portion of the message, the corresponding private key is required to decrypt the encrypted portion. Similarly, if the client signed a portion of the message using a session key inside a Kerberos ticket, the server will need a password to decrypt the ticket and extract the session key, without which the server cannot verify the signature (refer to Resources for more details on Kerberos). Therefore, the server may provide a secret to WSS4J corresponding to each token in a WSS message.
Notice that the server may not know all the secrets associated with all the authentication tokens in a WSS message. That's because a WSS message may be targeted for more than one server. The server only knows the secrets associated with tokens targeted for itself.
For example, if A receives a WSS message with a Kerberos ticket authentication token targeted for B, A will not be able to extract the session key and use it to verify signatures or decrypt encrypted portions.
-
The server may also ask the WSS4J to provide a list of all XMLDS signatures in the WSS message. After WSS4J has provided a list of all signatures, the server application can ask WSS4J to process any individual signature.
-
Similarly, the server may also ask WSS4J to provide a list of all encrypted elements in a WSS message. After WSS4J has provided a list of all encrypted elements, the server application can ask WSS4J to decrypt any individual encrypted element.
The WSS4J API
This section describes a programmatic interface (the WSS4J API) that exposes methods to implement the use case scenarios that we discussed in the previous section. In this column, we will provide a high level view of the WSS4J API. Later columns will incrementally implement the low level details of the API.
Look at Listing 2, which
shows a class named WSSMessage
, which is the main class in our WSS4J API. This
class represents both the client and server side functionality by implementing the
following
methods:
The WSSMessage()
constructor
This method takes a string named soapMessage
as a parameter. The
soapMessage
string represents a SOAP message. The constructor will load the
soapMessage
string into a DOM document object, thus becoming ready for WSS
authoring and processing.
The SOAP message may or may not already contain a WSS security header. If the SOAP
message
contains a WSS security header, the WSSMessage
constructor will parse the WSS
message to perform the following functions:
-
Load all WSS tokens, token references,
xenc:EncryptedKey
, andds:KeyInfo
elements present in the WSS security header into a list ofToken
objects.Token
is an interface (shown in Listing 3) which represents abstract functionality of a security token as well asxenc:EncryptedKey
andds:KeyInfo
elements.WSS allows a very flexible and extensible mechanism for defining and using many types of tokens, where each type of token will have its own functionality. That's why we have defined
Token
as an abstract Java interface and not as a Java class.We will later implement the
Token
interface separately for each type of token and explain the functionality of eachToken
implementation. For example, theToken
interface of Listing 3 contains a method namedsetSecret()
, which takes a byte array and treats the byte array as a secret. In case of an X509 certificate token, the secret will represent the private key associated with the certificate. While in case of a Kerberos service ticket, the recipient's password is the secret that will be used to decrypt the ticket and fetch the session key. -
Load all the
ds:Signature
elements into a list ofSignature
objects. We will provide details of theSignature
class in later columns of this series. For now just note that aSignature
object represents a singleds:Signature
element and exposes methods to verify the signature it represents. EachSignature
object also needs aToken
instance associated with it. So theWSSMessage
constructor will detect whichToken
object corresponds to each of theSignature
objects and store a reference of theToken
instance in theSignature
object. -
Load all the
xenc:EncryptedData
elements into a list ofxenc:EncryptedData
objects. We will discuss the details of theEncryptedData
class in a later column. For now just note that anEncryptedData
object represents a singlexenc:EncryptedData
element and exposes methods to decrypt the encrypted portion. EachEncryptedData
object also needs aToken
associated with it. So theWSSMessage
constructor will mapToken
objects corresponding to each of theEncryptedData
objects and store a reference of theToken
instance in theEncryptedData
object.
The addToken()
method
This method takes a Token
object as a parameter and adds the token to the WSS
message. Every new token is prepended to the existing header, which means it is added
as the
first child element of the WSS security header.
The addID()
method
This is a helper method that takes two string type parameters. The first parameter
(XPathExpression
) is an XPath filter that specifies an element of the WSS
message loaded into the WSSMessage
object. The second parameter
(wsuId
) specifies an identifier for the element. This method adds a
wsu:Id
attribute with a value equal to the value of the wsuId
parameter to the element that the XPathExpresession
parameter specifies.
Note if you sign a message first, and then insert any attribute after producing the
signature, you will break your signature. Applications should make sure to insert
a
wsu:Id
to an element before signing it.
The encryptElement()
method
This method is used to encrypt a portion of the WSS message already loaded into the
WSSMessage
object. The encrypt method takes four parameters:
- The first parameter (
wsuElementID
) is awsu:Id
for the element to be encrypted. - The second parameter (
token
) is theToken
object (the interface of Listing 3) to be used for encryption. - The third parameter (
wsuEncryptedElementID
) is the identifier that theencrypt()
method will insert into the encrypted element for its identification. - The fourth parameter (
encryptionAlgo
) identifies the encryption algorithm to be used for encryption.
When a client application calls this message, the method will look for an element
in the
WSS message whose wsu:Id
attribute value matches with the wsuElementID string.
It will then encrypt the element to produce the encrypted data structure according
to XML
encryption syntax.
The encryptElementWithXPath()
method
This method is the same as the encryptElement()
method described above,
except that the element to be encrypted is identified using an XPath expression instead
of a
wsu:Id
.
The sign()
method
This method takes six parameters:
- The first parameter (
wsuElementID
) is a WSU identifier for the element to be signed. - The second parameter (
token
) identifies theToken
instance to be used to produce the signature (e.g. the certificate to be used to produce the signature). - The third parameter (
wsuSignatureID
) is the identifier that thesign()
method will include in theds:Signature
element that it is going to produce. - The fourth parameter (
digestAlgo
) identifies the algorithm to be used in calculating the digest value of the data to be signed. - The fifth parameter (
signatureAlgo
) specifies the signature algorithm to be used. - The sixth parameter (
canonicalizationAlgo
) specifies the canonicalization algorithm to be used.
This method signs a particular element in the WSS message to generate a
ds:Signature
element, which is inserted in the WSS security header.
The signWithXPath()
method
This method is the same as the sign()
method described above, except that the
element to be signed is identified using an XPath expression instead of a
wsu:Id
.
The getAllTokens()
method
This method returns a list of all tokens (an array of objects implementing the
Token
interface of Listing 3)
associated with this WSS message. The WSS4J API takes all occurrences of keys -- public
or
encrypted symmetric keys -- certificates, Kerberos tickets, and so on, in a WSS message
as
tokens, whether or not they are used as a WSS token. For example, in Listing 1, the
first token (whose wsu:Id
is "myCertificate
") is a WSS token. On
the other hand, the symmetric key (whose wsu:Id
is
"mySymmetricKey
") is not a WSS token. Both these are taken as
Token
s in WSS4J API. As already explained, each type of token will implement
the Token
interface of Listing 3 to
expose its own processing logic.
The getAllSignatures()
method
This method returns a list of all Signature
objects associated with the WSS
message. As already mentioned, a Signature
object represents a
ds:Signature
element and exposes methods to process and verify the signature.
The getAllEncryptedData()
method
This method is similar to the getAllSignatures() method and returns a list of
EncryptedData
objects associated with the WSS message. As already mentioned,
every EncryptedData
object handles the functionality related to a particular
occurrence of an xenc:EncryptedData
element.
WSS4J Components
You have seen a high level view of the WSS4J API, which sets the scene for us to look into WSS4J implementation details. You will need the following Java-based components to implement the WSS4J API:
- XML Security Suite by Apache or IBM
- Java Authentication and Authorization Services (JAAS)
- Java API for XML Processing (JAXP)
- Java Cryptographic Architecture (JCA) and Java Cryptographic Extension (JCE), which are part of the Java Development Kit (JDK) version 1.4
- Apache Axis SOAP server
The next column in this series will explain the role of each of these components in implementing the client and server side features of WSS.
Resources
|