Self Host WebApi服务传输层SSL加密(服务器端+客户端调用)

接上篇《WebApi服务URI加密及验证的两种方式》,在实际开发中,仅对URI进行加密是不够的,在传输层采用SSL加密也是必须的。

如果服务寄宿于IIS,那对传输层加密非常简单仅需要配置一下项目属性开启SSL即可,所用的证书也是IIS帮你搞定(默认443端口证书),基本就是傻瓜式配置后即可强制使用HTTPS访问资源。

而在Self Host WebApi服务中使用传输层SSL加密,则需要自己手动绑定服务器端的证书,并且在服务器端对客户端Request验证其是否为HTTPS访问方式;在客户端也需要对原有(上一篇)代码做一些小小的补充。

 

服务端:

服务端的工作分为服务器证书绑定,以及WebApi自宿主代码修改。

1:服务器证书绑定

    关于如何在服务器端绑定一个自签名的证书,网上有很多资料,这里只是简要记录一下要点。因为认证机构颁发的证书都需要钱,开发测试过程中不需要购买证书,只需要在测试端口绑定一个自签名证书即可。

step 1: 找一个可用的证书指纹

    我是在开发环境下的IIS(服务器证书)中找的一个已安装的证书,即IIS Express Development Certificate。找到详细信息中的指纹(一串16进制加密数字),复制备用;

step 2:拿到项目的UUID,我就用的sln文件中该项目的UUID;

step 3:以管理员权限运行cmd,为指定端口绑定一个自签名的证书

    netsh命令不再详述,只给出这里需要用到得3个命令:

           1)端口7777绑定证书:netsh http add sslcert ipport=0.0.0.0:7777 certhash=8239ff15609faaa25f55d07931e78bee96bbXXXX appid={FAE04EC0-301F-11D3-BF4B-00C04F79EFBC},运行后如果绑定成功,将提示“成功添加SSL证书”;

      其中,certhash即step1复制的指纹,appid即step2的uuid;  

    2)端口7777证书删除:netsh http delete sslcert ipport=0.0.0.0:7777,运行成功后如果成功删除该端口上绑定的证书,则将提示“成功删除SSL证书”;

    3)查看SSL证书:netsh http show sslcert,执行后将列出机器上所有端口绑定的证书,找到端口7777的证书,将能看到证书哈希及应用程序ID信息,与之前添加的一致。

 

2:Web API 自宿主服务器端代码修改

服务器端的代码修改包括:1)uri的scheme验证,确保资源访问必须采用ssl加密;2)自宿主服务器启动监听uri改为https字头。

scheme验证仍然采用与上篇文章相同的方法,即实现一个新的HttpMessageHandler,并将其注入到消息处理管道中:

 1     public class HttpsGuard : DelegatingHandler
 2     {
 3         protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 4         {
 5             if (!request.RequestUri.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.CurrentCultureIgnoreCase))
 6             {
 7                 var response = new HttpResponseMessage(HttpStatusCode.BadRequest)
 8                 {
 9                     Content = new StringContent("Https is required for security reason")
10                 };
11                 return Task.FromResult(response);
12             }
13             return base.SendAsync(request, cancellationToken);
14         }
15     }
View Code

修改Startup的Configuration,将上面的HttpMessageHandler注入:

 1         public void Configuration(IAppBuilder app)
 2         {
 3             // Configure Web API for self-host. 
 4             var config = new HttpConfiguration();
 5             config.Routes.MapHttpRoute(
 6                 name: "DefaultApi",
 7                 routeTemplate: "api/{controller}/{action}/{param}",
 8                 defaults: new { id = RouteParameter.Optional }
 9             );
10             //注入response handler
11             config.MessageHandlers.Add(new ResponseHandler());
12             //注入httpmessagehandler用于验证request是否均采用SSL加密传输
13             config.MessageHandlers.Add(new HttpsGuard());
14             //允许WebApi的跨域访问 - 测试条件下允许所有域的跨域访问
15             var cors = new EnableCorsAttribute("*", "*", "*");
16             config.EnableCors(cors);
17 
18             app.UseWebApi(config);
19         }
View Code

服务器启动则改为如下:

1 var urlBase = new UriBuilder("HTTPS", ip, 7777).Uri;
2 
3 var server = WebApp.Start<Startup>(url: urlBase.AbsoluteUri);
View Code

以上三部分修改完成后,对web api服务端代码的修改完毕,可以启动服务,正常开始监听本地7777端口。此时如果在浏览器中访问,则会提示证书失效,是否继续访问等等大家熟悉的场景。

 

客户端:

如何远程调用SSL通信加密后的Web Api资源

以上步骤完成后,直接在客户端经由上一篇所重写的HttpClient访问加密后的资源,一般会报一个“基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。”这样的错(我的VS是中文版的),产生这个错误的原因是因为服务器上7777端口绑定的证书错了(因为我们搞了个自签名的测试证书),或者证书过期了啊什么的,也都会出现这个问题,总之就是证书不受信任了。绑定一个有效的受信证书就不会有这个问题了(没试过)。这里为了方便开发及测试,需要对客户端代码做如下修改,目的就是不验证证书的有效性,即接受所有证书的认证。

新增如下方法:

private static bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
        return true;
}
View Code

在base.SendAsync(request, cancellationToken)之前,调用下这个方法:

ServicePointManager.ServerCertificateValidationCallback += RemoteCertificateValidate;

即可。

或者不需要新增这个方法,直接在base.SendAsync(request, cancellationToken)之前新增一句:ServicePointManager.ServerCertificateValidationCallback += delegate { return true; };即可,简单粗暴,信任所有证书。

 

以上步骤完成之后,即可通过

var myResponse = myClient.GetAsync("https://192.168.1.166:7777/api/test/GetUser/id=123123&name=jiakai").Result;

类似的URI访问服务器资源。经测试,SSL加密后的客户端-服务器通信消息,在fiddler中无法抓取到包(网上有fiddler抓取https通信包的各种方法,不较真了)。

 

经过上篇与本篇的通信加密方法,即可建立一套基本的web api资源安全通信方式,可满足通常情况下的通信安全需求。

 

posted @ 2015-10-29 17:17  CeraSumat  阅读(446)  评论(0编辑  收藏  举报