We are building an integration between the Royal Mail SOAP Shipping API and Microsoft Dynamics AX 2012 R2. In parts 1-3 we have built a C# component to add some helper methods for interacting with the Royal Mail client proxy. In part 4 we called this client proxy in X++ and used our helper methods to extend the headers we send with the request message.
Now that we have the service client, we need to add WS Security headers to the request message to pass the Royal Mail authentication.
CLR Error Messages
When use CLR objects in X++, error checking can be an issue. However there is a handy static method available AifUtil::getClrErrorMessage().
//Something with .Net objects.
errorMessage = AifUtil::getClrErrorMessage();
throw error (errorMessage);
The specification that Royal Mail uses is the Web Services Security
Username Token Profile version 1.0 a . This mandates a SOAP security header (added to the header of the message, rather than the headers of http headers of the request), including a username, a password digest made up of a SHA-1 hash of the password, created datetime and the nonce; a nonce and the created datetime.
In my opinion for the type of transaction that this security is being used for, it is a bit overkill. Other carrier APIs (Parcelforce, DHL, Yodel, Asendia) just use a username/password combination. After all this is not a financial transaction, and the most vulnerable data – the customers name and address is being sent unencrypted in plain text.
A SHA-1 Hash is a type of cryptographic message digest that is designed to require several pieces of information to be able to generate the message. It was designed by the NSA for various security protocols and was originally used for the protection of unclassified sensitive data. It has now been superseded by SHA-2, especially after SHA-1 was shown to be vulnerable to collision attacks in 2015.
We can construct a simple function to have a password as follows:
Here we get the default encoding of the operating system, then encode the string in to a sequence of bytes. We pass this to the SHA1 hashing method available in the System.Security.Cryptography namespace, to get our message digest.
A nonce is a randomly generated number, that can only be used once, it is designed to protect against replay attacks using old communication data.
I had a little trouble with using the random function to produce a unique number, as I kept getting a similar number as had been produced before. So I decided to use a unique number that is available in AX tables the RecId. A RecId is unique for each row in a table.
I created an AX table that would hold details of each request, useful information like the SalesId, the request log file path, datetime created and whether the request received a response or not. Each time a message request is going to be sent I add a row to the table and retrieve the RecId, then use this to populate the nonce. I use the random function as a backup in case for some reason this table logging has failed and there is no RecId returned.
Then I update the table to add the nonce that was used for this request, which is useful to check what nonce is being used.
The password digest is a SHA1 hash of the nonce + created datetime + hashed password. This is then converted to Base 64 for transport.
Security Header XML
Then we need to create the XML that we will add to the request message with the authentication details in.
Putting everything together, we create the password digest, create the WSSE Security headers that are placed in the SOAP Envelope header, then store this in the client proxy’s SecurityHeaderType object.
We have created a password digest and added the WS security headers to our request message.
Everything else that you need to do to send a request message (like add shipment data, receive the response and handle error messages) is relatively straight forward once you have this set up.