如何使WCF客户端符合特定的WS-Security – 签署UsernameToken和SecurityTokenReference

我需要创建一个wcf客户端来调用我无法控制的服务。

我得到了一个wsdl和一个有效的soapui项目。

该服务使用用户名/密码和x509证书。


UPDATE

我现在明白问题是什么,但我仍然不确定我需要采取哪些步骤才能创建所需的消息,所以任何帮助都将非常感激。

我需要同时签署UsernameToken和SecurityTokenReference。

由于不再使用,我必须从此post中删除我必须创建自定义绑定的代码。 我不再向绑定添加SecurityBindingElement,而是添加一个将安全元素写入标头的新行为。

因此,通过inheritanceSignedXml类,添加签名引用然后调用ComputeSignature以在Security头中创建Signature节点,从头开始创建安全节点。

您需要传递xml以登录SignedXml构造函数才能使其生效。 传递UsernameToken是没问题的,这似乎是正确签名的。

问题是SecurityTokenReference仅在调用ComputeSignature()时创建,因此我无法向此元素添加签名引用,因为它在需要时不存在(在SignedXml的重写的GetIdElement方法中)在ComputeSignature()之前调用的)


我用来创建要插入Security头的签名块的代码如下

string certificatePath = System.Windows.Forms.Application.StartupPath + "\\" + "Certs\\sign-and- enc.p12"; XmlDocument xd = new XmlDocument(); xd.LoadXml(xml); // Set Certificate System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new X509Certificate2(certificatePath, "password"); MySignedXml signedXml = new MySignedXml(xd); signedXml.SigningKey = cert.PrivateKey; // Create a new KeyInfo object. KeyInfo keyInfo = new KeyInfo(); keyInfo.Id = ""; MemoryStream keyInfoStream = new MemoryStream(); XmlWriter keyInfoWriter = XmlWriter.Create(keyInfoStream); WSSecurityTokenSerializer.DefaultInstance.WriteKeyIdentifierClause(keyInfoWriter, new LocalIdKeyIdentifierClause("token_reference", typeof(X509SecurityToken))); keyInfoWriter.Flush(); keyInfoStream.Position = 0; XmlDocument keyInfoDocument = new XmlDocument(); keyInfoDocument.Load(keyInfoStream); XmlAttribute attrib = keyInfoDocument.CreateAttribute("ValueType"); attrib.InnerText = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"; keyInfoDocument.ChildNodes[1].ChildNodes[0].Attributes.Append(attrib); KeyInfoNode keyInfoNode = new KeyInfoNode(); keyInfoNode.LoadXml(keyInfoDocument.DocumentElement); keyInfo.AddClause(keyInfoNode); // Add the KeyInfo object to the SignedXml object. signedXml.KeyInfo = keyInfo; // Need to use External Canonicalization method. signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#"; // Create a reference to be signed. Reference reference = new Reference(); reference.Uri = "#UsernameToken-1"; XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); env.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; reference.AddTransform(env); reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1"; signedXml.AddReference(reference); Reference reference2 = new Reference(); reference2.Uri = "#token_reference"; XmlDsigEnvelopedSignatureTransform env2 = new XmlDsigEnvelopedSignatureTransform(); env2.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; reference2.AddTransform(env2); reference2.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1"; signedXml.AddReference(reference2); // Add the Signature Id signedXml.Signature.Id = "MYSIG_ID"; // Compute the signature. signedXml.ComputeSignature(); XmlElement xmlDigitalSignature = signedXml.GetXml(); 

其中xml变量是UsernameToken xml字符串,MySignedXml类是一个子类化的SignedXml,其中覆盖了GetIdElement方法(试图找到并正确地重新排列不存在的SecurityTokenReference)

我现在花了几天时间研究和测试,不幸的是,提供服务的公司没有任何帮助 – 但我需要使用他们的服务。

完整的肥皂消息(soapUI项目)

    MIIEnDCCBAWgAwIBAgIBAjANBgkqhkiG/RhRiqsopbUrb8AU1ClpfhbH2K68kg7V91VAY0qrwNxP+pPPo1vYKMU6pT38qJGQzffr+iV2BCJshZvSk9E7QSWO5mFNstdg19xc+5ST1Lgb3fefuRG2KZVxPx0sCAwEAAaOCAZUwggGRMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDQGCWCGSAGG+EIBDQQnFiVFYXN5LVJTQSBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRlMB0GA1UdDgQWBBSczRKXKPe3Sin7eFrVXfI7MXckzzCB+QYDVR0jBIHxMIHugBSLWxPSZd9otEj16vhLyovMCI9OMaGByqSBxzCBxDELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxHjAcBgNVBAMTFWRldmNhLmIyYi5pcmQuZ292dC5uejEXMBUGA1UEKRMORGV2ZWxvcG1lbnQgQ0ExKDAmBgkqhkiG9w0BCQEWGWNocmlzLnNjaHVsdHpAaXJkLmdvdnQubnqCCQDL/qDdlx2j6DATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAS4ZPIVVpgTOGN4XcIC3SiYlxF8wYg7qnUhH5wJsAD3VCKfj68j9FSJtdBWLlWvvRxEoDP2lZ0IbFl6Rjnl+2ibzFnyac2G1AVm5mwPrNKHBQJ9J5eDJi0iUVY7Wphz86tKnqj34GvlHPNXmrF7oGEcDhPwK0T8zRdE/pvKIUiJc=          3iVAUEAt8vAb7Ku+jf2gwSkSm0Q=       r4HLEAWJldJwmEmcAqV6Y8rnTPE=    YGh2I3VcukqjT0O7hKItiykWN5tlID18ZXRCwQjXriHmnVsO4wGcHjWfmhuNDecq+xRN+SjG8E7M 2Rx/5/BbFKbVlNOkQOSbSxIs1YT9GaThK16pMrX5KRkkJme1W3R0pGIIQh6gGRSUf79RZUIYxyVl LqdIe561TXXDdtbt/6Q=        XXXXXX XXXXXXX      2002-02-02T14:18:02.0Z bf9433d9-c6e9-4c12-9c98-724008a09c21    Trading Partner X Provider   XXXXXXX Requestor    urn:XXXXXXXXX urn:XXXXXXXX customerInformation e302426a-b2d9-4ff1-a14b-fbbc2f40a017       Bonjour    

