Как добавить SSL в приложение .net, использующее httplistener - оно * не * * будет работать в IIS

Последние изменения выделены жирным шрифтом. Я использую HttpListener класс .net , но я не буду запускать это приложение в IIS и не использую ASP.net. Этот веб-сайт описывает, какой код на самом деле использовать для реализации SSL с asp.net, и этот сайт описывает, как настроить сертификаты (хотя я не уверен, работает ли он только для IIS или нет).

В документации по классу описаны различные типы аутентификации (базовая, дайджест, Windows и т. Д.) - ни один из них не относится к SSL. В нем говорится, что если используется HTTPS, вам необходимо установить сертификат сервера . Будет ли это настройка свойства одной строкой, а HttpListener все остальное будет выяснено?

Короче говоря, мне нужно знать, как настроить сертификаты и как изменить код для реализации SSL.

Хотя этого не происходит, когда я пытаюсь получить доступ к HTTPS, я заметил ошибку в моем журнале системных событий - источником является «Schannel», а содержимое сообщения:

Произошла фатальная ошибка при попытке доступа к закрытому ключу учетных данных сервера SSL. Код ошибки, возвращаемый криптографическим модулем, - 0x80090016.

Изменить:
шаги, предпринятые до сих пор

  • Создал рабочий HTTPListener на C#, который работает для HTTP-соединений (например, « http: // localhost: 8089 / foldername / »
  • Создал сертификат с помощью makecert.exe
  • Добавлен доверенный сертификат с помощью certmgr.exe
  • Используется Httpcfg.exe для прослушивания SSL-соединений на тестовом порту (например, 8090)
  • Добавлен порт 8080 в HTTPListener через listener.Prefixes.Add ( https: // localhost: 8090 / foldername / ");
  • проверил соединение HTTP-клиента, например ( http: // localhost: 8089 / foldername / ") в браузере, и получил правильный ответ
  • протестировали клиентское соединение HTTPS, например ( http: // localhost: 8090 / foldername / ") в браузере и получили сообщение" Передача данных прервана "(в Firefox)
  • отладка в Visual Studio показывает, что обратный вызов слушателя, который получает запросы, никогда не срабатывает при запуске HTTPS-соединения - я не вижу места, где я мог бы установить точку останова, чтобы поймать что-либо еще раньше.
  • netstat показывает, что порты прослушивания открыты как для HTTPS, так и для HTTP. порт HTTPS переходит в TIME_WAIT после попытки подключения.
  • Fiddler и HTTPAnalyzer не улавливают какой-либо трафик, я думаю, он недостаточно далеко продвинулся в процессе, чтобы отобразиться в этих инструментах анализа HTTP.

Вопросов

  • В чем может быть проблема?
  • Есть ли кусок кода .Net, который мне не хватает (это означает, что я должен делать больше на C#, кроме простого добавления префикса к слушателю, который указывает на HTTPS, что я и сделал)
  • Вы где-то пропустили шаг настройки?
  • Что еще я могу сделать, чтобы проанализировать проблему?
  • Является ли сообщение об ошибке в журнале системных событий признаком проблемы? Если да, то как это исправить?

Ответов (6)

Я столкнулся с той же проблемой, что и вы. К счастью, после сложных шагов на этой странице SSL работает с моим HttpListener.

Вам просто нужно привязать сертификат к порту ip: и затем открыть свой слушатель с префиксом https: //. 0.0.0.0 применяется ко всем ip. appid - это любой случайный GUID, а certhash - это хэш сертификата (иногда называемый отпечатком пальца).

Выполните следующее с cmd.exe с правами администратора.

netsh http add sslcert ipport=0.0.0.0:1234 certhash=613bb67c4acaab06def391680505bae2ced4053b  appid={86476d42-f4f3-48f5-9367-ff60f2ed2cdc}

Если вы хотите создать самозаверяющий сертификат, чтобы проверить это,

  1. Откройте IIS

  2. Нажмите на имя вашего компьютера

  3. Щелкните значок Сертификаты сервера.

  4. Нажмите "Создать самоподписанный сертификат".

  5. Дважды щелкните и перейдите к деталям

  6. Там вы увидите отпечаток большого пальца, просто удалите пробелы.

     HttpListener listener = new HttpListener();
     listener.Prefixes.Add("https://+:1234/");
     listener.Start();
     Console.WriteLine("Listening...");
     HttpListenerContext context = listener.GetContext();
    
     using (Stream stream = context.Response.OutputStream)
     using (StreamWriter writer = new StreamWriter(stream))
         writer.Write("hello, https world");
    
     Console.ReadLine();
    

После запуска этой программы я просто перешел к https://localhost:1234 напечатанному тексту. Поскольку CN сертификата не соответствует URL-адресу и его нет в хранилище доверенных сертификатов, вы получите предупреждение о сертификате. Однако текст зашифрован, что можно проверить с помощью такого инструмента, как Wire Shark.

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


Также очень важно, чтобы, если вы общаетесь со службой https из кода, который имеет предупреждение ssl, вы должны настроить валидатор сертификата в диспетчере точек обслуживания, чтобы обойти его в целях тестирования.

ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, errors) => true;

Вот альтернативный способ привязать сертификат SSL к комбинации IP / PORT без использования httpcfg.exe (XP) или netsh.exe (Vista +).

http://dotnetcodebox.blogspot.com.au/2012/01/how-to-work-with-ssl-certificate.html

Суть в том, что вы можете использовать C++ HttpSetServiceConfiguration API, встроенный в Windows, чтобы делать это программно, а не через командную строку, тем самым удаляя зависимость от ОС и устанавливая httpcfg.

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

Документация класса

есть это примечание:

Если вы создаете HttpListener с использованием https, вы должны выбрать сертификат сервера для этого слушателя. В противном случае запрос HttpWebRequest этого HttpListener завершится ошибкой с неожиданным закрытием соединения.

и это:

Вы можете настроить сертификаты сервера и другие параметры прослушивателя с помощью HttpCfg.exe. См. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/http/http/httpcfg_exe.asp для получения дополнительных сведений. Исполняемый файл поставляется с Windows Server 2003 или может быть построен из исходного кода, доступного в Platform SDK.

Объясняется ли первая нота второй? Как указано в вопросе, я использовал httpcfg.exe для привязки сертификата к определенному порту. Если они хотят чего-то другого, то примечание двусмысленное.

У меня похожая проблема, и похоже, что проблема может быть в самом сертификате.

Вот путь, который сработал для меня:

makecert.exe -r -a sha1 -n CN=localhost -sky exchange -pe -b 01/01/2000 -e 01/01/2050 -ss my -sr localmachine

затем найдите отпечаток сертификата , скопируйте его в буфер обмена и удалите пробелы. Это будет параметр после -h в следующей команде:

HttpCfg.exe set ssl -i 0.0.0.0:801 -h 35c65fd4853f49552471d2226e03dd10b7a11755

затем запустите хост службы на https: // localhost: 801 /, и он отлично работает.

то, что я не могу заставить работать, - это запуск https на самогенерированном сертификате. Вот код, который я запускаю для его создания (обработка ошибок для ясности вынесена):

LPCTSTR pszX500 = subject;
DWORD cbEncoded = 0;
CertStrToName(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, NULL, pbEncoded, &cbEncoded, NULL);
pbEncoded = (BYTE *)malloc(cbEncoded);
CertStrToName(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, NULL, pbEncoded, &cbEncoded, NULL);

// Prepare certificate Subject for self-signed certificate
CERT_NAME_BLOB SubjectIssuerBlob;
memset(&SubjectIssuerBlob, 0, sizeof(SubjectIssuerBlob));
SubjectIssuerBlob.cbData = cbEncoded;
SubjectIssuerBlob.pbData = pbEncoded;

// Prepare key provider structure for self-signed certificate
CRYPT_KEY_PROV_INFO KeyProvInfo;
memset(&KeyProvInfo, 0, sizeof(KeyProvInfo));
KeyProvInfo.pwszContainerName = _T("my-container");
KeyProvInfo.pwszProvName = NULL;
KeyProvInfo.dwProvType = PROV_RSA_FULL;
KeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET;
KeyProvInfo.cProvParam = 0;
KeyProvInfo.rgProvParam = NULL;
KeyProvInfo.dwKeySpec = AT_SIGNATURE;

// Prepare algorithm structure for self-signed certificate
CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
memset(&SignatureAlgorithm, 0, sizeof(SignatureAlgorithm));
SignatureAlgorithm.pszObjId = szOID_RSA_SHA1RSA;

// Prepare Expiration date for self-signed certificate
SYSTEMTIME EndTime;
GetSystemTime(&EndTime);
EndTime.wYear += 5;

// Create self-signed certificate
pCertContext = CertCreateSelfSignCertificate(NULL, &SubjectIssuerBlob, 0, &KeyProvInfo, &SignatureAlgorithm, 0, &EndTime, 0);
hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"MY");
CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, 0);

Сертификат отображается нормально, и у него есть рабочий закрытый ключ, но время ожидания https истечет, как если бы отпечаток никогда не регистрировался. Если кто знает почему - прокомментируйте плз

РЕДАКТИРОВАТЬ1 : После некоторой игры я нашел инициализацию CertCreateSelfSignCertificate, которая генерирует правильный сертификат:

CRYPT_KEY_PROV_INFO KeyProvInfo;
memset(&KeyProvInfo, 0, sizeof(KeyProvInfo));
KeyProvInfo.pwszContainerName = _T("my-container");
KeyProvInfo.pwszProvName = _T("Microsoft RSA SChannel Cryptographic Provider");
KeyProvInfo.dwProvType = PROV_RSA_SCHANNEL;
KeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET;
KeyProvInfo.cProvParam = 0;
KeyProvInfo.rgProvParam = NULL;
KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;