Соединения SSL / TLS не работают при развертывании в качестве службы приложений Azure

У моей команды есть приложение, которое в настоящее время развернуто как облачная служба Azure. Приложение работает хорошо, но после развертывания в качестве службы приложения (в виде непрерывного веб-задания) мы наблюдаем множество типов сбоев подключения TLS. Сертификаты TLS загружаются в клиенты HTTPS и клиенты сокетов TCP. Почему они ломаются при работе в качестве службы приложений?

TCP:

System.ComponentModel.Win32Exception (0x80004005): The credentials supplied to the package were not recognized
System.Net.SSPIWrapper.AcquireCredentialsHandle(SSPIInterface SecModule, String package, CredentialUse intent, SecureCredential scc)
System.Net.Security.SecureChannel.AcquireCredentialsHandle(CredentialUse credUsage, SecureCredential& secureCredential)
System.Net.Security.SecureChannel.AcquireClientCredentials(Byte[]& thumbPrint)
System.Net.Security.SecureChannel.GenerateToken(Byte[] input, Int32 offset, Int32 count, Byte[]& output)
System.Net.Security.SecureChannel.NextMessage(Byte[] incoming, Int32 offset, Int32 count)
System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)

HTTP:

System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
System.Net.ConnectStream.WriteHeaders(Boolean async)   --- End of inner exception stack trace ---
System.Net.HttpWebRequest.GetRequestStream(TransportContext& context)
System.Net.HttpWebRequest.GetRequestStream()

Удаленный сертификат:

