简体   繁体   English

受 SSL 保护的 RESTFul WCF 自托管服务

[英]SSL-protected RESTFul WCF Self-hosting service

Summary概括

I am trying to implement an SSL-protected RESTFul WCF service, but following error occurred and communication failed.我正在尝试实现受 SSL 保护的 RESTFul WCF 服务,但出现以下错误并且通信失败。

making the HTTP request to ‘https://123.123.123.123:5000/TestService/PostMsg’. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server. on some customers machines.

Can anyone help me?谁能帮我? Best regards此致

Tried things:试过的东西:

I have succeeded to communicate without SSL-protection.我已经成功地在没有 SSL 保护的情况下进行通信。

Code代码

Service Implementation服务实施

using System;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.ServiceModel.Web;

namespace TestService
{
    class Program
    {
        static WebServiceHost host;

        static void Main()
        {
            WebHttpBinding binding = new WebHttpBinding();
            binding.Security.Mode = WebHttpSecurityMode.Transport;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
            binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;

            Uri uri = new Uri("https://localhost:5000/TestService");

            host = new WebServiceHost(typeof(TestService));
            ServiceEndpoint se = host.AddServiceEndpoint(typeof(ITestService), binding, uri);

            var behavior = new WebHttpBehavior();
            behavior.FaultExceptionEnabled = false;
            behavior.HelpEnabled = true;
            behavior.DefaultOutgoingRequestFormat = WebMessageFormat.Json;
            behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
            se.EndpointBehaviors.Add(behavior);

            ServiceDebugBehavior debug = host.Description.Behaviors.Find<ServiceDebugBehavior>();
            debug.IncludeExceptionDetailInFaults = true;

            ServiceMetadataBehavior metad = new ServiceMetadataBehavior();
            metad.HttpGetEnabled = true;
            metad.HttpsGetEnabled = true;
            host.Description.Behaviors.Add(metad);

            var certificate = new X509Certificate2(@"D:\Work\TestService\ServerCert1.pfx", "paswd", X509KeyStorageFlags.UserKeySet);
            host.Credentials.ServiceCertificate.Certificate = certificate;
            host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;

            host.Open();
            Console.WriteLine(string.Format(null, "URL : {0}", uri.ToString()));
            Console.WriteLine("Press <ENTER> to terminate");
            Console.ReadLine();
            host.Close();

        }
    }
}

Interface界面

using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace TestService
{
    [ServiceContract]
    interface ITestService
    {
        [OperationContract]
        [WebInvoke(Method = "POST"
                            , RequestFormat = WebMessageFormat.Json
                            , UriTemplate = "/PostMsg"
            )]
        MessageData PostMsg(MessageData msg);
    }

    [DataContract]
    public class MessageData
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public int Gender { get; set; }

        [DataMember]
        public int Age { get; set; }

    }

}
using System;

namespace TestService
{
    class TestService : ITestService
    {
        public MessageData PostMsg(MessageData msg)
        {
            Console.WriteLine(string.Format(null, "Recieved Name: {0}, Gender:{1}, Age:{2}", msg.Name , msg.Gender , msg.Age));

            return new MessageData()
            {
                Name = msg.Name,
                Gender = msg.Gender,
                Age = msg.Age + 1
            };
        }

    }
}

Client Implementation客户端实施

using System;
using System.Windows.Forms;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.ServiceModel.Web;
using TestService;

namespace TestClient
{
    public partial class Form1 : Form
    {
        WebChannelFactory<ITestService> cf = null;
        ITestService channel = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            listBox1.HorizontalScrollbar = true;

            Uri uri = new Uri("https://123.123.123.123:5000/TestService");
            EndpointAddress endpointAddress = new EndpointAddress(uri);

            cf = new WebChannelFactory<ITestService>(uri);
            WebHttpBinding binding = cf.Endpoint.Binding as WebHttpBinding;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
            binding.Security.Mode = WebHttpSecurityMode.Transport;

            var behavior = new WebHttpBehavior();
            behavior.FaultExceptionEnabled = false;
            behavior.HelpEnabled = true;
            behavior.DefaultOutgoingRequestFormat = WebMessageFormat.Json;
            behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
            cf.Endpoint.Behaviors.Add(behavior);

            var clientCertificate = new X509Certificate2(@"D:\Work\TestService\ServerCert1.pfx", "pswd", X509KeyStorageFlags.UserKeySet);
            cf.Credentials.ClientCertificate.Certificate = clientCertificate;
            cf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;

            channel = cf.CreateChannel();

        }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageData msg = new MessageData()
            {
                Name = "Taro",
                Gender = 1,
                Age = 3
            };
            MessageData rtn = channel.PostMsg(msg);
            listBox1.Items.Insert(0, string.Format("Name:{0}, Gender:{1}, Age{2} ", rtn.Name, rtn.Gender, rtn.Age));

        }
    }
}

