新年新氣象,也希望新年可以掙大錢。不管今年年底會不會跟去年一樣,滿懷抱負卻又壯志未酬。(不過沒事,我已為各位卜上一卦,卦象顯示各位都能掙錢...)。已經上班兩天了,公司大部分人還在休假,而我早已上班,估計今年我就是加班狗的命。(不說了,要堅強...) 以上扯淡已畢,下麵言歸正傳。 這次的.NET加密 ...
新年新氣象,也希望新年可以掙大錢。不管今年年底會不會跟去年一樣,滿懷抱負卻又壯志未酬。(不過沒事,我已為各位卜上一卦,卦象顯示各位都能掙錢...)。已經上班兩天了,公司大部分人還在休假,而我早已上班,估計今年我就是加班狗的命。(不說了,要堅強...)
以上扯淡已畢,下麵言歸正傳。
這次的.NET加密解析系列中,前面已經講解了散列加密、對稱加密、數字簽名三種加密方式,在這篇博文種,將會主要講解非對稱加密的原理,以及非對稱加密在.NET種的應用。
一.非對稱加密概述:
前面講解過對稱加密,對稱加密中加密和解密的密鑰是相同的,但是正因為如此,這會給協商過程帶來潛在的危險。所以產生了非對稱加密方式。
1.非對稱加密原理概述:
非對稱加密演算法需要兩個密鑰,分別是公鑰和私鑰。公鑰和私鑰是一對,如果公鑰對數據進行加密,只有使用私鑰才可以進行解密,反之亦然。對於非對稱加密的原理有如下圖:
以上是大致說明瞭消息利用非對稱加密和解密的方式,解析來我們再來看一下如果生成密鑰對。非對稱加密演算法包含一個“密鑰生成”協議,用戶可以使用該協議生成密鑰對。有如下圖:
在非對稱加密演算法中,使用兩個有關的函數,一個是加密函數,使用一個公鑰加密消息,加密函數只能加密數據;一個時解密函數,使用一個私鑰來解密被響應公鑰加密的消息。
2.非對稱加密特點概述:
非對稱加密演算法中,採用加密函數和解密函數,加密函數只能加密函數,解密函數只能解密函數。加密函數的單向性意味著一個發送者創建的消息不能被另一個發送者閱讀。非對稱加密相對於對稱加密來說,非對稱加密的速度非常慢,而且不適用於加密大量數據,公鑰加密(非對稱加密)是用來為對稱加密演算法解決密鑰協商的問題而產生的。RSA演算法中指定密鑰長度為最小的位數,這些位的個數使用二進位數表示密鑰繫數N的值。
3.非對稱加密演算法分類概述:
對於非對稱加密演算法的種類,有如下圖:
RSA演算法:此演算法是基於數論的非對稱密碼體制,採用分組加密方式。安全性是基於大整數因數分解的困難性,RSA演算法是第一個既能用於數據加密也能用與數字簽名的演算法。
DSA演算法(數字簽名演算法):次演算法是基於證書有限域離散對數難題。
ECC演算法(橢圓曲線密碼體制):橢圓曲線指的是由維爾斯特拉斯方程所確定的平面曲線。
Diffie-Hellman演算法:該演算法本身限於密鑰交換的用途,目的在於使得兩個用戶安全地交換一個秘密密鑰以便用與以後的報文加密。該演算法依賴於計算離散對數的難度。
以上是簡單介紹了一些演算法,沒有更加深入的介紹其演算法原理,由於涉及的知識面比較廣,分析起來比較的繁瑣,在這裡就不做講解,如果有興趣可以自行學習和瞭解。
二.DotNet非對稱加密核心對象解析:
上面簡單敘述了非對稱加密的原理,在這裡主要介紹非對稱加密演算法在.NET種的應用,以及實現該演算法所創建的對象。在這裡主要介紹RSA演算法的核心對象。
1.RSA加密和解密的方式:
2.DotNet種RSA演算法核心對象概述:
在.NET種對於非對稱加密演算法的結構體系有如下圖:
3.AsymmetricAlgorithm類解析:
(1).Create():創建用於執行非對稱演算法的預設加密對象。
public static AsymmetricAlgorithm Create() { return AsymmetricAlgorithm.Create("System.Security.Cryptography.AsymmetricAlgorithm"); }
該方法返回新的 RSACryptoServiceProvider 實例,除非已使用 <cryptoClass> 元素更改預設設置。
public static AsymmetricAlgorithm Create(string algName) { return (AsymmetricAlgorithm) CryptoConfig.CreateFromName(algName); }
該方法返回所指定的非對稱演算法實現的新實例。接收參數為要使用的非對稱演算法實現。CryptoConfig.CreateFromName()該方法在前面的加密方式中已經做過解析,這裡就不做介紹了。
(2).KeySize:獲取或設置非對稱演算法所用密鑰模塊的大小(以位為單位)。
public virtual int KeySize { get { return this.KeySizeValue; } set { for (int index = 0; index < this.LegalKeySizesValue.Length; ++index) { if (this.LegalKeySizesValue[index].SkipSize == 0) { if (this.LegalKeySizesValue[index].MinSize == value) { this.KeySizeValue = value; return; } } else { int minSize = this.LegalKeySizesValue[index].MinSize; while (minSize <= this.LegalKeySizesValue[index].MaxSize) { if (minSize == value) { this.KeySizeValue = value; return; } minSize += this.LegalKeySizesValue[index].SkipSize; } } } throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKeySize")); } }
由以上代碼可以發現,該屬性具有get和set兩個構造器,說明該屬性是可讀可寫的。該屬性返回非對稱演算法所用密鑰模塊的大小(以位為單位)。
4.RSA類解析:
(1).FromXmlString():通過 XML 字元串中的密鑰信息初始化Cryptography.RSA對象。
public override void FromXmlString(string xmlString) { if (xmlString == null) throw new ArgumentNullException("xmlString"); RSAParameters parameters = new RSAParameters(); SecurityElement topElement = new Parser(xmlString).GetTopElement(); string inputBuffer1 = topElement.SearchForTextOfLocalName("Modulus"); if (inputBuffer1 == null) { string key = "Cryptography_InvalidFromXmlString"; object[] objArray = new object[2]; int index1 = 0; string str1 = "RSA"; objArray[index1] = (object) str1; int index2 = 1; string str2 = "Modulus"; objArray[index2] = (object) str2; throw new CryptographicException(Environment.GetResourceString(key, objArray)); } parameters.Modulus = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer1)); string inputBuffer2 = topElement.SearchForTextOfLocalName("Exponent"); if (inputBuffer2 == null) { string key = "Cryptography_InvalidFromXmlString"; object[] objArray = new object[2]; int index1 = 0; string str1 = "RSA"; objArray[index1] = (object) str1; int index2 = 1; string str2 = "Exponent"; objArray[index2] = (object) str2; throw new CryptographicException(Environment.GetResourceString(key, objArray)); } parameters.Exponent = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer2)); string inputBuffer3 = topElement.SearchForTextOfLocalName("P"); if (inputBuffer3 != null) parameters.P = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer3)); string inputBuffer4 = topElement.SearchForTextOfLocalName("Q"); if (inputBuffer4 != null) parameters.Q = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer4)); string inputBuffer5 = topElement.SearchForTextOfLocalName("DP"); if (inputBuffer5 != null) parameters.DP = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer5)); string inputBuffer6 = topElement.SearchForTextOfLocalName("DQ"); if (inputBuffer6 != null) parameters.DQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer6)); string inputBuffer7 = topElement.SearchForTextOfLocalName("InverseQ"); if (inputBuffer7 != null) parameters.InverseQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer7)); string inputBuffer8 = topElement.SearchForTextOfLocalName("D"); if (inputBuffer8 != null) parameters.D = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer8)); this.ImportParameters(parameters); }
該方法是繼承自AsymmetricAlgorithm類,在RSA類種被重寫,該方法接收參數包含 RSA 密鑰信息的 XML 字元串。SecurityElement類表示用於編碼安全對象的XML對象模型。
(2).ToXmlString():創建並返回包含當前 RSA 對象的密鑰的 XML 字元串。
public override string ToXmlString(bool includePrivateParameters) { RSAParameters rsaParameters = this.ExportParameters(includePrivateParameters); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<RSAKeyValue>"); stringBuilder.Append("<Modulus>" + Convert.ToBase64String(rsaParameters.Modulus) + "</Modulus>"); stringBuilder.Append("<Exponent>" + Convert.ToBase64String(rsaParameters.Exponent) + "</Exponent>"); if (includePrivateParameters) { stringBuilder.Append("<P>" + Convert.ToBase64String(rsaParameters.P) + "</P>"); stringBuilder.Append("<Q>" + Convert.ToBase64String(rsaParameters.Q) + "</Q>"); stringBuilder.Append("<DP>" + Convert.ToBase64String(rsaParameters.DP) + "</DP>"); stringBuilder.Append("<DQ>" + Convert.ToBase64String(rsaParameters.DQ) + "</DQ>"); stringBuilder.Append("<InverseQ>" + Convert.ToBase64String(rsaParameters.InverseQ) + "</InverseQ>"); stringBuilder.Append("<D>" + Convert.ToBase64String(rsaParameters.D) + "</D>"); } stringBuilder.Append("</RSAKeyValue>"); return stringBuilder.ToString(); }
該方法同樣繼承自AsymmetricAlgorithm類,該方法接收一個布爾型的參數,true 表示同時包含 RSA 公鑰和私鑰;false 表示僅包含公鑰。該方法返回包含當前 RSA 對象的密鑰的 XML 字元串。RSAParameters為一個結構,表示System.Security.Cryptography.RSA演算法的標準參數。
5.RSACryptoServiceProvider類解析:
(1).Encrypt():使用 RSA演算法對數據進行加密。
[SecuritySafeCritical] public byte[] Encrypt(byte[] rgb, bool fOAEP) { if (rgb == null) throw new ArgumentNullException("rgb"); this.GetKeyPair(); byte[] o = (byte[]) null; RSACryptoServiceProvider.EncryptKey(this._safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack<byte[]>(ref o)); return o; }
該方法接受兩個參數,要加密的數據。fOAEP如果為 true,則使用 OAEP 填充(僅在運行 Microsoft Windows XP 或更高版本的電腦上可用)執行直接的 RSA 加密;否則,如果為 false,則使用 PKCS#1 1.5 版填充。該方法返回一個已加密的數據,為一個位元組數組。
(2).Decrypt():使用 RSA演算法對數據進行解密。
[SecuritySafeCritical] public byte[] Decrypt(byte[] rgb, bool fOAEP) { if (rgb == null) throw new ArgumentNullException("rgb"); this.GetKeyPair(); if (rgb.Length > this.KeySize / 8) { string key = "Cryptography_Padding_DecDataTooBig"; object[] objArray = new object[1]; int index = 0; // ISSUE: variable of a boxed type __Boxed<int> local = (ValueType) (this.KeySize / 8); objArray[index] = (object) local; throw new CryptographicException(Environment.GetResourceString(key, objArray)); } if (!this.CspKeyContainerInfo.RandomlyGenerated && !CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { KeyContainerPermission containerPermission = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags); KeyContainerPermissionAccessEntry accessEntry = new KeyContainerPermissionAccessEntry(this._parameters, KeyContainerPermissionFlags.Decrypt); containerPermission.AccessEntries.Add(accessEntry); containerPermission.Demand(); } byte[] o = (byte[]) null; RSACryptoServiceProvider.DecryptKey(this._safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack<byte[]>(ref o)); return o; }
該方法接受兩個參數,rgb要解密的數據。fOAEP如果為 true,則使用 OAEP 填充(僅在運行 Microsoft Windows XP 或更高版本的電腦上可用)執行直接的 <see cref="T:System.Security.Cryptography.RSA"/> 解密;否則,如果為 false,則使用 PKCS#1 1.5 版填充。該方法返回 已解密的數據,它是加密前的原始純文本。
三.應用實例:
1.RsaHelper類:
using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; namespace BasicMmethodExtensionClass.EncryptHelper { /// <summary> /// 非對稱RSA加密類 /// 需要BigInteger類來輔助 /// </summary> public static class RsaHelper { /// <summary> /// RSA的容器 可以解密的源字元串長度為 DWKEYSIZE/8-11 /// </summary> public const int Dwkeysize = 1024; /// <summary> /// RSA加密的密匙結構 公鑰和私匙 /// </summary> public struct RsaKey { public string PublicKey { get; set; } public string PrivateKey { get; set; } } /// <summary> /// 得到RSA的解謎的密匙對 /// </summary> /// <returns></returns> public static RsaKey GetRasKey() { RSACryptoServiceProvider.UseMachineKeyStore = true; //聲明一個指定大小的RSA容器 RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(Dwkeysize); //取得RSA容易里的各種參數 RSAParameters p = rsaProvider.ExportParameters(true); return new RsaKey { PublicKey = ComponentKey(p.Exponent, p.Modulus), PrivateKey = ComponentKey(p.D, p.Modulus) }; } /// <summary> /// 檢查明文的有效性 DWKEYSIZE/8-11 長度之內為有效 中英文都算一個字元 /// </summary> /// <param name="source"></param> /// <returns></returns> public static bool CheckSourceValidate(string source) { return (Dwkeysize / 8 - 11) >= source.Length; } /// <summary> /// 組合成密匙字元串 /// </summary> /// <param name="b1"></param> /// <param name="b2"></param> /// <returns></returns> private static string ComponentKey(byte[] b1, byte[] b2) { var list = new List<byte> { (byte) b1.Length }; list.AddRange(b1); list.AddRange(b2); var b = list.ToArray<byte>(); return Convert.ToBase64String(b); } /// <summary> /// 解析密匙 /// </summary> /// <param name="key">密匙</param> /// <param name="b1">RSA的相應參數1</param> /// <param name="b2">RSA的相應參數2</param> private static void ResolveKey(string key, out byte[] b1, out byte[] b2) { //從base64字元串 解析成原來的位元組數組 byte[] b = Convert.FromBase64String(key); //初始化參數的數組長度 b1 = new byte[b[0]]; b2 = new byte[b.Length - b[0] - 1]; //將相應位置是值放進相應的數組 for (int n = 1, i = 0, j = 0; n < b.Length; n++) { if (n <= b[0]) { b1[i++] = b[n]; } else { b2[j++] = b[n]; } } } /// <summary> /// 字元串加密 /// </summary> /// <param name="source">源字元串 明文</param> /// <param name="key">密匙</param> /// <returns>加密遇到錯誤將會返回原字元串</returns> public static string EncryptString(string source, string key) { string encryptString; try { if (!CheckSourceValidate(source)) { throw new Exception("明文太長"); } //解析這個密鑰 byte[] d; byte[] n; ResolveKey(key, out d, out n); var biN = new BigInteger(n); var biD = new BigInteger(d); encryptString = EncryptString(source, biD, biN); } catch { encryptString = source; } return encryptString; } /// <summary> /// 字元串解密 /// </summary> /// <param name="encryptString">密文</param> /// <param name="key">密鑰</param> /// <returns>遇到解密失敗將會返回原字元串</returns> public static string DecryptString(string encryptString, string key) { string source; try { //解析這個密鑰 byte[] e; byte[] n; ResolveKey(key, out e, out n); var biE = new BigInteger(e); var biN = new BigInteger(n); source = DecryptString(encryptString, biE, biN); } catch { source = encryptString; } return source; } /// <summary> /// 用指定的密匙加密 /// </summary> /// <param name="source">明文</param> /// <param name="d">可以是RSACryptoServiceProvider生成的D</param> /// <param name="n">可以是RSACryptoServiceProvider生成的Modulus</param> /// <returns>返回密文</returns> private static string EncryptString(string source, BigInteger d, BigInteger n) { var len = source.Length; int len1; if ((len % 128) == 0) len1 = len / 128; else len1 = len / 128 + 1; var result = new StringBuilder(); for (var i = 0; i < len1; i++) { var blockLen = len >= 128 ? 128 : len; var block = source.Substring(i * 128, blockLen); byte[] oText = Encoding.UTF8.GetBytes(block); var biText = new BigInteger(oText); var biEnText = biText.modPow(d, n); var temp = biEnText.ToHexString(); result.Append(temp).Append("@"); len -= blockLen; } return result.ToString().TrimEnd('@'); } /// <summary> /// 用指定的密匙加密 /// </summary> /// <param name="encryptString"></param> /// <param name="e">可以是RSACryptoServiceProvider生成的Exponent</param> /// <param name="n">可以是RSACryptoServiceProvider生成的Modulus</param> /// <returns>返回明文</returns> private static string DecryptString(string encryptString, BigInteger e, BigInteger n) { var result = new StringBuilder(); var strarr1 = encryptString.Split(new[] { '@' }, StringSplitOptions.RemoveEmptyEntries); foreach (var block in strarr1) { var biText = new BigInteger(block, 16); var biEnText = biText.modPow(e, n); var temp = Encoding.UTF8.GetString(biEnText.getBytes()); result.Append(temp); } return result.ToString(); } } }
2.BigInteger類:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BasicMmethodExtensionClass.EncryptHelper { public class BigInteger { // maximum length of the BigInteger in uint (4 bytes) // change this to suit the required level of precision. private const int maxLength = 70; // primes smaller than 2000 to test the generated prime number public static readonly int[] primesBelow2000 = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301,