无法为具有权限的SSL / TLS建立安全通道' site.com'

时间:2017-11-08 15:06:30

标签: 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>

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

成功

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

失败

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

  

无法为具有权限的SSL / TLS建立安全通道   &#39; site.com&#39;

问题解决方法

我尝试创建一个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可以清楚地看到证书,尽管它们存储在&#34;受信任的根证书颁发机构&#34;中。可以使用商店检索过期日期的唯一方法。

1 个答案:

答案 0 :(得分:2)

启用CAPI2登录事件日志可能会为您提供答案,为什么默认情况下Could not create SSL/TLS secure channel. CAPI2日志已被禁用。启用它后,尝试再次发出请求。应该有一些错误事件,其中包含有关原因的有用信息。

CAPI2 event log

我也会检查(也许会改变)一些事情:

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