Download
FAQ
History
PrevHomeNext API
Search
Feedback
Divider

Writing SecurityEnvironmentHandlers for XWS-Security Applications

The signing and encryption operations require private-keys and certificates. An application can obtain such information in various ways, such as looking up a keystore with an alias, using the default key-pairs available with the container, looking up a truststore with an alias, etc. Similarly if an application wants to send a username-password in a UsernameToken, it can choose to obtain the username-password pair in various ways, such as reading from a file, prompting the user on the console, using a popup window, etc. The authentication of the username-password on the receiving application can similarly be done by plugging into existing authentication infrastructure, using a proprietary username-password database, etc.

To support these possibilities, XWS-Security defines a set of CallBack classes and requires the application to define a CallBackHandler to handle these callbacks. The xwss:SecurityEnvironmentHandler element is a compulsory child element that needs to be specified. The value of this element is the class name of a Java class that implements the javax.security.auth.callback.CallbackHandler interface and handles the set of callbacks defined by XWS-Security. There are a set of callbacks that are mandatory and every CallbackHandler needs to implement them. A few callbacks are optional and can be used to supply some fine-grained property information to the XWS-Security run-time.

Because information such as private keys and certificates for signing and encryption can be obtained in various ways (looking up a keystore with an alias, using the default key-pairs available with the container, looking up a truststore with an alias, etc.), every callback defines a set of Request inner classes and a callback can be initialized with any of its request inner classes. A tagging Request interface is also defined within the callback to tag all Request classes. For example, the XWS-Security configuration schema defines an xwss:X509Token element containing an optional attribute certificateAlias. When the xwss:X509Token element embedded inside a xwss:Sign element has a certificateAlias attribute specified as shown in the following code snippet, the XWS-Security run-time would invoke the SecurityEnvironmentHandler of the application with a SignatureKeyCallback object to obtain the private-key required for the signing operation.

<xwss:Sign>
        <xwss:X509Token certificateAlias="xws-security-client"/>
</xwss:Sign> 

The SignatureKeyCallback will be initialized by XWS-Security run-time with an AliasPrivKeyCertRequest in the following manner:

SignatureKeyCallback sigKeyCallback = new 
SignatureKeyCallback(new
   SignatureKeyCallback.AliasPrivKeyCertRequest(alias)); 

