如何配置WCF .NET客户端解密双重加密的SOAP响应?
解决方案:解决WCF客户端解密双向认证加密SOAP响应的问题
我之前也碰到过类似的WCF双向证书认证加加密响应的问题,你的异常核心是WCF无法找到解密EncryptedKey所需的证书令牌,咱们一步步拆解解决:
1. 先定位异常根源
你的错误日志明确指出:WCF无法通过响应里的X509IssuerSerialKeyIdentifierClause(对方证书的Issuer和Serial)找到对应的证书,或者你的自定义令牌提供者返回了错误的证书用于解密。
先确认一个关键细节:响应里的两个证书信息有拼写差异,你要格外注意:
- EncryptedKey中的Issuer:
CN=Their Name,O=Their Organisation(完整拼写) - Signature中的Issuer:
CN=Their Name,O=Their Organisatio(少了最后一个n)
确保你加载的对方公钥证书(ServiceEncryptingCert)的Issuer和Serial和EncryptedKey里的完全匹配,拼写差一个字符都会导致匹配失败。
2. 修正自定义令牌提供者的逻辑
你的CreateSecurityTokenProvider方法在处理输入消息(接收响应)时,证书返回逻辑有误。解密EncryptedKey需要用客户端自己的私钥证书(对方用你的公钥加密了会话密钥),而验证签名才需要对方的公钥证书。
修改后的代码如下:
Public Overrides Function CreateSecurityTokenProvider(requirement As SecurityTokenRequirement) As SecurityTokenProvider Dim result As SecurityTokenProvider If requirement.TokenType = System.IdentityModel.Tokens.SecurityTokenTypes.X509Certificate Then Dim direction As MessageDirection Dim noDirection As Boolean = False Try direction = requirement.GetProperty(Of MessageDirection)(ServiceModelSecurityTokenRequirement.MessageDirectionProperty) Catch ex As Exception noDirection = True End Try ' 处理无方向的令牌请求(通常是客户端自身证书) If noDirection Then result = New X509SecurityTokenProvider(Me.credentials.ClientEncryptingCert) Else If direction = MessageDirection.Output Then ' 发送请求时:签名用客户端签名证书,加密用对方公钥证书 If requirement.KeyUsage = IdentityModel.Tokens.SecurityKeyUsage.Signature Then result = New X509SecurityTokenProvider(Me.credentials.ClientSigningCert) Else result = New X509SecurityTokenProvider(Me.credentials.ServiceEncryptingCert) End If Else ' 接收响应时:验证签名用对方公钥证书,解密会话密钥用客户端加密证书(带私钥) If requirement.KeyUsage = IdentityModel.Tokens.SecurityKeyUsage.SignatureVerification Then result = New X509SecurityTokenProvider(Me.credentials.ServiceEncryptingCert) ElseIf requirement.KeyUsage = IdentityModel.Tokens.SecurityKeyUsage.Decryption Then result = New X509SecurityTokenProvider(Me.credentials.ClientEncryptingCert) Else result = MyBase.CreateSecurityTokenProvider(requirement) End If End If End If Else result = MyBase.CreateSecurityTokenProvider(requirement) End If Return result End Function
3. 确保证书被正确加入令牌解析器
仅设置ClientCredentials有时不足以让WCF在令牌解析器中找到所有需要的证书,我们可以自定义ClientCredentials的CreateSecurityTokenResolver方法,强制把所有相关证书加入解析器:
Public Class CustomClientCredentials Inherits ClientCredentials Private ReadOnly _customCreds As YourCredentialClass ' 替换成你的凭证存储类 Public Sub New(creds As YourCredentialClass) MyBase.New() _customCreds = creds End Sub Protected Overrides Function CloneCore() As ClientCredentials Return New CustomClientCredentials(_customCreds) End Function Public Overrides Function CreateSecurityTokenResolver(tokens As SecurityTokenCollection, abortOnFailure As Boolean) As SecurityTokenResolver ' 手动添加所有需要的证书到令牌列表 Dim tokenList As New List(Of SecurityToken) From { New X509SecurityToken(_customCreds.ClientEncryptingCert), New X509SecurityToken(_customCreds.ClientSigningCert), New X509SecurityToken(_customCreds.ServiceEncryptingCert) } ' 合并默认令牌和自定义令牌 tokenList.AddRange(tokens) Return SecurityTokenResolver.CreateDefaultSecurityTokenResolver(tokenList.AsReadOnly(), abortOnFailure) End Function ' 把你之前修改后的CreateSecurityTokenProvider方法放在这里 End Class
然后在客户端配置中替换默认的ClientCredentials:
Dim credentials As New CustomClientCredentials(yourCredentialInstance) ' 传入你的凭证实例 client.ChannelFactory.Endpoint.EndpointBehaviors.Remove(GetType(Description.ClientCredentials)) client.ChannelFactory.Endpoint.EndpointBehaviors.Add(credentials)
4. 调整SecurityBindingElement配置
确保你的安全绑定元素配置和对方的SOAP安全规范匹配:
Dim securityBE As SymmetricSecurityBindingElement = SecurityBindingElement.CreateMutualCertificateBindingElement( MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10) securityBE.MessageProtectionOrder = Security.MessageProtectionOrder.SignBeforeEncryptAndEncryptSignature securityBE.RequireSignatureConfirmation = True ' 响应包含签名,开启确认 securityBE.SetKeyDerivation(False) ' 如果不需要密钥派生,关闭此选项 securityBE.IncludeTimestamp = True ' 响应包含Timestamp,确保开启此配置
5. 验证证书的私钥可用性
最后确认你的客户端证书(ClientEncryptingCert和ClientSigningCert)确实包含可访问的私钥:
Dim clientEncryptCert = GetCert("e3debd49................e9a6cd39c5") Console.WriteLine($"加密证书是否有私钥:{clientEncryptCert.HasPrivateKey}") Console.WriteLine($"私钥是否可访问:{clientEncryptCert.PrivateKey IsNot Nothing}") Dim clientSignCert = GetCert("40091641................d622761b") Console.WriteLine($"签名证书是否有私钥:{clientSignCert.HasPrivateKey}") Console.WriteLine($"私钥是否可访问:{clientSignCert.PrivateKey IsNot Nothing}")
如果私钥不可访问,需要检查证书的存储位置(比如是否在本地机器的Personal存储区)和当前用户的权限。
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

