Самостоятельно размещенная пользовательская привязка WCF, двоичное сообщение, транспорт HTTPS БЕЗ сертификата

У меня есть несколько собственных служб WCF, использующих протокол CustomBinding для HTTP на определенном порту. Я использую BinaryMessageEncodingBindingElement и HttpTransportBindingElement до сих пор без проблем.

Теперь мне нужно немного обезопасить себя, используя HTTPS, но без сертификата. Я переключился на HttpsTransportBindingElement и установил для RequireClientCertificate значение false.

У меня нет сертификата, установленного на этом порту. Я проверил, запустив «netsh http show sslcert».

И я получаю следующую ошибку, когда пытаюсь добавить свою службу в приложение WPF (просматривая Chrome, я получаю «Эта веб-страница недоступна»):


При загрузке https://localhost:8080/myhost/myservice.svc произошла ошибка.
Базовое соединение было закрыто: при отправке произошла непредвиденная ошибка.
Невозможно прочитать данные из транспортного соединения: существующее соединение было принудительно закрыто удаленным узлом.
Существующее соединение было принудительно закрыто удаленным узлом.
Метаданные содержат ссылку, которую невозможно разрешить. : 'https://localhost:8080/myhost/myservice.svc'.
Произошла ошибка при отправке HTTP-запроса к «https://localhost:8080/myhost/myservice.svc».
Это может быть связано с тем, что сертификат сервера неправильно настроен с HTTP.SYS в случае HTTPS.
Это также может быть вызвано несоответствием привязки безопасности между клиентом и сервером.
Базовое соединение было закрыто: при отправке произошла непредвиденная ошибка.
Невозможно прочитать данные из транспортного соединения: существующее соединение было принудительно закрыто удаленным узлом.
Существующее соединение было принудительно закрыто удаленным узлом.
Если служба определена в текущем решении, попробуйте создать решение и снова добавить ссылку на службу.

Вот моя привязка:

private System.ServiceModel.Channels.Binding GetHttpBinding(String pName)
    {
        System.ServiceModel.Channels.BindingElementCollection elements = new System.ServiceModel.Channels.BindingElementCollection();

        System.ServiceModel.Channels.BinaryMessageEncodingBindingElement binaryMessageEncoding = new System.ServiceModel.Channels.BinaryMessageEncodingBindingElement();
        binaryMessageEncoding.MessageVersion = System.ServiceModel.Channels.MessageVersion.Default;
        binaryMessageEncoding.ReaderQuotas.MaxArrayLength = this._maxArrayLength;
        binaryMessageEncoding.ReaderQuotas.MaxBytesPerRead = this._maxBytesPerRead;
        binaryMessageEncoding.ReaderQuotas.MaxDepth = this._maxDepth;
        binaryMessageEncoding.ReaderQuotas.MaxNameTableCharCount = this._maxNameTableCharCount;
        binaryMessageEncoding.ReaderQuotas.MaxStringContentLength = this._maxStringContentLength;

        elements.Add(binaryMessageEncoding);

        if (this._applyHttps)
        {
            System.ServiceModel.Channels.HttpsTransportBindingElement transport = new System.ServiceModel.Channels.HttpsTransportBindingElement()
                {
                    MaxBufferSize = this._maxBufferSize,
                    MaxReceivedMessageSize = this._maxReceivedMessageSize,
                    AllowCookies = false,
                    BypassProxyOnLocal = false,
                    HostNameComparisonMode = HostNameComparisonMode.StrongWildcard,
                    MaxBufferPoolSize = this._maxBufferPoolSize,
                    TransferMode = TransferMode.Buffered,
                    UseDefaultWebProxy = true,
                    ProxyAddress = null,
                    RequireClientCertificate = false
                };
            elements.Add(transport);
        }
        else
        {
            System.ServiceModel.Channels.HttpTransportBindingElement transport = new System.ServiceModel.Channels.HttpTransportBindingElement()
                {
                    MaxBufferSize = this._maxBufferSize,
                    MaxReceivedMessageSize = this._maxReceivedMessageSize,
                };
            elements.Add(transport);
        }


        System.ServiceModel.Channels.CustomBinding custB = new System.ServiceModel.Channels.CustomBinding(elements);
        custB.Name = pName;
        custB.SendTimeout = new TimeSpan(0, 2, 0);
        return custB;
}

И я настраиваю узел службы с помощью этого метода:

