WebApi實現通訊加密
一. 場景介紹:
如題如何有效的,最少量的現有程式碼侵入從而實現客戶端與服務器之間的資料交換加密呢?
二.探究:
1.需求分析
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 /// <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_0
#寫版本1.1 des加密解密
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 try17 {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 catch29 {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 try42 {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 catch54 {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 }
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 try17 {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 catch29 {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 try42 {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 catch54 {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 // 同 body38 // 读取请求 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加密解密
以上是WebApi實現通訊加密的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

C 語言中符號的使用方法涵蓋算術、賦值、條件、邏輯、位運算符等。算術運算符用於基本數學運算,賦值運算符用於賦值和加減乘除賦值,條件運算符用於根據條件執行不同操作,邏輯運算符用於邏輯操作,位運算符用於位級操作,特殊常量用於表示空指針、文件結束標記和非數字值。

在 C 語言中,char 類型在字符串中用於:1. 存儲單個字符;2. 使用數組表示字符串並以 null 終止符結束;3. 通過字符串操作函數進行操作;4. 從鍵盤讀取或輸出字符串。

C語言中通過轉義序列處理特殊字符,如:\n表示換行符。 \t表示製表符。使用轉義序列或字符常量表示特殊字符,如char c = '\n'。注意,反斜杠需要轉義兩次。不同平台和編譯器可能有不同的轉義序列,請查閱文檔。

多線程和異步的區別在於,多線程同時執行多個線程,而異步在不阻塞當前線程的情況下執行操作。多線程用於計算密集型任務,而異步用於用戶交互操作。多線程的優勢是提高計算性能,異步的優勢是不阻塞 UI 線程。選擇多線程還是異步取決於任務性質:計算密集型任務使用多線程,與外部資源交互且需要保持 UI 響應的任務使用異步。

在 C 語言中,char 和 wchar_t 的主要區別在於字符編碼:char 使用 ASCII 或擴展 ASCII,wchar_t 使用 Unicode;char 佔用 1-2 個字節,wchar_t 佔用 2-4 個字節;char 適用於英語文本,wchar_t 適用於多語言文本;char 廣泛支持,wchar_t 依賴於編譯器和操作系統是否支持 Unicode;char 的字符範圍受限,wchar_t 的字符範圍更大,並使用專門的函數進行算術運算。

在 C 語言中,char 類型轉換可以通過:強制類型轉換:使用強制類型轉換符將一種類型的數據直接轉換為另一種類型。自動類型轉換:當一種類型的數據可以容納另一種類型的值時,編譯器自動進行轉換。

char 和 unsigned char 是存儲字符數據的兩種數據類型,主要區別在於處理負數和正數的方式:值範圍:char 有符號 (-128 到 127),unsigned char 無符號 (0 到 255)。負數處理:char 可以存儲負數,unsigned char 不能。位模式:char 最高位表示符號,unsigned char 無符號位。算術運算:char 和 unsigned char 作為有符號和無符號類型,其算術運算方式不同。兼容性:char 和 unsigned char

char 數組在 C 語言中存儲字符序列,聲明為 char array_name[size]。訪問元素通過下標運算符,元素以空終止符 '\0' 結尾,用於表示字符串終點。 C 語言提供多種字符串操作函數,如 strlen()、strcpy()、strcat() 和 strcmp()。
