WebApi實現通訊加密

来源:http://www.cnblogs.com/jonneydong/archive/2017/02/25/WebApi_Encryption.html
-Advertisement-
Play Games

一. 場景介紹: 如題如何有效的,最少量的現有代碼侵入從而實現客戶端與伺服器之間的數據交換加密呢? 二. 探究: 1.需求分析 webapi服務端 有如下介面: public class ApiTestController : ApiController { // GET api/<controll ...


一. 場景介紹:

如題如何有效的,最少量的現有代碼侵入從而實現客戶端與伺服器之間的數據交換加密呢?

 

二. 探究:

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 https://git.oschina.net/jonneydong/Webapi_Encryption github https://github.com/JonneyDong/Webapi_Encryption  
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前述: 對資料庫操作的封裝,相信網路上已經有一大堆,ORM框架,或者是.NET本身的EF,都很好的支持資料庫操作。這篇文章是分享自己所思考的,對資料庫操作的簡單封裝。我對於這篇文章,認為被瀏覽者所關註重點的是怎麼分析設計資料庫操作封裝,代碼是其次。而且,這是我第一篇文章,為了想好怎麼實現花了些天,代 ...
  • 年前經常聊天的大佬群里有人寫了窗體的倒計時來計算下班時間和放假時間:) 簡直就是在嘲諷我這種沒有工作的人,哈哈哈 窗體的倒計時相當的沒有技術含量,主要是不夠炫酷,不能夠體現我們程式員的身份。 那什麼才叫炫酷?必須是控制台啊! 電視劇上黑客噼里啪啦噼里啪啦滾屏的畫面多炫酷! 所以,研究了一下怎麼樣在控 ...
  • 適用Zero版本:ASP.NET Core & Angular 2+ (aspnet-zero-core-3.1.0)。 該版本官方有兩個solution文件夾:Angular(前端) 和 aspnet-core(後臺服務)。 在開始以下步驟之前需要能夠成功發佈程式,對於後臺服務只要能運行即可,如有 ...
  • 註釋,是代碼中的一些“說明性文字”。註釋本身不會參與程式的編譯和運行,僅僅供程式員閱讀。 註釋分為:單行註釋、多行註釋、文檔註釋。 單行註釋的符號是2條斜線“//”,2條斜線右側的內容就是註釋,左側的代碼不會受影響。 多行註釋以“/*”開始,以“*/”結束,之間的內容就是註釋,可以包含多行。 文檔註 ...
  • OSS最新進度,包括OSS.Social,OSS.Http,新增微信支付項目OSS.PayCenter。 ...
  • 簡介 RabbitMQ:一個消息系統,基於 AMQP 系統協議,由 erlang 語言開發。 優點:健壯、使用簡單、開源和支持各種流行的語言等。 MQ(Message Queue):消息隊列的簡稱,是一種應用程式之間的通信機制。 作用:將部分無需立即回調獲取結果,並且耗時的操作,使用非同步處理的方式提 ...
  • 在開發商城系統的時候,大家會遇到這樣的需求,商城系統里支持多種商品類型,比如衣服,手機,首飾等,每一種產品類型都有自己獨有的參數信息,比如衣服有顏色,首飾有材質等,大家可以上淘寶看一下就明白了。現在的問題是,如果我程式發佈後,要想增加一種新的商品類型怎麼辦,如果不在程式設計時考慮這個問題的話,可能每 ...
  • 1..NET Core基本介紹 a 作為一個.NET的開發者,在以前的開發中,我們開發的項目基本都是部署在windows伺服器上,但是在windows伺服器上的話某些比較流行的解決訪問量的方案基本都是先出現在linux上,而後才能遷移出現windows上,而且效率處理方面也不再一個級別。曾經讓.NE ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...