-----BEGIN CERTIFICATE-----
MIIHMDCCBhigAwIBAgIQTVJjt9EMcV3VkbZ5cKqBNzANBgkqhkiG9w0BAQsFADBDMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMuMR0wGwYDVQQDExR0aGF3dGUgU0hBMjU2IFNTTCBDQTAeFw0xNzAxMDkwMDAwMDBaFw0yMDAxMTMyMzU5NTlaMIGJMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHQXJpem9uYTEQMA4GA1UEBwwHUGhvZW5peDEpMCcGA1UECgwgQmVzdCBXZXN0ZXJuIEludGVybmF0aW9uYWwsIEluYy4xDzANBgNVBAsMBkJXSSBJVDEaMBgGA1UEAwwRKi5iZXN0d2VzdGVybi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfGMbiDXX6nJRxXqUuRXNaRX89lhEDmfEZkhdIcVBcaOiplb/e+lECPVtQt9+b8e8P+cOcz8vkH7tK5v/z0kkzjnoewVZCpXUHjrlJ7zjMgxPAS6oXR92UMQuVF2t0FbYDQpVOH5Sd5tB/nIiTz2bkTL+9ugUNeXNZGxGcXJOQGbpCB2s+a85bqy1Fd8R2apfKmBNrYRGHzW/WmuWaJBBjnKJDLjDuT/zdhrlwmNGerKNsxVNDycWKdoWTvR0Kc79vVuy8yKn3iMvti87xlFOhKtKupJeQdJZrGOD7fxXTwrXNU2f+Xs9AzaEnHk8OONBLNyGaDEnnQKJ1cDqoMHEXAgMBAAGjggPXMIID0zCBhgYDVR0RBH8wfYIVKi5kZXYuYmVzdHdlc3Rlcm4uY29tghMqLmguYmVzdHdlc3Rlcm4uY29tghQqLnFhLmJlc3R3ZXN0ZXJuLmNvbYIVKi51YXQuYmVzdHdlc3Rlcm4uY29tghEqLmJlc3R3ZXN0ZXJuLmNvbYIPYmVzdHdlc3Rlcm4uY29tMAkGA1UdEwQCMAAwbgYDVR0gBGcwZTBjBgZngQwBAgIwWTAmBggrBgEFBQcCARYaaHR0cHM6Ly93d3cudGhhd3RlLmNvbS9jcHMwLwYIKwYBBQUHAgIwIwwhaHR0cHM6Ly93d3cudGhhd3RlLmNvbS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIFoDAfBgNVHSMEGDAWgBQrmjWuARg4MOFwegXgEXajzr2QFDArBgNVHR8EJDAiMCCgHqAchhpodHRwOi8vdGcuc3ltY2IuY29tL3RnLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzABhhNodHRwOi8vdGcuc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vdGcuc3ltY2IuY29tL3RnLmNydDCCAfUGCisGAQQB1nkCBAIEggHlBIIB4QHfAHUA3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvswAAAFZg/5pBAAABAMARjBEAiAO/RJNQohPu9QH3jhKEAauOhwgipewvLI46YdajWVXhwIgGVLL+3CzMnstr3dbpTg4pTLPTQMLj7qFZhv7SjrzNOQAdgDuS723dc5guuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAVmD/mlVAAAEAwBHMEUCIQC/THA9utbL280ZvItjzqQ2RzwByfujkNfva5c9LW/52QIgc6AqANaG+4ZX7JdnTcSXZWxASM+bQYzR3Yjg+EXRoGUAdQC8eOHfxfY8aEZJM02hD6FfCXlpIAnAgbTz9pF/Ptm4pQAAAVmD/mn2AAAEAwBGMEQCICOuhzZtHyvKhRuVkMSYlppmuXKWbhVYkgekN+wbnphSAiAwhIV7WJtGM8yhvvUmUj28Vle6taNvoB9YDF8uv/Z6BwB3AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAABWYP+aSMAAAQDAEgwRgIhAJI8jzswB8cmOXVp8IKLspA7Orq8MGmOc+3cv0U/i0T8AiEAszn07XmjFxWBztILLpNTo4IRXUhyFaeh8BjyzFLk+2EwDQYJKoZIhvcNAQELBQADggEBACcA2n8ltg34ZZiNkUmefNja1sZ/9tXJzROmCXkrjFE8zuUwKp0Ie7w5T0DYpdolvb5OvVG05C5Yo7Uke086XzYroFWnU+bcQIdn57cZKc5+aOuAhofIGouKi99srXjJuNatT/K0q1XKEkjMB8VYLHELXsFixwxRQz1MDeEEw+9+AS09qkKfflCoWvmerk8QXwxWwbMGvdO+PI21sw12dHk0bLUuIRriSbga8/PbE/sUM4vPKxMiimDUhwp7vjan06XtjsjHZryGbiNrB77iJH69ob8cKNzcgxM9+MdoqFV+EgC33iaTgSW2wL2w2s64IoJHWLbagec3qAcqySO+4Ng=
-----END CERTIFICATE-----

person B. McCartney    schedule 29.12.2017    source источник
comment
Итак, что это за удаленный сертификат? Можно ли его проверить с помощью стандартных корневых центров сертификации Windows Server 2016 (запустить Get-Childitem cert:LocalMachine\root -Recurse в Kudu)?   -  person evilSnobu    schedule 30.12.2017
comment
Я добавил удаленный сертификат выше. Из kudu powershell я могу запускать $cert.Verify() и Test-Certificate -Cert $cert. Оба они успешны   -  person B. McCartney    schedule 04.01.2018


Ответы (2)


Сертификат thawte Primary Root CA - G3 с thumbprintF18B538D1BE903B6A6F056435B171589CAF36BF2 отсутствует в надежном корневом хранилище на работнике службы приложений, поэтому удаленная проверка сертификата не выполняется. Листовой сертификат (промежуточный) также отсутствует.

Путь сертификации

Сравнение доверенного корневого хранилища, локального и Kudu

А это не очень хорошие новости. Я вижу два варианта: либо создать цепочку на лету (который загружает недостающий сертификат CA с диска) или слепо возвращает true из WebRequestHandler и его ServerCertificateValidationCallback свойства делегата .

