最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

WebApi实现通讯加密

来源:动视网 责编:小采 时间:2020-11-27 22:35:54
文档

WebApi实现通讯加密

WebApi实现通讯加密:一. 场景介绍: 如题如何有效的,最少量的现有代码侵入从而实现客户端与服务器之间的数据交换加密呢? 二. 探究: 1.需求分析 webapi服务端 有如下接口: public class ApiTestController : ApiController { // GET api/<c
推荐度:
导读WebApi实现通讯加密:一. 场景介绍: 如题如何有效的,最少量的现有代码侵入从而实现客户端与服务器之间的数据交换加密呢? 二. 探究: 1.需求分析 webapi服务端 有如下接口: public class ApiTestController : ApiController { // GET api/<c


一. 场景介绍:

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

二. 探究:

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加密

/// <summary>
 /// 加密解密接口
 /// </summary>
 public interface IMessageEnCryption
 {
 /// <summary>
 /// 加密
 /// </summary>
 /// <param name="content"></param>
 /// <returns></returns>
 string Encode(string content);
 /// <summary>
 /// 解密
 /// </summary>
 /// <param name="content"></param>
 /// <returns></returns>
 string Decode(string content);
 }

IMessageEnCryption

编写版本1.0 base64加密解密

/// <summary>
 /// 加解密 只做 base64
 /// </summary>
 public class MessageEncryptionVersion1_0 : IMessageEnCryption
 {
 public string Decode(string content)
 {
 return content?.DecryptBase64();
 }

 public string Encode(string content)
 {
 return content.EncryptBase64();
 }
 }

MessageEncryptionVersion1_0

编写版本1.1 des加密解密

/// <summary>
 /// 数据加解密 des
 /// </summary>
 public class MessageEncryptionVersion1_1 : IMessageEnCryption
 {
 public static readonly string KEY = "fHil/4]0";
 public string Decode(string content)
 {
 return content.DecryptDES(KEY);
 }
 public string Encode(string content)
 {
 return content.EncryptDES(KEY);
 }
 }

MessageEncryptionVersion1_1

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

public static class EncrypExtends
 {
 //默认密钥向量
 private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
 internal static string Key = "*@&$(@#H";

 //// <summary>
 /// DES加密字符串
 /// </summary>
 /// <param name="encryptString">待加密的字符串</param>
 /// <param name="encryptKey">加密密钥,要求为8位</param>
 /// <returns>加密成功返回加密后的字符串,失败返回源串</returns>
 public static string EncryptDES(this string encryptString, string encryptKey)
 {
 try
 {
 byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8));
 byte[] rgbIV = Keys;
 byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
 DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
 MemoryStream mStream = new MemoryStream();
 CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
 cStream.Write(inputByteArray, 0, inputByteArray.Length);
 cStream.FlushFinalBlock();
 return Convert.ToBase64String(mStream.ToArray());
 }
 catch
 {
 return encryptString;
 }
 }
 //// <summary>
 /// DES解密字符串
 /// </summary>
 /// <param name="decryptString">待解密的字符串</param>
 /// <param name="decryptKey">解密密钥,要求为8位,和加密密钥相同</param>
 /// <returns>解密成功返回解密后的字符串,失败返源串</returns>
 public static string DecryptDES(this string decryptString, string key)
 {
 try
 {
 byte[] rgbKey = Encoding.UTF8.GetBytes(key.Substring(0, 8));
 byte[] rgbIV = Keys;
 byte[] inputByteArray = Convert.FromBase64String(decryptString);
 DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
 MemoryStream mStream = new MemoryStream();
 CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
 cStream.Write(inputByteArray, 0, inputByteArray.Length);
 cStream.FlushFinalBlock();
 return Encoding.UTF8.GetString(mStream.ToArray());
 }
 catch
 {
 return decryptString;
 }
 }
 public static string EncryptBase64(this string encryptString)
 {
 return Convert.ToBase64String(Encoding.UTF8.GetBytes(encryptString));
 }
 public static string DecryptBase64(this string encryptString)
 {
 return Encoding.UTF8.GetString(Convert.FromBase64String(encryptString));
 }
 public static string DecodeUrl(this string cryptString)
 {
 return System.Web.HttpUtility.UrlDecode(cryptString);
 }
 public static string EncodeUrl(this string cryptString)
 {
 return System.Web.HttpUtility.UrlEncode(cryptString);
 }
 }

EncrypExtends

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

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

header例:

  api_version: 1.0

  api_version: 1.1

