I need to assign digital signature on header for my soap request, here are the example of the SOAP request.
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelopexmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12"xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Header><SOAP-SEC:Signaturesoapenv:actor=""soapenv:mustUnderstand="0"><ds:Signaturexmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethodAlgorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><ds:SignatureMethodAlgorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:ReferenceURI="#Body"><ds:DigestMethodAlgorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue></ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue></ds:SignatureValue></ds:Signature></SOAP-SEC:Signature></soapenv:Header><soapenv:BodyId="Body"><CalFireRequestsoapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><DETAFFReqhref="#id0"/></CalFireRequest><multiRefid="id0"soapenc:root="0"soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"xmlns:ns1="urn:DETAFFServices"xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"xsi:type="ns1:CalFireRequest"><inputFieldNamexsi:type="soapenc:string"></inputFieldName><inputFieldValuexsi:type="soapenc:string"></inputFieldValue><usrIdxsi:type="soapenc:string"></usrId></multiRef></soapenv:Body></soapenv:Envelope>
Then I decided to generated the request by using XML Writer which show as below:
privatestaticstringBuildEnvelope(X509Certificate2 certificate)
{string envelope = null;// note - lots of bits here specific to my thirdpartyusing (var stream = new MemoryStream())
{
Encoding utf8 = new UTF8Encoding(false); // omit BOMusing (var writer = new XmlTextWriter(stream, utf8))
{// soap envelope
writer.WriteStartDocument();
writer.WriteStartElement("soapenv:Envelope");
writer.WriteAttributeString("xmlns", "SOAP-SEC", null, "http://schemas.xmlsoap.org/soap/security/2000-12");
writer.WriteAttributeString("xmlns", "soapenv", null, "http://schemas.xmlsoap.org/soap/envelope/");
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
writer.WriteStartElement("soapenv", "Header", null);
writer.WriteStartElement("SOAP-SEC","Signature",null);
writer.WriteAttributeString("soapenv", "actor", null, "");
writer.WriteAttributeString("soapenv", "mustUnderstand", null, "0");
writer.WriteEndElement(); //Security
writer.WriteEndElement(); //Header////<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">//writer.WriteStartElement("soapenv", "Body", null);//writer.WriteAttributeString(null , "Id", null, "Body");//writer.WriteStartElement("CalFireRequest", null, null);//writer.WriteAttributeString("soapenv", "encodingStyle", null, "http://schemas.xmlsoap.org/soap/encoding/");//writer.WriteStartElement("DETAFFReq", null, null);//writer.WriteAttributeString("null", "href", null, "#id0");//writer.WriteEndElement(); // CalFireRequest//// your 3rd-party soap payload goes here//writer.WriteStartElement("???", "http://docs.oasis-open.org/ws-sx/ws-trust/200512");//// ... //writer.WriteEndElement(); // //writer.WriteEndElement(); // Body
writer.WriteEndElement(); //Envelope
}// signing passvar signable = Encoding.UTF8.GetString(stream.ToArray());
XmlDocument doc = new XmlDocument();
doc.LoadXml(signable);// see https://stackoverflow.com/a/6467877var signedXml = new SignedXml(doc);
RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)certificate.PrivateKey;
signedXml.SigningKey = rsaKey;
// these values may not be supported by your 3rd party - they may use e.g. SHA256 miniumum
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url;//
KeyInfo keyInfo = new KeyInfo();
KeyInfoX509Data x509data = new KeyInfoX509Data(certificate);
keyInfo.AddClause(x509data);
signedXml.KeyInfo = keyInfo;// 3rd party wants us to only sign the timestamp fragment- ymmv
Reference reference0 = new Reference();
reference0.Uri = "BODY";var t0 = new XmlDsigExcC14NTransform();
reference0.AddTransform(t0);
reference0.DigestMethod = SignedXml.XmlDsigSHA1Url;
signedXml.AddReference(reference0);// etc// get the sig fragment
signedXml.ComputeSignature();
XmlElement xmlDigitalSignature = signedXml.GetXml();var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("o", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
nsmgr.AddNamespace("s", "http://www.w3.org/2003/05/soap-envelope");var security_node = doc.SelectSingleNode("/soapenv:Envelope/soapenv:Header/SOAP-SEC:Signature", nsmgr);
security_node.AppendChild(xmlDigitalSignature);
envelope = doc.OuterXml;
}
return envelope;
}
When I run the program I found out that, this part the tag SOAP-SEC can't be close using writer.WriteEndElement()
writer.WriteStartElement("soapenv", "Header", null); writer.WriteStartElement("SOAP-SEC","Signature",null);writer.WriteAttributeString("soapenv", "actor", null, "");writer.WriteAttributeString("soapenv", "mustUnderstand", null, "0");writer.WriteEndElement(); //Securitywriter.WriteEndElement(); //Header
Example of XML result generate
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelopexmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12"xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Header><SOAP-SEC:Signaturesoapenv:actor=""soapenv:mustUnderstand="0" /></soapenv:Header></soapenv:Envelope>
Is there anything I am missing? or can anyone provide a better solution on the SOAP request from the example of SOAP Request above?