循序漸進學.Net Core Web Api開發系列【16】:應用安全續-加密與解密

来源:https://www.cnblogs.com/seabluescn/archive/2018/07/25/9366090.html
-Advertisement-
Play Games

應用安全除了用戶許可權認證外,還要考慮到數據安全,傳輸安全、系統漏洞等方面。本篇文章重點討論數據存儲安全和傳輸安全,主要技術手段就是加密和解密。 ...


系列目錄

循序漸進學.Net Core Web Api開發系列目錄

 本系列涉及到的源碼下載地址:https://github.com/seabluescn/Blog_WebApi

 

一、概述

應用安全除了用戶許可權認證外,還要考慮到數據安全,傳輸安全、系統漏洞等方面。本篇文章重點討論數據存儲安全和傳輸安全,主要技術手段就是加密和解密。

 

二、基本概念

信息在傳輸和存儲的過程中有泄密的風險,加密的目的就是解決這些風險。

1、信息存儲在資料庫中,如果資料庫泄露會造成敏感數據泄露,如用戶密碼,手機號碼、工資信息;

2、數據傳輸過程中數據遭到劫持,泄露用戶名、密碼等信息。

針對不同的應用場景,對應採用的加密手段:

1、密碼存儲:任何人都不可以看到密碼,驗證是只要比對加密後的字元串是否一致即可,所以要採用非可逆加密演算法;

2、敏感信息存儲:存儲的信息是加密的,同時需要解密還原數據,所以採用可逆的演算法,加密後可以解密,加密的密碼和解密的密碼可以一致。

3、敏感信息傳輸:在瀏覽器向伺服器傳輸數據的過程,建議優先採用HTTPS傳輸模式即可解決問題。如果不採用https的傳輸模式,用戶提交的數據會有被竊取的風險,由於客戶端加密是採用js進行操作,分析其網頁源碼可以查詢到加密的密鑰,所以就不能採用上面的對稱加密演算法,而是必須採用非對稱加密演算法,即:加密密碼和解密密碼不一樣,加密密碼是可以公開的,有加密密碼無法解密信息,這樣通過客戶端加密、服務端解密解決數據被竊取的風險。

總結如下:

問題場景

採用加密手段

加密演算法

用戶密碼存儲過程泄密

非可逆加密演算法

MD5

敏感數據存儲過程泄密

可逆對稱加密演算法

DES/AES

敏感數據傳輸過程泄密

可逆非對稱加密演算法

RSA

 

三、MD5加密

 首先需要通過NuGet獲取包:NETCore.Encrypt,下同。

加密代碼:

var srcString = "ghc123456";
var hashed = EncryptProvider.Md5(srcString);

非可逆加密演算法,不好解密。

 

四、DES加密與解密

 DES的加密與解密: 

           //需要加密的信息
            var srcString = "ghc123456";

            //密碼:24位字元串,通過EncryptProvider.CreateDesKey()來生成。
            var desKey = "i4FftwEAP7enqKRl8JhBC7gv";  
           
           //加密
            var encrypted = EncryptProvider.DESEncrypt(srcString, desKey);

            //解密
            var decrypted = EncryptProvider.DESDecrypt(encrypted, desKey);

 

五、RSA客戶端加密、服務端解密

 1、利用工具生成密鑰

RSA加密與解密需要用到一對密鑰,可以通過第三方工具來生成密鑰,我這裡用的是阿裡螞蟻金服的一個簽名工具:

工具下載:https://docs.open.alipay.com/291/105971

 

密鑰格式選擇PKCS1

公鑰給客戶端加密使用,私鑰給伺服器端解密使用。

  

2、前端加密

 首先通過Bower引入兩個js的庫:jQuery、jsencrypt

前端加密代碼:

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <title>Using jQuery</title>
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/jsencrypt/bin/jsencrypt.min.js"></script>

    <script>
        $(document).ready(function () {
            $("#query").click(function (event) {

                //需要加密的信息
                var pwd = "ghc123456";

                //公鑰
                var publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr8a8EjoY4op+k/BF0hjkaOgMDAmSl+VCc2rU6j/Ji9BsX/tRWM4o3wZgkMdAWim9vwM0Ll4x/jTsVr1+Qe7ffnBl6ZLpMs77MbVVG5wowVvie1rMi2fB7akdi+Id+R3SHZNSTnbtEATXtY8nGV5ZDcHMrw6PI30+HlaclxmhFSr+UGERyi6/mLKnC3Y5elxu8Zlj8eHPZhV6DV87ngkGdp4aLdWWfqP1Iz0cIMAJ7hiYP4o4/W/vn1YSyJeTs/oQvUeEaMVGxGIXOY+m9ToZbPwjXWPjeZJ1RLRKfhYoobguyGezQNC0xlBdWNU9gfVw7mk0+q1eCe5XfA45O54MbwIDAQAB";

                var encrypt = new JSEncrypt();
                encrypt.setPublicKey(publicKey);
               
                var encrypttext = encrypt.encrypt(pwd);

                $("#text").val(encrypttext);
            });

        });
    </script>

