Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member190457
Contributor
0 Kudos

Security beyond security

The subject of web service security has always been one of the most important any industrial Application Server has had to face. Netweaver product suite offers good coverage of all the security standards commonly used, but certain requirements might need additional tweaking.
By the term “security” we will be generically addressing all the several facets of this issue: authentication, integrity, confidentiality, non-repudiation and so on. Specifically, it is important to make a distinction between transport “security” and message “security”: 
  • By transport security we are stating that all the concerns above are handled at http packet level (transport), irrespective of the actual contents of the packet.
  • By message level, we are digging into the application data being transmitted, i.e. SOAP.
So, both these types of security are pretty much supported in NW AS. But what if both are required at the same time? It should come as no surprise that if you wish to consume a web service which requires both you’d be in a bit of trouble.
In the rest of the article we will see how to consume a web service requiring both SSL mutual authentication AND authentication at the message level according to OASIS UsernameToken Plain Text Password Specification.
The release used is Netweaver Java 7.3.
 

Choose your strategy

So, having discovered that NW AS only supports one of the two standards at a time, we need to make a choice. We might use the standard implementation of SSL and go custom for Username Token Auth, or the opposite. First option wins, as we definitely don’t want to mess with SSL apis.

Standard and children first

As usual, we need to create a deployable proxy for the web service we are going to consume. The procedure to do so is beyond the scope of this article, so please check out the SDN as there’s plenty of related blogs around.
A service group is not needed for the purpose of this article, as we may as well be going with the good old WebService destination.
SSL mutual authentication requires that both the server and the client provide each other public key certificates issued by trusted certification authorities. It is therefore mandatory that at this point you (the consumer side developer) have a client certificate issued by a CA the server accepts. In addition, you also need the server certificate.
Next, we deploy our proxy within an EAR archive to the AS. Accessing the SOA console from NWA, we reach our consumer proxy configuration.
Two settings must be made as far as transport security is concerned. First off we configure our client so as to present itself with the certificate we previously got from our trusted CA.
Next, we configure our consumer proxy to accept (or ignore at all) the server certificate. To do so, we must have copied the issuer certificate into one of NW AS keystores.
As far as transport security is concerned, we are good to go. The NW AS will take good care of everything from public key verification, to SSL handshake and so on. Now on to the hard stuff.

To JEE or not to JEE

A complete description of the Oasis UsernameToken Profile - Plain text Password can be found at http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf.
For the sake of this article we will just develop an api which will enable us to add SOAP security header blocks to our SOAP request which will look just like this.
<soap:Header>
   <wsse:Security soap:mustUnderstand="1">
      <wsu:Timestamp wsu:Id="Timestamp-8da3df53-4b64-477d-8407-5c9e0c3f8d29">    
          <wsu:Created>2012-02-21T16:04:38Z</wsu:Created>
          <wsu:Expires>2012-02-21T16:09:38Z</wsu:Expires>      </wsu:Timestamp>
      <wsse:UsernameToken wsu:Id="SecurityToken-a3d307d8-82f6-4441-ae12-02498e75805e">    
          <wsse:Username>username</wsse:Username>
          <wsse:Password>password</wsse:Password>
          <wsse:Nonce>WRDUi3oDRSk1iYrFhnT4NA==</wsse:Nonce>      
          <wsu:Created>2012-02-21T16:04:38Z</wsu:Created>
     </wsse:UsernameToken>
    </wsse:Security>
</soap:Header>
As you can see, several pieces of information are required: a timestamp for defining creation and expiration time; the actual username and password, a challenge (nonce) for the server to prove its identity and another “created date” block.
The most experienced developers might just throw in their JEE knowledge and try to solve this using the standard SOAPHandler and HandlerResolver api. This will certainly succeed in adding the required blocks to the SOAP header. The problem is, as a matter of fact, NW AS will automatically cut off all of the standard security processing once a SOAP Handler Resolver is registered. So you will end up with a correctly formatted SOAP header, but no SSL support from the AS.
So what now?
 

Code in the hole

