Technical Articles
SAP Cloud Integration: Web Services Security UsernameToken
Introduction
This document describes how to use the UsernameToken with the WSS: SOAP Message Security specification in SAP CPI iFlow. A web service consumer can supply a UsernameToken as a means of identifying the requestor by “username”, and optionally using a password (or shared secret, or password equivalent) to authenticate that identity to the web service producer.
There is standard configuration feature provided in SOAP 1.x channel in SAP CPI. But there might be some custom requirement which requires additional scripts.
Main
The <wsse:UsernameToken> element is introduced in the WSS: SOAP Message Security documents as a way of providing a username. Under <wsse:UsernameToken> element, we can specify below 3 elements.
- <wsse:Username> : This element contains the username of the user which is known to the webservice provider.
- <wsse:Password> : (Optional) Any password equivalent such as a derived password or S/KEY (one time password) can be used. This element has an attribute name type which can have 2 values.
- PasswordText: Password in clear text format
- PasswordDigest: Passwords of type PasswordDigest are defined as being the Base64 encoded, SHA-1 hash value, of the UTF8 encoded password. (Password_Digest = Base64(SHA-1(nonce + created + password)))
- <wsse:Nonce> : A nonce is a random value that the sender creates to include in each UsernameToken that it sends.
- <wsu:Created> : Creation Timestamp.
We will discuss below 3 scenarios.
- Standard SAP CPI SOAP channel with UsernameToken password type as “PasswordText”.
- Standard SAP CPI SOAP channel with UsernameToken password type as “PasswordDigest”.
- Custom requirement to have Nonce and created time with password type as “PasswordText”.
- Password should have password type “PasswordText”.
- Password should be SHA256 encrypted.
- Created time should be in OffsetDateTime Java format rather than Instant Java format.
Scenario 1
Standard SAP CPI SOAP channel with UsernameToken password type as “PasswordText”.
Channel Configuration
MPL Log Payload
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsse:Security soap:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-0f4c1454-cc6a-45b5-ac7e-6e97a0859743">
<wsse:Username>asutosh</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<NumberToDollars xmlns="http://www.dataaccess.com/webservicesserver/">
<dNum>500</dNum>
</NumberToDollars>
</soap:Body>
</soap:Envelope>
Observation
By default, it’s not generating the Nonce and created element if we choose Password Type as “PasswordText”.
Scenario 2
Standard SAP CPI SOAP channel with UsernameToken password type as “PasswordDigest”.
Channel Configuration
MPL Log Payload
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsse:Security soap:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-eb17926b-f05e-47ed-b745-4e3da22b7f71">
<wsse:Username>asutosh</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">zzNBkzinVYR0DEBt45zR2eRyD40=</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">XbGt7Y3GbO701D48CcLiTQ==</wsse:Nonce>
<wsu:Created>2021-12-19T13:30:07.211Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<NumberToDollars xmlns="http://www.dataaccess.com/webservicesserver/">
<dNum>500</dNum>
</NumberToDollars>
</soap:Body>
</soap:Envelope>
Observation
This configuration generates the password in hashed format but there is no standard configuration available to change the Created element as per time zone requirement.
Scenario 3
Custom requirement to have Nonce and created time with password type as “PasswordText”.
- Password should have password type “PasswordText”.
- Password should be SHA256 encrypted.
- Created time should be in OffsetDateTime Java format rather than Instant Java format.
SOAP Message via HTTPS Adapter using Script
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import java.io.IOException;
import java.security.MessageDigest;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.util.*;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.message.WSSecHeader;
import org.apache.wss4j.dom.message.WSSecUsernameToken;
import org.w3c.dom.Document;
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.xml.sax.InputSource;
import org.apache.commons.codec.digest.DigestUtils;
def Message processData(Message message) {
//Build w3c Document from CPI message Body which should be in SOAP format
def body = message.getBody(String);
def username = "asutosh";
def password = "password";
String sha256hexPassword = org.apache.commons.codec.digest.DigestUtils.sha256Hex(password);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(body)));
//Add WSS Header to the Document
WSSecHeader secHeader = new WSSecHeader(doc);
secHeader.insertSecurityHeader();
//Add UsernameToken to the WSS Header
WSSecUsernameToken builder1 = new WSSecUsernameToken(secHeader);
builder1.setPasswordType(WSConstants.PASSWORD_TEXT); //Define Password type
builder1.setUserInfo(username, sha256hexPassword); //Define Username and Password
builder1.addNonce(); //Add Nonce
Instant instant = Instant.now(); //Get Current UTC time
ZoneOffset offset = ZoneId.of("-05:00").getRules().getOffset(instant);
OffsetDateTime odt = instant.atOffset(offset); //Get Peru time which is -0500
builder1.addCreated(); //Add Created time to UsernameToken
Document utDoc = builder1.build();
//Convert Document to string
DOMSource domSource = new DOMSource(utDoc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
def body3 = writer.toString();
//Replace UTC time with Peru Time
def body4 = body3.replaceAll(instant.toString(), odt.toString());
body4 = body4.replaceAll("Header>", "soap:Header>");
//Set SOAP Specific Header
message.setHeader("Content-Type", "text/xml");
message.setHeader("SOAPAction", "\"#POST\"");
//Set the message to Body
message.setBody(body4);
return message;
}
MPL Log Payload
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken-fefbc88c-ad91-4bff-9887-c8514eeb6dda">
<wsse:Username>asutosh</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">/u4eo1lijXzNerUn/6VLZg==</wsse:Nonce>
<wsu:Created>2021-12-19T08:48:20.029-05:00</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<NumberToDollars xmlns="http://www.dataaccess.com/webservicesserver/">
<dNum>500</dNum>
</NumberToDollars>
</soap:Body>
</soap:Envelope>
Observation
I have used Apache WSS4J library which requires no external jar to be imported in SAP CPI. You can explore the library for all kinds of custom requirements for WS-Security specifications. We were able to generate Nonce and Created element for Password Type “PasswordText”. It’s also possible to modify the created time for time zone specific requirement.
Conclusion
There are certain requirements which require custom scripting. You can take reference of this document for further understanding of SOAP Web Services Security Specifications.
Nice blog!
Was looking for this for a very long time.