</head>

<body>
    <h1>Query</h1>

    <button id='query'>Qyery</button>
    <textarea id="text"></textarea>

</body>
</html>

 

3、後端解密

 把前端加密獲得的密文Copy到下麵代碼中,驗證解密後的信息。

using System.Security.Cryptography;namespace Encrypt
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Study Encrypt!");

            //從前端傳來的加密後的密文
            var encryptedMessage = "Qw0W7P3dScUe3isXIVkumhgTxKk0fcaxzyvyCVnFEHBH6qw/daMKt0BYK1SrFZrqmcX1tRx/yeAa0ea2QsYmLL2OynffoUnPtjLjNHZd9R7+Uqh8e6nmD1r4yK6o37tfb9SmPxJtgLR45dnxcFAIt3xRY5eqXFCMCJKdKwV7aLMayG1+Rg7YBhTfUoNbJoLTODupCweaUVwZdvcxOZrqd5x3SRw3x+bPkQpchJRXc4QoLhEfh/Hw5tiNkuMUzPt/3iyx7iuKQR4q+9Wnd81UaenLaihVlkiU5MEjZJOnz1HaEDhMc9rMlTXV36jAOOAVTufb2jILbNycuhGQkFh9SA==";

            //私鑰
            var privateKey = "MIIEowIBAAKCAQEAr8a8EjoY4op+k/BF0hjkaOgMDAmSl+VCc2rU6j/Ji9BsX/tRWM4o3wZgkMdAWim9vwM0Ll4x/jTsVr1+Qe7ffnBl6ZLpMs77MbVVG5wowVvie1rMi2fB7akdi+Id+R3SHZNSTnbtEATXtY8nGV5ZDcHMrw6PI30+HlaclxmhFSr+UGERyi6/mLKnC3Y5elxu8Zlj8eHPZhV6DV87ngkGdp4aLdWWfqP1Iz0cIMAJ7hiYP4o4/W/vn1YSyJeTs/oQvUeEaMVGxGIXOY+m9ToZbPwjXWPjeZJ1RLRKfhYoobguyGezQNC0xlBdWNU9gfVw7mk0+q1eCe5XfA45O54MbwIDAQABAoIBAH0lfVlsy7Le7+fcNZmz50tZito3JovGylzqPtTYvWIx7jcX837KqQbAv5fUhNisx09rtIcewXE/tNS87Vt7+ttGowh9dFKcUvO9Ku8Ra2LfTIyOxPqr0MKomUSypKxssuAjt4Ht4jJ5gCrf1PKW3ciRpm0sbHTUApoPCEX8FVe/qlxDVv+9S7chJxsGuzIXNDGMW4XfHw+RaUvjOFQgy6R9HPtvEw3EWY53OY+Mvd7Pr0o2ATaetFnBwyZa3AJF+SU1wzqtJZhxd9/ACly1s4yXmG+rvD+2k7bhAXWQRxwxSDXDCd/ibRM4YklNRkAD+IWJrbZ1rh3uoHisiBv3O2ECgYEA2BHdBDKD2Odd2861kkVck5Ar0ii883y4vNKLNPGQaUmPbHCo+i6CREUDW0SxKXkViaIAFjj0Kmhqa6XASu4SCCc8s+1/2Pyrxb5qgSSJY2LvbZ8SeZi8DfBOXKPdCtUqyHMIYBw3rPyzXwoJEAdHSCrk83tt4urm7qKSqgYpRx0CgYEA0EKcQ3mT+Hd0cx+ISJ2D2X01Z1+zb+Dusn23fr5qs5Nox+VE71+9DN9GVAlBFNg4oz7FCsFS3mJRbvQmmybw0KwU/rKHdnvk15B6IglPgshkVar7j7G51UVntHMW4IBU7jTUoMs1yQ6PPzb9E2Z6UeHbGaoRZCVYZidt55rDL/sCgYEApbI9PcTHW4VCcxg4Ie3TKs567HWVQVw6B4OmgXlmd3eT52MWEpWsDFKoWkt5WQakP6HeUyxmAkeEpPy9VDjx1xLP+GN/kZVi3QhDgLnWKkNqvTQp5Nn+DOpmDaEUGASVBJdCqwG4qI45t/5oKMSMI4nRfe7/u+7MHeDKfFyxNvkCgYBYJPkybdC9BwIYf64U3eYiNSZXPGAb6B3fGeqCEGHk420jvdvxXJoNSqrfgpMzGVjPbw/Cv5QtX3uL9HYqkM634z13l2RSN5nhytqGcV5fwiUFRTr31IcMxzVfYJ68IlTQBThBXgDDug/S95khjuwSn/81249EzbGeeu2/avdV5QKBgGhTmFO9/1HRq0F538XlL8WVvUeyM+XUUNT1qQQsc22anUkM09cv+64ZyQZEqDNX46Tbl2BfTwFhL50MMj7Edjp4L+2dROiuffzcuEQCBCkCoV6B58a9NQehwu8h1j1kxNjDDPSeZnmC0ZJ/Eum1TVcnNa2Zb6II5R72qXThwJM9";

            //根據私鑰生成RSA提供者
            RSACryptoServiceProvider rsaCryptoServiceProvider = CreateRsaProviderFromPrivateKey(privateKey);

            byte[] decrypted = rsaCryptoServiceProvider.Decrypt(Convert.FromBase64String(encryptedMessage), false);

            Console.WriteLine($"decrypted = {Encoding.UTF8.GetString(decrypted)}");

            Console.ReadLine();
        }

        private static RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey)
        {
            var privateKeyBits = System.Convert.FromBase64String(privateKey);

            var RSA = new RSACryptoServiceProvider();
            var RSAparams = new RSAParameters();

            using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
            {
                byte bt = 0;
                ushort twobytes = 0;
                twobytes = binr.ReadUInt16();
                if (twobytes == 0x8130)
                    binr.ReadByte();
                else if (twobytes == 0x8230)
                    binr.ReadInt16();
                else
                    throw new Exception("Unexpected value read binr.ReadUInt16()");

                twobytes = binr.ReadUInt16();
                if (twobytes != 0x0102)
                    throw new Exception("Unexpected version");

                bt = binr.ReadByte();
                if (bt != 0x00)
                    throw new Exception("Unexpected value read binr.ReadByte()");

                RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.D = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.P = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
            }

            RSA.ImportParameters(RSAparams);
            return RSA;
        }
        private static int GetIntegerSize(BinaryReader binr)
        {
            byte bt = 0;
            byte lowbyte = 0x00;
            byte highbyte = 0x00;
            int count = 0;
            bt = binr.ReadByte();
            if (bt != 0x02)
                return 0;
            bt = binr.ReadByte();

            if (bt == 0x81)
                count = binr.ReadByte();
            else
                if (bt == 0x82)
            {
                highbyte = binr.ReadByte();
                lowbyte = binr.ReadByte();
                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
                count = BitConverter.ToInt32(modint, 0);
            }
            else
            {
                count = bt;
            }

            while (binr.ReadByte() == 0x00)
            {
                count -= 1;
            }
            binr.BaseStream.Seek(-1, SeekOrigin.Current);
            return count;
        }
    }
}

