I need to call web service that I have to send such soap request below by using C#. SoapBody and TimeStamp must be signed.
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:web="http://xyzt.com/">
<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">
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1" wsu:Id="X509-F4AF9673207AC5E0B614180667985061">MIIFsDCCBawwggSUoAMCAQICBgCaWhnEajANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJUUjFNMEsGA1UEAwxETWFsaSBNw7xow7xyIEVsZWt0cm9uaWsgU2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgLSBTw7xyw7xtIDEwHhcNMT</wsse:BinarySecurityToken>
<ds:Signature Id="SIG-3" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="soap web" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#id-2">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="web" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>IZVrIpPCxiPcvyVOVv/d4nRPZWM=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#TS-1">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="wsse soap web" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>fltghgDztDtuVQX7y4t0ZJxAnxE=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>IOVXxBTp053aNJMbQj+VTiBblZ63peyJ1vWazKmEWNxN7RaeFfKELoxede8xQEqzSaB/u8exC7LLGYiEdChboVCf9liLMN4MmNj5JR6gfDrsL3azThf5hxLQ+WIE20PRoU6ozpp20zC1IaO3IU4ZaRLw</ds:SignatureValue>
<ds:KeyInfo Id="KI-F4AF9673207AC5E0B614180667986422">
<wsse:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1" wsu:Id="STR-F4AF9673207AC5E0B614180667986643" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd">
<wsse:Reference URI="#X509-F4AF9673207AC5E0B614180667985061" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<wsu:Timestamp wsu:Id="TS-1">
<wsu:Created>2014-12-08T21:26:36.191Z</wsu:Created>
<wsu:Expires>2014-12-08T21:36:36.191Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body wsu:Id="id-2" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<web:getStatus>
<itemID>1234567</itemID>
</web:getStatus>
</soap:Body>
</soap:Envelope>
I created this soap request and get good response by using WCF Client CustomBinding and .pfx file that has certificate with private key.
Most of examples about signing soap messages use certificate from certificate store or pfx file. But in my scenario user's certificate (with private key) stored on smart card that certificate private key can't be exported. So in this case I can not use WCF CustomBinding or I can not use SignedXml class to sign SOAP message because when i try to get certificate programatically then private key is missing. (Also I got private key by using NCryptoki - PKCS wrapper but this private key type is different then RSA that i can't set for WCF client or SignedXmlClass private key.)
So, I try to create this SOAP message as a string, and manually create DigestValues, BinarySecurityToken and SignatureValue by using smart card.
I can calculate BinarySecurityToken value as :
var certificate = GetX5092Certificate(); // X5092 certificate on smart card without private key
string binarySecToken= Convert.ToBase64String(certificate.RawData);
Also i have some code to calculate digest value as:
byte[] dataToHashTS = Encoding.UTF8.GetBytes(TimeStampReference.OuterXml);
XmlDsigExcC14NTransform transformDataTS = new XmlDsigExcC14NTransform("wsse soap web");
transformDataTS.LoadInput(new MemoryStream(dataToHashTS));
byte[] bDigestDataTS = transformDataTS.GetDigestedOutput(SHA1Managed.Create());
string sDigestDataTS = Convert.ToBase64String(bDigestDataTS); //timestamp digest
I am not sure that If I calculate digest values right or not?
To calculate SignatureValue, I think I need to get hash of SignedInfo part. I have method that sign content (byte array) by using smart card. So how I can send SignedInfo content to this method? I mean is it enough to get hash of SignedInfo block as a string? OR I have get SignedInfo as XmlElement then transform + hash as i did to calculate digest values?
Any help would be much appreciated. Thanks.