ADFS 令牌加密证书链验证失败

ADFS token encryption certificate chain validation fails

我有 ASP.NET MVC 网站,我将其配置为通过 Active Directory 联合身份验证服务进行身份验证。一切正常,直到我尝试启用令牌加密。像往常一样,我在 IIS 上又创建了一个自签名证书,将其添加到我的 Web 服务器和 ADFS 服务器和 运行 应用程序上的受信任的根权限,以验证其工作原理。

我的应用程序正确地将我重定向到 ADFS 服务页面以输入凭据。但是当我提交我的登录名和密码时,我立即在同一个登录页面上收到“An error occured”消息,其中没有非常有用的详细信息部分:

Activity ID: 00000000-0000-0000-b039-0080010000e4
Relying party: [My relying party name]
Error time: Fri, 21 Oct 2016 18:48:24 GMT
Cookie: enabled
User agent string: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36

此后我没有被重定向到我的网站,网络面板不包含任何请求。

但我发现,如果我将以下设置添加到我网站的 web.config,它又开始工作了:

<certificateValidation certificateValidationMode="None" />

所以错误一定与我的证书是自签名的有关。但我已将它添加到 Web 服务器和 ADFS 服务器(以及其他一些 "suspicious" 证书)上的受信任的根权限。

有人知道可能遗漏了什么吗?我可以做些什么来使我的测试环境在验证证书链时使用自签名证书?

我用 api 处理程序做了类似的事情,它充当传递并且必须询问证书。

可能有助于您进行故障排除的内容。

将证书验证回调设置为如下内容:

// validate server cert
ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertificate;

然后在验证方法中你可以查询链:

private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            // default validation bool to false
            var isValid = false;

            // If the certificate is a valid, signed certificate, return true to short circuit any add'l processing.
            if (sslPolicyErrors == SslPolicyErrors.None)
            {
                return true;
            }
            else
            {
                // cast cert as v2 in order to expose thumbprint prop
                var requestCertificate = (X509Certificate2)certificate;

                // init string builder for creating a long log entry
                var logEntry = new StringBuilder();

                // capture initial info for the log entry
                logEntry.AppendFormat("Certificate Validation Error - SSL Policy Error: {0} - Cert Issuer: {1} - SubjectName: {2}",
                   sslPolicyErrors.ToString(),
                   requestCertificate.Issuer,
                   requestCertificate.SubjectName.Name);

                //init special builder for thumprint mismatches
                var thumbprintMismatches = new StringBuilder();

                // load valid certificate thumbs for comparison later
                var validThumbprints = new string[] { "thumbprint A", "thumbprint N" };

                // else if a cert name mismatch then assume api pass thru issue and verify thumb print
                if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateNameMismatch) 
                {
                    // compare thumbprints
                    var hasMatch = validThumbprints.Contains(requestCertificate.Thumbprint, StringComparer.OrdinalIgnoreCase);

                    // if match found then we're valid so clear builder and set global valid bool to true
                    if (hasMatch)
                    {
                        thumbprintMismatches.Clear();
                        isValid = true;
                    }
                    // else thumbprint did not match so append to the builder
                    else
                    {
                        thumbprintMismatches.AppendFormat("|Thumbprint mismatch - Issuer: {0} - SubjectName: {1} - Thumbprint: {2}",
                             requestCertificate.Issuer,
                             requestCertificate.SubjectName.Name,
                             requestCertificate.Thumbprint);
                    }
                }
                // else if chain issue, then iterate over the chain and attempt find a matching thumbprint
                else if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors) //Root CA problem
                {
                    // check chain status and log
                    if (chain != null && chain.ChainStatus != null)
                    {
                        // check errors in chain and add to log entry
                        foreach (var chainStatus in chain.ChainStatus)
                        {
                            logEntry.AppendFormat("|Chain Status: {0} - {1}", chainStatus.Status.ToString(), chainStatus.StatusInformation.Trim());
                        }

                        // check for thumbprint mismatches
                        foreach (var chainElement in chain.ChainElements)
                        {
                            // compare thumbprints
                            var hasMatch = validThumbprints.Contains(chainElement.Certificate.Thumbprint, StringComparer.OrdinalIgnoreCase);

                            // if match found then we're valid so break, clear builder and set global valid bool to true
                            if (hasMatch)
                            {
                                thumbprintMismatches.Clear();
                                isValid = true;
                                break;
                            }
                            // else thumbprint did not match so append to the builder
                            else
                            {
                                thumbprintMismatches.AppendFormat("|Thumbprint mismatch - Issuer: {0} - SubjectName: {1} - Thumbprint: {2}",
                                     chainElement.Certificate.Issuer,
                                     chainElement.Certificate.SubjectName.Name,
                                     chainElement.Certificate.Thumbprint);
                            }
                        }
                    }
                }

                // if still invalid and thumbprint builder has items, then continue 
                if (!isValid && thumbprintMismatches != null && thumbprintMismatches.Length > 0)
                {
                    // append thumbprint entries to the logentry as well
                    logEntry.Append(thumbprintMismatches.ToString());
                }

                // log as WARN here and not ERROR - if method ends up returning false then it will bubble up and get logged as an ERROR
                LogHelper.Instance.Warning((int)ErrorCode.CertificateValidation, logEntry.ToString().Trim());
            }

            // determine env
            var isDev = EnvironmentHelper.IsDevelopment();
            var isTest = EnvironmentHelper.IsTest();

            // if env is dev or test then ignore cert errors and return true (reference any log entries created from logic above for troubleshooting)
            if (isDev || isTest)
                isValid = true;

            return isValid;
        }

注意:您需要disable/change一些自定义代码——指纹、日志等

将证书添加到您的 CA 受信任存储仅意味着您信任证书的颁发者,在这种情况下是证书本身,因为它是自签名证书。缺少的是证书验证执行链检查和吊销检查,并且这两项检查中的任何一项对您来说都失败了。请注意,即使您信任证书,它仍然可能最近被吊销,因此不应再被信任。因此,撤销检查总是必要的。对于测试,禁用吊销检查是一种方法。在 ADFS 方面,您可以禁用每个依赖方的吊销检查。如果检查发生在您自己的代码上,您可以完全禁用检查或使用 Stinky Towel 的代码有选择地仅允许某些证书。

似乎要解决错误,只需在我的 Web 服务器上将 ADFS 令牌签名证书添加为受信任的根证书颁发机构。

PS: 我不确定为什么禁用加密后令牌签名证书链验证没有引发错误以及它与加密有什么关系全部,但事实是它对我们用于测试的两种环境都有帮助。