ПРИМЕЧАНИЕ: у меня есть где-то рабочий образец для первой части, дайте мне знать, если я должен его поискать. По сути:

  • Загрузите отсутствующий корневой ЦС с диска в X509Certificate2
  • Постройте цепочку и добавьте ее в
  • Передайте ServerCertificateValidationCallback в WebRequestHandler
person evilSnobu    schedule 04.01.2018
comment
Вероятно, вам следует открыть запрос в службу поддержки Azure, чтобы добавить как корневой центр сертификации, так и промежуточный - я вижу, что этот сертификат поставляется в комплекте с Windows 10, поэтому он должен быть популярным поставщиком. - person evilSnobu; 04.01.2018
comment
мы продвигаемся вперед с заявкой в ​​службу поддержки, большое спасибо за совет! - person B. McCartney; 05.01.2018
comment
Оказывается, нам нужно было добавить WEBSITE_LOAD_CERTIFICATES = * в настройках приложения на портале, хотя мы не загружаем наши сертификаты в область SSL-сертификатов портала. Если эта конфигурация не задана, сертификат клиента не пройдет проверку. Таким образом, удаленный сертификат никогда не имел возможности оценить. Это также было очевидно по тому факту, что функция ServerCertificateValidationCallback не вызывалась. - person B. McCartney; 12.01.2018
comment
Я думаю, что корневые сертификаты загружаются как часть процесса рукопожатия. Я попытался воспроизвести проблему на своем локальном компьютере, удалив сертификаты thawte. Но когда я запускал свой образец, сертификат thawte переустанавливался. - person B. McCartney; 12.01.2018
comment
Корневые центры сертификации никогда не загружаются как часть рукопожатия TLS, только конечные сертификаты (промежуточные) и только клиентами, которые поддерживают AIA (расширение доступа к информации о полномочиях) - на практике это означает только браузеры. Подумайте об этом, если они также отправят вам корневой сертификат, они могут также подписать его самостоятельно или выпустить его через центр сертификации Joe's Crab Shack ... он все равно будет действителен. - person evilSnobu; 12.01.2018
comment
Верно, поэтому они, вероятно, не загружаются, но корневой ЦС снова появляется в моем магазине, просто запустив мой образец exe. Поскольку это вызывает предположение, что наличие сертификата в магазине и его доверие не совсем 1 к 1, я не мог быть на 100% уверен, что поиск корневого сертификата в хранилище объяснит, почему соединение не работает. - person B. McCartney; 12.01.2018

Добавьте это в настройки приложения на портале для своей службы приложений.

WEBSITE_LOAD_CERTIFICATES = *
  • Это все еще необходимо, даже если вы не загружаете сертификаты в области SSL-сертификатов портала.
  • Это повлияет на веб-задания для этой службы приложения.
person B. McCartney    schedule 11.01.2018
comment
Это не объясняет, поскольку этот параметр приложения извлекает сертификаты, которые вы загружаете через портал, в личное хранилище (My store) на веб-воркере, это не имеет ничего общего с хранилищем Trusted Root. Но если это заставит его работать, тогда в игре есть недокументированные функции. - person evilSnobu; 12.01.2018
comment
Можете ли вы проверить, что возвращает Get-Childitem cert:LocalMachine\root -Recurse? Вы видите отпечаток большого пальца Thawte? - person evilSnobu; 12.01.2018
comment
Во время тестирования служба была фактически подключена к прокси-серверу, что вызывало дополнительную проблему с рукопожатием. Я продолжил тестирование с другим сервисом, не использующим прокси. - person B. McCartney; 12.01.2018
comment
Для другой моей службы корневой ЦС был виден в магазине и вызвал такое же исключение (TCP). Как и в случае с другими службами, с которыми у нас возникла проблема, сертификат клиента извлекается из нашего репозитория (а не через портал). После вызова AuthenticateAsClient появляется исключение. ServerCertificateValidationCallback не вызывается, но если посмотреть на журнал сетевой трассировки, похоже, выполняется оценка удаленного сертификата. - person B. McCartney; 12.01.2018