The application's SecurityEnvironmentHandler implementation then needs to handle the SignatureKeyCallback and use the alias to locate and set the private-key and X.509 certificate pair on the AliasPrivKeyCertRequest. The following code shows how this callback is handled in the handle() method of SecurityEnvironmentHandler shipped with the simple sample.

} else if (callbacks[i] instanceof SignatureKeyCallback) {
                SignatureKeyCallback cb = 
(SignatureKeyCallback)callbacks[i];
                                                                                                                     
                if (cb.getRequest() instanceof 
SignatureKeyCallback.AliasPrivKeyCertRequest) {
                    SignatureKeyCallback.AliasPrivKeyCertRequest 
request =
                        
(SignatureKeyCallback.AliasPrivKeyCertRequest) 
cb.getRequest();
                    String alias = request.getAlias();
                    if (keyStore == null)
                        initKeyStore();
                    try {
                        X509Certificate cert =
                            (X509Certificate) 
keyStore.getCertificate(alias);
                        request.setX509Certificate(cert);
                        // Assuming key passwords same as the 
keystore password
                        PrivateKey privKey =
                            (PrivateKey) keyStore.getKey(alias, 
keyStorePassword.toCharArray());
                        request.setPrivateKey(privKey);
                    } catch (Exception e) {
                        throw new IOException(e.getMessage());
                    } 
                } else {
                     throw  new UnsupportedCallbackException(null, 
"Unsupported Callback 
                         Type Encountered");
                }
           } 

This handler uses a keystore to locate the private key and certificate pair, and sets it using AliasPrivKeyCertRequest.

As shown in the sample code, the SecurityEnvironmentHandler should throw an UnsupportedCallbackException whenever it cannot handle a Callback or a particular Request type of a Callback.

The type of Request with which the Callback is initialized often depends on the information specified in the security configuration file of the application. For example if the xwss:X509Token specified under an xwss:Sign element did not contain the certificateAlias attribute, XWS-Security would invoke the application's SecurityEnvironmentHandler with SignatureKeyCallback.DefaultPrivKeyCertRequest to try and obtain the default private-key and certificate pair. If the SecurityEnvironmentHandler does not handle this request and throws an UnsupportedCallbackException, the signature operation would fail.

For more information, read the API documentation for callbacks from the <JWSDP_HOME>/xws-security/docs/api/com/sun/xml/wss/impl/callback/package-summary.html. This documentation includes the list of mandatory and optional callbacks and the details of the Callback classes and supported methods. Table 3-24 provides a brief summary of all the mandatory Callback classes and their associated Request types.

Table 3-24 Summary of Callback classes and their Request types 
Callback
Description
  Request Inner Classes Defined
  Methods in the Request Classes
Signature
Key
Callback
Used by XWS-Security run-time to obtain the private key to be used for signing the corresponding X.509 certificate. There are two ways in which an application can supply the private-key and certificate information.
1. Lookup a keystore using an alias.
2. Obtain the default private-key and certificate from the container/environment in which the application is running.
Accordingly, there are two Request inner classes with which the SignatureKeyCallback can be initialized.
1. AliasPrivKeyCertRequest: A Callback initialized with this request should be handled if the private key to be used for signing is mapped to an alias.
2. DefaultPrivKeyCertRequest: A Callback initialized with this request should be handled if there's some default private key to be used for signing.
The following four methods are present in all Request Classes of this Callback:
public void setPrivateKey(
   PrivateKey privateKey)
public PrivateKey getPrivateKey()
 
public void setX509Certificate(
   X509Certificate certificate)
public X509Certificate
   getX509Certificate()
Signature
Verification
Key
Callback
Obtains the certificate required for signature verification. There are currently two situations in which XWS-Security would require this Callback to resolve the certificate:
1. When the signature to be verified references the key using an X.509 SubjectKeyIdentifier. For example, when the sender specifies the attribute xwss:keyReferenceType="Identifier" on the xwss:X509Token child of the xwss:Sign element.
2. When the signature to be verified references the key using an X.509 IssuerSerialNumber. For example, when the sender specifies the attribute xwss:keyReferenceType="IssuerSerialNumber" on the xwss:X509Token child of the xwss:Sign element.
Accordingly, there are two Request inner classes with which a SignatureVerificationKeyCallback can be initialized.
Note: Additional Requests may be defined in a future release.
1. X509SubjectKeyIdentifierBasedRequest: Request for an X.509 certificate whose X.509 SubjectKeyIdentifier value is given.
2. X509IssuerSerialBasedRequest: Request for an X.509 certificate whose issuer name and serial number values are given.
 
The following two methods are present in all the Request classes of this Callback:
 
public void setX509Certificate(
   X509Certificate certificate)
public X509Certificate
   getX509Certificate()
 
Encryption
Key
Callback
Obtains the certificate for key-encryption or a symmetric-key for data encryption. There are currently three situations in which XWS-Security would require this Callback for performing encryption:
1. When the xwss:Encrypt element contains an xwss:X509Token child with certificateAlias attribute set to an alias. The certificateAlias indicates that a random symmetric key is used for encryption of the specified message part and the certificate is then used to encrypt the random symmetric-key to be sent along with the message.
2. When the xwss:Encrypt element contains an xwss:X509Token child with no certificateAlias attribute set on it. XWS-Security tries to obtain a default certificate from the Callback to be used for encrypting the random symmetric key.
3. When the xwss:Encrypt element contains an xwss:SymmetricKey child specifying the keyAlias attribute. This alias indicates that a symmetric key corresponding to this alias needs to be located and used for encryption of the specified message part.
Accordingly, there are three Request inner classes with which an EncryptionKeyCallback can be initialized.
1. AliasX509CertificateRequest: A Callback initialized with this request should be handled if the X.509 certificate to be used for encryption is mapped to an alias.
2. DefaultX509CertificateRequest: A Callback initialized with this request should be handled if there's a default X.509 certificate to be used for encryption.
3. AliasSymmetricKeyRequest: A Callback initialized with this request should be handled if the symmetric key to be used for encryption is mapped to an alias.
The following two methods are present in the AliasX509CertificateRequest and DefaultX509CertificateRequest Request classes of this Callback:
 
public void setX509Certificate(
   X509Certificate certificate)
public X509Certificate
   getX509Certificate()
 
The following methods are present in the AliasSymmetricKeyRequest class of this Callback:
 
public void setSymmetricKey(
   javax.crypto.SecretKey
      symmetricKey)
public javax.crypto.SecretKey
   getSymmetricKey()
Decryption
Key
Callback
Obtains the symmetric key to be used for decrypting the encrypted data or obtaining the private-key for decrypting the encrypted random symmetric key that was sent with the message (along with the encrypted data).
There are currently four situations in which XWS-Security will require this Callback to perform decryption.
1. When the EncryptedKey references the key (used for encrypting the symmetric key) using an X.509 SubjectKeyIdentifier. For example, when the sender specifies the attribute keyReferenceType="Identifier" on the xwss:X509Token child of the xwss:Encrypt element.
2. When the EncryptedKey references the key (used for encrypting the symmetric key) using an X.509 IssuerSerialNumber. For example, when the sender specifies the attribute keyReferenceType="IssuerSerialNumber" on the xwss:x509Token child of xwss:Encrypt element.
 
1. X509SubjectKeyIdentifierBasedRequest: Request for a private-key when the X.509 SubjectKeyIdentifier value for a corresponding X.509 certificate is given.
2. X509IssuerSerialBasedRequest: Request for a private key when the issuer name and serial number values for a corresponding X.509 certificate are given.
3. X509CertificateBasedRequest: Request for a private key when a corresponding X.509 certificate is given.
 
The following two methods are present in the X509SubjectKeyIdentifierBasedRequest, X509IssuerSerialBasedRequest, and X509CertificateBasedRequest Request classes of this Callback:
 
public void setPrivateKey(
   PrivateKey privateKey)
public PrivateKey
   getPrivateKey()
 
 
Decryption
Key
Callback (continued)
3. When the EncryptedKey contains a wsse:Direct reference to the key used for encrypting the symmetric key. This means the X.509 certificate is present as a wsse:BinarySecurityToken in the message. For example, when the sender specifies the attribute keyReferenceType="Direct" on the xwss:x509Token child of xwss:Encrypt element.
4. When the EncryptedData contains a ds:keyName reference to the symmetric key that was used for encryption. For example, when the sender specifies the xwss:SymmetricKey child of xwss:Encrypt and specifies the keyAlias attribute on it.
Accordingly, there are four Request classes with which a DecryptionKeyCallback can be initialized.
4. AliasSymmetricKeyRequest: A Callback initialized with this request should be handled if the symmetric key to be used for decryption is mapped to some alias.
The following methods are present in the AliasSymmetricKeyRequest class of this
Callback:
 
public void setSymmetricKey(
   javax.crypto.SecretKey
      symmetricKey)
public javax.crypto.SecretKey
   getSymmetricKey()
 
Password
Validation
Callback
Username-Password validation. A validator that implements the PasswordValidator interface should be set on the callback by the callback handler.
There are currently two situations in which XWS-Security will require this Callback to perform username-password validation:
1. When the receiver gets a UsernameToken with plain-text user name and password.
2. When the receiver gets a UsernameToken with a digested password (as specified in the WSS UsernameToken Profile).
Accordingly there are two Request classes with which the PasswordValidationCallback can be initialized.
Note: A validator for WSS Digested Username-Password is provided as part of this callback, with classname PasswordValidationCallback.DigestPasswordValidator.
This class implements WSS digest password validation. The method for computing password digest is described in http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf.
For more information, see the ServerSecurityEnvironmentHandler in <JWSDP_HOME>/xws-security/samples/jaas-sample/src/com/sun/xml/wss/sample.
1. PlainTextPasswordRequest: Represents a validation request when the password in the username token is in plain text.
2. DigestPasswordRequest: Represents a validation request when the password in the username token is in digested form.
 
The following methods are present in the PlainTextPasswordRequest:
public String getUsername()
public String getPassword()
 
The following methods are present in the DigestPasswordRequest:
public void setPassword(String password)
 
This method must be invoked by the CallbackHandler while handling a Callback initialized with DigestPasswordRequest to set the plain-text password on the Callback.
 
public java.lang.String
   getPassword()
public java.lang.String
   getUsername()
public java.lang.String getDigest()
public java.lang.String getNonce()
public java.lang.String getCreated()
 
Username
Callback
To supply the user name for the UsernameToken at run-time. It contains the following two methods:
public void setUsername(
String username)
public String getUsername()
 
Refer to the ClientSecurityEnvironmentHandler of the
jaas-sample sample located in
<JWSDP_HOME>/xws-security/samples/jaas-sample/src/com/sun/xml/wss/sample for more details on using the UsernameCallback.
 
 
PasswordCallback
To supply the password for the username token at run-time. It contains the following two methods:
 
public void setPassword(String
   password)
public String getPassword()
Refer to the ClientSecurityEnvironmentHandler of the jaas-sample sample located in
<JWSDP_HOME>/xws-security/samples/jaas-sample/src/com/sun/xml/wss/sample for more details on using the PasswordCallback.
 
 
Property
Callback
Optional callback to specify the values of properties configurable with XWS-Security run-time.
Refer to the API documentation at <JWSDP_HOME>/xws-security/docs/api/com/sun/xml/wss/impl/callback/PropertyCallback.html for a list of configurable properties methods supported by this callback.
 
 
Prefix
Namespace
Mapping
Callback
Optional callback to register any prefix versus namespace-uri mappings that the developer wants to make use of in the security configuration (while specifying Targets as xpaths).
Refer to the API documentation at <JWSDP_HOME>/xws-security/docs/api/com/sun/xml/wss/impl/callback/PrefixNamespaceMappingCallback.html for more details.
 
 

The following code snippet shows the handle() method skeleton for an application's SecurityEnvironmentHandler that handles all the mandatory Callbacks (except UsernameCallback and PasswordCallback) and associated Requests defined by XWS-Security. A particular application may choose to throw an UnsupportedCallbackException for any of the Callbacks or its Requests that it cannot handle. The UsernameCallback and PasswordCallback are useful for obtaining a username-password pair at run-time and are explained later in this section.


Note: In this release of XWS-Security, users will have to ensure that the SecurityEnvironmentHandler implementation they supply is thread safe.

public  class SecurityEnvironmentHandler implements 
CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
       UnsupportedCallbackException {
    
        for (int i=0; i < callbacks.length; i++) {
    
            if (callbacks[i] instanceof 
PasswordValidationCallback) {
                PasswordValidationCallback cb = 
(PasswordValidationCallback) callbacks[i];
                if (cb.getRequest() instanceof
                   PasswordValidationCallback.PlainTextPasswor
dRequest) {
                    // setValidator for plain-text password 
validation on callback cb
                } else if (cb.getRequest() instanceof
                      PasswordValidationCallback.DigestPasswor
dRequest) {
                    
PasswordValidationCallback.DigestPasswordRequest request =
                        
(PasswordValidationCallback.DigestPasswordRequest) 
cb.getRequest();
                    // set plaintext password on  request
                    // setValidator for digest password validation 
on cb

                } else {
                    // throw unsupported;
                }
    
            } else if (callbacks[i] instanceof 
SignatureVerificationKeyCallback) {
                SignatureVerificationKeyCallback cb =
                   (SignatureVerificationKeyCallback)callbacks
[i];
    
                if (cb.getRequest() instanceof
                   SignatureVerificationKeyCallback.X509Subjec
tKeyIdentifierBasedRequest) {
                    // subject keyid request
                    
SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBased
Request 
                        request =
                        
(SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBase
dRequest)
                           cb.getRequest();
                    // locate and setX509Certificate on the request   
                } else if (cb.getRequest() instanceof
                      SignatureVerificationKeyCallback.X509Iss
uerSerialBasedRequest) {
                    // issuer serial request
                    
SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest 
request =
                        
(SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest
)
                            cb.getRequest();
                    // locate and setX509Certificate on the request

                } else  {
                     // throw unsupported;
                }
    
            } else if (callbacks[i] instanceof 
SignatureKeyCallback) {
                 SignatureKeyCallback cb = 
(SignatureKeyCallback)callbacks[i];
                 if (cb.getRequest() instanceof 
SignatureKeyCallback.DefaultPrivKeyCertRequest) {
                    // default priv key cert req
                    SignatureKeyCallback.DefaultPrivKeyCertRequest 
request =
                        
(SignatureKeyCallback.DefaultPrivKeyCertRequest) 
cb.getRequest();
                    // locate and set default privateKey and 
X509Certificate on request  
                } else if (cb.getRequest() instanceof 
SignatureKeyCallback.AliasPrivKeyCertRequest) {
                    // Alias priv key cert req
                    SignatureKeyCallback.AliasPrivKeyCertRequest 
request =
                        
(SignatureKeyCallback.AliasPrivKeyCertRequest) 
cb.getRequest();
                    // locate and set default privateKey and 
X509Certificate on request
                   
                } else {
                     // throw unsupported;
                }
    
            } else if (callbacks[i] instanceof 
DecryptionKeyCallback) {
                DecryptionKeyCallback cb = 
(DecryptionKeyCallback)callbacks[i];
    
                if (cb.getRequest() instanceof 
                   DecryptionKeyCallback.X509SubjectKeyIdentif
ierBasedRequest) {
                    //ski  request
                    
DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest 
request =
                        
(DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest)
                           cb.getRequest();
                    // locate and set the privateKey on the request   

                } else if (cb.getRequest() instanceof
                     DecryptionKeyCallback.X509IssuerSerialBas
edRequest) {
                    // issuer serial request
                    
DecryptionKeyCallback.X509IssuerSerialBasedRequest request =
                        
(DecryptionKeyCallback.X509IssuerSerialBasedRequest) 
cb.getRequest();
                    // locate and set the privateKey on the request   
                } else if (cb.getRequest() instanceof
                      DecryptionKeyCallback.X509CertificateBas
edRequest) {
                    // X509 cert request
                    
DecryptionKeyCallback.X509CertificateBasedRequest request =
                        
(DecryptionKeyCallback.X509CertificateBasedRequest) 
cb.getRequest();
                    // locate and set private key on the request
                } else if (cb.getRequest() instanceof
                      DecryptionKeyCallback.AliasSymmetricKeyR
equest) {
                    DecryptionKeyCallback.AliasSymmetricKeyRequest 
request =
                        
(DecryptionKeyCallback.AliasSymmetricKeyRequest) 
cb.getRequest();
                    // locate and set symmetric key on request
   
                } else  {
                    // throw unsupported;
                }
    
            } else if (callbacks[i] instanceof 
EncryptionKeyCallback) {
                EncryptionKeyCallback cb = 
(EncryptionKeyCallback)callbacks[i];
    
                if (cb.getRequest() instanceof 
EncryptionKeyCallback.AliasX509CertificateRequest) {
                    
EncryptionKeyCallback.AliasX509CertificateRequest request =
                        
(EncryptionKeyCallback.AliasX509CertificateRequest) 
cb.getRequest();
                    // locate and set certificate on request   
                } else if (cb.getRequest() instanceof
                     EncryptionKeyCallback.AliasSymmetricKeyRe
quest) {
                    EncryptionKeyCallback.AliasSymmetricKeyRequest 
request =
                        
(EncryptionKeyCallback.AliasSymmetricKeyRequest) 
cb.getRequest();
                    // locate and set symmetric key on request
   
                } else {
                    // throw unsupported;
                }
    
            } else if (callbacks[i] instanceof 
CertificateValidationCallback) {
                CertificateValidationCallback cb = 
(CertificateValidationCallback)callbacks[i];
                // set an X509 Certificate Validator on the callback
            } else {
                // throw unsupported;
            }
        }
    }
} 

