【问题标题】:Could not establish secure channel for SSL/TLS with authority 'site.com'无法为具有权限“site.com”的 SSL/TLS 建立安全通道
【发布时间】:2018-04-21 08:07:09
【问题描述】:

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

问题

在 web.config 中声明一个 webservice 客户端端点,如下所示:

<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 中运行它时,我收到了错误消息

无法为具有权限的 SSL/TLS 建立安全通道 '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):

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

【问题讨论】:

    标签: asp.net wcf ssl webservice-client bankid


    【解决方案1】:

    在事件日志中启用 CAPI2 日志可能会给您答案,为什么 Could not create SSL/TLS secure channel. CAPI2 日志默认被禁用。当您启用它时,请尝试再次发出请求。应该有一些错误事件将包含有关原因的有用信息。

    我还会检查(并且可能会更改)一些事情:

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

    【讨论】:

    • 如何使用 certlm.msc 向应用程序池伪用户添加私钥权限?我现在使用 icacls.exe 解决了这个问题,这是我必须下载的工具。如果我可以使用 certlm.msc 来代替,我可以简化未来应用程序的工作流程。 github.com/EricHerlitz/Mobile-BankId-.NET-Example/issues/5
    • 右击certlm.msc工具中的证书,然后All tasks-&gt;Manage Private Keys...。当您授予文件权限时,它将打开一个窗口。将`IIS APPPOOL\name_of_the_apppool_your_app_runs_under`作为用户并点击OK。
    猜你喜欢
    • 2011-05-26
    • 2014-11-26
    • 2019-12-05
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多