2022-12-16重新編輯C# AES加密解密
在調用RijndaelManaged 時發現生成加密的時候設置參數的順序和解密賦值參數的順序不一致時解密不了。這可能是因為RijndaelManaged封裝的get/set時加了處理。
所以要統一加密和解密的RijndaelManaged 對象
1 /// <summary> 2 /// 統一創建加密和解密參數順序設置 3 /// </summary> 4 /// <param name="key">key 是明文,不需要bas64這些</param> 5 /// <param name="sha1">是否對key進行sha1,是的話key值只需要大於16位即可,取keySize/8的值</param> 6 /// <returns></returns> 7 private static RijndaelManaged CreateAES(string key, bool sha1 = false) 8 { 9 if (sha1 == false) 10 { 11 if (key.Length != 16 && key.Length != 24) 12 { 13 throw new Exception("key的位數必須是16或24"); 14 } 15 } 16 else 17 { 18 if (key.Length < 16) 19 { 20 throw new Exception("key的位數必須是大於16"); 21 } 22 } 23 RijndaelManaged rm = new RijndaelManaged 24 { 25 Mode = CipherMode.ECB, 26 Padding = PaddingMode.PKCS7, 27 KeySize = 128, 28 BlockSize = 128 29 }; 30 if (sha1 == false) 31 { 32 rm.Key = Encoding.UTF8.GetBytes(key); 33 } 34 else 35 { 36 rm.Key = GetSecretKey(key, rm.KeySize / 8); 37 } 38 return rm; 39 }
key應該是16或24位。但是這就比較局限性了,所以在我們不經意間設置了不是這個長度的key時,就會報錯啦。為了決解這個問題,我們就要固定這個key的長度。通過哈希來實現。
1 /// <summary> 2 /// .NET與JAVA的AES互通【SecureRandom.getInstance("SHA1PRNG")】 3 /// </summary> 4 /// <param name="password"></param> 5 /// <returns></returns> 6 private static byte[] GetSecretKey(string password, int size = 16) 7 #region 8 { 9 byte[] keyArray; 10 byte[] seed = Encoding.UTF8.GetBytes(password); 11 using (var st = new SHA1CryptoServiceProvider()) 12 { 13 using (var nd = new SHA1CryptoServiceProvider()) 14 { 15 var rd = nd.ComputeHash(st.ComputeHash(seed)); 16 keyArray = rd.Take(size).ToArray(); 17 } 18 } 19 return keyArray; 20 } 21 #endregion
SHA1:返回一個160位的byte[]數組,20字節,我們取前面16位剛好滿足。
創建加密實例:
1 public static string Encrypt(string text, string key, bool keyNeedHash = false) 2 { 3 var rm = CreateAES(key, keyNeedHash); 4 ICryptoTransform transform = rm.CreateEncryptor(); 5 byte[] plainText = Encoding.UTF8.GetBytes(text); 6 byte[] cipherBytes = transform.TransformFinalBlock(plainText, 0, plainText.Length); 7 return Convert.ToBase64String(cipherBytes); 8 }
解密方法:
public static string Decrypt(string base64_encodeText, string key, bool keyNeedHash = false) { var rm = CreateAES(key, keyNeedHash); ICryptoTransform transform = rm.CreateDecryptor(); byte[] plainText = Convert.FromBase64String(base64_encodeText); byte[] cipherBytes = transform.TransformFinalBlock(plainText, 0, plainText.Length); return Encoding.UTF8.GetString(cipherBytes); }
以上是新整理的筆記。
以下是之前的筆記,做個對比。
/// AES加密解密方式
/// ECB模式不許偏移量iv
/// 提供輸出base64和Hex 16進制格式
1 public class AES_Cipher 2 { 3 /// <summary> 4 /// AES 算法加密(ECB模式) 將明文加密,加密后進行Hex編碼,返回密文 5 /// </summary> 6 /// <param name="str">明文</param> 7 /// <param name="key">密鑰</param> 8 /// <returns>加密后Hex編碼的密文</returns> 9 public static string AesEncryptor_ECB_Hex(string str, string key) 10 { 11 if (string.IsNullOrEmpty(str)) return null; 12 Byte[] toEncryptArray = Encoding.UTF8.GetBytes(str); 13 14 RijndaelManaged rm = new RijndaelManaged 15 { 16 //Key = StrToHexByte(key), //把key轉成16進制 17 Key = Encoding.UTF8.GetBytes(key), 18 Mode = CipherMode.ECB, 19 Padding = PaddingMode.PKCS7 20 }; 21 22 ICryptoTransform cTransform = rm.CreateEncryptor(); 23 Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); 24 25 return ToHexString(resultArray); 26 } 27 28 29 /// <summary> 30 ///AES 算法解密(ECB模式) 將密文Hex解碼后進行解密,返回明文 31 /// </summary> 32 /// <param name="str">密文</param> 33 /// <param name="key">密鑰</param> 34 /// <returns>明文</returns> 35 public static string AesDecryptor_ECB_Hex(string str, string key) 36 { 37 if (string.IsNullOrEmpty(str)) return null; 38 Byte[] toEncryptArray = StrToHexByte(str); 39 40 RijndaelManaged rm = new RijndaelManaged 41 { 42 //Key = StrToHexByte(key), //key16進制解碼 43 Key = Encoding.UTF8.GetBytes(key), 44 Mode = CipherMode.ECB, 45 Padding = PaddingMode.PKCS7 46 }; 47 48 ICryptoTransform cTransform = rm.CreateDecryptor(); 49 Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); 50 51 return Encoding.UTF8.GetString(resultArray); 52 } 53 54 /// <summary> 55 /// byte數組Hex編碼 56 /// </summary> 57 /// <param name="bytes">需要進行編碼的byte[]</param> 58 /// <returns></returns> 59 public static string ToHexString(byte[] bytes) 60 { 61 string hexString = string.Empty; 62 if (bytes != null) 63 { 64 StringBuilder strB = new StringBuilder(); 65 for (int i = 0; i < bytes.Length; i++) 66 { 67 strB.Append(bytes[i].ToString("X2")); 68 } 69 hexString = strB.ToString(); 70 } 71 return hexString; 72 } 73 /// <summary> 74 /// 字符串進行Hex解碼(Hex.decodeHex()) 75 /// </summary> 76 /// <param name="hexString">需要進行解碼的字符串</param> 77 /// <returns></returns> 78 public static byte[] StrToHexByte(string hexString) 79 { 80 hexString = hexString.Replace(" ", ""); 81 if ((hexString.Length % 2) != 0) 82 hexString += " "; 83 byte[] returnBytes = new byte[hexString.Length / 2]; 84 for (int i = 0; i < returnBytes.Length; i++) 85 returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); 86 return returnBytes; 87 } 88 89 90 /// <summary> 91 /// AES 算法加密(ECB模式) 將明文加密,加密后進行Base64編碼,返回密文 92 /// </summary> 93 /// <param name="str">明文</param> 94 /// <param name="key">密鑰</param> 95 /// <returns>加密后Base64編碼的密文</returns> 96 public static string AesEncryptor_ECB_Base64(string str, string key) 97 { 98 if (string.IsNullOrEmpty(str)) return null; 99 Byte[] toEncryptArray = Encoding.UTF8.GetBytes(str); 100 101 RijndaelManaged rm = new RijndaelManaged 102 { 103 //Key = Convert.FromBase64String(key), //如果key是base64編碼過的就解碼轉換,如果按照規范明文和key都應該傳入base64 104 Key = Encoding.UTF8.GetBytes(key), 105 Mode = CipherMode.ECB, 106 Padding = PaddingMode.PKCS7 107 }; 108 109 ICryptoTransform cTransform = rm.CreateEncryptor(); 110 Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); 111 112 return Convert.ToBase64String(resultArray); 113 } 114 115 /// <summary> 116 ///AES 算法解密(ECB模式) 將密文Base64解碼后進行解密,返回明文 117 /// </summary> 118 /// <param name="base64Str">密文(base64編碼格式)</param> 119 /// <param name="key">密鑰</param> 120 /// <returns>明文</returns> 121 public static string AesDecryptor_ECB_Base64(string base64Str, string key) 122 { 123 if (string.IsNullOrEmpty(base64Str)) return null; 124 Byte[] toEncryptArray = Convert.FromBase64String(base64Str); 125 126 RijndaelManaged rm = new RijndaelManaged 127 { 128 //Key = Convert.FromBase64String(key), //如果key是base64編碼過的就解碼轉換,如果按照規范密文和key都應該傳入base64 129 Key = Encoding.UTF8.GetBytes(key), 130 Mode = CipherMode.ECB, 131 Padding = PaddingMode.PKCS7 132 }; 133 134 ICryptoTransform cTransform = rm.CreateDecryptor(); 135 Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); 136 137 return Encoding.UTF8.GetString(resultArray); 138 }
微信敏感數據解密
1 /// <summary> 2 /// 微信敏感數據解密 3 /// </summary> 4 /// <param name="encryptedData">密文</param> 5 /// <param name="iv">加密算法的初始向量</param> 6 /// <param name="sessionKey">登錄獲取的session_key</param> 7 /// <returns></returns> 8 public static string AESDecrypt(string encryptedData, string iv, string sessionKey) 9 { 10 var encryptedDataByte = Convert.FromBase64String(encryptedData); 11 var rijndaelCipher = new RijndaelManaged 12 { 13 Key = Convert.FromBase64String(sessionKey), 14 IV = Convert.FromBase64String(iv), 15 Mode = CipherMode.CBC, 16 Padding = PaddingMode.PKCS7 17 }; 18 19 var transform = rijndaelCipher.CreateDecryptor(); 20 var plainText = transform.TransformFinalBlock(encryptedDataByte, 0, encryptedDataByte.Length); 21 var result = Encoding.UTF8.GetString(plainText); 22 return result; 23 }