poniedziałek, 26 stycznia 2015

UsernameToken in Websphere Message Broker 7.0

This post should be written two years ago...

WMB 7.0 lacks of some basic functionality related to WS-Security - UsernameToken for Consumer scenario. It was probably implemented in next Broker version.
Some companies still use this version though. 

Two years ago I had to implement WS-Security based on UsernameToken and Signature. Second part was easier as it is supported by Policy Sets.
First part wasn't supported and I had to write it by myself. Below you can find snippets with code chunks.

What is Username Token?

According to oasis-open.org  UsernameToken element was introduced to SOAP Security as a way of providing username. 

syntax:
 <wsse:UsernameToken>  
   <wsse:Username> ... </wsse:Username>  
   <wsse:Password Type="..."> ... </wsse:Password>  
   <wsse:Nonce EncodingType="..."> ... </wsse:Nonce>  
   <wsu:Created> ... </wsu:Created>  
 </wsse:UsernameToken>  

Where:
- Username is plaintext username
- Password is a password or password equivalent - for example of type #Password_Digest - Base64 ( SHA-1 ( nonce + created + password ) )
- Nonce is random value to detect replay attacks encoded with Base64
- Created is datetime of creation time of the request, helps detect replay attacks

You can read more here: http://www.ws-i.org/profiles/basicsecurityprofile-1.0.html#UsernameToken  

Implementation


Code below should be pasted to compute module where SOAP envelope is created.

1) Define procedures:
 create PROCEDURE encrypt (IN P1 CHARACTER) RETURNS CHARACTER LANGUAGE JAVA EXTERNAL NAME "com.zagwozdka.broker.UsernameTokenUtils.encrypt";  
 create PROCEDURE getTime () RETURNS CHARACTER LANGUAGE JAVA EXTERNAL NAME "com.zagwozdka.broker.UsernameTokenUtils.getTime";  
 create PROCEDURE getNonce () RETURNS CHARACTER LANGUAGE JAVA EXTERNAL NAME "com.zagwozdka.broker.UsernameTokenUtils.getNonce";  
 create PROCEDURE b64Encode(IN source BLOB) RETURNS CHARACTER LANGUAGE JAVA EXTERNAL NAME "com.ibm.broker.javacompute.Base64.encode";  

2) Java UsernameTokenUtils class with static methods:
 public class UsernameTokenUtils {  
 public static synchronized String getTime(){  
 Calendar c = new GregorianCalendar();  
 SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");  
 return s.format(c.getTime());  
 }  
 public static synchronized String getNonce(){  
 Random generator = new Random();  
   String nonceString = String.valueOf(generator.nextInt(999999999));  
   return nonceString;  
 }  
  public static synchronized String encrypt(String plaintext)   
  {  
   MessageDigest md = null;  
   try  
   {  
    md = MessageDigest.getInstance("SHA");  
   }  
   catch(NoSuchAlgorithmException e)  
   {  
    return null;  
   }  
   try  
   {  
    md.update(plaintext.getBytes("UTF-8"));  
   }  
   catch(UnsupportedEncodingException e)  
   {  
    return null;  
   }  
   byte raw[] = md.digest();  
   String hash = (new BASE64Encoder()).encode(raw);  
   return hash;  
  }  
 }  

3) declare oasis-open namespaces
 DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';   
 DECLARE wsse NAMESPACE 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';   
 DECLARE wsu NAMESPACE 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';  

4) declare variables, generate time and nonce
 DECLARE toEncode BLOB;  
 DECLARE ctime CHARACTER;  
 DECLARE nonce CHARACTER;  
 SET ctime= getTime();  
 SET nonce = getNonce();  

5) prepare soap header with UsernameToken and fill it with proper values
 CREATE LASTCHILD OF OutputRoot.SOAP DOMAIN('SOAP') TYPE Name NAME 'Header';   
 Set OutputRoot.SOAP.Header.wsse:Security.wsse:UsernameToken.wsse:Username = username; -- here is username   
 Set OutputRoot.SOAP.Header.wsse:Security.wsse:UsernameToken.wsse:Password = encrypt(nonce||ctime||password); -- here is password  
 Set OutputRoot.SOAP.Header.wsse:Security.wsse:UsernameToken.wsse:Password.(XMLNSC.Attribute)"Type"='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest';  
 SET toEncode = CAST(nonce as BLOB CCSID 1208);  
 Set OutputRoot.SOAP.Header.wsse:Security.wsse:UsernameToken.wsse:Nonce = b64Encode(toEncode);  
 Set OutputRoot.SOAP.Header.wsse:Security.wsse:UsernameToken.wsse:Nonce.(XMLNSC.Attribute)"EncodingType"='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary';  
 Set OutputRoot.SOAP.Header.wsse:Security.wsse:UsernameToken.wsu:Created=ctime;  
 Set OutputRoot.SOAP.Header.wsse:Security.(XMLNSC.Attribute)soapenv:mustUnderstand='true';   

I hope someone will google this article and find it useful :-)

3 komentarze:

  1. Very clearly written good article! I am not able to work out the b64Encode method is it a static method defined separately or part of esql execution?

    OdpowiedzUsuń
    Odpowiedzi
    1. sorry for late answer:
      b64Encode is a static method from ibm jar:
      create PROCEDURE b64Encode(IN source BLOB) RETURNS CHARACTER LANGUAGE JAVA EXTERNAL NAME "com.ibm.broker.javacompute.Base64.encode";

      Usuń