http://www.cnblogs.com/jonneydong/p/WebApi_Encryption.html

一. 场景介绍:

如题如何有效的,最少量的现有代码侵入从而实现客户端与服务器之间的数据交换加密呢?

二. 探究:

1.需求分析

webapi服务端 有如下接口:

public class ApiTestController : ApiController
{ // GET api/<controller>/5
public object Get(int id)
{
return "value" + id;
}
}

ApiTestController

无加密请求

GET /api/apitest?id=10

 
返回结果
 response "value10"
 
 
我们想要达到的效果为:
Get /api/apitest?aWQ9MTA=
response InZhbHVlMTAi  (解密所得 "value10")
 
或者更多其它方式加密
 

2.功能分析

 要想对现有代码不做任何修改, 我们都知道所有api controller 初始化在router确定之后, 因此我们应在router之前将GET参数和POST的参数进行加密才行.
 
看下图 webapi 生命周期:
 
 
我们看到在 路由routing 之前 有DelegationgHander 层进行消息处理.
 
因为我们要对每个请求进行参数解密处理,并且又将返回消息进行加密处理, 因此我们 瞄准 MessageProcessingHandler
 
    //
// 摘要:
// A base type for handlers which only do some small processing of request and/or
// response messages.
public abstract class MessageProcessingHandler : DelegatingHandler
{
//
// 摘要:
// Creates an instance of a System.Net.Http.MessageProcessingHandler class.
protected MessageProcessingHandler();
//
// 摘要:
// Creates an instance of a System.Net.Http.MessageProcessingHandler class with
// a specific inner handler.
//
// 参数:
// innerHandler:
// The inner handler which is responsible for processing the HTTP response messages.
protected MessageProcessingHandler(HttpMessageHandler innerHandler); //
// 摘要:
// Performs processing on each request sent to the server.
//
// 参数:
// request:
// The HTTP request message to process.
//
// cancellationToken:
// A cancellation token that can be used by other objects or threads to receive
// notice of cancellation.
//
// 返回结果:
// Returns System.Net.Http.HttpRequestMessage.The HTTP request message that was
// processed.
protected abstract HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken);
//
// 摘要:
// Perform processing on each response from the server.
//
// 参数:
// response:
// The HTTP response message to process.
//
// cancellationToken:
// A cancellation token that can be used by other objects or threads to receive
// notice of cancellation.
//
// 返回结果:
// Returns System.Net.Http.HttpResponseMessage.The HTTP response message that was
// processed.
protected abstract HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken);
//
// 摘要:
// Sends an HTTP request to the inner handler to send to the server as an asynchronous
// operation.
//
// 参数:
// request:
// The HTTP request message to send to the server.
//
// cancellationToken:
// A cancellation token that can be used by other objects or threads to receive
// notice of cancellation.
//
// 返回结果:
// Returns System.Threading.Tasks.Task`1.The task object representing the asynchronous
// operation.
//
// 异常:
// T:System.ArgumentNullException:
// The request was null.
protected internal sealed override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
}

MessageProcessingHandler

三. 实践:

现在我们将来 先实现2个版本的通讯加密解密功能,定为 版本1.0 base64加密, 版本1.1 Des加密
 
 1     /// <summary>
2 /// 加密解密接口
3 /// </summary>
4 public interface IMessageEnCryption
5 {
6 /// <summary>
7 /// 加密
8 /// </summary>
9 /// <param name="content"></param>
10 /// <returns></returns>
11 string Encode(string content);
12 /// <summary>
13 /// 解密
14 /// </summary>
15 /// <param name="content"></param>
16 /// <returns></returns>
17 string Decode(string content);
18 }

IMessageEnCryption

编写版本1.0 base64加密解密

 1     /// <summary>
2 /// 加解密 只做 base64
3 /// </summary>
4 public class MessageEncryptionVersion1_0 : IMessageEnCryption
5 {
6 public string Decode(string content)
7 {
8 return content?.DecryptBase64();
9 }
10
11 public string Encode(string content)
12 {
13 return content.EncryptBase64();
14 }
15 }

MessageEncryptionVersion1_0

编写版本1.1 des加密解密

 1     /// <summary>
2 /// 数据加解密 des
3 /// </summary>
4 public class MessageEncryptionVersion1_1 : IMessageEnCryption
5 {
6 public static readonly string KEY = "fHil/4]0";
7 public string Decode(string content)
8 {
9 return content.DecryptDES(KEY);
10 }
11
12 public string Encode(string content)
13 {
14 return content.EncryptDES(KEY);
15 }
16 }

MessageEncryptionVersion1_1

附上加密解密的基本的一个封装类

 1     public static class EncrypExtends
