Как очистить любые данные SSL-сертификата - PullRequest
7 голосов
/ 27 июля 2011

У меня клиент-серверная настройка. Клиент создает прокси для связи с сервером. Когда протоколом связи является HTTPS, прокси-сервер прослушивает событие проверки сертификата SSL через следующую строку:

ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate);

Метод ValidateRemoteCertificate работает с исключениями сертификатов.

В клиенте пользователь может выбрать один из 3 уровней безопасности: низкий, средний и высокий. На низком уровне метод ValidateRemoteCertificate игнорирует любые ошибки и всегда возвращает true. На среднем уровне метод ValidateRemoteCertificate запускает событие, уведомляющее клиента о проблеме. На этом этапе пользователю появляется сообщение, информирующее его о том, что сертификат проблематичен, и позволяет пользователю выбрать, следует ли продолжить и принять соединение с сервером или отклонить. На высоком уровне метод ValidateRemoteCertificate отклоняет соединение для любой ошибки.

Пока все хорошо.

Сценарий таков:

  1. Клиент загружается с предопределенным уровнем безопасности Medium, который уже был принят пользователем, и соединение с сервером устанавливается без распространения каких-либо проблем с сертификатом.
  2. Пользователь отключает клиента от сервера (специальной кнопкой).
  3. Пользователь пытается повторно подключить клиента. На этом этапе клиент имеет возможность проверить соединение с помощью кнопки тестирования. Метод test возвращает успех, хотя для проверки соединения был создан новый прокси-сервер, и все методы ValidateRemoteCertificate были очищены из ServerCertificateValidationCallback (определенного типа прокси-сервера). Кроме того, для проблемного сертификата не генерируется событие, и метод ValidateRemoteCertificate не вызывается.

Поведение, которого я пытаюсь добиться, заключается в том, что при выполнении теста ServerCertificateValidationCallback будет вести себя так, как если бы это был первый вызов к нему после запуска клиента и вступления в действие ValidateRemoteCertificate.

Я пытался найти любой метод, который очищает любые делегаты / события в ServicePointManager, но я не смог найти ни одного.

Есть ли здесь кеш, который можно очистить? Надеюсь, сценарий достаточно ясен.

1 Ответ

6 голосов
/ 29 апреля 2015

Я знаю, что прошло почти 4 года, но у меня была такая же проблема, и я хотел поделиться своим решением, если кто-нибудь еще найдет это.

Я не смог найти какой-либо встроенный способ справиться с этим, поэтомупосмотрел на исходный код ServicePoint и ServicePointManager и вот что я придумал:

    public void EnsureNoServicePointCertificate(Uri uri)
    {
        // find the service point for the Uri
        ServicePoint sp = ServicePointManager.FindServicePoint(uri);
        // Check if there is a service point and there is a certificate
        if (sp != null && sp.Certificate != null)
        {
            try
            {
                // ServicePointManager has a hashtable (private static Hashtable s_ServicePointTable) of all service points
                Type servicePointType = sp.GetType();
                // ServicePoint.LookupString is the key for the hashtable
                PropertyInfo lookupStringProperty = servicePointType.GetProperty("LookupString", BindingFlags.Instance | BindingFlags.NonPublic);
                string lookupString = (string)lookupStringProperty.GetValue(sp, null);

                // Get the hashtable from ServicePointManager
                Hashtable s_ServicePointTable = (Hashtable)typeof(ServicePointManager).InvokeMember("s_ServicePointTable",
                    BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetField, null, null, null);

                // ServicePointManager locks the hashtable and calls 
                // s_ServicePointTable.Remove(servicePoint.LookupString);
                lock (s_ServicePointTable)
                {
                    s_ServicePointTable.Remove(lookupString);
                }

                // At this point, ServicePointManager calls
                // servicePoint.ReleaseAllConnectionGroups();
                MethodInfo release = servicePointType.GetMethod("ReleaseAllConnectionGroups", BindingFlags.Instance | BindingFlags.NonPublic);
                release.Invoke(sp, null);
            }
            catch { }
        }
    }
...