asp.net - 无法使用权​​限 'site.com' 为 SSL/TLS 建立安全通道

标签 asp.net wcf ssl webservice-client bankid

免责声明:像这样标题的问题很常见,但没有答案为我提供解决方案,所以无论如何我都需要问它(使用一组新参数)。

问题

Web 服务客户端端点在 web.config 中声明如下:

<behaviors>
  <endpointBehaviors>
    <behavior name="bankid">
      <clientCredentials>
        <clientCertificate findValue="FP Testcert 2"
          storeLocation="LocalMachine"
          storeName="Root"
          x509FindType="FindBySubjectName"/>
        <serviceCertificate>
          <defaultCertificate findValue="Test BankID SSL Root CA v1 Test"
            storeLocation="LocalMachine"
            storeName="Root"
            x509FindType="FindBySubjectName"/>
        <authentication certificateValidationMode="None"
          revocationMode="NoCheck"
          trustedStoreLocation="LocalMachine"/>
        </serviceCertificate>
      </clientCredentials>
    </behavior>
  </endpointBehaviors>
</behaviors>

证书(客户端和服务器证书)是使用“管理计算机证书”应用程序安装的。它们分别存储在 .cer 文件(服务器证书)和 .pfx 文件(客户端证书)中。它们都存储在“受信任的根证书颁发机构”中。

成功

使用 Visual Studio 调试网络服务器 (IIS Express) 运行客户端成功

失败

但是,当我尝试在 IIS 中运行它时,出现错误消息

Could not establish secure channel for SSL/TLS with authority 'site.com'

问题解决方法

我尝试创建一个 Web API 函数,让我知道服务器是否找到了有问题的证书。确实如此。代码看起来像

[HttpGet]
[Route("Debug/certs")]
public CertsOutput certs()
{
    var certStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);

    certStore.Open(OpenFlags.ReadOnly);

    var config = System.Web.Configuration.WebConfigurationManager
      .OpenWebConfiguration("~");
    var group = ServiceModelSectionGroup.GetSectionGroup(config);
    var endPointBehaviors = group.Behaviors.EndpointBehaviors;
    var endpointBehavior = endPointBehaviors[0];

    var ClientCredential = (ClientCredentialsElement) endpointBehavior[0];

    var clientCert = ClientCredential.ClientCertificate;
    var serverCert = ClientCredential.ServiceCertificate.DefaultCertificate;

    var result = new CertsOutput
    {
        clientCert = new CertsOutput.Cert
        {
            FindValue = clientCert.FindValue,
            StoreName = clientCert.StoreName.ToString(),
            StoreLocation = clientCert.StoreLocation.ToString(),
            FindType = clientCert.X509FindType.ToString()
        },

        serverCert = new CertsOutput.Cert
        {
            FindValue = serverCert.FindValue,
            StoreName = serverCert.StoreName.ToString(),
            StoreLocation = serverCert.StoreLocation.ToString(),
            FindType = serverCert.X509FindType.ToString()
        }
    };

    return result;
}

public class CertsOutput
{
    public Cert clientCert { get; set; }
    public Cert serverCert { get; set; }

    public class Cert
    {
        public string FindValue { get; set; }
        public string StoreName { get; set; }
        public string StoreLocation { get; set; }
        public string FindType { get; set; }

        public string Expiration => Certificate?.GetExpirationDateString() 
           ?? "Cant find cert";

        X509Certificate _certificate = null;
        private X509Certificate Certificate
        {
            get
            {
                if (_certificate != null)
                    return _certificate;

                StoreName storeNameEnum;
                switch(StoreName)
                {
                    case "My":
                        storeNameEnum = System_StoreName.My;
                        break;
                    case "Root":
                        storeNameEnum = System_StoreName.Root;
                        break;
                    default:
                        throw new Exception("Unknown store name: " + StoreName);
                }

                StoreLocation storeLocationEnum;
                switch(StoreLocation)
                {
                    case "LocalMachine":
                        storeLocationEnum = System_StoreLocation.LocalMachine;
                        break;
                    case "CurrentUser":
                        storeLocationEnum = System_StoreLocation.CurrentUser;
                        break;
                    default:
                        throw new Exception("Unknown store location: " + StoreLocation);
                }

                var certStore = new X509Store(storeNameEnum, storeLocationEnum);

                certStore.Open(OpenFlags.ReadOnly);

                var certCollection = certStore.Certificates.Find
                    (X509FindType.FindBySubjectName, FindValue, validOnly:false);

                certStore.Close();

                var result = certCollection[0];
                _certificate = result;

                return result;
            }
        }
    }
}

即使我在 IIS 上运行它,我也会得到这样的输出(在 chrome 中使用 console.log):

output revealing that the IIS actually can use the certificates

因此 IIS 可以清楚地看到证书,尽管它们存储在“受信任的根证书颁发机构”中。检索过期日期的唯一方法是使用商店。

最佳答案

在事件日志中启用 CAPI2 日志可能会告诉您为什么它无法创建 SSL/TLS 安全通道。 CAPI2 日志默认情况下处于禁用状态。当您启用它时,请尝试再次发出请求。应该有一些错误事件,其中包含有关原因的有用信息。

CAPI2 event log

我还会检查(并可能更改)一些内容:

  • 在 IE 中打开 WCF 端点并检查该站点是否受 IS 信任。如果不是找出原因。这是你应该做的第一件事。
  • 客户端证书 (pfx) 应放在 LocalMachine/My(个人)存储中。根 CA 证书应放在受信任的根证书颁发机构存储中(你没看错),中间 CA 证书应放在中间证书颁发机构存储中
  • 应将私钥的权利授予运行 WCF 客户端的 IIS 应用程序池。可以使用 certlm.msc 工具来完成。
  • 检查私钥是否可用于 web api 方法。所以检查 PrivateKey 属性,用它签署一些 hello world 数据等。

关于asp.net - 无法使用权​​限 'site.com' 为 SSL/TLS 建立安全通道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47183063/

相关文章:

Jquery 自动完成在 Enter 按键时触发

c# - 为什么我不能在没有 runat=server 的情况下绑定(bind)可见的控件?

wordpress - 将 SSL 添加到我的 wordpress 网站后出现找不到页面错误

Java:HttpsUrlConnection - PKIX 路径构建失败

c# - 有什么方法可以让小网页在 iPhone 上显示为全尺寸?

asp.net - 垂直滚动时卡住 gridview 寻呼机

wcf - 我可以在具有相同命名空间的 IIS 上部署 WCF 服务的多个实例吗?

asp.net - iOS 和 ADFS 保护的网站

c# - JSON API 的.Net 选项?

带有 TLS 的 PHP 用于安全 LDAP