private void ConfigureBinaryService(ServiceHost pHost, Type pType, String pServiceName)
    {
        pHost.AddServiceEndpoint(pType, this.GetHttpBinding(pType.Name), String.Empty);
        pHost.AddServiceEndpoint(pType, this.GetNetTcpBinding(pType.Name), String.Empty);

        pHost.Description.Endpoints[0].Name = pType.Name + "_BasicBin";
        pHost.Description.Endpoints[1].Name = pType.Name + "_TCP";

        pHost.OpenTimeout = new TimeSpan(0, 2, 0);
        pHost.CloseTimeout = new TimeSpan(0, 2, 0);

        System.ServiceModel.Description.ServiceMetadataBehavior metadataBehavior = pHost.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>();
        if (metadataBehavior == null)
        {
            metadataBehavior = new System.ServiceModel.Description.ServiceMetadataBehavior();
            pHost.Description.Behaviors.Add(metadataBehavior);
        }
        if (this._applyHttps)
            metadataBehavior.HttpsGetEnabled = true;
        else
            metadataBehavior.HttpGetEnabled = true;

        metadataBehavior.MetadataExporter.PolicyVersion = System.ServiceModel.Description.PolicyVersion.Policy15;

        if (this._applyHttps)
            pHost.AddServiceEndpoint(System.ServiceModel.Description.ServiceMetadataBehavior.MexContractName
                , System.ServiceModel.Description.MetadataExchangeBindings.CreateMexHttpsBinding(), "mex");
        else
            pHost.AddServiceEndpoint(System.ServiceModel.Description.ServiceMetadataBehavior.MexContractName
                , System.ServiceModel.Description.MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

        pHost.AddServiceEndpoint(System.ServiceModel.Description.ServiceMetadataBehavior.MexContractName
            , System.ServiceModel.Description.MetadataExchangeBindings.CreateMexTcpBinding(), this._NetTcpComm + @"/" + pServiceName + @"/mex");

        pHost.Description.Endpoints[2].Name = pType.Name + "_mex_BasicBin";
        pHost.Description.Endpoints[3].Name = pType.Name + "_mex_TCP";

        foreach (var item in pHost.Description.Endpoints[0].Contract.Operations)
            item.Behaviors.Find<System.ServiceModel.Description.DataContractSerializerOperationBehavior>().MaxItemsInObjectGraph = System.Int32.MaxValue;

        foreach (var item in pHost.Description.Endpoints[1].Contract.Operations)
            item.Behaviors.Find<System.ServiceModel.Description.DataContractSerializerOperationBehavior>().MaxItemsInObjectGraph = System.Int32.MaxValue;


        System.ServiceModel.Description.ServiceDebugBehavior debugBehavior =
            pHost.Description.Behaviors.Find<System.ServiceModel.Description.ServiceDebugBehavior>();
        if (debugBehavior == null)
        {
            debugBehavior = new System.ServiceModel.Description.ServiceDebugBehavior();
            pHost.Description.Behaviors.Add(debugBehavior);
        }
        debugBehavior.IncludeExceptionDetailInFaults = true;
    }

Когда this._applyHttps имеет значение false, мой сервис доступен как через браузер, так и по ссылке в проекте WPF.

Поэтому я впервые прошу о помощи после того, как так долго наслаждался всей вашей помощью, не спрашивая напрямую. Что мне не хватает? Поскольку он не размещен в IIS, мне все еще нужен сертификат для установки на стороне сервера только для определенного порта?

Спасибо, ребята, заранее! И если кто-то уже ответил на этот случай, я извиняюсь, что не нашел его...


person Frankidoze    schedule 13.07.2012    source источник
comment
Сертификаты используются для аутентификации другой стороны, поэтому без сертификата (или аналогичного механизма аутентификации) ваше общение может быть атаковано посредником. Также я сомневаюсь, что WCF поддерживает механизмы SSL без сертификатов (SRP, PSK и т. д.). В WCF можно использовать самоподписанный сертификат, и это несколько раз обсуждалось здесь, в SO.   -  person Eugene Mayevski 'Callback    schedule 13.07.2012
comment
Вы не можете использовать HTTPS без сертификата.   -  person Phil Degenhardt    schedule 14.07.2012
comment
Итак, как я и предполагал, мне просто нужно было создать самозаверяющий сертификат только для серверной части и привязать его к порту с помощью команды netsh.   -  person Frankidoze    schedule 17.07.2012


Ответы (1)


Итак, как я и предполагал, мне просто нужно было создать самозаверяющий сертификат только для серверной части и привязать его к порту с помощью команды netsh. На стороне клиента не требуется сертификат, что означает квази-HTTPS БЕЗ CERT.

Примечание. У меня это работало на моем компьютере. Я постараюсь обновить этот пост после развертывания в реальной среде и если мне придется исправить какую-либо ошибку.

И я знаю, что в какой-то момент мы получим полный сертификат на стороне клиента. Один камень за раз.

person Frankidoze    schedule 17.07.2012