An application can also choose not to handle certain callbacks if it knows that the particular application will never require those callbacks. For example if the security application only deals with signing the requests and does not deal with encryption or username tokens, its handle() method only needs to worry about SignatureKeyCallback (with its associated Requests) and SignatureVerificationKeyCallback (with its associated Requests). It can then throw an UnsupportedCallbackException for any other callback. The following code shows the handle() method skeleton for such an application:

public  class SecurityEnvironmentHandler implements 
CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
       UnsupportedCallbackException {
    

        for (int i=0; i < callbacks.length; i++) {
    
            if (callbacks[i] instanceof 
SignatureVerificationKeyCallback) {
                SignatureVerificationKeyCallback cb =
                   (SignatureVerificationKeyCallback)callbacks
[i];
    
                if (cb.getRequest() instanceof
                   SignatureVerificationKeyCallback.X509Subjec
tKeyIdentifierBasedRequest) {
                    // subject keyid request
                    
SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBased
Request 
                       request =
                        
(SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBase
dRequest)
                            cb.getRequest();
                    // locate and setX509Certificate on the request   
                } else if (cb.getRequest() instanceof
                     SignatureVerificationKeyCallback.X509Issu
erSerialBasedRequest) {
                    // issuer serial request
                    
SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest 
request =
                        
(SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest
)
                           cb.getRequest();
                    // locate and setX509Certificate on the request

                } else  {
                    // throw unsupported;
                }
    
            } else if (callbacks[i] instanceof 
SignatureKeyCallback) {
                 SignatureKeyCallback cb = 
(SignatureKeyCallback)callbacks[i];
                 if (cb.getRequest() instanceof 
SignatureKeyCallback.DefaultPrivKeyCertRequest) {
                    // default priv key cert req
                    SignatureKeyCallback.DefaultPrivKeyCertRequest 
request =
                        
(SignatureKeyCallback.DefaultPrivKeyCertRequest) 
cb.getRequest();
                    // locate and set default privateKey and 
X509Certificate on request  
                } else if (cb.getRequest() instanceof 
SignatureKeyCallback.AliasPrivKeyCertRequest) {
                    // Alias priv key cert req
                    SignatureKeyCallback.AliasPrivKeyCertRequest 
request =
                        
(SignatureKeyCallback.AliasPrivKeyCertRequest) 
cb.getRequest();
                    // locate and set default privateKey and 
X509Certificate on request
                   
                } else {
                    //  throw unsupported;
                }

            } else {
                // throw unsupported;
            }
        }
    }
} 

