使用C#将签名的SOAP消息创建为字符串

我需要调用Web服务,我必须使用C#发送下面的肥皂请求。 必须签署SoapBody和TimeStamp。

   MIIFsDCCBawwggSUoAMCAQICBgCaWhnEajANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJUUjFNMEsGA1UEAwxETWFsaSBNw7xow7xyIEVsZWt0cm9uaWsgU2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgLSBTw7xyw7xtIDEwHhcNMT              IZVrIpPCxiPcvyVOVv/d4nRPZWM=         fltghgDztDtuVQX7y4t0ZJxAnxE=   IOVXxBTp053aNJMbQj+VTiBblZ63peyJ1vWazKmEWNxN7RaeFfKELoxede8xQEqzSaB/u8exC7LLGYiEdChboVCf9liLMN4MmNj5JR6gfDrsL3azThf5hxLQ+WIE20PRoU6ozpp20zC1IaO3IU4ZaRLw        2014-12-08T21:26:36.191Z 2014-12-08T21:36:36.191Z      1234567    

我创建了这个soap请求并通过使用带有私钥的证书的WCF客户端CustomBinding和.pfx文件获得了良好的响应。

有关签署soap消息的大多数示例都使用证书存储或pfx文件中的证书。 但在我的方案中,用户的证书(带有私钥)存储在智能卡上,无法导出证书私钥。 所以在这种情况下我不能使用WCF CustomBinding或者我不能使用SignedXml类来签署SOAP消息,因为当我尝试以编程方式获取证书时,则缺少私钥。 (此外,我通过使用NCryptoki – PKCS包装器获得了私钥,但是此私钥类型与RSA不同,我无法为WCF客户端或SignedXmlClass私钥设置。)

因此,我尝试将此SOAP消息创建为字符串,并使用智能卡手动创建DigestValues,BinarySecurityToken和SignatureValue。

我可以将BinarySecurityToken值计算为:

 var certificate = GetX5092Certificate(); // X5092 certificate on smart card without private key string binarySecToken= Convert.ToBase64String(certificate.RawData); 

我也有一些代码来计算摘要值:

 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 
  • 我不确定如果我正确计算摘要值?

  • 要计算SignatureValue,我想我需要得到SignedInfo部分的哈希值。 我有使用智能卡签名内容(字节数组)的方法。 那我怎么能把SignedInfo内容发送给这个方法呢? 我的意思是将SignedInfo块的哈希值作为字符串得到了吗? 或者我将SignedInfo作为XmlElement,然后像我一样转换+ hash来计算摘要值?

任何帮助将非常感激。 谢谢。

  1. 对于DigestValue,您需要规范化字符串,如下所示:
   2016-06-14T22:56:10.896Z 2016-06-14T23:01:10.896Z  

所以你可以把这个字符串作为参数放在这里:

 private string CanonicalizeDsig(string input) { XmlDocument doc = new XmlDocument(); doc.PreserveWhitespace = false; try { doc.LoadXml(input); XmlDsigC14NTransform trans = new XmlDsigC14NTransform(); trans.LoadInput(doc); String c14NInput = new StreamReader((Stream)trans.GetOutput(typeof(Stream))).ReadToEnd(); return c14NInput; } catch (Exception ex) { return String.Empty; } } 

规范化后,您现在可以计算Hash :(我的是一个SHA1示例)。 所以把上面方法的返回值放在这个参数上。 得到像JCMdwz5g8iq05Lj6tjfDOxKqT4k =

 private string ComputeHashSHA1(string input) { try { SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider(); byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(input)); string digestValue = Convert.ToBase64String(hashedDataBytes); return digestValue; } catch (Exception ex) { MessageBox.Show(ex.Message); return String.Empty; } } 
  1. 签名值是一个棘手的,我只能涵盖一个具体的例子。 如果有一个类似于此的策略,请检查服务的WSDL。
       

这意味着您需要组合客户端熵(这是您的密钥 – 您在获取令牌请求时发送给服务器的任何基于64的字符串)和服务器熵(返回基础64密钥)

您可以使用具有KeyGenerator对象的Microsoft.IdentityModel dll组合它们。

您的输入将是这样的,它还需要使用DsigExcC14N进行规范化:

          JCMdwz5g8iq05Lj6tjfDOxKqT4k=   

这是规范化:

 private string CanonicalizeExc(string input) { XmlDocument doc = new XmlDocument(); doc.PreserveWhitespace = false; try { doc.LoadXml(input); XmlDsigExcC14NTransform trans = new XmlDsigExcC14NTransform(); trans.LoadInput(doc); String c14NInput = new StreamReader((Stream)trans.GetOutput(typeof(Stream))).ReadToEnd(); return c14NInput; } catch (Exception ex) { MessageBox.Show(ex.ToString()); return String.Empty; } } 

然后,您将获得签名值:

  private string ComputeHMACSHA1_PSHA(string input, string serversecret, string clientsecret) { try { byte[] signedInfoBytes = Encoding.UTF8.GetBytes(input); byte[] binarySecretBytesServer = Convert.FromBase64String(serversecret); byte[] binarySecretBytesClient = Convert.FromBase64String(clientsecret); byte[] key = KeyGenerator.ComputeCombinedKey(binarySecretBytesClient, binarySecretBytesServer, 256); HMACSHA1 hmac = new HMACSHA1(key); hmac.Initialize(); byte[] hmacHash = hmac.ComputeHash(signedInfoBytes); string signatureValue = Convert.ToBase64String(hmacHash); return signatureValue; } catch (Exception ex) { return string.Empty; } } 

它会给你这样的东西。 kykmlowWIW4TXRcCi46OfZPUBKQ =