2 {
3
4 //默认密钥向量
5 private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
6 internal static string Key = "*@&$(@#H";
7
8 //// <summary>
9 /// DES加密字符串
10 /// </summary>
11 /// <param name="encryptString">待加密的字符串</param>
12 /// <param name="encryptKey">加密密钥,要求为8位</param>
13 /// <returns>加密成功返回加密后的字符串,失败返回源串</returns>
14 public static string EncryptDES(this string encryptString, string encryptKey)
15 {
16 try
17 {
18 byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8));
19 byte[] rgbIV = Keys;
20 byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
21 DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
22 MemoryStream mStream = new MemoryStream();
23 CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
24 cStream.Write(inputByteArray, 0, inputByteArray.Length);
25 cStream.FlushFinalBlock();
26 return Convert.ToBase64String(mStream.ToArray());
27 }
28 catch
29 {
30 return encryptString;
31 }
32 }
33 //// <summary>
34 /// DES解密字符串
35 /// </summary>
36 /// <param name="decryptString">待解密的字符串</param>
37 /// <param name="decryptKey">解密密钥,要求为8位,和加密密钥相同</param>
38 /// <returns>解密成功返回解密后的字符串,失败返源串</returns>
39 public static string DecryptDES(this string decryptString, string key)
40 {
41 try
42 {
43 byte[] rgbKey = Encoding.UTF8.GetBytes(key.Substring(0, 8));
44 byte[] rgbIV = Keys;
45 byte[] inputByteArray = Convert.FromBase64String(decryptString);
46 DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
47 MemoryStream mStream = new MemoryStream();
48 CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
49 cStream.Write(inputByteArray, 0, inputByteArray.Length);
50 cStream.FlushFinalBlock();
51 return Encoding.UTF8.GetString(mStream.ToArray());
52 }
53 catch
54 {
55 return decryptString;
56 }
57 }
58 public static string EncryptBase64(this string encryptString)
59 {
60 return Convert.ToBase64String(Encoding.UTF8.GetBytes(encryptString));
61 }
62 public static string DecryptBase64(this string encryptString)
63 {
64 return Encoding.UTF8.GetString(Convert.FromBase64String(encryptString));
65 }
66 public static string DecodeUrl(this string cryptString)
67 {
68 return System.Web.HttpUtility.UrlDecode(cryptString);
69 }
70 public static string EncodeUrl(this string cryptString)
71 {
72 return System.Web.HttpUtility.UrlEncode(cryptString);
73 }
74 }

EncrypExtends

OK! 到此我们前题工作已经完成了80%,开始进行HTTP请求的 消息进和出的加密解密功能的实现.

我们暂时将加密的版本信息定义为 HTTP header头中 以 api_version 的value 来判别分别是用何种方式加密解密

header例:

  api_version: 1.0

  api_version: 1.1

 1     /// <summary>
2 /// API消息请求处理
3 /// </summary>
4 public class JoyMessageHandler : MessageProcessingHandler
5 {
6
7 /// <summary>
8 /// 接收到request时 处理
9 /// </summary>
10 /// <param name="request"></param>
11 /// <param name="cancellationToken"></param>
12 /// <returns></returns>
13 protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken)
14 {
15 if (request.Content.IsMimeMultipartContent())
16 return request;
17 // 获取请求头中 api_version版本号
18 var ver = System.Web.HttpContext.Current.Request.Headers.GetValues("api_version")?.FirstOrDefault();
19 // 根据api_version版本号获取加密对象, 如果为null 则不需要加密
20 var encrypt = MessageEncryptionCreator.GetInstance(ver);
21
22 if (encrypt != null)
23 {
24 // 读取请求body中的数据
25 string baseContent = request.Content.ReadAsStringAsync().Result;
26 // 获取加密的信息
27 // 兼容 body: 加密数据 和 body: code=加密数据
28 baseContent = baseContent.Match("(code=)*(?<code>[\\S]+)", 2);
29 // URL解码数据
30 baseContent = baseContent.DecodeUrl();
31 // 用加密对象解密数据
32 baseContent = encrypt.Decode(baseContent);
33
34 string baseQuery = string.Empty;
35 if (!request.RequestUri.Query.IsNullOrEmpty())
36 {
37 // 同 body
38 // 读取请求 url query数据
39 baseQuery = request.RequestUri.Query.Substring(1);
40 baseQuery = baseQuery.Match("(code=)*(?<code>[\\S]+)", 2);
41 baseQuery = baseQuery.DecodeUrl();
42 baseQuery = encrypt.Decode(baseQuery);
43 }
44 // 将解密后的 URL 重置URL请求
45 request.RequestUri = new Uri($"{request.RequestUri.AbsoluteUri.Split('?')[0]}?{baseQuery}");
46 // 将解密后的BODY数据 重置
47 request.Content = new StringContent(baseContent);
48 }
49
50 return request;
51 }
52
53 /// <summary>
54 /// 处理将要向客户端response时
55 /// </summary>
56 /// <param name="response"></param>
57 /// <param name="cancellationToken"></param>
58 /// <returns></returns>
59 protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken)
60 {
61 //var isMediaType = response.Content.Headers.ContentType.MediaType.Equals(mediaTypeName, StringComparison.OrdinalIgnoreCase);
62 var ver = System.Web.HttpContext.Current.Request.Headers.GetValues("api_version")?.FirstOrDefault();
63 var encrypt = MessageEncryptionCreator.GetInstance(ver);
64 if (encrypt != null)
65 {
66 if (response.StatusCode == HttpStatusCode.OK)
67 {
68 var result = response.Content.ReadAsStringAsync().Result;
69 // 返回消息 进行加密
70 var encodeResult = encrypt.Encode(result);
71 response.Content = new StringContent(encodeResult);
72 }
73 }
74
75 return response;
76 }
77
78 }