Script for creating pfx file.用于创建 pfx 文件的脚本。

@echo ----------------------------------------------
@echo     Script for creating self certificate
@echo ----------------------------------------------
@set "TOOL_DIR=E:\Windows Kits\10\bin\10.0.18362.0\x86"
@if not exist "%TOOL_DIR%" (
    @echo Tools do not exists. %TOOL_DIR%
    @goto ERROR_EXIT
)
@set "PATH=%TOOL_DIR%;%PATH%"

@set "WORK_DIR=D:\Work\TestService"
@if not exist %WORK_DIR% ( 
    @echo Work folder does not exist. %WORK_DIR%
    @goto ERROR_EXIT
)
cd  /d %WORK_DIR%

@openfiles > NUL 2>&1 
@if NOT %ERRORLEVEL% EQU 0 (
    @echo It is not being executed as an administor.
    goto ERROR_EXIT
)

@SET /P ANS="Create a certificate file. Are you sure (Y / N)?"
@if /i %ANS% NEQ y if /i %ANS% NEQ Y goto ERROR_EXIT

del %WORK_DIR%\*.*
@echo;

@echo Create Self-Signed Certificate
makecert -n "CN=ServerCN1" -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv ServerCert1.pvk ServerCert1.cer -cy authority -b 11/06/2019 -e 12/31/2019
@echo;

@echo Create Software Publisher Certificate File
cert2spc ServerCert1.cer ServerCert1.spc
@echo;

@echo Create Personal Information Exchange File
pvk2pfx -pvk ServerCert1.pvk -spc ServerCert1.spc -po pswd -pfx ServerCert1.pfx -f
@echo;

:ERROR_EXIT
@SET /P ANS="Finished."

Requisite必要的

  • I don't want to use IIS because I think there are a lot of settings with it.我不想使用 IIS 因为我认为它有很多设置。 Instead, I am using X509Certificate2 method of X509Certificate2 class in "System.Security.Cryptography.X509Certificates" Name space.相反,我在“System.Security.Cryptography.X509Certificates”命名空间中使用 X509Certificate2 class 的 X509Certificate2 方法。 (.NETFramework\v4.7.2\System.dll) (.NETFramework\v4.7.2\System.dll)

  • Server and client are on the same computer at this moment.此时服务器和客户端在同一台计算机上。

  • Ports of Windows defender firewall are opened. Windows防御者防火墙的端口打开。

Some Images for understanding what I am doing.一些用于理解我在做什么的图像。

https://i.stack.imgur.com/MCFl9.png https://i.stack.imgur.com/MCFl9.png

https://i.stack.imgur.com/DgNid.png https://i.stack.imgur.com/DgNid.png

Using a certificate to secure the communication requires that we bind the certificate to the particular computer port with the below command.使用证书来保护通信需要我们使用以下命令将证书绑定到特定的计算机端口。

Netsh http add sslcert ipport=0.0.0.0:5000 certhash= 0102030405060708090A0B0C0D0E0F1011121314 appid= appid={00112233-4455-6677-8899-AABBCCDDEEFF} Netsh http 添加 sslcert ipport=0.0.0.0:5000 certhash= 0102030405060708090A0B0C0D0E0F1011121314 appid= appid={00112233-4455-6677AAFF7-BBC8

https://docs.microsoft.com/en-us/windows/win32/http/add-sslcert https://docs.microsoft.com/en-us/windows/win32/http/add-sslcert
https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate

This feature is auto-completed in IIS when we set up the site binding.当我们设置站点绑定时,此功能在 IIS 中自动完成。 在此处输入图像描述

  WebHttpBinding binding = new WebHttpBinding();
            binding.Security.Mode = WebHttpSecurityMode.Transport;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Cert

About authenticating the client with a certificate, please refer to the below official document.关于使用证书对客户端进行身份验证,请参考以下官方文档。 https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/transport-security-with-certificate-authentication https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/transport-security-with-certificate-authentication
There are usually two points we need to pay attention to.通常有两点我们需要注意。

    1. We should ensure that both the service certificate and the client certificate should have client authentication and server authentication purpose. 1、我们要保证服务证书和客户端证书都具有客户端认证和服务器认证的目的。

Server Authentication (1.3.6.1.5.5.7.3.1)服务器身份验证 (1.3.6.1.5.5.7.3.1)
Client Authentication (1.3.6.1.5.5.7.3.2)客户端身份验证 (1.3.6.1.5.5.7.3.2)

    2. We had better build the project over the Dotnet framwork4.6.2 since there is something wrong with reading the private key of the certificate created by using PowerShell certificate. 2.我们最好在Dotnet framwork4.6.2上构建项目,因为读取使用PowerShell证书创建的证书的私钥有问题。

Feel free to let me know if there is anything I can help with.如果有什么我可以帮忙的,请随时告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM