Почему .NET CF вызывает исключение при работе с HTTPS-серверами
Пятница, 23 Ноябрь, 2007Недавно в .NET CF версии 2.0 (SP2 и ранее) и 3.5 был обнаружен баг, приводящий к отказу в запросах HttpWebRequest к https серверам. Ошибка (для веб сервисов) выглядит следующим образом:
The error is “Unable to read data from the transport connection.”
System.Web.Services.dll!System.Web.Services.Protocols.WebClientProtocol.GetWebResponse(System.Net.WebRequest request = {System.Net.HttpWebRequest}) + 0×14 bytes
System.Web.Services.dll!System.Web.Services.Protocols.HttpWebClientProtocol.GetWebResponse(System.Net.WebRequest request = {System.Net.HttpWebRequest}) + 0×7 bytes
System.Web.Services.dll!System.Web.Services.Protocols.SoapHttpClientProtocol.doInvoke(string methodName = “…”, object[] parameters = {Dimensions:[2]}, System.Web.Services.Protocols.WebClientAsyncResult asyncResult = null) + 0×2ce bytes
System.Web.Services.dll!System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(string methodName = “…”, object[] parameters = {Dimensions:[2]}) + 0×9 bytes
Для стандартного вызова HttpWebRequest лог стека может быть другой, несмотря на то, что ошибка происходит по одной и той же причине.
Несмотря на множество сообщений об ошибке, причина заключается в том, что сервер отправляет NetCF-клиенту пустой шифрованный пакет. Ниже иллюстрация процесса шифрования, происходящего на сервере для https:
Сначала инициализируется буфер, содержащий незашифрованные данные, которые сервер собирается отправить клиенту по сети.
Шаг 1: незашифрованные данные
Затем, сервер вызывает EncryptMessage и незашифрованные данные шифруются, оставаясь на том же месте.
Шаг 2: заголовок (header) зашифрованные данные футер (footer)
Длина заголовка — 5 байт, длина футера немного больше. Получившийся пакет имеет большую длину, чем оригинальный незашифрованный. Он отправляется по сети и на выходе разворачивается процессом с помощью функции DecryptMessage.
Проблема с NetCF SSL- стеком возникает в момент, когда сервер шифрует буфер нулевой длины и отправляет его клиенту.
Шаг 1: 0 байт незашифрованных данных Шаг 2: заголовок (header) зашифрованное представление буфера нулевой длины футер (footer)
В то время как нешифрованные данные имеют нулевую длину, зашифрованный блок будет иметь длину не равную нулю. Когда этот пакет будет отправлен NetCF-клиенту, текущая версия NetCF дешифрует пакет и вернет буфер нулевой длины вызывающему клиенту. Семантика сетевого метода Read состоит в том, что возврат буфера нулевой длины означает закрытие сокета. Т.к. NetCF после дешифрации «пустого» зашифрованного пакета вернет пустой буфер, вызывающий клиент может интерпретировать это, как сигнал о закрытии сокета и прервет соединение.
Именно это и происходит, когда NetCF веб-сервис (вызываемый через SSL) возвращает пустой зашифрованный пакет. В результате соединение обрывается до момента получения полного ответа и, как следствие, срабатывает исключение.
Что заставляет сервер посылать эти пустые зашифрованные пакеты? Технически, это не запрещено. Все зависит от вашего сервера и его конфигурации. Как следствие, может происходить регулярно, а может никогда не произойти.
К сожалению, нет способа заставить NetCF игнорировать такие пакеты, разве что реорганизовать процесс (по возрастанию сложности):
- 1. Не использовать SSL для запросов (как следствие, снижение безопасности, т.к. данные отправляются открытым текстом).
- 2. Переконфигурировать сервер
- 3. Построить новый веб-сервер, который будет перенаправлять запросы с устройства и обратно.
- 4. Подождать новых версий NetCF, в которых проблема будет устранена.
- 5. Написать собственный https клиент, используя нативный код или P/Invoke в NetCF.
[…]
Эндрю Арнот (Andrew Arnott), 19 ноября 2007, 22:18
Why .NET Compact Framework fails to call some HTTPS web servers