Luckily two SAP proprietary interfaces come in handy.
  • com.sap.engine.services.webservices.espbase.client.api.SOAPHeaderIFactory;
  • com.sap.engine.services.webservices.espbase.client.api.SOAPHeaderInterface;
This api actually allows us to add arbitrary header blocks to the SOAP request without excluding the standard security features offered by the AS. The header blocks must be structured as DOM nodes: this powerful api enables us to add attributes, elements and whatever is required to make our header compliant to the OASIS specification above. Much can be found on DOM for java just googling around.
So, first off we must get an instance of our web service port. I tipically do this using jndi lookup within a stateless ejb:
MyWebServiceInterface service = (MyWebServiceInterface) new InitialContext().lookup(PROXY_JNDI_ADDRESS);
MyWebServicePort port = service.getMyWebServicePort();
Then we retrieve an header interface like this:
SOAPHeaderInterface header = SOAPHeaderIFactory.getInterface(port);
Leveraging the SOAPHeaderInterface we have at our disposal one method to solve it all:  SOAPHeaderInterface.setOutputHeader(QName,Object). This method is a bit tricky, so I recommend you use it with a first null parameter and a org.w3c.dom.Element as second. Please note that even though the method is a “set”, it actually behaves as an “add” method. You can therefore add as many header blocks as you wish, provided they have different qualified names. We will see how to use it in a while.
Now let’s start with creating a Security element which will in turn contain the timestamp and username children.


public Element createSecurity() throws Exception
{
// timestamp
GregorianCalendar now = new GregorianCalendar();
GregorianCalendar later = new GregorianCalendar();
later.add(GregorianCalendar.MINUTE, 5);
Element security = domDoc.createElementNS(WSSE_URI, "wsse:Security");
createTimestamp(now, later, domDoc, security);
createUsernameToken(now, later, domDoc, security);
return security;
}
The String namespace constants have been omitted for sake of simplicity. Two additional methods are required for creating the Timestamp and Username token. Let’s see the Username token creation:
private void createUsernameToken(GregorianCalendar now, Document domDoc, Element security) {
Element usnToken = domDoc.createElementNS(WSSE_URI,
    "wsse:UsernameToken");
  usnToken.setAttributeNS(WSU_URI, "wsu:Id", generateSecurityTokenId());
 
  Element username = domDoc.createElementNS(WSSE_URI, "wsse:Username");
  username.setTextContent(USERNAME);
  Element password = domDoc.createElementNS(WSSE_URI, "wsse:Password");
  password.setTextContent(PASSWORD);
 
Element nonce = domDoc.createElementNS(WSSE_URI, "wsse:Nonce");
  nonce.setTextContent(generateNonce());
  Element created = domDoc.createElementNS(WSSE_URI, "wsse:Created");
  created.setTextContent(new DateFormatter().format(now));
  usnToken.appendChild(username);
  usnToken.appendChild(password);
  usnToken.appendChild(nonce);
  usnToken.appendChild(created);
  security.appendChild(usnToken);
}
I’ll leave out the coding for generating nonces and security tokens which are more or less random strings. The timestamp generation is very similar and easier. So we now have a DOM Element object representing our security header. We can go back to the SAP SOAPHeaderInterface we retrieved before. So goes the code:
Element security = createSecurity();
header.setOutputHeader(null, security);
//actual invocation
MyReturnValue retVal = port.doSomething(someParameter);
Thanks to the SAP proprietary API, the SSL configuration we made before will be kept into consideration by the AS and our message will have a SOAP security header while still travelling through a secure SSL transport channel.

Conclusion

We have seen how to consume a web service which requires both transport level security through SSL mutual authentication and UsernameToken Profile at the message level. SAP Netweaver Java AS only allows one type of security to be enforced, so we chose to use the standard configuration for transport level, and go for custom coding for message level.
The standard JEE api won’t be of any use since the AS skips any standard security processing once a HandlerResolver is registered, so we turned to SAP proprietary api.
The SOAPHeaderInterface api we have seen might as well be used to add further header blocks if required, such as WS-Addressing.
6 Comments