離過年又近了一天,回家已是近在咫尺,有人歡喜有人愁,因為過幾天就得經歷每年一度的裝逼大戲,親戚朋友加同學的各方顯擺,所以得靠一劑年終獎來裝飾一個安穩的年,在這裡我想起了一個題目“論裝逼的技術性和重要性”。
都是老司機了,不扯淡,站在外面的都進來,然後請後面的把門關一下,我們接著出發。
上一篇主要介紹.NET的散列加密,散列算法主要用於簽名等操作,在我們的項目中,如果對加密沒有特別的要求,一般都是采用的對稱加密方式,因為這種加密方式相較其他加密方式較為簡單,但是這種加密方式比較的高效,所以今天就介紹一下.NET的對稱加密方式。
對稱加密是采用單密鑰加密方式,這也就意味著加密和解密都是用同一個密鑰。根據密碼學的相關定義,對稱加密系統的組成部分有5個,分別是明文空間,密文空間,密鑰空間,加密空間,解密算法。接下來用一個示意圖來表示一下:
算法名稱
算法說明
DES加密算法 采用的是分組加密方式,使用56位密鑰加密64位明文,最後產生64位密文 3DES加密算法 采用168位的密鑰,三重加密,速度比較的慢 TripleDES加密算法 用兩個密鑰對數據進行3次加密/解密運算 RC2加密算法 運用密鑰長度可變,對明文采取64位分組加密 RC4加密算法 運用一個密鑰長度可變的面向字節流的加密算法,以隨機置換為基礎 RC5加密算法 運用一種分組長度、密鑰長度、加密迭代輪數都可變的分組加密算法。(包含密鑰擴展、加密算法、解密算法) RC6加密算法 RC6繼承了RC5的循環移位思想,RC6是輸入的明文由原先2個區擴展為4個塊區 Rijndael加密算法 運用反復運算的加密算法,允許數據區塊及密鑰的長度可變。數據區塊與密鑰長度的變動時各自獨立的在.NET中對稱算法的層次結構如下圖:
public virtual byte[] IV { get { if (this.IVValue == null) this.GenerateIV(); return (byte[]) this.IVValue.Clone(); } set { if (value == null) throw new ArgumentNullException("value"); if (value.Length != this.BlockSizeValue / 8) throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidIVSize")); this.IVValue = (byte[]) value.Clone(); } }
該屬於使用字節數組的形式表示Key,該屬性具有get和set屬性,表明該屬性是可讀可寫的,該屬性為虛屬性,可以在子類中重寫。Key屬性是用來獲取或設置對稱算法的密鑰,密鑰即可使用於加密也可以使用於解密。
(2).LegalBlockSizes屬性: 獲取對稱算法支持的塊大小(以位為單位)。
public virtual KeySizes[] LegalBlockSizes { get { return (KeySizes[]) this.LegalBlockSizesValue.Clone(); } }
該屬性為虛屬性,在子類中可重寫,該屬性是只讀屬性。
(3).Create()方法:創建用於執行對稱算法的指定加密對象。
public static SymmetricAlgorithm Create(string algName) { return (SymmetricAlgorithm) CryptoConfig.CreateFromName(algName); }
該方法CryptoConfig.CreateFromName()方法在前面一篇介紹過,在這裡就不做具體的介紹,Create()接收一個SymmetricAlgorithm類型的字符串參數,指定本次System.Security.Cryptography.SymmetricAlgorithm字符串。
(4).Mode屬性:獲取或設置對稱算法的運算模式。
public virtual CipherMode Mode { get { return this.ModeValue; } set { if (value < CipherMode.CBC || CipherMode.CFB < value) throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidCipherMode")); this.ModeValue = value; } }
該屬性是一個虛屬性,獲取和設置密碼代碼,拉取准備數據,由代碼可以看出,該屬性含有一個枚舉類型CipherMode,我們接下來了解一下這個枚舉類型:
CipherMode枚舉類型:指定用於加密的塊加密模式。
[ComVisible(true)] public enum CipherMode { CBC = 1, ECB = 2, OFB = 3, CFB = 4, CTS = 5 }
CBC(密碼塊鏈):該模式引入類反饋;ECB(電子密碼本):該模式分別加密每個塊;OFB(輸出反饋):該模式將少量遞增的純文本處理改成密碼文本,而不是以此處理整個塊;CFB(密碼反饋):該模式將少量遞增的純文本處理成密碼文本,而不是一次處理整個塊;CTS(密碼文本竊用):該模式處理任何長度的純文本並產生長度與純文本長度匹配的密碼文本。
(5).Padding屬性: 獲取或設置對稱算法中使用的填充模式。
public virtual PaddingMode Padding { get { return this.PaddingValue; } set { if (value < PaddingMode.None || PaddingMode.ISO10126 < value) throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidPaddingMode")); this.PaddingValue = value; } }
該屬性是對稱算法中使用的填充模式,默認值為 PKCS7。該屬性可讀可寫,填充數據的部分塊。由該屬性可知一個枚舉類型PaddingMode。
PaddingMode枚舉:指定當消息數據塊較短時要應用的填充類型,比加密操作所需的全部字節數。
[ComVisible(true)] public enum PaddingMode { None = 1, PKCS7 = 2, Zeros = 3, ANSIX923 = 4, ISO10126 = 5 }
該枚舉類型有5個成員, None = 1:不填充;PKCS7 = 2:PKCS#7填充字符串由字節序列組成,每個字節都是等於添加的填充字節的總數; Zeros = 3:填充字符串由設置為零的字節組成; ANSIX923 = 4:ANSI X 923填充字符串由長度前面填充零的字節序列組成;ISO10126 = 5:ISO10126填充字符串由長度之前的隨機數據組成。
ICryptoTransform定義基本的加密轉換運算,該接口的實例可以將文純文本轉化成加密文本,或者將加密文本轉化為純文本,每一個ICryptoTransform都是單向的,只能被用於其創建的目的。該接口的屬性和方法如下:
/// <summary> /// 獲取輸入塊大小。 /// </summary> int InputBlockSize { get; } /// <summary> /// 獲取輸出塊大小。 /// </summary> int OutputBlockSize { get; } /// <summary> /// 獲取一個值,該值指示是否可以轉換多個塊。 /// </summary> bool CanTransformMultipleBlocks { get; } /// <summary> /// 獲取一個值,該值指示是否可重復使用當前轉換。 /// </summary> bool CanReuseTransform { get; } /// <summary> /// 轉換輸入字節數組的指定區域,並將所得到的轉換復制到輸出字節數組的指定區域。 /// </summary> int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); /// <summary> /// 轉換指定字節數組的指定區域。 /// </summary> byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount);
ICryptoTransform接口實例並不能使用於自己,.NET提供了CryptoStream類,定義將數據流鏈接到加密轉換的流。創建CryptoStream的實例需要一個真實流、ICryptoTransform、CryptoStreamMode枚舉的值。
/// <summary> /// 加密數據 /// </summary> /// <param name="text"></param> /// <param name="sKey"></param> /// <returns></returns> public static string Encrypt(string text, string sKey) { if (string.IsNullOrEmpty(text)) { throw new ArgumentNullException(text); } if (string.IsNullOrEmpty(sKey)) { throw new ArgumentNullException(sKey); } MemoryStream ms = null; DESCryptoServiceProvider des = null; try { des = new DESCryptoServiceProvider(); var inputByteArray = Encoding.Default.GetBytes(text); var bKey = Encoding.ASCII.GetBytes(Md5Hash(sKey).Substring(0, 8)); des.Key = bKey; des.IV = bKey; ms = new MemoryStream(); var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); var ret = new StringBuilder(); foreach (byte b in ms.ToArray()) { ret.AppendFormat("{0:X2}", b); } return ret.ToString(); } catch (NotSupportedException nsex) { throw nsex; } catch (ArgumentNullException arnex) { throw arnex; } catch (EncoderFallbackException efex) { throw efex; } catch (ArgumentException arex) { throw arex; } catch (CryptographicException crex) { throw crex; } finally { if (ms != null) { ms.Close(); } if (des != null) { des.Clear(); } } }
/// <summary> /// 解密數據 /// </summary> /// <param name="text"></param> /// <param name="sKey"></param> /// <returns></returns> public static string Decrypt(string text, string sKey) { if (string.IsNullOrEmpty(text)) { throw new ArgumentNullException(text); } if (string.IsNullOrEmpty(sKey)) { throw new ArgumentNullException(sKey); } MemoryStream ms = null; DESCryptoServiceProvider des = null; try { des = new DESCryptoServiceProvider(); var len = text.Length / 2; byte[] inputByteArray = new byte[len]; int x; for (x = 0; x < len; x++) { var i = Convert.ToInt32(text.Substring(x * 2, 2), 16); inputByteArray[x] = (byte)i; } var bKey = Encoding.ASCII.GetBytes(Md5Hash(sKey).Substring(0, 8)); des.Key = bKey; des.IV = bKey; ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); return Encoding.Default.GetString(ms.ToArray()); } catch (NotSupportedException nsex) { throw nsex; } catch (ArgumentNullException arnex) { throw arnex; } catch (EncoderFallbackException efex) { throw efex; } catch (ArgumentException arex) { throw arex; } catch (CryptographicException crex) { throw crex; } finally { if (ms != null) { ms.Close(); } if (des != null) { des.Clear(); } } }
這篇博文主要講解.NET的對稱加密方式,從原理上講解和源碼分析,以及提供了對應的實例,輔助我們去理解加密。如有錯誤和不足之處,歡迎評批指正。