Httplistener с поддержкой HTTPS

Кажется, существует много запутанной, иногда противоречивой информации о том, как сделать .NET HTTPListener совместимым с HTTPS. Я понимаю следующее:

  • Код C # нуждается в префиксе https (например, https://*:8443), чтобы слушатель понял, что ему необходимо обслуживать запросы SSL на этом порту.

  • Фактическое рукопожатие SSL происходит под прикрытием и обрабатывается http.sys (похоронено где-то на машине Windows). Код C # не должен явно управлять подтверждением SSL, потому что это происходит скрыто.

  • На httpListener машине должен быть «доверенный сертификат X.509», и каким-то образом этот сертификат должен быть привязан к порту 8443 (в этом примере).

Правильно ли мое понимание выше? Если нет, пожалуйста, просветите меня.

Что касается сертификатов X.509, я понимаю:

  • Используйте makecert для создания сертификата X.509. Этот сертификат сохраняется в личном хранилище, и его необходимо переместить в надежное хранилище (именно здесь будет искать HTTP-прослушиватель). Кажется, я могу использовать certMgr, чтобы выполнить движение, или я могу использовать mmc, чтобы выполнить движение. Кажется, существует более одного формата сертификата X.509 (DER, Base64, pks, pswd protected, pks private и т. Д.) ... Есть ли предпочтительный формат, который я должен использовать?

Как только я получу сертификат в доверенном хранилище, мне нужно привязать его к TCP-порту. Я использую Windows 7: мне следует использовать httpcfg или netsh?


person Walter Kelt    schedule 09.07.2012    source источник


Ответы (5)


Я сделал кучу домашних заданий, и это сработало. Шаги по добавлению поддержки SSL для .NET HttpListener:

  1. Обновите код приложения C #, включив в него префикс https. Пример:

    String[] prefixes = { "http://*:8089/","https://*:8443/" };
    

    Это с точки зрения кода.

  2. Что касается сертификатов, с помощью командной консоли Windows SDK или Visual Studio Professional.

    • Используйте makecert.exe для создания центра сертификации. Пример:

      makecert -n "CN=vMargeCA" -r -sv vMargeCA.pvk vMargeCA.cer
      
    • Используйте makecert.exe для создания сертификата SSL

      makecert -sk vMargeSignedByCA -iv vMargeCA.pvk -n "CN=vMargeSignedByCA" -ic vMargeCA.cer vMargeSignedByCA.cer -sr localmachine -ss My
      
    • Используйте графический интерфейс MMC для установки CA в хранилище Trusted Authority

    • Используйте графический интерфейс MMC для установки сертификата SSL в личном хранилище
    • Свяжите сертификат с IP address:port и приложением. Пример:

      netsh http add sslcert ipport=0.0.0.0:8443 certhash=585947f104b5bce53239f02d1c6fed06832f47dc appid={df8c8073-5a4b-4810-b469-5975a9c95230}
      

      Certhash - это отпечаток вашего SSL-сертификата. Вы можете найти это с помощью mmc. Appid находится в Visual Studio ... обычно в assembly.cs ищите значение GUID.

Могут быть и другие способы сделать это, но у меня это сработало.

person Walter Kelt    schedule 12.07.2012
comment
Ха, я пробовал делать все в соответствии с этими подсказками, но я не могу пройти последний шаг - он говорит, что какой-то параметр недействителен ... - person Tomasz Kapłoński; 12.10.2013
comment
Я заметил, что когда я копирую и вставляю в командную строку, иногда появляется '?' появляется между certhash = и фактическим ключом. Еще раз проверьте вводимые данные. - person jklemmack; 11.12.2013
comment
Есть ли способ связать сертификат корневого ЦС с промежуточным сертификатом? - person Hugh Jeffner; 16.06.2015
comment
@WalterKelt Ваш ответ мне очень помог и заполнил почти все пробелы в существующей документации. Однако было несколько, которые мне пришлось заполнить самому, поэтому я опубликовал свой пошаговый процесс в качестве ответа. Ваше здоровье! - person jpaugh; 25.11.2015
comment
@ Moby04 Вы случайно не использовали PowerShell? См. Мой ответ для обходного пути. - person jpaugh; 25.11.2015
comment
извините @WalterKelt, как мы получили appid? на Привязать сертификат к ip: порт и раздел приложения - person Ryan Arief; 20.04.2016
comment
@WalterKelt просто предположение, но, вероятно, Guid в вашем файле AssemblyInfo в папке свойств исполняемого проекта - person Chris Klepeis; 11.08.2016
comment
В моем случае не было необходимости использовать mmc. Просто щелкните правой кнопкой мыши и выберите «Установить сертификат», затем дважды щелкните, чтобы открыть сертификат и получить хэш сертификата. - person Milad; 09.05.2017
comment
А как насчет сертификата от Let's Encrypt? Вы используете какой-нибудь cli в Windows для получения сертификата? - person Vitaly Zdanevich; 07.05.2019
comment
Он очень старый, но сейчас я столкнулся с аналогичной проблемой. Последние шаги включают добавление сертификата на локальный компьютер, но как это сделать для приложения, которое распространяется среди других людей? - person jstuardo; 14.10.2020
comment
Это позволяет добавить только один сертификат SSL к порту 443 ... - person user1034912; 01.12.2020