我们得到了一些文件。 安全部分复制如下

必须按以下顺序对每个请求应用以下安全性:必须包含wsse:UsernameToken并包含:

  1. 代理程序的门户网站用户名为wsse:Username元素的值
  2. 密码元素中的wsse:PasswordText(明文)值的代理的门户密码签名块:数字签名是使用x509证书创建的。 每个软件提供商都必须持有并保护由[待定]发布的有效证书和私钥。 对于每个服务请求,软件必须:

  3. 将与用于签名的私钥匹配的证书包含为wsse:BinarySecurityToken,并将其用作签名的wsse:SecurityTokenReference

  4. 签署wsse:BinarySecurityToken
  5. 签署wsse:UsernameToken
  6. 使用规范化方法算法: http : //www.w3.org/2001/10/xml-exc-c14n#
  7. 使用签名方法算法: http : //www.w3.org/2000/09/xmldsig#rsa-sha1
  8. 使用摘要方法算法: http : //www.w3.org/2000/09/xmldsig#sha1

更新

我最初给出的SoapUI配置的图像 SoapUIConfig

最后整理了今天的问题。 在术语方面,我需要签署的不是SecurityTokenReference,而是Binary Security Token。

为此,我需要隐藏Initiator和Recipient的证书,并添加签名的支持令牌。

我回去使用配置来创建和签名消息,而不是尝试手动添加签名。

其他可以解决这个问题的问题是我的自定义’Messaging’标题上有一个不正确的命名空间,所以要注意命名空间,我不认为它们会像它们那样重要。

下面是创建绑定的代码

  private System.ServiceModel.Channels.Binding GetCustomBinding() { System.ServiceModel.Channels.AsymmetricSecurityBindingElement asbe = new AsymmetricSecurityBindingElement(); asbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12; asbe.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never }; asbe.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never }; asbe.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt; asbe.SecurityHeaderLayout = SecurityHeaderLayout.Strict; asbe.EnableUnsecuredResponse = true; asbe.IncludeTimestamp = false; asbe.SetKeyDerivation(false); asbe.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128Rsa15; asbe.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters()); asbe.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters()); CustomBinding myBinding = new CustomBinding(); myBinding.Elements.Add(asbe); myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8)); HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement(); httpsBindingElement.RequireClientCertificate = true; myBinding.Elements.Add(httpsBindingElement); return myBinding; } 

使用绑定时,我设置ClientCredentials UserName,ServiceCertificate和ClientCertificate,并且所有工作都按预期工作。


使用代码如下

 using (CredentialingService.SOAPPortTypeClient client = GetCredentialingClient()) { client.Open(); etc.... } private static CredentialingService.SOAPPortTypeClient GetCredentialingClient() { CredentialingService.SOAPPortTypeClient client = new CredentialingService.SOAPPortTypeClient(GetCustomBinding(), new EndpointAddress(new Uri(Settings.AppSettings.B2BUrl), new DnsEndpointIdentity(Settings.AppSettings.B2BDNSEndpoint), new AddressHeaderCollection())); client.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None; SetClientCredentialsSecurity(client.ClientCredentials); return client; } 

在我的post中指定了GetCustomBinding

SetClientCredentialsSecurity是设置证书的位置,如下所示

 private static void SetClientCredentialsSecurity(ClientCredentials clientCredentials) { clientCredentials.UserName.UserName = Settings.AppSettings.B2BUserName; clientCredentials.UserName.Password = Settings.AppSettings.B2BPassword; string directoryName = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); clientCredentials.ServiceCertificate.DefaultCertificate = new X509Certificate2(Path.Combine(directoryName, Settings.AppSettings.B2BServerCertificateName)); clientCredentials.ClientCertificate.Certificate = new X509Certificate2(Path.Combine(directoryName, Settings.AppSettings.B2BClientCertificateName), Settings.AppSettings.B2BClientCertificatePassword); } 

希望这会让它更清晰一些

也许是因为您的证书不是公开有效的。 试试这个:

 System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(delegate { return true; }); //... using (var client = new SrvClient()) { client.ClientCredentials.UserName.UserName = "usr"; client.ClientCredentials.UserName.Password = "psw"; //... }