باستخدام شهادة موقعة ذاتيا مع صافي في HttpWebRequest / الاستجابة

StackOverflow https://stackoverflow.com/questions/526711

سؤال

وأنا أحاول الاتصال إلى API يستخدم شهادة SSL موقعة ذاتيا. أنا أفعل ذلك باستخدام HttpWebRequest وHttpWebResponse الأشياء. NET ل. وأنا الحصول على استثناء ما يلي:

<اقتباس فقرة>   

تم إغلاق الاتصال الأساسي: لا يمكن تأسيس علاقة ثقة للSSL / TLS قناة آمنة

وأنا أفهم ما يعنيه هذا. وأنا أفهم <م> لماذا . NET يشعر أنه يجب التحذير وإغلاق الاتصال. ولكن في هذه الحالة، أود أن ربط فقط إلى API على أي حال، والرجل في داخل المتوسطة يدن الهجمات.

وهكذا، كيف أذهب حول إضافة استثناء لهذه الشهادة موقعة ذاتيا؟ أو هو النهج أن أقول HttpWebRequest / الاستجابة لا للتحقق من صحة الشهادة على الإطلاق؟ كيف أفعل ذلك؟

هل كانت مفيدة؟

المحلول

وDomster: أن يعمل، ولكن قد ترغب في فرض نوعا من الأمن عن طريق فحص إذا تجزئة الشهادة يطابق ما كنت تتوقع. لذا نسخة موسعة يبدو قليلا مثل هذا (على أساس بعض الرموز الحية نستخدمه):

static readonly byte[] apiCertHash = { 0xZZ, 0xYY, ....};

/// <summary>
/// Somewhere in your application's startup/init sequence...
/// </summary>
void InitPhase()
{
    // Override automatic validation of SSL server certificates.
    ServicePointManager.ServerCertificateValidationCallback =
           ValidateServerCertficate;
}

/// <summary>
/// Validates the SSL server certificate.
/// </summary>
/// <param name="sender">An object that contains state information for this
/// validation.</param>
/// <param name="cert">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the
/// remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote
/// certificate.</param>
/// <returns>Returns a boolean value that determines whether the specified
/// certificate is accepted for authentication; true to accept or false to
/// reject.</returns>
private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        // Good certificate.
        return true;
    }

    log.DebugFormat("SSL certificate error: {0}", sslPolicyErrors);

    bool certMatch = false; // Assume failure
    byte[] certHash = cert.GetCertHash();
    if (certHash.Length == apiCertHash.Length)
    {
        certMatch = true; // Now assume success.
        for (int idx = 0; idx < certHash.Length; idx++)
        {
            if (certHash[idx] != apiCertHash[idx])
            {
                certMatch = false; // No match
                break;
            }
        }
    }

    // Return true => allow unauthenticated server,
    //        false => disallow unauthenticated server.
    return certMatch;
}

نصائح أخرى

وتبين، إذا كنت ترغب فقط في تعطيل التحقق من صحة الشهادة تماما، يمكنك تغيير ServerCertificateValidationCallback على ServicePointManager، كما يلي:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

وهذا سوف تحقق من كافة الشهادات (بما في ذلك باطلة، انتهت أو تلك الموقعة ذاتيا).

ملحوظة، وذلك في. NET 4.5 يمكنك تجاوز التحقق من صحة SSL في HttpWebRequest نفسه (وليس عن طريق مندوب العالمي الذي يؤثر على جميع الطلبات):

http://msdn.microsoft.com/ أون لنا / مكتبة / system.net.httpwebrequest.servercertificatevalidationcallback.aspx

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.ServerCertificateValidationCallback = delegate { return true; };

وإضافة موقعة ذاتيا سيرت إلى المصدقة الجذر الموثوق بها الحاسوب المحلية

ويمكنك استيراد سيرت عن طريق تشغيل MMC كمسؤول.

كيفية: عرض شهادات مع MMC الإضافية في

ونطاق رد المصادقة المستخدمة في الجواب Domster في يمكن أن تقتصر على طلب محدد باستخدام المعلمة المرسل على مندوب ServerCertificateValidationCallback. يستخدم فئة نطاق البسيطة التالية هذه التقنية لسلك مؤقتا حتى رد التحقق من صحة الذي ينفذ فقط لكائن طلب معين.

public class ServerCertificateValidationScope : IDisposable
{
    private readonly RemoteCertificateValidationCallback _callback;

    public ServerCertificateValidationScope(object request,
        RemoteCertificateValidationCallback callback)
    {
        var previous = ServicePointManager.ServerCertificateValidationCallback;
        _callback = (sender, certificate, chain, errors) =>
            {
                if (sender == request)
                {
                    return callback(sender, certificate, chain, errors);
                }
                if (previous != null)
                {
                    return previous(sender, certificate, chain, errors);
                }
                return errors == SslPolicyErrors.None;
            };
        ServicePointManager.ServerCertificateValidationCallback += _callback;
    }

    public void Dispose()
    {
        ServicePointManager.ServerCertificateValidationCallback -= _callback;
    }
}

ويمكن استخدام الفئة المذكورة أعلاه إلى تجاهل كافة الأخطاء شهادة لطلب محدد كما يلي:

var request = WebRequest.Create(uri);
using (new ServerCertificateValidationScope(request, delegate { return true; }))
{
    request.GetResponse();
}

لإضافة بمثابة مساعدة ممكنة لشخص آخر ... إذا كنت تريد أن مطالبة المستخدم لتثبيت سيرت موقعة ذاتيا، يمكنك استخدام هذا الرمز (معدلة من أعلاه).

