淺析非對稱加密在接口參數(shù)中的實現(xiàn)
背景
接口層做數(shù)據(jù)加密應該算是老生常談的一件事了,業(yè)界用的比較多的,不外乎是對稱加密,非對稱加密以及兩者的結合。
對稱加密,比較有代表性的就是 AES,密鑰只有一個,客戶端和服務端都要進行存儲,但是對客戶端來說,比較容易泄露,需要定期進行更換。
非對稱加密,比較有代表性的就是 RSA,有公鑰和私鑰,正常是服務端生成,將私鑰保留在服務端,公鑰派發(fā)出去,然后是客戶端用公鑰進行加密,服務端用私鑰進行解密。相對于對稱加密來說,是安全了一些,但是加解密的速度會慢一些,如果要加密的內容還比較多,還要進行分段處理,比較麻煩。
非對稱加密 + 對稱加密,這個應該是用的比較多的一種,做了一個折中處理,用 RSA 的公鑰加密 AES 的密鑰,然后用 AES 去加密請求數(shù)據(jù)。
下面老黃就用幾個簡單的例子來演示一下非對稱加密這一塊。
非對稱加密
這里介紹的是純純的非對稱加密,還不是結合對稱加密的。
這種情況,如果真的使用,一般是會在登錄接口,再細一點的話,就是密碼那個字段的加密。
先來看看簡單的流程圖
后端接口處理
后端 API 接口需要提供兩個接口
- 根據(jù)應用客戶端獲取公鑰(當然把公鑰寫死在客戶端代碼里也是可以的)
- 解密處理數(shù)據(jù)
獲取公鑰
[HttpGet("req-pub")] public IActionResult ReqPub([FromQuery] string appId) { if(string.IsNullOrWhiteSpace(appId)) return BadRequest("invalid param"); // 模擬從數(shù)據(jù)庫或緩存中取數(shù)據(jù) var publicKey = RSAKeyMapping.GetServerPublicKeyByAppId(appId); if(string.IsNullOrWhiteSpace(publicKey)) return BadRequest("invalid appId"); return Ok(new { data = publicKey }); }
解密處理
[HttpPost] public async Task<IActionResult> Post([FromHeader] string appId) { if (string.IsNullOrWhiteSpace(appId)) return BadRequest("invalid appId"); // 這里本可以用參數(shù)接受,不過有一些網站的登陸接口是直接傳密文 // 所以這里也演示一下這種方式 var data = await new StreamReader(Request.Body).ReadToEndAsync(); if (string.IsNullOrWhiteSpace(data)) return BadRequest("invalid param"); // 模擬從數(shù)據(jù)庫或緩存中取數(shù)據(jù) var rsaKey = RSAKeyMapping.GetByAppId(appId); if (rsaKey == null) return BadRequest("invalid appId"); // 解密,正常解密后會是一個 JSON 字符串,然后反序列化即可 var decData = EncryptProvider.RSADecrypt( rsaKey.ServerPrivateKey, Convert.FromBase64String(data), RSAEncryptionPadding.Pkcs1, true); return Ok($"Hello, {System.Text.Encoding.UTF8.GetString(decData)}"); }
前端頁面處理
前端用最原生的 HTML + JavaScript 來演示,這里需要用到 jsencrypt 和 crypto-js。
大致流程的話,打開頁面就從服務端獲取到公鑰,點擊按鈕,就會把文本框中的內容用公鑰去加密,然后調用服務端的解密接口。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>RSA sample</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/jsencrypt/3.3.1/jsencrypt.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script> <script> let appId = "appId-2"; let url = "http://localhost:7775"; $(function () { // 獲取公鑰 getPublicKeyFromServer(); $("#btnSubmit").click(function () { var encrypt = new JSEncrypt(); encrypt.setPublicKey(localStorage.getItem("spk")); var encData = encrypt.encrypt($("#txtData").val()); sendBizReq(encData) }); }); function getPublicKeyFromServer() { $.get(url + "/com/req-pub?appId=" + appId, function (data, status) { localStorage.setItem("spk", data.data) }); } function sendBizReq(data) { $.ajax({ url: url + "/biz", type: 'post', // dataType: 'json', data: data, headers: { 'appId': appId, 'Content-Type': 'application/json' }, success: function (res) { console.log(res) alert(res); }, error: function (e) { console.log(e) } }); } </script> </head> <body> <div> <input type="text" id="txtData" /> <button id="btnSubmit">submit</button> </div> </body> </html>
運行效果大致如下:
這個例子不算復雜,應該比較好理解。
下面再來看看非對稱加密 + 對稱解密的方式。
非對稱加密 + 對稱加密
這兩種加密結合的,網上其實很多例子,不過每個實現(xiàn)都會有一些細微的差別。
這種相對來說,適用的場景就比較多了,基本都可以覆蓋。
同樣看看簡單的流程圖,再看如何實現(xiàn)。
后端接口處理
后端 API 接口也是需要提供兩個接口,公鑰獲取和上面的是一樣的,變動的是解密這一塊,因為這里還引入了 AES 。
[HttpPost] public IActionResult Post([FromHeader] string appId, [FromBody] RequestDto dto) { if(string.IsNullOrWhiteSpace(appId)) return BadRequest("invalid appId"); // 這里正常用實體接收,不從流讀取了 if (dto == null || string.IsNullOrWhiteSpace(dto.EP) || string.IsNullOrWhiteSpace(dto.EAK)) return BadRequest("invalid param"); // 模擬從數(shù)據(jù)庫或緩存中取數(shù)據(jù) var rsaKey = RSAKeyMapping.GetByAppId(appId); if (rsaKey == null) return BadRequest("invalid appId"); // 解密客戶端傳過來的 AES 密鑰 var decAesKey = EncryptProvider.RSADecrypt( rsaKey.PrivateKey, Convert.FromBase64String(dto.EAK), RSAEncryptionPadding.Pkcs1, true); // 根據(jù)解密的密鑰,進行 AES 解密 var decData = EncryptProvider.AESDecrypt( dto.EP, System.Text.Encoding.UTF8.GetString(decAesKey)); return Ok($"Hello, {decData}"); }
前端頁面處理
前端這一塊其實變動也不會大,主要是多了一步 AES 密鑰的生成和 AES 的加密。
$(function () { getPublicKeyFromServer(); $("#btnSubmit").click(function () { var encrypt = new JSEncrypt(); encrypt.setPublicKey(localStorage.getItem("spk")); // 隨機生成 aes 的密鑰 var aesKey = getAesKey(); // 用公鑰去加密這個密鑰 var encAesKey = encrypt.encrypt(aesKey); // 用 aes 的密鑰去加密數(shù)據(jù) var encData = aesEncrypt($("#txtData").val(), aesKey); sendBizReq(encData, encAesKey) }); }); function sendBizReq(data, aesKey) { $.ajax({ url: url + "/biz", type: 'post', // dataType: 'json', data: JSON.stringify({ ep: data, eak: aesKey }), headers: { 'appId': appId, 'Content-Type': 'application/json' }, success: function (res) { console.log(res) alert(res); }, error: function (e) { console.log(e) } }); } function getAesKey() { var s = []; var hexDigits = "0123456789abcdef"; for (var i = 0; i < 32; i++) { s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); } s[14] = "4"; s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); s[8] = s[13] = s[18] = s[23]; var uuid = s.join(""); return uuid; } function aesEncrypt(data, key) { var encryptedData = CryptoJS.AES.encrypt( CryptoJS.enc.Utf8.parse(data), CryptoJS.enc.Utf8.parse(key), { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); return encryptedData.toString(); }
效果已經出來了。
一些考慮
可能有朋友會問,前端生成的 AES 密鑰可信嗎?
畢竟有流傳類似這樣一句話 任何客戶端傳過來的數(shù)據(jù)都是不能直接信任的。
有這種顧慮也算正常。
這個時候就需要考慮服務端生成密鑰的方案了:
生成其實是一件小事,傳輸是一件比較核心的事。
首先考慮密鑰也是密文傳輸?shù)?,所以服務端和客戶端要同時擁有一對公鑰和私鑰。
服務端在向客戶端傳輸密鑰時,要用客戶端的公鑰進行加密,然后客戶端用自己私鑰進行解密獲得,這樣才能保證密鑰的“安全性”。
這種的話,交互邏輯會復雜一些。
除了 RSA 算法,后面可能還要嘗試一下國密算法中的 SM2。
到此這篇關于淺析非對稱加密在接口參數(shù)中的實現(xiàn)的文章就介紹到這了,更多相關非對稱加密在接口參數(shù)中實現(xiàn)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
spring中的BeanFactory與FactoryBean的講解
今天小編就為大家分享一篇關于spring中的BeanFactory與FactoryBean的講解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01spring聲明式事務@Transactional開發(fā)常犯的幾個錯誤及最新解決方案
使用聲明式事務@Transactional進行事務一致性的管理,在開發(fā)過程中,發(fā)現(xiàn)很多開發(fā)同學都用錯了spring聲明式事務@Transactional或使用不規(guī)范,導致出現(xiàn)各種事務問題,這篇文章主要介紹了spring聲明式事務@Transactional開發(fā)常犯的幾個錯誤及解決辦法,需要的朋友可以參考下2024-02-02單臺Spring Cloud Eureka升級到三臺Eureka高可用集群
今天小編就為大家分享一篇關于單臺Spring Cloud Eureka升級到三臺Eureka高可用集群,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12spring boot mybatis多數(shù)據(jù)源解決方案過程解析
這篇文章主要介紹了spring boot mybatis多數(shù)據(jù)源解決方案過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-11-11