.Net HttpWebRequest ClientCertificates не отправлены в API - PullRequest
1 голос
/ 21 мая 2019

Я пытаюсь вызвать API через HTTPS. API ожидает отправки сертификата клиента, который затем будет проверен. API не возвращает некоторый список принятых CA или около того (как в стандартном потоке TLS веб-сервера), он просто ожидает проверки сертификата.

На клиентском компьютере, на котором запускается мое приложение, сертификат клиента устанавливается на локальном компьютере \ в личном хранилище (где он также выглядит нормально, отметив его в certlm.msc). Я могу получить сертификат в магазине и добавить его в мой HttpWebRequest, например, request.ClientCertificates.Add(cert). Проверка сертификата в коде приложения во время выполнения имеет закрытый ключ (HasPrivateKey - это true, а свойство PrivateKey содержит данные). В целях тестирования я предоставил everyone доступ на чтение к сертификату, чтобы убедиться, что их закрытый ключ может быть получен вместе с сертификатом из приложения, похоже, работает.

Однако API жалуется, что сертификат не был отправлен. Проверка запроса с Wireshark показывает TLSv1.2 рукопожатие типа Certificate, однако Certificate Length: 0. Так что сертификат действительно не отправлен.

Использование Postman для вызова точно такого же API с точно таким же сертификатом (из файла, поскольку Postman нужен файл, файл .pfx, из которого сертификат был импортирован в машину \ личный магазин) отправляет сертификат. Wireshark показывает Certificate Length: 770, и я могу просмотреть информацию о сертификате.

Похоже, по какой-то причине, хотя я прикрепляю сертификат к HttpWebRequest.ClientCertificates, он не отправляется из кода приложения. Там нет исключений, он просто не будет отправлен.

Подведем итог:

  • HttpWebRequest.ClientCertificates содержит сертификат, полученный от машины \ личного магазина
  • сертификат имеет закрытый ключ
  • сертификат (в certlm.msc) выглядит хорошо (доверенный CA и т. Д.)
  • сертификат не настроен на API
  • однако тот же сертификат настраивается при вызове API от почтальона
  • приложение является приложением .Net 4.6.1 wpf

Есть идеи, что мне не хватает? Спасибо!

(позднее редактировать)

Я отладил класс SecureChannel в платформе, который обрабатывает отправку сертификатов, и обнаружил некоторые неприятные вещи. Сначала несколько комментариев:

Here is how we work:
                case 0: Cert Selection delegate is present
                        Alwasys use its result as the client cert answer.
                        Try to use cached credential handle whenever feasible.
                        Do not use cached anonymous creds if the delegate has returned null
                        and the collection is not empty (allow responding with the cert later).

                case 1: Certs collection is empty
                        Always use the same statically acquired anonymous SSL Credential

                case 2: Before our Connection with the Server
                        If we have a cached credential handle keyed by first X509Certificate
                        **content** in the passed collection, then we use that cached
                        credential and hoping to restart a session.

                        Otherwise create a new anonymous (allow responding with the cert later).

                case 3: After our Connection with the Server (ie during handshake or re-handshake)
                        The server has requested that we send it a Certificate then
                        we Enumerate a list of server sent Issuers trying to match against
                        our list of Certificates, the first match is sent to the server.

                        Once we got a cert we again try to match cached credential handle if possible.
                        This will not restart a session but helps miminizing the number of handles we create.

Отладка кода, комментарии, похоже, содержат:

  • сначала он проверяет делегата выбора сертификата: его нет, и коллекция сертификатов содержит мой сертификат, поэтому он переходит к шагу 2
  • здесь он берет мой сертификат, проверяет действительный закрытый ключ, успешен, затем приходит новый комментарий
// (see VsWhidbey#363953) For some (probably good) reason IIS does not renegotiate a restarted session if client cert is needed.
                    // So we don't want to reuse **anonymous** cached credential for a new SSL connection if the client has passed some certificate.
                    // The following block happens if client did specify a certificate but no cached creds were found in the cache
                    // Since we don't restart a session the server side can still challenge for a client cert.

... после чего этот код ...

   if ((object)clientCertificate != (object)selectedCert)
                        selectedCert.Reset();
                    guessedThumbPrint = null;
                    selectedCert = null;
                    clientCertificate = null;

Таким образом, в основном это будет null ссылка на сертификат, который я передал. Если я правильно понимаю, это обойдет некоторую проблему IIS, и это означает, что он ожидает, что сервер отправит обратно список эмитентов для фильтрации сертификаты (это будет случай 3 из комментария), что действительно то, что я вижу в коде. Только если этот список эмитентов будет получен с сервера, код будет проходить через клиентские сертификаты и фильтровать их по эмитентам, а затем отправлять соответствующие сертификаты.

Теперь у меня возникает вопрос: можно ли отправлять клиентские сертификаты только в том случае, если сервер запрашивает список эмитентов? Потому что в моем случае вызываемый API не делает этого, он просто ожидает сертификат. Надеюсь, я ошибаюсь, и кто-то может пролить свет на это.

(редактировать позже)

API, который я сейчас вызываю, возвращает список CA. Мой тестовый CA был добавлен туда, и он возвращается во время рукопожатия SSL. Теперь отладка SecureChannel показывает, что мои предыдущие предположения были верны:

  • при первом прохождении добавленный сертификат не отправляется
  • однако после получения списка CA от сервера он проверяется на наличие одного, соответствующего издателю моего сертификата, и совпадение действительно найдено!
  • после успешного совпадения SecureChannel успешно устанавливает m_SelectedClientCertificate для моего сертификата, но ...
  • ... к сожалению, он все еще не отправляется на сервер со следующим шагом протокола: (

Как только SecureChannel.m_SelectedClientCertificate установлен, он, похоже, больше не используется из SecureChannel, но выставлен:

internal X509Certificate LocalClientCertificate {
            get {
                return m_SelectedClientCertificate;
            }
        }

К сожалению, это свойство никогда не вызывается в моем случае: (

1 Ответ

1 голос
/ 27 мая 2019

Нашел проблему: мой сертификат был подписан с SHA1.Ушел в отставку с SHA256 и вот, его отправили!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...