Вот подробные шаги, которые я выполнил для настройки автономного сервера в Windows с использованием OpenSSL для создания самозаверяющего сертификата для приложения C # HTTPListener. Он включает в себя множество ссылок на случай, если вы захотите продолжить исследование.

  1. Создайте автономный сервер в .NET через HttpListener:

    var prefixes = {"http://localhost:8080/app/root", "https://localhost:8443/app/root"};
    var listener = new HttpListener();
    foreach (string s in prefixes)
        listener.Prefixes.Add(s);
    listener.Start();
    
  2. Создать самозаверяющий сертификат: *

    1. openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365, which will prompt you for the value of each of the certificate's fields on the command line. For the common name, type the domain name (e.g. localhost)
    2. openssl pkcs12 -inkey bob_key.pem -in bob_cert.cert -export -out bob_pfx.pfx, чтобы его можно было импортировать с его ключом на целевой компьютер.

    * Альтернативный вариант использования makecert см. в ответе Уолтера.

  3. Откройте диспетчер сертификатов для локального компьютера. Когда вы запускаете certmgr.msc, он открывает диспетчер сертификатов для текущего пользователя, чего мы здесь не хотим. Вместо:

    1. From an administrative command prompt on the target machine, run mmc
    2. Нажмите Ctrl + M или нажмите Файл> Добавить / удалить оснастку < / kbd>
    3. Выберите Certificates и нажмите Добавить>
    4. В появившемся диалоговом окне выберите Computer Account и нажмите Далее.
    5. Выберите Local Computer. Нажмите Готово, затем Хорошо
  4. Импортируйте сертификат (pfx) в хранилище сертификатов Windows на целевом компьютере.

    1. In the mmc window previously opened, drill down to Certificates (Local Computer) > Personal
    2. Щелкните правой кнопкой мыши Personal, затем щелкните Все задачи -> Импорт ...
    3. На 2-м экране появившегося диалогового окна найдите и импортируйте свой сертификат. Вам нужно будет изменить фильтр типа файла на Personal Information Exchange или All Files, чтобы найти его.
    4. На следующем экране введите пароль, который вы выбрали на шаге 2.1, и обратите внимание на первый флажок. Это определяет, насколько надежно хранится ваш сертификат, а также насколько удобно им пользоваться.
    5. На последнем экране выберите Place all certificates in the following store. Убедитесь, что написано Personal, затем нажмите Готово
    6. Повторите описанную выше процедуру импорта для раздела Trusted Root Certification Authorities сертификатов.
  5. Создайте ассоциации портов для вашего приложения. В Windows Vista и более поздних версиях используйте netsh, как и я. (Для Windows XP и более ранних версий используйте httpcfg)

    • В административной командной строке введите следующее, чтобы настроить Привязка SSL * к вашему приложению и соответствующий порт. NB: Эту команду легко ошибиться, потому что (в PowerShell) фигурные скобки должны быть экранированным. Следующая команда PowerShell будет работать:

      netsh http add sslcert ipport=0.0.0.0:8443 `
          certhash=110000000000003ed9cd0c315bbb6dc1c08da5e6 `
          appid=`{00112233-4455-6677-8899-AABBCCDDEEFF`}
      

      Для cmd.exe вместо этого следует использовать следующее:

      netsh http add sslcert ipport=0.0.0.0:8443 certhash=110000000000003ed9cd0c315bbb6dc1c08da5e6 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
      
      • The ipport parameter will cause the SSL certificate to bind to the port 8443 on every network interface; to bind to a specific interface (only), choose the IP address associated with that network interface.
      • certhash - это просто отпечаток сертификата без пробелов.
      • appid - это идентификатор GUID, который хранится в информации о сборке вашего приложения. (Примечание: механизм netsh, очевидно, является интерфейсом COM, судя по этому вопросу и ответам на него)

      * Microsoft перенаправила ссылку SSL Binding с здесь на там.

  6. Запустите свой веб-сервер, и все готово!