JoyMessageHandler

最后在 webapiconfig 中将我们的消息处理添加到容器中
 
 1     public static class WebApiConfig
2 {
3 public static void Register(HttpConfiguration config)
4 {
5 // Web API 配置和服务
6 // 将 Web API 配置为仅使用不记名令牌身份验证。
7 config.SuppressDefaultHostAuthentication();
8 config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
9
10 // Web API 路由
11 config.MapHttpAttributeRoutes();
12
13 config.Routes.MapHttpRoute(
14 name: "DefaultApi",
15 routeTemplate: "api/{controller}/{id}",
16 defaults: new { id = RouteParameter.Optional }
17 );
18
19 // 添加自定义消息处理
20 config.MessageHandlers.Add(new JoyMessageHandler());
21
22 }
23 }

WebApiConfig

编写单元测试:
 1  [TestMethod()]
2 public void GetTest()
3 {
4 var id = 10;
5 var resultSuccess = $"\"value{id}\"";
6 //不加密
7 Trace.WriteLine($"without encryption.");
8 var url = $"api/ApiTest?id={id}";
9 Trace.WriteLine($"get url : {url}");
10 var response = http.GetAsync(url).Result;
11 var result = response.Content.ReadAsStringAsync().Result;
12 Assert.AreEqual(result, resultSuccess);
13 Trace.WriteLine($"result : {result}");
14
15 //使用 方案1加密
16 Trace.WriteLine($"encryption case one.");
17
18 url = $"api/ApiTest?code=" + $"id={id}".EncryptBase64().EncodeUrl();
19
20 Trace.WriteLine($"get url : {url}");
21
22 http.DefaultRequestHeaders.Clear();
23 http.DefaultRequestHeaders.Add("api_version", "1.0");
24 response = http.GetAsync(url).Result;
25
26 result = response.Content.ReadAsStringAsync().Result;
27
28 Trace.WriteLine($"result : {result}");
29
30 result = result.DecryptBase64();
31
32 Trace.WriteLine($"DecryptBase64 : {result}");
33
34 Assert.AreEqual(result, resultSuccess);
35
36 //使用 方案2 加密通讯
37 Trace.WriteLine($"encryption case one.");
38
39 url = $"api/ApiTest?code=" + $"id={id}".EncryptDES(MessageEncryptionVersion1_1.KEY).EncodeUrl();
40
41 Trace.WriteLine($"get url : {url}");
42
43 http.DefaultRequestHeaders.Clear();
44 http.DefaultRequestHeaders.Add("api_version", "1.1");
45 response = http.GetAsync(url).Result;
46
47 result = response.Content.ReadAsStringAsync().Result;
48
49 Trace.WriteLine($"result : {result}");
50
51 result = result.DecryptDES(MessageEncryptionVersion1_1.KEY);
52
53 Trace.WriteLine($"DecryptBase64 : {result}");
54
55 Assert.AreEqual(result, resultSuccess);
56 }

ApiTestControllerTests

至此为止功能实现完毕..
 

四.思想延伸

要想更加安全的方案,可以将给每位用户生成不同的 private key , 利用AES加密解密
 
 
 
 
本Demo开源地址:
oschina
github
 
如须转载请说明出处
多抽出一分钟时间学习,让你的生命更加精彩,敢想敢做,努力做最好!
博客园: JonneyDong 地址:http://www.cnblogs.com/jonneydong/
 
分类: C#
标签: webapi, c#
好文要顶             关注我     收藏该文         

董侨 关注 - 0 粉丝 - 16        
 
 
+加关注    
2    
0    
 
 
 

