3

几年前,我编写了一个 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 的机器发出的请求与我的开发机器发出的请求进行了比较,似乎客户端证书没有包含在握手中,而应该是:

没有客户证书

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

完整的证书链

有很多关于 WCF、SOAP 和密码学的内容我不了解,所以对于导致这种行为的环境可能有什么不同,以及需要改变什么来纠正它,我有点茫然。

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

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

4

3 回答 3

1

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

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

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

于 2012-11-02T15:45:33.443 回答
0

我在使用需要在 Windows 7 服务器上的传输级别进行证书身份验证的第三方 Web 服务时遇到了同样的问题。我们必须在套接字级别设置跟踪以找到根本原因,即在自定义应用程序 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 访问私有密钥的权限,它就能够完成 tls 握手并将我们的证书发送到服务。在 Windows 7 服务器中授予对私钥的访问权限比以前在 xp 机器上要容易得多。只需在 mmc 中打开证书存储,找到您的客户端证书,然后右键单击 alltask-->manage private keys

于 2013-12-18T14:10:51.370 回答
0

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

查看“客户端身份验证方案‘匿名’禁止 HTTP 请求?

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

我从未见过任何创建自定义客户端服务验证器的方法,但是最好的办法是打开客户端诊断跟踪和消息日志记录,然后从那里进行分析。

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

于 2012-10-31T13:36:56.270 回答