註意:+號的處理:因為數據在網路上傳輸時,非字母數字字元都將被替換成百分號(%)後跟兩位十六進位數,而base64編碼在傳輸到後端的時候,+會變成空格,因此先替換掉。後端再替換回來。
前端js代碼:

var str = encodeURI(pwddata).replace(/\+/g, '%2B')

後端C#代碼:

var str = pwd.Replace("%2B","+")

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 正如我們在《控制反轉》提到過的,很多人將IoC理解為一種“面向對象的設計模式”,實際上IoC自身不僅與面向對象沒有必然的聯繫,它也算不上是一種設計模式。一般來講,設計模式提供了一種解決某種具體問題的方案,但是IoC既沒有一個針對性的問題領域,其自身沒有提供一種可實施的解決方案,所以我更加傾向於將Io... ...
  • 基於Ocelot、Consul、Registrator搭建微服務架構,實現服務發現和自動註冊,後續逐步完善許可權驗證、負載均衡等功能。 ...
  • 某些業務需要根據不同的功能將日誌記錄到不同的位置,以便於區分。 日誌工具類(這裡只是簡單的封裝): 瀏覽頁面後,可以看到日誌目錄如下: 參考資料:https://stackoverflow.com/questions/11930381/log4net-multiple-appenders-writi ...
  • .NET Core TDD 前傳: 編寫易於測試的代碼 -- 縫 為什麼要編寫易於測試的代碼? 如何創造縫隙? ...
  • //post請求 public static string PostRequest(string url, HttpContent data) { var handler = new HttpClientHandler() { UseCookies = false }; HttpClient cli... ...
  • .Net Core 編碼規範 概述 規範制定原則 方便代碼的交流和維護。 不影響編碼的效率,不與大眾習慣衝突。 使代碼更美觀、閱讀更方便。 使代碼的邏輯更清晰、更易於理解。 術語定義 Pascal 大小寫 將標識符的首字母和後面連接的每個單詞的首字母都大寫。可以對三字元或更多字元的標識符使用Pasc ...
  • 上一篇做了一個smart qq機器人。 前幾天,因為突然上不了 smart qq,以為TX 要拋棄了。。所以就沒有接著完善smart 機器人。應朋友要求,做一個多開微信。 做了幾天已經做好了,理論上最少可以28開微信,最多可以56開微信。同時線上同時收發信息,業務處理等。。(不知道同一個IP多個微信 ...
  • 控制器中的內容: public ActionResult Update(string carid) { Car car = db.Car.Find(carid); if (car == null) { return HttpNotFound(); } return View(car); } 視圖js ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...