person jpaugh    schedule 24.11.2015
comment
@Jez IIRC, у меня не было проблем с использованием только хранилища сертификатов Trusted Root Certificate Authorities. Есть ли что-то особенное в вашей настройке, требующее, чтобы сертификат также был в магазине Personal? - person jpaugh; 24.02.2016
comment
Когда я запустил команду netsh на машине, на которой я установил сертификат, я получил сообщение об ошибке «Ошибка добавления сертификата SSL», Ошибка 1312 - указанный сеанс входа в систему не существует. Возможно, он уже был прекращен. Глядя на этот ответ на этот вопрос, похоже, что сертификат должен находиться в личном хранилище, чтобы установить его через netsh (в нем говорится о запуске certutil с my, а не root): stackoverflow.com/a/19766650/178757 - person Jez; 24.02.2016
comment
В моей Windows команда для создания файла .pfx зависает с помощью Git- (Bash) -for-Windows. В качестве решения просто добавьте winpty перед командой в соответствии с openssl-зависает-во-время-pkcs12-export. - person dwettstein; 23.02.2017
comment
@dwettstein Хороший совет! К сожалению, очень сложно (в целом) предсказать, какие команды будут работать лучше с winpty, а не без него. - person jpaugh; 24.02.2017
comment
@jpaugh, я выполнил все шаги, которые вы указали выше, но получаю сообщение об ошибке, так как сертификат не является доверенным, потому что он самоподписанный. Код ошибки: MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT - person subha; 21.09.2018
comment
@spr Это ожидается, потому что сертификат самозаверяющий. Переход на сайт с самозаверяющим сертификатом должен быть исключением, а не правилом, поэтому ваш браузер прав, чтобы вас предупредить. Убедившись, что отпечаток сертификата совпадает с вашим, вы можете указать браузеру сделать для него исключение. (Не знаю, почему я не видел ваш комментарий раньше, но, возможно, другим он будет полезен.) - person jpaugh; 26.06.2019
comment
Спасибо за ваше время, но я сам забыл, когда я это комментировал, но я проголосовал за ваш ответ, так что, думаю, ваш ответ сработал для меня, спасибо :) - person subha; 26.06.2019

Мы можем импортировать сертификаты с помощью PowerShell и C # (ручные действия не требуются).

Подробнее см. https://blog.davidchristiansen.com/2016/09/howto-create-self-signed-certificates-with-powershell/

Я использую этот код:

/// <summary>
/// Create and install a self-signed certificate for HTTPS use
/// </summary>
private static void CreateInstallCert(int expDate, string password, string issuedBy)
{
    // Create/install certificate
    using (var powerShell = System.Management.Automation.PowerShell.Create())
    {
        var notAfter = DateTime.Now.AddYears(expDate).ToLongDateString();
        var assemPath = Assembly.GetCallingAssembly().Location;
        var fileInfo = new FileInfo(assemPath);
        var saveDir = Path.Combine(fileInfo.Directory.FullName, "CertDir");
        if (!Directory.Exists(saveDir))
        {
            Directory.CreateDirectory(saveDir);
        }

        // This adds certificate to Personal and Intermediate Certification Authority
        var rootAuthorityName = "My-RootAuthority";
        var rootFriendlyName = "My Root Authority";
        var rootAuthorityScript =
            $"$rootAuthority = New-SelfSignedCertificate" +
            $" -DnsName '{rootAuthorityName}'" +
            $" -NotAfter '{notAfter}'" +
            $" -CertStoreLocation cert:\\LocalMachine\\My" +
            $" -FriendlyName '{rootFriendlyName}'" +
            $" -KeyUsage DigitalSignature,CertSign";
        powerShell.AddScript(rootAuthorityScript);

        // Export CRT file
        var rootAuthorityCrtPath = Path.Combine(saveDir, "MyRootAuthority.crt");
        var exportAuthorityCrtScript =
            $"$rootAuthorityPath = 'cert:\\localMachine\\my\\' + $rootAuthority.thumbprint;" +
            $"Export-Certificate" +
            $" -Cert $rootAuthorityPath" +
            $" -FilePath {rootAuthorityCrtPath}";
        powerShell.AddScript(exportAuthorityCrtScript);

        // Export PFX file
        var rootAuthorityPfxPath = Path.Combine(saveDir, "MyRootAuthority.pfx");
        var exportAuthorityPfxScript =
            $"$pwd = ConvertTo-SecureString -String '{password}' -Force -AsPlainText;" +
            $"Export-PfxCertificate" +
            $" -Cert $rootAuthorityPath" +
            $" -FilePath '{rootAuthorityPfxPath}'" +
            $" -Password $pwd";
        powerShell.AddScript(exportAuthorityPfxScript);

        // Create the self-signed certificate, signed using the above certificate
        var gatewayAuthorityName = "My-Service";
        var gatewayFriendlyName = "My Service";
        var gatewayAuthorityScript =
            $"$rootcert = ( Get-ChildItem -Path $rootAuthorityPath );" +
            $"$gatewayCert = New-SelfSignedCertificate" +
            $" -DnsName '{gatewayAuthorityName}'" +
            $" -NotAfter '{notAfter}'" +
            $" -certstorelocation cert:\\localmachine\\my" +
            $" -Signer $rootcert" +
            $" -FriendlyName '{gatewayFriendlyName}'" +
            $" -KeyUsage KeyEncipherment,DigitalSignature";
        powerShell.AddScript(gatewayAuthorityScript);

        // Export new certificate public key as a CRT file
        var myGatewayCrtPath = Path.Combine(saveDir, "MyGatewayAuthority.crt");
        var exportCrtScript =
            $"$gatewayCertPath = 'cert:\\localMachine\\my\\' + $gatewayCert.thumbprint;" +
            $"Export-Certificate" +
            $" -Cert $gatewayCertPath" +
            $" -FilePath {myGatewayCrtPath}";
        powerShell.AddScript(exportCrtScript);

        // Export the new certificate as a PFX file
        var myGatewayPfxPath = Path.Combine(saveDir, "MyGatewayAuthority.pfx");
        var exportPfxScript =
            $"Export-PfxCertificate" +
            $" -Cert $gatewayCertPath" +
            $" -FilePath {myGatewayPfxPath}" +
            $" -Password $pwd"; // Use the previous password
        powerShell.AddScript(exportPfxScript);

        powerShell.Invoke();
    }
}

