How to : Sign XML messages with a SHA-1 signature, for Adobe Content Server
14 Feb 2010The past week I’ve been doing a spike to talk to Adobe Content Server 4 (ACS4). Querying the content in ACS4 is done in a REST style. The client sends a XML message via HTTP POST to the admin endpoint. The endpoint details are in the documentation but are vague. This is usually at http://youracs4server/admin/EndPoint.
These need a signed XML message, in the POST body. A typical message looks like this
<request> <nonce>ABCD123==</nonce> <hmac>XXXXXXXXX===</hmac> <distributor>uid:8888-43434-34343434</distributor> <resource> ...... </resouce> </request>
The hmac element contains the SHA1 signature of the XML message. The signature is generated using a shared secret. The signature is for the whole XML except the hmac element. Before signing the message, we have to construct the XML without the hmac, then sign it, and then add then hmac.
I did this by using a two stage Xml serialization process. This may not be the best way to do it, and there has to be a better solution. I created a class named SignedXMLSerializer, and this inherits from XmlSerializer. SignedXMLSerializer serializes objects that are of the base type Signable. The Signable class has two properties, Nonce and Hmac. Any class that has to be sent as a signed XML message must inherit from Signable
public abstract class Signable { [XmlElement("nonce")] public string Nonce { get; set; } [XmlElement("hmac")] public string HMAC { get; set; } }
In the serialize method of the SignedXMLSerializer, we first generate the nonce. The nonce makes each message unique. Then we serialize the object to a string. The signature is generated using this string, and assigned it to the Hmac property.
We then serialize the signed object to the writer that was passed in.
public void Serialize(T o, XmlTextWriter writer) { o.Nonce = GenerateNonce(); StringBuilder stringBuilder = GetXMLToSign(o); string hmac = GetSignature(stringBuilder); o.HMAC = hmac; Serialize(writer, o); }
To use the SignedXMLSerializer, pass in one of the HashAlgorithm types. I’ve used SHA1 in my case.
This makes it easier to use with different signing algorithms.
Typical usage of the SignedXMLSerializer is as follows
HMACSHA1 hmacsha1 = new HMACSHA1 {Key = Encoding.ASCII.GetBytes("consumerSecret")}; SignedXmlSerializer<Request> signedXmlSerializer = new SignedXmlSerializer<Request>(hmacsha1); StringBuilder sb = new StringBuilder(); StringWriter writer = new StringWriter(sb); signedXmlSerializer.Serialize(req, new XmlTextWriter(writer));
The final serialized string can be sent via HTTP post to ACS4.
Code for the SignedXMLSerializer class is here http://gist.github.com/303962