为什么我的客户端证书不包含在TLS握手中?

时间:2012-10-29 16:08:09

标签: .net vb.net wcf soap wcf-security

几年前我写了一个Windows服务,处理来自多个来源的数据,其中一个是第三方SOAP Web服务。它使用此Web服务使用第三方提供的客户端证书进行身份验证。这在当时运行良好,并且在部署它的原始机器上仍然正常工作,但现在它们正在迁移到新机器,其中连接尝试抛出异常并显示消息:

  

使用客户端身份验证方案禁止HTTP请求   '匿名'

现在我的开发机器上也出现了同样的情况。这是配置与Web服务的连接的代码:

Friend Shared Function GetProperlyConfiguredConnection() As SEMOService.MIWebServiceClient
    ' Ensure that the settings are valid
    ValidateSettings()

    Dim destAddress As New System.ServiceModel.EndpointAddress(Url)
    ' The service uses SOAP 1.1 which is what BasicHttpBinding uses. WSHttpBinding uses SOAP 1.2.
    'Dim destBinding As New System.ServiceModel.WSHttpBinding
    Dim destBinding As New System.ServiceModel.BasicHttpBinding
    With destBinding
        .CloseTimeout = New TimeSpan(0, 1, 0)
        .OpenTimeout = New TimeSpan(0, 1, 0)
        .ReceiveTimeout = New TimeSpan(0, 10, 0)
        .SendTimeout = New TimeSpan(0, 1, 0)
        .BypassProxyOnLocal = False
        .HostNameComparisonMode = ServiceModel.HostNameComparisonMode.StrongWildcard
        .MaxBufferPoolSize = 524288
        .MaxReceivedMessageSize = 2147483647
        .MessageEncoding = ServiceModel.WSMessageEncoding.Text
        .TextEncoding = System.Text.Encoding.UTF8
        .UseDefaultWebProxy = True
        .AllowCookies = False

        With .ReaderQuotas
            .MaxDepth = 32
            .MaxStringContentLength = 2147483647
            .MaxArrayLength = 50000000
            .MaxBytesPerRead = 4096
            .MaxNameTableCharCount = 16384
        End With

        .Security.Mode = ServiceModel.BasicHttpSecurityMode.Transport
        .Security.Transport.ClientCredentialType = ServiceModel.HttpClientCredentialType.Certificate
    End With

    Dim wcfService As New SEMOService.MIWebServiceClient(destBinding, destAddress)

    ' Load the certificate from the specified file.
    Dim keyData As Byte()
    Dim keyFileInfo As New IO.FileInfo(SEMOServiceSettings.KeyFilePath)
    Dim keyFileReader As New IO.BinaryReader(New IO.FileStream(SEMOServiceSettings.KeyFilePath, IO.FileMode.Open, IO.FileAccess.Read))
    keyData = keyFileReader.ReadBytes(keyFileInfo.Length)
    keyFileReader.Close()

    Dim cert As New X509Certificates.X509Certificate2
    cert = New X509Certificates.X509Certificate2(keyData, SEMOServiceSettings.KeyFilePassword)

    wcfService.ClientCredentials.ClientCertificate.Certificate = cert

    Return wcfService
End Function

它实际上不是WCF Web服务,但是当从WSDL自动生成客户端代码时,Visual Studio似乎并不介意。客户端证书是从文件而不是从证书存储中加载的。所有计算机上都使用相同的证书文件,并且在单步执行代码时似乎可以正常加载。

我使用WireShark将使用机器的请求与我的开发机器中的请求进行了比较,并且看起来客户端证书不应该包含在握手中:

No client certificates

这是来自机器上的请求的相应数据包:

Full certificate chain

我不了解WCF,SOAP和加密的内容很多,所以我对于导致这种行为的环境有什么不同,以及需要改变哪些内容有点不知所措它

This question似乎是相关的,但我似乎无法通过代码访问任何RequireClientCertificate属性,并且我没有使用app.config来配置绑定。

可以向ServicePointManager类添加回调,以执行服务器证书的自定义验证。基本客户端类或绑定是否在将其发送到服务器之前执行客户端证书的某些验证?如果是这样,我是否有办法以与服务器证书验证相同的方式干预该流程,以便我可以看到发生了什么?

