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. Java错误和异常解析

    Java错误和异常解析 错误和异常 在Java中, 根据错误性质将运行错误分为两类: 错误和异常. 在Java程序的执行过程中, 如果出现了异常事件, 就会生成一个异常对象. 生成的异常对象将传递Ja ...

  2. CP and Tucker Tensor Decomposition

    1.. 2..

  3. 深度排序模型概述(二)PNN/NFM/AFM

    在CTR预估中,为了解决稀疏特征的问题,学者们提出了FM模型来建模特征之间的交互关系.但是FM模型只能表达特征之间两两组合之间的关系,无法建模两个特征之间深层次的关系或者说多个特征之间的交互关系,因此 ...

  4. Windows&Appium&Python自动化测试-环境搭建之安卓SDK

    一.摘要 本博文将详细讲述在Windows环境下的jdk安装.配置以及环境校验:安卓sdk安装.配置以及环境校验 二.安装包工具准备: jdk1.8.0(64 位) installer_r24.4.1 ...

  5. 用tensorflow实现SVM

    环境配置 win10 Python 3.6 tensorflow1.15 scipy matplotlib (运行时可能会遇到module tkinter的问题) sklearn 一个基于Python ...

  6. [2019HDU多校第一场][HDU 6588][K. Function]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6588 题目大意:求\(\sum_{i=1}^{n}gcd(\left \lfloor \sqrt[3] ...

  7. 01 salt平台,软件架构图

    1.前期调研 1.别人家的 https://www.cnblogs.com/ssyfj/p/9060367.html#top https://www.cnblogs.com/evilliu/artic ...

  8. 【Wince-禁止重复启动程序】Wince 不重复启动程序

    创建类Mutex.cs: using System; using System.Linq; using System.Collections.Generic; using System.Text; u ...

  9. 关于dll注入

    例如:     有一个游戏修改器:其中有一个按钮“自动打怪”:点击时游戏会实现相应的功能:     对于游戏程序来说,自动打怪操作本质上就是call调用一个函数:     但是修改器和游戏是两个独立的 ...

  10. react-native-page-scrollview 的使用方法(实现酷炫的分页轮播效果,还支持自定义View)

    react-native-page-scrollview 对ScrollView的封装,可以很方便的实现水平,垂直分页轮播效果.而且可以自定义分页宽高,和侧边View的旋转,透明度,大小等. 对于原生 ...