WebApi实现通讯加密 (转)的更多相关文章

  1. WebApi实现通讯加密

    一. 场景介绍: 如题如何有效的,最少量的现有代码侵入从而实现客户端与服务器之间的数据交换加密呢? 二. 探究: 1.需求分析 webapi服务端 有如下接口: public class ApiTes ...

  2. WebApi服务Uri加密及验证的两种方式

    最近的一个项目要求服务端与UI层分离,业务层以WebApi方式向外提供所有业务服务,服务在数据保密性方面提出了要求,主要包括: 1:客户端认证: 2:服务请求超时(默认5分钟): 3:服务Get请求的 ...

  3. ECC(Ellipse Curve Cryptography)+AES(Advanced Encryption Standard)前端通讯加密模拟(使用eccrypto-js)

    前置知识 不了解对称加密与非对称加密的小伙伴可以看看下面的文章,想详细学习与区块链有关的加密算法可以戳这里 对称与非对称加密 https://blog.csdn.net/u013320868/arti ...

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

    接上篇<WebApi服务URI加密及验证的两种方式>,在实际开发中,仅对URI进行加密是不够的,在传输层采用SSL加密也是必须的. 如果服务寄宿于IIS,那对传输层加密非常简单仅需要配置一 ...

  5. SQLite学习笔记(十)&&加密

    随着移动互联网的发展,手机使用越来越广泛,sqlite作为手机端存储的一种解决方案,使用也非常普遍.但是sqlite本身安全特性却比较弱,比如不支持用户权限,只要能获取到数据库文件就能进行访问:另外也 ...

  6. .NET中的DES对称加密

    DES是一种对称加密(Data Encryption Standard)算法,于1977年得到美国政府的正式许可,是一种用56位密钥来加密64位数据的方法.一般密码长度为8个字节,其中56位加密密钥, ...

  7. SQLite XXTea加密学习

    这几天优化数据库读写,移植了xxtea加密到最新的数据库sqlite 3.12.2里,一些好文章放在这里.移植后,数据库读写性能异常优秀! 这几天又发现,数据库还是发生了无法写入情况,数据库崩溃掉了. ...

  8. 单片机上使用TEA加密通信(转)

    源:单片机上使用TEA加密通信 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境: 主机:WIN7 开发环境:MDK4.72 单片机:STM32 说 ...

  9. Nginx集群之SSL证书的WebApi微服务

    目录 1       大概思路... 1 2       Nginx集群之SSL证书的WebApi微服务... 1 3       HTTP与HTTPS(SSL协议)... 1 4       Ope ...

随机推荐

  1. anaconda环境中---py2.7下安装tf1.0 + py3.5下安装tf1.5

    anaconda环境中---py2.7下安装tf1.0 + py3.5下安装tf1.5 @wp20181030 环境:ubuntu18.04, anaconda2, ubuntu系统下事先安装了pyt ...

  2. sklearn & ml tutorial

    第一章 引言 pd.scatter_matrix(pd.DataFrame(X_train),c=y_train_name,figsize=(15,15),marker='o',hist_kwds={ ...

  3. 【CQOI2017】老C的方块

    Description https://loj.ac/problem/3022 Solution 他讲得很清楚 将那篇博客中的红色标号为 \(0\),黄色为 \(1\),蓝色为 \(2\),绿色为 \ ...

  4. Windows环境下使用uiautomatorviewer进行元素定位

    一.摘要 元素定位本篇主要介绍如何使用uiautomatorviewer,通过定位到页面上的元素,然后进行相应的点击等操作,uiautomatorviewer 是 android-sdk 自带的一个元 ...

  5. Python 获得程序 exe 的版本号

    Python 获得程序 exe 的版本号 python中需要安装 pywin32 包 # based on http://stackoverflow.com/questions/580924/pyth ...

  6. dockerfile-maven plugin自动镜像制作并发布

    环境准备:win10+docker 1.打开hyper-v 2.下载最新版本docker:https://store.docker.com/editions/community/docker-ce-d ...

  7. 记一次 解决 vue 兼容ie11 的问题

    vue2.0 最近项目需要做到兼容ie11,找问题找半天,于是各种百度, 发现引入 babel-polyfill 还是有问题  空白页面 参考它的配置 ( 最后一句代码为引入的资源路径 )  我跟他差 ...

  8. python中else与finally的总结

    1.else的用法 对try...except的补充: else子句的使用比在子句中添加其他代码更好,try因为它避免了意外捕获由try... except语句保护的代码未引发的异常. for arg ...

  9. 小技巧——直接在目录中输入cmd然后就打开cmd命令窗口

    直接在目录中输入cmd然后就打开cmd命令窗口

  10. 洛谷P2135 方块消除

    洛谷题目链接 动态规划(真毒瘤!) 变量声明: $val[i]$:表示第$i$块颜色 $num[i]$:表示第$i$块颜色数量 $sum[i]$:表示$num$的前缀和 我们设计状态$f[l][r][ ...