How to make WCF Client conform to specific WS-Security - sign UsernameToken and SecurityTokenReference
我需要创建一个WCF客户机来调用我无法控制的服务。
我得到了一个WSDL和一个正在运行的SOAPUI项目。
该服务同时使用用户名/密码和X509证书。
更新
我现在明白了问题所在,但是我仍然不确定我需要采取什么步骤来创建所需的消息,因此任何帮助都将非常感谢。
我需要同时签署用户名令牌和SecurityTokenReference。
我必须创建自定义绑定的代码已从此日志中删除,因为它不再使用。我不再向绑定中添加SecurityBindingElement,而是添加一个将安全元素写入头中的新行为。
因此,安全节点是通过对SignedXML类进行子类化、添加签名引用,然后调用ComputeSignature在安全头中创建签名节点来从头开始创建的。
您需要传递XML以登录到SignedXML构造函数,这样才能工作。传入usernametoken没有问题,而且似乎已正确签名。
问题是,SecurityTokenReference仅在调用ComputeSignature()时创建,因此我无法添加对此元素的签名引用,因为它在需要时不存在(在ComputeSignature()之前调用的SignedXML的重写GetIdelement方法中)
我用来创建要插入到安全头中的签名块的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | 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)
我现在花了几天的时间研究和测试这个问题,不幸的是,提供服务的公司没有任何帮助——但我需要使用他们的服务。
完整工作的SOAP消息(SOAPUI项目)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:XXXXX"> <soapenv:Header xmlns:ebxml="http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/"> <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-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#X509v3" wsu:Id="CertId-D05E596B5ABC341FEB13505090224061" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">MIIEnDCCBAWgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBxDELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxHjAcBgNVBAMTFWRldmNhLmIyYi5pcmQuZ292dC5uejEXMBUGA1UEKRMORGV2ZWxvcG1lbnQgQ0ExKDAmBgkqhkiG9w0BCQEWGWNocmlzLnNjaHVsdHpAaXJkLmdvdnQubnowHhcNMTEwOTE1MDIwNjIwWhcNMjEwOTEyMDIwNjIwWjCByTELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxJTAjBgNVBAMTHHNpZ24tYW5kLWVuYy5kZXYuaXJkLmdvdnQubnoxFTATBgNVBCkTDHNpZ24tYW5kLWVuYzEoMCYGCSqGSIb3DQEJARYZY2hyaXMuc2NodWx0ekBpcmQuZ292dC5uejCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAykyZHVnXjsG220zB3kNOsGBeGP2rdNbLlIqW1D8yZO1fcj3/RhRiqsopbUrb8AU1ClpfhbH2K68kg7V91VAY0qrwNxP+pPPo1vYKMU6pT38qJGQzffr+iV2BCJshZvSk9E7QSWO5mFNstdg19xc+5ST1Lgb3fefuRG2KZVxPx0sCAwEAAaOCAZUwggGRMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDQGCWCGSAGG+EIBDQQnFiVFYXN5LVJTQSBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRlMB0GA1UdDgQWBBSczRKXKPe3Sin7eFrVXfI7MXckzzCB+QYDVR0jBIHxMIHugBSLWxPSZd9otEj16vhLyovMCI9OMaGByqSBxzCBxDELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxHjAcBgNVBAMTFWRldmNhLmIyYi5pcmQuZ292dC5uejEXMBUGA1UEKRMORGV2ZWxvcG1lbnQgQ0ExKDAmBgkqhkiG9w0BCQEWGWNocmlzLnNjaHVsdHpAaXJkLmdvdnQubnqCCQDL/qDdlx2j6DATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAS4ZPIVVpgTOGN4XcIC3SiYlxF8wYg7qnUhH5wJsAD3VCKfj68j9FSJtdBWLlWvvRxEoDP2lZ0IbFl6Rjnl+2ibzFnyac2G1AVm5mwPrNKHBQJ9J5eDJi0iUVY7Wphz86tKnqj34GvlHPNXmrF7oGEcDhPwK0T8zRdE/pvKIUiJc=</wsse:BinarySecurityToken> <ds:Signature Id="Signature-2" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <ds:Reference URI="#CertId-D05E596B5ABC341FEB13505090224061"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>3iVAUEAt8vAb7Ku+jf2gwSkSm0Q=</ds:DigestValue> </ds:Reference> <ds:Reference URI="#UsernameToken-1"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>r4HLEAWJldJwmEmcAqV6Y8rnTPE=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue> YGh2I3VcukqjT0O7hKItiykWN5tlID18ZXRCwQjXriHmnVsO4wGcHjWfmhuNDecq+xRN+SjG8E7M 2Rx/5/BbFKbVlNOkQOSbSxIs1YT9GaThK16pMrX5KRkkJme1W3R0pGIIQh6gGRSUf79RZUIYxyVl LqdIe561TXXDdtbt/6Q= </ds:SignatureValue> <ds:KeyInfo Id="KeyId-D05E596B5ABC341FEB13505090224372"> <wsse:SecurityTokenReference wsu:Id="STRId-D05E596B5ABC341FEB13505090224373" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsse:Reference URI="#CertId-D05E596B5ABC341FEB13505090224061" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/></wsse:SecurityTokenReference> </ds:KeyInfo> </ds:Signature> <wsse:UsernameToken wsu:Id="UsernameToken-1" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsse:Username>XXXXXX</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXXXXX</wsse:Password> </wsse:UsernameToken> </wsse:Security> <ebxml:Messaging xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ebxml:UserMessage> <ebxml:MessageInfo> <ebxml:Timestamp>2002-02-02T14:18:02.0Z</ebxml:Timestamp> <ebxml:MessageId>bf9433d9-c6e9-4c12-9c98-724008a09c21</ebxml:MessageId> </ebxml:MessageInfo> <ebxml:PartyInfo> <ebxml:From> <ebxml:PartyId type="identifier">Trading Partner X</ebxml:PartyId> <ebxml:Role>Provider</ebxml:Role> </ebxml:From> <ebxml:To> <ebxml:PartyId type="identifier">XXXXXXX</ebxml:PartyId> <ebxml:Role>Requestor</ebxml:Role> </ebxml:To> </ebxml:PartyInfo> <ebxml:CollaborationInfo> <ebxml:AgreementRef>urn:XXXXXXXXX</ebxml:AgreementRef> <ebxml:Service type="Web Service">urn:XXXXXXXX</ebxml:Service> <ebxml:Action>customerInformation</ebxml:Action> <ebxml:ConversationId>e302426a-b2d9-4ff1-a14b-fbbc2f40a017</ebxml:ConversationId> </ebxml:CollaborationInfo> </ebxml:UserMessage> </ebxml:Messaging> </soapenv:Header> <soapenv:Body> <urn:ConnectionTest> <Message>Bonjour</Message> </urn:ConnectionTest> </soapenv:Body> </soapenv:Envelope> |
我们收到了一些文件。安全部分复制在下面
以下安全性必须按以下顺序应用于每个请求:必须包含wsse:usernametoken并包含:
代理人?密码元素中wsse:password text(明文)值的门户密码签名块:数字签名是使用X509证书创建的。每个软件供应商必须持有并保护[待定]颁发的有效证书和私钥。对于每个服务请求,软件必须:
包括与用于作为wsse:BinarySecurityToken签名的私钥匹配的证书,并将其用作签名的wsse:SecurityTokenReference
更新
最初给出的soapui配置的图像
今天终于解决了这个问题。在术语方面,我需要签名的不是SecurityTokenReference,而是二进制安全令牌。
为了做到这一点,我需要隐藏发起人和接收人的证书,并添加一个签名的支持令牌。
我回到使用配置来创建和签署消息,而不是尝试手动添加签名。
另一个问题是,我的自定义"消息传递"头上有一个不正确的名称空间,所以请注意名称空间,我认为它们不如它们重要。
创建绑定的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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用户名、servicecertificate和clientcertificate,所有这些都按预期工作。
使用代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } |
其中getCustomBinding在我的日志中指定
setclientCredentialsSecurity是设置证书的位置,如下所示
1 2 3 4 5 6 7 8 9 | 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); } |
希望这能更清楚一点
可能是因为你的证书不是公开有效的。试试这个:
1 2 3 4 5 6 7 8 | 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"; //... } |