具有 X.509 证书和 Java Web 服务的 WCF 客户端
WCF Client With X.509 Certificate and Java Web Service
我目前正在尝试开发一个与第 3 方 Web 服务交互的客户端。第三方 Web 服务是用 Java 编写的,我们为他们提供了用于签署消息的 CA X509 证书。第 3 方指定 WS-Security 1.1 (http://docs.oasis-open.org/wss/v1.1/wss-v1.1-spec-os-SOAPMessageSecurity.pdf).
我们能够通过 SoapUI 成功签署消息、发送和获得响应,但我无法在 WCF (.NET 4.5) 中获得相同的功能。我查看了从 WCF 客户端发送的消息(通过 SvcTraceViewer 和消息日志),它的格式似乎与 SoapUI 中的格式略有不同。
当我发送消息时,出现以下异常:
There was no endpoint listening at https://<service addres> that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
内部异常是 Unable to connect to the remote server
,内部异常是 A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond XXX.XXX.XXX.XXX:443
。
由于端点已启动且可用,我认为我的绑定设置不完全正确。
我已经在此处检查了关于 SO 的问题,包括 Signing SOAP messages using X.509 certificate from WCF service to Java webservice and read Yaron Naveh's 12 common wcf interop confusions 有帮助,但还没有完全解决问题。
绑定定义:
<customBinding>
<binding name="myCustomBinding">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="MutualCertificate"
defaultAlgorithmSuite="Basic128Sha256Rsa15"
includeTimestamp="true"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
securityHeaderLayout="LaxTimestampLast" />
<httpsTransport />
</binding>
</customBinding>
创建代理工厂的方法:
private Task<ChannelFactory<T>> CreateChannelFactory<T>(string bindingConfig, string address)
{
EndpointAddress endpoint = new EndpointAddress(new Uri(address), EndpointIdentity.CreateDnsIdentity(<identity name>));
ChannelFactory<T> proxy = new ChannelFactory<T>(new CustomBinding(bindingConfig), endpoint);
proxy.Credentials.ClientCertificate.SetCertificate(storeLocation, storeName, findByType, findByValue);
proxy.Credentials.ServiceCertificate.SetDefaultCertificate(storeLocation, storeName, findByType, findByValue);
return Task.FromResult<ChannelFactory<T>>(proxy);
}
第 3 方实际上有多个服务(基本上每个服务一个操作合同),所以我在客户端开始时使用 async/await
为每个服务生成 ChannelFactory<T>
个对象,并且值 storeLocation
、storeName
、findByType
和 findByValue
证书全局存储在客户端中(以防万一有人想知道)。
当我发送消息时,我从适当的工厂创建了一个频道。然后我得到了错误。
这是通过 SoapUI 发送的消息的相关部分(有效):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:view="http://<service namespace>">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1"
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#X509v3"
wsu:Id="X509-F4F47BCAA968D14D08143033737254925">(data)</wsse:BinarySecurityToken>
<ds:Signature Id="SIG-F4F47BCAA968D14D08143033737254928"
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="soapenv view"
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="#TS-F4F47BCAA968D14D08143033737254624">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="wsse soapenv view"
xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>hg5n7PfuAfYb/LEawatI4ZBK0wmy14+Y6DihGhgBMI4=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>(data)</ds:SignatureValue>
<ds:KeyInfo Id="KI-F4F47BCAA968D14D08143033737254926">
<wsse:SecurityTokenReference wsu:Id="STR-F4F47BCAA968D14D08143033737254927">
<wsse:Reference URI="#X509-F4F47BCAA968D14D08143033737254925"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<wsu:Timestamp wsu:Id="TS-F4F47BCAA968D14D08143033737254624">
<wsu:Created>2015-04-29T19:56:12Z</wsu:Created>
<wsu:Expires>2015-04-29T19:56:17Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<!-- Not relevant. Not signed -->
</soapenv:Body>
</soapenv:Envelope>
这是 WCF 使用指定绑定生成的消息:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo8j1TaWVCpRLgEy0S8UuwAcBAAAAAkQP7Rrb00auQ0G7/7Q0C1x7YNOf+kFOt9ioJVgbfFYACQAA</VsDebuggerCausalityData>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:BinarySecurityToken>
<!-- Removed-->
</o:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
<DigestValue>8KAbTZRA1cC60emKdKIiIm3zvv1jPPfVaia3a9l1c3g=</DigestValue>
</Reference>
<Reference URI="#uuid-5cb02cb6-0ae6-486d-ad2b-f9f9107b4576-2">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
<DigestValue>zR0k1GizuQekuM9WcSzVGssZowuzj3Dza/WGYmMqjSo=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>(data)</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
URI="#uuid-96e7c3a3-cbe9-409e-ad49-9dcc07ef4360-2"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
<u:Timestamp u:Id="uuid-5cb02cb6-0ae6-486d-ad2b-f9f9107b4576-2">
<u:Created>2015-04-30T18:00:28.886Z</u:Created>
<u:Expires>2015-04-30T18:05:28.886Z</u:Expires>
</u:Timestamp>
</o:Security>
</s:Header>
<s:Body u:Id="_1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</s:Body>
</s:Envelope>
以下是 WCF 版本与 SoapUI 版本的 differences/missing 项对比:
<BinarySecurityToken>
缺少 EncodingType
、ValueType
和 Id
属性。
<Signature>
缺少 Id
属性。
- '规范化方法
missing
<Reference>
两个元素而不是 1 个。不确定 URI
属性的用途(在 WCF 版本中)。
<KeyInfo>
缺少 Id
属性。
<SecurityTokenReference>
缺少 Id
属性。
其他一切似乎都匹配(名称空间前缀及其分配位置除外)。我更愿意通过自定义绑定来执行此操作,但如果我需要处理传出请求,我愿意实施 IClientMessageInspector
。
我今天早上解决了这个问题。手头有 2 个问题。
第一个似乎与我公司的 VPN 有关。我昨天能够在办公室发送和接收回复(而不是我发布问题的那天,当时我使用的是 VPN)。昨晚再次在 VPN 上我再次遇到 There was no endpoint listening at https://<service addres> that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
异常,所以我将客户端部署到我们的一个开发服务器并且能够再次成功通信。
第二个问题是在我能够发送消息后发现的。我在跟踪消息日志中看到了响应,但我收到了一个新的异常:
Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.
请求已签名(由客户端),但响应未签名。将自定义绑定的 enableUnsecuredResponse
属性设置为 true
解决了这个问题。
最终的自定义绑定配置如下所示:
<customBinding>
<textMessageEncoding messageVersion="1.1" />
<security authenticationMode="MutualCertificate"
defaultAlgorithmSuite="Basic128Sha256Rsa15"
enableUnsecuredResponse="true"
includeTimestamp="true"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
securityHeaderLayout="LaxTimestampLast" />
<httpsTransport />
</customBinding>
设置证书的代码没有变化,除了使传递给 EndpointIdentity.CreateDnsIdentity
的名称可配置。
我目前正在尝试开发一个与第 3 方 Web 服务交互的客户端。第三方 Web 服务是用 Java 编写的,我们为他们提供了用于签署消息的 CA X509 证书。第 3 方指定 WS-Security 1.1 (http://docs.oasis-open.org/wss/v1.1/wss-v1.1-spec-os-SOAPMessageSecurity.pdf).
我们能够通过 SoapUI 成功签署消息、发送和获得响应,但我无法在 WCF (.NET 4.5) 中获得相同的功能。我查看了从 WCF 客户端发送的消息(通过 SvcTraceViewer 和消息日志),它的格式似乎与 SoapUI 中的格式略有不同。
当我发送消息时,出现以下异常:
There was no endpoint listening at https://<service addres> that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
内部异常是 Unable to connect to the remote server
,内部异常是 A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond XXX.XXX.XXX.XXX:443
。
由于端点已启动且可用,我认为我的绑定设置不完全正确。
我已经在此处检查了关于 SO 的问题,包括 Signing SOAP messages using X.509 certificate from WCF service to Java webservice and read Yaron Naveh's 12 common wcf interop confusions 有帮助,但还没有完全解决问题。
绑定定义:
<customBinding>
<binding name="myCustomBinding">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="MutualCertificate"
defaultAlgorithmSuite="Basic128Sha256Rsa15"
includeTimestamp="true"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
securityHeaderLayout="LaxTimestampLast" />
<httpsTransport />
</binding>
</customBinding>
创建代理工厂的方法:
private Task<ChannelFactory<T>> CreateChannelFactory<T>(string bindingConfig, string address)
{
EndpointAddress endpoint = new EndpointAddress(new Uri(address), EndpointIdentity.CreateDnsIdentity(<identity name>));
ChannelFactory<T> proxy = new ChannelFactory<T>(new CustomBinding(bindingConfig), endpoint);
proxy.Credentials.ClientCertificate.SetCertificate(storeLocation, storeName, findByType, findByValue);
proxy.Credentials.ServiceCertificate.SetDefaultCertificate(storeLocation, storeName, findByType, findByValue);
return Task.FromResult<ChannelFactory<T>>(proxy);
}
第 3 方实际上有多个服务(基本上每个服务一个操作合同),所以我在客户端开始时使用 async/await
为每个服务生成 ChannelFactory<T>
个对象,并且值 storeLocation
、storeName
、findByType
和 findByValue
证书全局存储在客户端中(以防万一有人想知道)。
当我发送消息时,我从适当的工厂创建了一个频道。然后我得到了错误。
这是通过 SoapUI 发送的消息的相关部分(有效):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:view="http://<service namespace>">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1"
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#X509v3"
wsu:Id="X509-F4F47BCAA968D14D08143033737254925">(data)</wsse:BinarySecurityToken>
<ds:Signature Id="SIG-F4F47BCAA968D14D08143033737254928"
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="soapenv view"
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="#TS-F4F47BCAA968D14D08143033737254624">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="wsse soapenv view"
xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>hg5n7PfuAfYb/LEawatI4ZBK0wmy14+Y6DihGhgBMI4=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>(data)</ds:SignatureValue>
<ds:KeyInfo Id="KI-F4F47BCAA968D14D08143033737254926">
<wsse:SecurityTokenReference wsu:Id="STR-F4F47BCAA968D14D08143033737254927">
<wsse:Reference URI="#X509-F4F47BCAA968D14D08143033737254925"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<wsu:Timestamp wsu:Id="TS-F4F47BCAA968D14D08143033737254624">
<wsu:Created>2015-04-29T19:56:12Z</wsu:Created>
<wsu:Expires>2015-04-29T19:56:17Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<!-- Not relevant. Not signed -->
</soapenv:Body>
</soapenv:Envelope>
这是 WCF 使用指定绑定生成的消息:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo8j1TaWVCpRLgEy0S8UuwAcBAAAAAkQP7Rrb00auQ0G7/7Q0C1x7YNOf+kFOt9ioJVgbfFYACQAA</VsDebuggerCausalityData>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:BinarySecurityToken>
<!-- Removed-->
</o:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
<DigestValue>8KAbTZRA1cC60emKdKIiIm3zvv1jPPfVaia3a9l1c3g=</DigestValue>
</Reference>
<Reference URI="#uuid-5cb02cb6-0ae6-486d-ad2b-f9f9107b4576-2">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
<DigestValue>zR0k1GizuQekuM9WcSzVGssZowuzj3Dza/WGYmMqjSo=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>(data)</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
URI="#uuid-96e7c3a3-cbe9-409e-ad49-9dcc07ef4360-2"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
<u:Timestamp u:Id="uuid-5cb02cb6-0ae6-486d-ad2b-f9f9107b4576-2">
<u:Created>2015-04-30T18:00:28.886Z</u:Created>
<u:Expires>2015-04-30T18:05:28.886Z</u:Expires>
</u:Timestamp>
</o:Security>
</s:Header>
<s:Body u:Id="_1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</s:Body>
</s:Envelope>
以下是 WCF 版本与 SoapUI 版本的 differences/missing 项对比:
<BinarySecurityToken>
缺少EncodingType
、ValueType
和Id
属性。<Signature>
缺少Id
属性。- '规范化方法
missing
<Reference>
两个元素而不是 1 个。不确定URI
属性的用途(在 WCF 版本中)。<KeyInfo>
缺少Id
属性。<SecurityTokenReference>
缺少Id
属性。
其他一切似乎都匹配(名称空间前缀及其分配位置除外)。我更愿意通过自定义绑定来执行此操作,但如果我需要处理传出请求,我愿意实施 IClientMessageInspector
。
我今天早上解决了这个问题。手头有 2 个问题。
第一个似乎与我公司的 VPN 有关。我昨天能够在办公室发送和接收回复(而不是我发布问题的那天,当时我使用的是 VPN)。昨晚再次在 VPN 上我再次遇到 There was no endpoint listening at https://<service addres> that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
异常,所以我将客户端部署到我们的一个开发服务器并且能够再次成功通信。
第二个问题是在我能够发送消息后发现的。我在跟踪消息日志中看到了响应,但我收到了一个新的异常:
Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.
请求已签名(由客户端),但响应未签名。将自定义绑定的 enableUnsecuredResponse
属性设置为 true
解决了这个问题。
最终的自定义绑定配置如下所示:
<customBinding>
<textMessageEncoding messageVersion="1.1" />
<security authenticationMode="MutualCertificate"
defaultAlgorithmSuite="Basic128Sha256Rsa15"
enableUnsecuredResponse="true"
includeTimestamp="true"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
securityHeaderLayout="LaxTimestampLast" />
<httpsTransport />
</customBinding>
设置证书的代码没有变化,除了使传递给 EndpointIdentity.CreateDnsIdentity
的名称可配置。