/// <summary>
 /// API消息请求处理
 /// </summary>
 public class JoyMessageHandler : MessageProcessingHandler
 {
 /// <summary>
 /// 接收到request时 处理
 /// </summary>
 /// <param name="request"></param>
 /// <param name="cancellationToken"></param>
 /// <returns></returns>
 protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken)
 {
 if (request.Content.IsMimeMultipartContent())
 return request;
 // 获取请求头中 api_version版本号
 var ver = System.Web.HttpContext.Current.Request.Headers.GetValues("api_version")?.FirstOrDefault();
 // 根据api_version版本号获取加密对象, 如果为null 则不需要加密
 var encrypt = MessageEncryptionCreator.GetInstance(ver);
 if (encrypt != null)
 {
 // 读取请求body中的数据
 string baseContent = request.Content.ReadAsStringAsync().Result;
 // 获取加密的信息
 // 兼容 body: 加密数据 和 body: code=加密数据
 baseContent = baseContent.Match("(code=)*(?<code>[\\S]+)", 2);
 // URL解码数据
 baseContent = baseContent.DecodeUrl();
 // 用加密对象解密数据
 baseContent = encrypt.Decode(baseContent);
 string baseQuery = string.Empty;
 if (!request.RequestUri.Query.IsNullOrEmpty())
 {
 // 同 body
 // 读取请求 url query数据
 baseQuery = request.RequestUri.Query.Substring(1);
 baseQuery = baseQuery.Match("(code=)*(?<code>[\\S]+)", 2);
 baseQuery = baseQuery.DecodeUrl();
 baseQuery = encrypt.Decode(baseQuery);
 }
 // 将解密后的 URL 重置URL请求
 request.RequestUri = new Uri($"{request.RequestUri.AbsoluteUri.Split('?')[0]}?{baseQuery}");
 // 将解密后的BODY数据 重置
 request.Content = new StringContent(baseContent);
 }
 return request;
 }
 /// <summary>
 /// 处理将要向客户端response时
 /// </summary>
 /// <param name="response"></param>
 /// <param name="cancellationToken"></param>
 /// <returns></returns>
 protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken)
 {
 //var isMediaType = response.Content.Headers.ContentType.MediaType.Equals(mediaTypeName, StringComparison.OrdinalIgnoreCase);
 var ver = System.Web.HttpContext.Current.Request.Headers.GetValues("api_version")?.FirstOrDefault();
 var encrypt = MessageEncryptionCreator.GetInstance(ver);
 if (encrypt != null)
 {
 if (response.StatusCode == HttpStatusCode.OK)
 {
 var result = response.Content.ReadAsStringAsync().Result;
 // 返回消息 进行加密
 var encodeResult = encrypt.Encode(result);
 response.Content = new StringContent(encodeResult);
 }
 }
 return response;
 }
 }

JoyMessageHandler

最后在 webapiconfig 中将我们的消息处理添加到容器中

public static class WebApiConfig
 {
 public static void Register(HttpConfiguration config)
 {
 // Web API 配置和服务
 // 将 Web API 配置为仅使用不记名令牌身份验证。
 config.SuppressDefaultHostAuthentication();
 config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
 // Web API 路由
 config.MapHttpAttributeRoutes();
 config.Routes.MapHttpRoute(
 name: "DefaultApi",
 routeTemplate: "api/{controller}/{id}",
 defaults: new { id = RouteParameter.Optional }
 );
 // 添加自定义消息处理
 config.MessageHandlers.Add(new JoyMessageHandler());
 }
 }

WebApiConfig

编写单元测试:

[TestMethod()]
 public void GetTest()
 {
 var id = 10;
 var resultSuccess = $"\"value{id}\"";
 //不加密
 Trace.WriteLine($"without encryption.");
 var url = $"api/ApiTest?id={id}";
 Trace.WriteLine($"get url : {url}");
 var response = http.GetAsync(url).Result;
 var result = response.Content.ReadAsStringAsync().Result;
 Assert.AreEqual(result, resultSuccess);
 Trace.WriteLine($"result : {result}");
 //使用 方案1加密
 Trace.WriteLine($"encryption case one.");
 url = $"api/ApiTest?code=" + $"id={id}".EncryptBase64().EncodeUrl();
 Trace.WriteLine($"get url : {url}");
 http.DefaultRequestHeaders.Clear();
 http.DefaultRequestHeaders.Add("api_version", "1.0");
 response = http.GetAsync(url).Result;
 result = response.Content.ReadAsStringAsync().Result;
 Trace.WriteLine($"result : {result}");
 result = result.DecryptBase64();
 Trace.WriteLine($"DecryptBase64 : {result}");
 Assert.AreEqual(result, resultSuccess);
 //使用 方案2 加密通讯
 Trace.WriteLine($"encryption case one.");
 url = $"api/ApiTest?code=" + $"id={id}".EncryptDES(MessageEncryptionVersion1_1.KEY).EncodeUrl();
 Trace.WriteLine($"get url : {url}");
 http.DefaultRequestHeaders.Clear();
 http.DefaultRequestHeaders.Add("api_version", "1.1");
 response = http.GetAsync(url).Result;
 result = response.Content.ReadAsStringAsync().Result;
 Trace.WriteLine($"result : {result}");
 result = result.DecryptDES(MessageEncryptionVersion1_1.KEY);
 Trace.WriteLine($"DecryptBase64 : {result}");
 Assert.AreEqual(result, resultSuccess);
 }

ApiTestControllerTests

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

四.思想延伸

要想更加安全的方案,可以将给每位用户生成不同的 private key , 利用AES加密解密

本Demo开源地址:

oschina

https://git.oschina.net/jonneydong/Webapi_Encryption

github

https://github.com/JonneyDong/Webapi_Encryption

文档

WebApi实现通讯加密

WebApi实现通讯加密:一. 场景介绍: 如题如何有效的,最少量的现有代码侵入从而实现客户端与服务器之间的数据交换加密呢? 二. 探究: 1.需求分析 webapi服务端 有如下接口: public class ApiTestController : ApiController { // GET api/<c
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top