【问题标题】:Certificate Pinning in Windows 10 Universal appWindows 10 通用应用程序中的证书固定
【发布时间】:2018-10-09 23:00:21
【问题描述】:

我需要一种在 Windows 10 通用应用程序中实现证书或公钥固定的好方法。所有代码都在 C# 中,所有连接都是 HTTPS,所以 Windows.Web.Http.HttpClient 类的东西会很棒。是否有一个简单的类/库,或者至少是一个分步指南,用于如何实现这些东西,这些东西可以由不知道 X.509 证书的神秘细节的人安全地完成等等?

我发现一些关于使用本机代码或 OpenSSL 等第三方库的内容(非常复杂!)。我发现的最好的东西是this question 关于在 WP8.0 中的固定,其中包括一个应该适用于 WP8.1 并且希望也适用于 Win10 的代码示例,但它有点丑陋和令人困惑,我不确定如何在发送带有敏感信息的请求时要求服务器的证书是固定的证书之一。由于检查时间/使用时间 (TOCTOU) 攻击,事先检查似乎并不安全(除非 HttpRequestMessage.TransportInformation 函数打开连接,然后将其保持打开状态,因此攻击者没有机会在新连接上获得中间人位置)。理想情况下,有一种过滤 HttpClient 的方法,因此它只连接到具有固定证书的服务器,但我能找到的最接近的东西恰恰相反(忽略某些证书错误,如 here 所述)通过@ 987654326@ 属性,它似乎没有任何限制有效证书的选项。

这里有人有好的解决方案吗?如果 HttpRequestMessage.TransportInformation 方法(使用自定义证书验证代码)是唯一的选择,那么在发送请求之前检查该属性是否可以安全地抵御 TOCTOU 攻击?

【问题讨论】:

  • 您好,我也遇到了和您一样的问题,请问您有什么办法吗?

标签: .net ssl-certificate dotnet-httpclient windows-10-universal


【解决方案1】:

您是否查看过 HttpBaseProtocolFilter 的 ServerCustomValidationRequested 事件?对我来说,棘手的部分是从 Certificate 对象中提取公共证书。为此,我必须引入 System.Security.Cryptography.X509Certificates nuget 包。我的代码如下所示:

private void DoIt()
{
    using (var filter = new HttpBaseProtocolFilter())
    {
        filter.ServerCustomValidationRequested += FilterOnServerCustomValidationRequested;
        var httpClient = new Windows.Web.Http.HttpClient(filter);
        var myString = await httpClient.GetStringAsync(new Uri("https://myserver.com"));
        // I guess we should be kind and unsubscribe?
        filter.ServerCustomValidationRequested -= FilterOnServerCustomValidationRequested;
    }
}

private void FilterOnServerCustomValidationRequested(HttpBaseProtocolFilter sender, HttpServerCustomValidationRequestedEventArgs args)
{
    if (!IsCertificateValid(args.RequestMessage, args.ServerCertificate, args.ServerCertificateErrors))
    {
        args.Reject();
    }
}

private bool IsCertificateValid(Windows.Web.Http.HttpRequestMessage httpRequestMessage, Certificate cert, IReadOnlyList<ChainValidationResult> sslPolicyErrors)
{
    // disallow self-signed certificates or certificates with errors
    if (sslPolicyErrors.Count > 0)
    {
        return false;
    }

    if (RequestRequiresCheck(httpRequestMessage.RequestUri))
    {
        var certificateSubject = cert?.Subject;
        bool subjectMatches = certificateSubject == CERTIFICATE_COMMON_NAME;

        var certArray = cert?.GetCertificateBlob().ToArray();
        var x509Certificate2 = new X509Certificate2(certArray);
        var certificatePublicKey = x509Certificate2.GetPublicKey();
        var certificatePublicKeyString = Convert.ToBase64String(certificatePublicKey);
        bool publicKeyMatches = certificatePublicKeyString == CERTIFICATE_PUBLIC_KEY;

        return subjectMatches && publicKeyMatches;
    }

    return true;
}

private bool RequestRequiresCheck(Uri uri)
{
    return uri.IsAbsoluteUri &&
            uri.AbsoluteUri.StartsWith("https://", StringComparison.CurrentCultureIgnoreCase) &&
            uri.AbsoluteUri.StartsWith(BASE_URL, StringComparison.CurrentCultureIgnoreCase);
}

附言如果您有兴趣了解更多详细信息,我最后写了一个关于 UWP 中证书固定的blog post

【讨论】:

  • 顺便提一下,我从这里得到了提取公钥的解决方案:stackoverflow.com/a/38126387/40783。我很想在没有额外的 nuget 参考的情况下做到这一点,也许是通过Windows.Security.Cryptography.Core.PersistedKeyProvider.OpenPublicKeyFromCertificate,但我无法让参数正确以使其工作并返回正确的字节数。如果有人能找到更清洁的解决方案,请分享。
【解决方案2】:

以下文章有一个很好的解决方案。试试看。

https://www.codeproject.com/Articles/849510/Certificate-Pinning-on-Windows-Phone

我在 UWP 方面没有太多经验,但在 Window 8.1 商店应用程序中有 package.appxmanifest 文件,可让您在“声明选项卡”上定义根证书,因此您可以在代码上进行适当的验证。

以下文章还定义了一些关于如何保护您的应用程序和 HTTP 连接的好方法。 https://blogs.windows.com/buildingapps/2015/10/13/create-more-secure-apps-with-less-effort-10-by-10/#bWSeoR0pMyW2H8fg.97

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 2015-11-09
    • 2015-11-04
    • 2019-04-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多