3 个答案:

答案 0 :(得分:1)

我找到了解决方案,但我不确定我是否完全理解它。问题是客户端证书(或者可能只是链中的一些其他证书)需要出现在发出请求的用户的个人证书存储中。我以Windows服务运行的用户身份登录,将证书导入其商店,一切正常。

这表明客户端证书以某种方式进行验证,即使它们是从文件加载而不是在商店中引用的。这在ProcMon的输出中实际上是显而易见的,如果我一直在更加注意,我会意识到它正在搜索证书存储并提出NOT FOUND结果。

如果Microsoft的WCF客户端代码在证书出现问题时抛出异常,而不仅仅是尝试在没有证书的情况下继续运行,那将是很好的。好吧......

答案 1 :(得分:0)

似乎这可能是他们的新服务器的部署问题。我只会说这是因为“它之前有效”。我会让他们检查配置,特别是应用程序池和应该运行它的应用程序/用户帐户。他们很有可能在没有访问权限的系统或本地服务下安装服务。在许多情况下,我看到了一个部署,他们复制了所有内容,包括服务/用户帐户,但从未创建过帐户!这会给你这个错误,如果服务没有访问机器证书或运行链的权限,它将永远不会请求客户端发送证书!

查看"The HTTP request was forbidden with client authentication scheme 'Anonymous'?

编辑:回答您之前没有提到的其他问题...客户端在发送之前不会对客户端证书进行任何类型的检查,除了它使用证书在消息凭证中签名消息模式。如果您使用的是服务凭证,您的客户端确实会验证该服务,特别是如果您使用的服务证书(甚至是通过IIS的SSL),您的WCF客户端将确保证书在信任之前和商店之前允许任何通信

我从未见过创建自定义客户端服务验证程序的方法,但最好的办法是打开Diagnostic Tracing and Message Logging客户端,然后从那里进行分析。

EDIT2:好的,所以我没有完全读到你的问题。我会假设您的原始服务是作为服务帐户运行的,可能是网络服务(因为这最有意义吗?)。嗯,事实证明,在较新版本的Windows中,一些内置服务帐户已被阻止,以帮助防止安全问题,例如机器人等会做类似于您尝试做的事情。最重要的是,您正在使用代理,根据您的设置,我冒昧地猜测可能需要Windows身份验证才能工作。我建议遵循“正确”的做法并为该服务创建特定的服务用户。我愿意打赌,如果你将服务设置为“本地系统”,它可能会起作用(虽然不确定代理)。

答案 2 :(得分:0)

我在使用第三方Web服务时遇到了同样的问题,该服务需要在Windows 7服务器上的传输级别进行证书身份验证。我们必须在套接字级别设置跟踪以找到根本原因,即我们在自定义应用程序ID下运行的服务使用者无法读取客户端证书上的私钥。我们看到证书被发现但由于无法读取私钥,AcquireCredentailsHandle将失败并显示错误0X8009030D。

套接字跟踪:

System.Net Information: 0 : [5708] SecureChannel#15271547 - Certificate is of type     X509Certificate2 and contains the private key.
ProcessId=6572
ThreadId=1
DateTime=2013-12-18T03:01:54.0189534Z
Timestamp=38085697406996
System.Net Information: 0 : [5708] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent  = Outbound, scc     = System.Net.SecureCredential)
ProcessId=6572
ThreadId=1
DateTime=2013-12-18T03:01:54.0194535Z
Timestamp=38085697408545
System.Net Error: 0 : [5708] AcquireCredentialsHandle() failed with error 0X8009030D.
ProcessId=6572
ThreadId=1
DateTime=2013-12-18T03:01:54.0724641Z
Timestamp=38085698170444

一旦我们授予我们的应用程序ID访问priavte密钥,它就能完成tls握手,我们的证书被发送到服务。在xp机器上使用的Windows 7服务器中,授予对私钥的访问权限要容易得多。只需打开mmc中的证书商店找到您的客户端证书然后右键单击alltask - >管理私钥