Similarly, an application dealing only with UsernameToken but not signature or encryption requirements can simply throw UnsupportedCallbackException for all non-username related callbacks.

The SecurityEnvironmentHandler implementation for the simple sample is located in the directory <JWSDP_HOME>/xws-security/samples/simple/src/com/sun/xml/wss/sample. The simple sample uses the same SecurityEnvironmentHandler for both the client and server side.

The jaas-sample sample requires a different set of callbacks to be handled on the client and server side. The CallbackHandlers for the jaas-sample sample are located in the directory <JWSDP_HOME>/xws-security/samples/jaas-sample/src/com/sun/xml/wss/sample. The two CallbackHandlers defined for the jaas-sample are:

Using the SubjectAccessor API

XWS-Security applications might require access to the authenticated subject of the sender from within the SEI implementation methods. The SubjectAccessor API contains a single method:

public static Subject getRequesterSubject(Object context)
   throws XWSSecurityException 

This method returns the Subject if one is available or else it returns NULL. The context argument to be passed into this method is the ServletEndpointContext which is available with the SEI implementation class. For an example on how the SubjectAccessor is used to obtain the authenticated sender subject, refer to the PingImpl.java class in the jaas-sample sample located at <JWSDP_HOME>/xws-security/samples/jaas-sample/server/src/sample. The API for SubjectAccessor viewed from <JWSDP_HOME>/xws-security/docs/api/com/sun/xml/wss/SubjectAccessor.html.

Divider
Download
FAQ
History
PrevHomeNext API
Search
Feedback
Divider

All of the material in The Java(TM) Web Services Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.