Требуется PowerShell 4 или выше.

person Trevy Burgess    schedule 14.04.2019

Поскольку создание ваших собственных самозаверяющих сертификатов в ответах не сработало для меня, и поскольку вопрос, в частности, требует обеспечения поддержки .NET HTTPListener HTTPS и требует каких-либо советов / советов, я хочу поделиться своим подходом.

Вам нужно имя хоста, например www.made-up.com, которое должно указывать на ваш IP-адрес в глобальной сети (например, запросить инструкции у вашего хост-провайдера) и перенаправлять его порт, например 443 на ваш локальный компьютер. Не забудьте открыть этот входящий порт 443 в брандмауэре локального компьютера.

Я использовал https://letsencrypt.org/. В Windows это не так просто, как в Linux, потому что нет официального клиента ACME certbot для Windows. Однако вы можете использовать https://github.com/Lone-Coder/letsencrypt-win-simple, среди которых также есть двоичные файлы. Однако «В настоящее время поддерживается только IIS». Но вы можете легко обмануть его, чтобы создать сертификат на вашем компьютере, чтобы вы могли подойти к своему HTTP-слушателю как SSL:

  1. Установите IIS (с помощью функций Windows в / из), создайте веб-сайт в IIS и назначьте имя хоста. Также сделайте из него безопасный (443 порта) веб-сайт.
  2. Запустите EXE-файл letsencrypt-win-simple (я использовал версию 1.9.1). Ответьте на вопросы, чтобы он сгенерировал сертификат.
  3. После этого вы можете остановить сервер IIS.

Я считаю, что вы должны принять к сведению сгенерированную задачу обновления, поскольку я не уверен, что она будет успешной через несколько месяцев (вам, вероятно, придется снова запустить IIS, чтобы сертификат был обновлен).

person Stefan Verhagen    schedule 27.12.2016
comment
certbot работает с cygwin и IIS, если вы можете добавить расширение. - ›MIME-тип text / html. Выполните pip3 install certbot, затем смонтируйте веб-корень по стандартному пути posix, затем запустите certbot certonly. Не самый плавный, но работает. - person David Dombrowsky; 14.11.2018

Следующая команда генерирует самозаверяющий сертификат для localhost на 10 лет, импортирует его в хранилище локального компьютера и отображает в выходных данных Thumbprint (certhash):

powershell -Command "New-SelfSignedCertificate -DnsName localhost -CertStoreLocation cert:\LocalMachine\My -NotAfter (Get-Date).AddYears(10)"

Затем вы можете скопировать Thumbprint из вывода и прикрепить сертификат к localhost: 443 с помощью netsh.exe, например:

netsh http add sslcert ipport=localhost:443 certhash=110000000000003ed9cd0c315bbb6dc1c08da5e6 appid={00112233-4455-6677-8899-AABBCCDDEEFF}

Работает на Windows 8 и выше. Требуются права администратора.

person Andrey K.    schedule 17.11.2020