Windows 10 Universal应用程序中的证书固定

时间:2016-01-07 23:38:47

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

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

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

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

2 个答案:

答案 0 :(得分:2)

您是否看过HttpBaseProtocolFilter的ServerCustomValidationRequested事件?对我来说棘手的部分是从证书对象中提取公共证书。为此,我必须引入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);
}

P.S。如果您对更多细节感兴趣,我最终会在UWP中写一个关于证书的blog post

答案 1 :(得分:1)

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

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

我在UWP方面没有太多经验,但在Window 8.1商店应用程序中有<?php var_dump($customers); ?> <?php foreach ($customers as $customer) { ?> <h3>Customer ID: <?php echo $customer['customer_id']; ?></h3> <h3>Customer Name:</h3> <?php } ?> 文件,您可以在“声明”选项卡上定义根证书,因此,在代码上您可以进行适当的验证。

下面的文章也没有很好的方法来定义如何保护您的应用和HTTP连接。 https://blogs.windows.com/buildingapps/2015/10/13/create-more-secure-apps-with-less-effort-10-by-10/#bWSeoR0pMyW2H8fg.97