لا يتطلب حقوق مشرف، يثبت للمستخدمين المحليين الوثوق الشخصية:

    private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
        {
            // Good certificate.
            return true;
        }

        Common.Helpers.Logger.Log.Error(string.Format("SSL certificate error: {0}", sslPolicyErrors));
        try
        {
            using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
            {
                store.Open(OpenFlags.ReadWrite);
                store.Add(new X509Certificate2(cert));
                store.Close();
            }
            return true;
        }
        catch (Exception ex)
        {
            Common.Helpers.Logger.Log.Error(string.Format("SSL certificate add Error: {0}", ex.Message));
        }

        return false;
    }

ويبدو أن هذا العمل بشكل جيد لطلبنا، وإذا كان المستخدم بالضغط لا، فإن الاتصالات لا تعمل.

<القوي> تحديث: 2015/12/11 - يمكنك تغيير StoreName.Root إلى StoreName.My - بلدي سيتم تثبيت في متجر المستخدمين المحليين، بدلا من الجذر. والجذر على بعض الأنظمة لا تعمل، حتى لو كنت "تشغيل كمسؤول"

وفقط بناء على إجابة من devstuff لتضمين تعليقات الموضوع والمصدر ... ترحيب ...

public class SelfSignedCertificateValidator
{
    private class CertificateAttributes
    {
        public string Subject { get; private set; }
        public string Issuer { get; private set; }
        public string Thumbprint { get; private set; }

        public CertificateAttributes(string subject, string issuer, string thumbprint)
        {
            Subject = subject;
            Issuer = issuer;                
            Thumbprint = thumbprint.Trim(
                new char[] { '\u200e', '\u200f' } // strip any lrt and rlt markers from copy/paste
                ); 
        }

        public bool IsMatch(X509Certificate cert)
        {
            bool subjectMatches = Subject.Replace(" ", "").Equals(cert.Subject.Replace(" ", ""), StringComparison.InvariantCulture);
            bool issuerMatches = Issuer.Replace(" ", "").Equals(cert.Issuer.Replace(" ", ""), StringComparison.InvariantCulture);
            bool thumbprintMatches = Thumbprint == String.Join(" ", cert.GetCertHash().Select(h => h.ToString("x2")));
            return subjectMatches && issuerMatches && thumbprintMatches; 
        }
    }

    private readonly List<CertificateAttributes> __knownSelfSignedCertificates = new List<CertificateAttributes> {
        new CertificateAttributes(  // can paste values from "view cert" dialog
            "CN = subject.company.int", 
            "CN = issuer.company.int", 
            "f6 23 16 3d 5a d8 e5 1e 13 58 85 0a 34 9f d6 d3 c8 23 a8 f4") 
    };       

    private static bool __createdSingleton = false;

    public SelfSignedCertificateValidator()
    {
        lock (this)
        {
            if (__createdSingleton)
                throw new Exception("Only a single instance can be instanciated.");

            // Hook in validation of SSL server certificates.  
            ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertficate;

            __createdSingleton = true;
        }
    }

    /// <summary>
    /// Validates the SSL server certificate.
    /// </summary>
    /// <param name="sender">An object that contains state information for this
    /// validation.</param>
    /// <param name="cert">The certificate used to authenticate the remote party.</param>
    /// <param name="chain">The chain of certificate authorities associated with the
    /// remote certificate.</param>
    /// <param name="sslPolicyErrors">One or more errors associated with the remote
    /// certificate.</param>
    /// <returns>Returns a boolean value that determines whether the specified
    /// certificate is accepted for authentication; true to accept or false to
    /// reject.</returns>
    private bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;   // Good certificate.

        Dbg.WriteLine("SSL certificate error: {0}", sslPolicyErrors);
        return __knownSelfSignedCertificates.Any(c => c.IsMatch(cert));            
    }
}

وشيء واحد أن نأخذ في الاعتبار هو أن وجود ServicePointManager.ServerCertificateValidationCallback لا يبدو أن يعني أن الاختيار CRL والتحقق من صحة SERVERNAME هي لم تفعل، انها لا تقدم سوى وسيلة لتجاوز نتائجها. حيث الخدمة الخاص بك قد لا تزال تأخذ بعض الوقت للحصول على CRL، عليك أن تعرف فقط بعد ذلك أنه فشل بعض الشيكات.

ولقد تم تشغيل في نفس المشكلة مثل OP حيث طلب ويب، فإنه يرمي هذا الاستثناء المحدد. كنت قد الإعداد كل شيء بشكل صحيح فكرت، تم تثبيت الشهادة، ويمكنني أن العثور عليه في مخزن الجهاز على ما يرام ونعلق عليه لطلب شبكة الإنترنت، وكنت قد تعطيل التحقق من الشهادات على سياق الطلب.

وتبين أن الأول كان يعمل تحت حساب المستخدم الخاص بي، وأنه تم تثبيت الشهادة إلى مخزن الجهاز. يحدث هذا طلب ويب لرمي هذا الاستثناء. لحل هذه المشكلة كان لي أن يكون إما تشغيل كمسؤول أو تثبيت الشهادة إلى مخزن المستخدم وقراءتها من هناك.

ويبدو أن C # غير قادرة على العثور على الشهادة في مخزن الجهاز على الرغم من أنه لا يمكن استخدامها مع طلب ويب، وهذا يؤدي إلى استثناء وOP والتي ألقيت مرة يتم إصدار طلب ويب.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top