Encrypt and decrypt a string in C#?
如何用C加密和解密字符串?
编辑2013年10月:尽管我一直在编辑这个答案以解决缺点,但请参阅jbtule的答案以获得更强大、更明智的解决方案。
https://stackoverflow.com/a/10366194/188474
原始答案:
下面是一个从"RijndaelManaged类"文档和MCTS培训工具包派生的工作示例。
编辑2012年4月:根据Jbtule的建议,编辑此答案以预挂IV,如图所示:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged%28v=vs.95%29.aspx
祝你好运!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | public class Crypto { //While an app specific salt is not the best practice for //password based encryption, it's probably safe enough as long as //it is truly uncommon. Also too much work to alter this answer otherwise. private static byte[] _salt=__To_Do__("Add a app specific salt here"); /// <summary> /// Encrypt the given string using AES. The string can be decrypted using /// DecryptStringAES(). The sharedSecret parameters must match. /// </summary> /// <param name="plainText">The text to encrypt.</param> /// <param name="sharedSecret">A password used to generate a key for encryption.</param> public static string EncryptStringAES(string plainText, string sharedSecret) { if (string.IsNullOrEmpty(plainText)) throw new ArgumentNullException("plainText"); if (string.IsNullOrEmpty(sharedSecret)) throw new ArgumentNullException("sharedSecret"); string outStr = null; // Encrypted string to return RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data. try { // generate the key from the shared secret and the salt Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); // Create a RijndaelManaged object aesAlg = new RijndaelManaged(); aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); // Create a decryptor to perform the stream transform. ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); // Create the streams used for encryption. using (MemoryStream msEncrypt = new MemoryStream()) { // prepend the IV msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int)); msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length); using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { //Write all data to the stream. swEncrypt.Write(plainText); } } outStr = Convert.ToBase64String(msEncrypt.ToArray()); } } finally { // Clear the RijndaelManaged object. if (aesAlg != null) aesAlg.Clear(); } // Return the encrypted bytes from the memory stream. return outStr; } /// <summary> /// Decrypt the given string. Assumes the string was encrypted using /// EncryptStringAES(), using an identical sharedSecret. /// </summary> /// <param name="cipherText">The text to decrypt.</param> /// <param name="sharedSecret">A password used to generate a key for decryption.</param> public static string DecryptStringAES(string cipherText, string sharedSecret) { if (string.IsNullOrEmpty(cipherText)) throw new ArgumentNullException("cipherText"); if (string.IsNullOrEmpty(sharedSecret)) throw new ArgumentNullException("sharedSecret"); // Declare the RijndaelManaged object // used to decrypt the data. RijndaelManaged aesAlg = null; // Declare the string used to hold // the decrypted text. string plaintext = null; try { // generate the key from the shared secret and the salt Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); // Create the streams used for decryption. byte[] bytes = Convert.FromBase64String(cipherText); using (MemoryStream msDecrypt = new MemoryStream(bytes)) { // Create a RijndaelManaged object // with the specified key and IV. aesAlg = new RijndaelManaged(); aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); // Get the initialization vector from the encrypted stream aesAlg.IV = ReadByteArray(msDecrypt); // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(csDecrypt)) // Read the decrypted bytes from the decrypting stream // and place them in a string. plaintext = srDecrypt.ReadToEnd(); } } } finally { // Clear the RijndaelManaged object. if (aesAlg != null) aesAlg.Clear(); } return plaintext; } private static byte[] ReadByteArray(Stream s) { byte[] rawLength = new byte[sizeof(int)]; if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length) { throw new SystemException("Stream did not contain properly formatted byte array"); } byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)]; if (s.Read(buffer, 0, buffer.Length) != buffer.Length) { throw new SystemException("Did not read byte array properly"); } return buffer; } } |
字符串的对称认证加密的现代示例。
对称加密的一般最佳实践是对关联数据使用经过身份验证的加密(AEAD),但是这不是标准.NET加密库的一部分。所以第一个例子使用的是AES256,然后是HMAC256,这是一个两步加密,然后是MAC,这需要更多的开销和更多的密钥。
第二个例子使用了使用开源BouncyCastle(viaNuget)的AES256-GCM的简单实践。
这两个示例都有一个主函数,它接受机密消息字符串、密钥和可选的非机密有效负载,并返回经过身份验证的加密字符串(可选地预先添加了非机密数据)。理想情况下,您可以使用这些随机生成的256bit密钥,请参阅
这两个示例都有一个助手方法,该方法使用字符串密码来生成键。这些帮助器方法是为了方便与其他示例匹配而提供的,但是它们的安全性要差得多,因为密码的强度远小于256位密钥。
更新:添加了
.NET内置加密(aes)-然后是mac(hmac)[gist]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | /* * This work (Modern Encryption of a String C#, by James Tuley), * identified by James Tuley, is free of known copyright restrictions. * https://gist.github.com/4336842 * http://creativecommons.org/publicdomain/mark/1.0/ */ using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace Encryption { public static class AESThenHMAC { private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create(); //Preconfigured Encryption Parameters public static readonly int BlockBitSize = 128; public static readonly int KeyBitSize = 256; //Preconfigured Password Key Derivation Parameters public static readonly int SaltBitSize = 64; public static readonly int Iterations = 10000; public static readonly int MinPasswordLength = 12; /// <summary> /// Helper that generates a random key on each call. /// </summary> /// <returns></returns> public static byte[] NewKey() { var key = new byte[KeyBitSize / 8]; Random.GetBytes(key); return key; } /// <summary> /// Simple Encryption (AES) then Authentication (HMAC) for a UTF8 Message. /// </summary> /// <param name="secretMessage">The secret message.</param> /// <param name="cryptKey">The crypt key.</param> /// <param name="authKey">The auth key.</param> /// <param name="nonSecretPayload">(Optional) Non-Secret Payload.</param> /// <returns> /// Encrypted Message /// </returns> /// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception> /// <remarks> /// Adds overhead of (Optional-Payload + BlockSize(16) + Message-Padded-To-Blocksize + HMac-Tag(32)) * 1.33 Base64 /// </remarks> public static string SimpleEncrypt(string secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null) { if (string.IsNullOrEmpty(secretMessage)) throw new ArgumentException("Secret Message Required!","secretMessage"); var plainText = Encoding.UTF8.GetBytes(secretMessage); var cipherText = SimpleEncrypt(plainText, cryptKey, authKey, nonSecretPayload); return Convert.ToBase64String(cipherText); } /// <summary> /// Simple Authentication (HMAC) then Decryption (AES) for a secrets UTF8 Message. /// </summary> /// <param name="encryptedMessage">The encrypted message.</param> /// <param name="cryptKey">The crypt key.</param> /// <param name="authKey">The auth key.</param> /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param> /// <returns> /// Decrypted Message /// </returns> /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception> public static string SimpleDecrypt(string encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0) { if (string.IsNullOrWhiteSpace(encryptedMessage)) throw new ArgumentException("Encrypted Message Required!","encryptedMessage"); var cipherText = Convert.FromBase64String(encryptedMessage); var plainText = SimpleDecrypt(cipherText, cryptKey, authKey, nonSecretPayloadLength); return plainText == null ? null : Encoding.UTF8.GetString(plainText); } /// <summary> /// Simple Encryption (AES) then Authentication (HMAC) of a UTF8 message /// using Keys derived from a Password (PBKDF2). /// </summary> /// <param name="secretMessage">The secret message.</param> /// <param name="password">The password.</param> /// <param name="nonSecretPayload">The non secret payload.</param> /// <returns> /// Encrypted Message /// </returns> /// <exception cref="System.ArgumentException">password</exception> /// <remarks> /// Significantly less secure than using random binary keys. /// Adds additional non secret payload for key generation parameters. /// </remarks> public static string SimpleEncryptWithPassword(string secretMessage, string password, byte[] nonSecretPayload = null) { if (string.IsNullOrEmpty(secretMessage)) throw new ArgumentException("Secret Message Required!","secretMessage"); var plainText = Encoding.UTF8.GetBytes(secretMessage); var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload); return Convert.ToBase64String(cipherText); } /// <summary> /// Simple Authentication (HMAC) and then Descryption (AES) of a UTF8 Message /// using keys derived from a password (PBKDF2). /// </summary> /// <param name="encryptedMessage">The encrypted message.</param> /// <param name="password">The password.</param> /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param> /// <returns> /// Decrypted Message /// </returns> /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception> /// <remarks> /// Significantly less secure than using random binary keys. /// </remarks> public static string SimpleDecryptWithPassword(string encryptedMessage, string password, int nonSecretPayloadLength = 0) { if (string.IsNullOrWhiteSpace(encryptedMessage)) throw new ArgumentException("Encrypted Message Required!","encryptedMessage"); var cipherText = Convert.FromBase64String(encryptedMessage); var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength); return plainText == null ? null : Encoding.UTF8.GetString(plainText); } public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null) { //User Error Checks if (cryptKey == null || cryptKey.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize),"cryptKey"); if (authKey == null || authKey.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize),"authKey"); if (secretMessage == null || secretMessage.Length < 1) throw new ArgumentException("Secret Message Required!","secretMessage"); //non-secret payload optional nonSecretPayload = nonSecretPayload ?? new byte[] { }; byte[] cipherText; byte[] iv; using (var aes = new AesManaged { KeySize = KeyBitSize, BlockSize = BlockBitSize, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }) { //Use random IV aes.GenerateIV(); iv = aes.IV; using (var encrypter = aes.CreateEncryptor(cryptKey, iv)) using (var cipherStream = new MemoryStream()) { using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write)) using (var binaryWriter = new BinaryWriter(cryptoStream)) { //Encrypt Data binaryWriter.Write(secretMessage); } cipherText = cipherStream.ToArray(); } } //Assemble encrypted message and add authentication using (var hmac = new HMACSHA256(authKey)) using (var encryptedStream = new MemoryStream()) { using (var binaryWriter = new BinaryWriter(encryptedStream)) { //Prepend non-secret payload if any binaryWriter.Write(nonSecretPayload); //Prepend IV binaryWriter.Write(iv); //Write Ciphertext binaryWriter.Write(cipherText); binaryWriter.Flush(); //Authenticate all data var tag = hmac.ComputeHash(encryptedStream.ToArray()); //Postpend tag binaryWriter.Write(tag); } return encryptedStream.ToArray(); } } public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0) { //Basic Usage Error Checks if (cryptKey == null || cryptKey.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("CryptKey needs to be {0} bit!", KeyBitSize),"cryptKey"); if (authKey == null || authKey.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("AuthKey needs to be {0} bit!", KeyBitSize),"authKey"); if (encryptedMessage == null || encryptedMessage.Length == 0) throw new ArgumentException("Encrypted Message Required!","encryptedMessage"); using (var hmac = new HMACSHA256(authKey)) { var sentTag = new byte[hmac.HashSize / 8]; //Calculate Tag var calcTag = hmac.ComputeHash(encryptedMessage, 0, encryptedMessage.Length - sentTag.Length); var ivLength = (BlockBitSize / 8); //if message length is to small just return null if (encryptedMessage.Length < sentTag.Length + nonSecretPayloadLength + ivLength) return null; //Grab Sent Tag Array.Copy(encryptedMessage, encryptedMessage.Length - sentTag.Length, sentTag, 0, sentTag.Length); //Compare Tag with constant time comparison var compare = 0; for (var i = 0; i < sentTag.Length; i++) compare |= sentTag[i] ^ calcTag[i]; //if message doesn't authenticate return null if (compare != 0) return null; using (var aes = new AesManaged { KeySize = KeyBitSize, BlockSize = BlockBitSize, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }) { //Grab IV from message var iv = new byte[ivLength]; Array.Copy(encryptedMessage, nonSecretPayloadLength, iv, 0, iv.Length); using (var decrypter = aes.CreateDecryptor(cryptKey, iv)) using (var plainTextStream = new MemoryStream()) { using (var decrypterStream = new CryptoStream(plainTextStream, decrypter, CryptoStreamMode.Write)) using (var binaryWriter = new BinaryWriter(decrypterStream)) { //Decrypt Cipher Text from Message binaryWriter.Write( encryptedMessage, nonSecretPayloadLength + iv.Length, encryptedMessage.Length - nonSecretPayloadLength - iv.Length - sentTag.Length ); } //Return Plain Text return plainTextStream.ToArray(); } } } } public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null) { nonSecretPayload = nonSecretPayload ?? new byte[] {}; //User Error Checks if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength) throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength),"password"); if (secretMessage == null || secretMessage.Length ==0) throw new ArgumentException("Secret Message Required!","secretMessage"); var payload = new byte[((SaltBitSize / 8) * 2) + nonSecretPayload.Length]; Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length); int payloadIndex = nonSecretPayload.Length; byte[] cryptKey; byte[] authKey; //Use Random Salt to prevent pre-generated weak password attacks. using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations)) { var salt=generator.Salt; //Generate Keys cryptKey = generator.GetBytes(KeyBitSize / 8); //Create Non Secret Payload Array.Copy(salt, 0, payload, payloadIndex, salt.Length); payloadIndex += salt.Length; } //Deriving separate key, might be less efficient than using HKDF, //but now compatible with RNEncryptor which had a very similar wireformat and requires less code than HKDF. using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations)) { var salt=generator.Salt; //Generate Keys authKey = generator.GetBytes(KeyBitSize / 8); //Create Rest of Non Secret Payload Array.Copy(salt, 0, payload, payloadIndex, salt.Length); } return SimpleEncrypt(secretMessage, cryptKey, authKey, payload); } public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0) { //User Error Checks if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength) throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength),"password"); if (encryptedMessage == null || encryptedMessage.Length == 0) throw new ArgumentException("Encrypted Message Required!","encryptedMessage"); var cryptSalt=new byte[SaltBitSize / 8]; var authSalt=new byte[SaltBitSize / 8]; //Grab Salt from Non-Secret Payload Array.Copy(encryptedMessage, nonSecretPayloadLength, cryptSalt, 0, cryptSalt.Length); Array.Copy(encryptedMessage, nonSecretPayloadLength + cryptSalt.Length, authSalt, 0, authSalt.Length); byte[] cryptKey; byte[] authKey; //Generate crypt key using (var generator = new Rfc2898DeriveBytes(password, cryptSalt, Iterations)) { cryptKey = generator.GetBytes(KeyBitSize / 8); } //Generate auth key using (var generator = new Rfc2898DeriveBytes(password, authSalt, Iterations)) { authKey = generator.GetBytes(KeyBitSize / 8); } return SimpleDecrypt(encryptedMessage, cryptKey, authKey, cryptSalt.Length + authSalt.Length + nonSecretPayloadLength); } } } |
弹跳城堡AES-GCM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | /* * This work (Modern Encryption of a String C#, by James Tuley), * identified by James Tuley, is free of known copyright restrictions. * https://gist.github.com/4336842 * http://creativecommons.org/publicdomain/mark/1.0/ */ using System; using System.IO; using System.Text; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; namespace Encryption { public static class AESGCM { private static readonly SecureRandom Random = new SecureRandom(); //Preconfigured Encryption Parameters public static readonly int NonceBitSize = 128; public static readonly int MacBitSize = 128; public static readonly int KeyBitSize = 256; //Preconfigured Password Key Derivation Parameters public static readonly int SaltBitSize = 128; public static readonly int Iterations = 10000; public static readonly int MinPasswordLength = 12; /// <summary> /// Helper that generates a random new key on each call. /// </summary> /// <returns></returns> public static byte[] NewKey() { var key = new byte[KeyBitSize / 8]; Random.NextBytes(key); return key; } /// <summary> /// Simple Encryption And Authentication (AES-GCM) of a UTF8 string. /// </summary> /// <param name="secretMessage">The secret message.</param> /// <param name="key">The key.</param> /// <param name="nonSecretPayload">Optional non-secret payload.</param> /// <returns> /// Encrypted Message /// </returns> /// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception> /// <remarks> /// Adds overhead of (Optional-Payload + BlockSize(16) + Message + HMac-Tag(16)) * 1.33 Base64 /// </remarks> public static string SimpleEncrypt(string secretMessage, byte[] key, byte[] nonSecretPayload = null) { if (string.IsNullOrEmpty(secretMessage)) throw new ArgumentException("Secret Message Required!","secretMessage"); var plainText = Encoding.UTF8.GetBytes(secretMessage); var cipherText = SimpleEncrypt(plainText, key, nonSecretPayload); return Convert.ToBase64String(cipherText); } /// <summary> /// Simple Decryption & Authentication (AES-GCM) of a UTF8 Message /// </summary> /// <param name="encryptedMessage">The encrypted message.</param> /// <param name="key">The key.</param> /// <param name="nonSecretPayloadLength">Length of the optional non-secret payload.</param> /// <returns>Decrypted Message</returns> public static string SimpleDecrypt(string encryptedMessage, byte[] key, int nonSecretPayloadLength = 0) { if (string.IsNullOrEmpty(encryptedMessage)) throw new ArgumentException("Encrypted Message Required!","encryptedMessage"); var cipherText = Convert.FromBase64String(encryptedMessage); var plainText = SimpleDecrypt(cipherText, key, nonSecretPayloadLength); return plainText == null ? null : Encoding.UTF8.GetString(plainText); } /// <summary> /// Simple Encryption And Authentication (AES-GCM) of a UTF8 String /// using key derived from a password (PBKDF2). /// </summary> /// <param name="secretMessage">The secret message.</param> /// <param name="password">The password.</param> /// <param name="nonSecretPayload">The non secret payload.</param> /// <returns> /// Encrypted Message /// </returns> /// <remarks> /// Significantly less secure than using random binary keys. /// Adds additional non secret payload for key generation parameters. /// </remarks> public static string SimpleEncryptWithPassword(string secretMessage, string password, byte[] nonSecretPayload = null) { if (string.IsNullOrEmpty(secretMessage)) throw new ArgumentException("Secret Message Required!","secretMessage"); var plainText = Encoding.UTF8.GetBytes(secretMessage); var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload); return Convert.ToBase64String(cipherText); } /// <summary> /// Simple Decryption and Authentication (AES-GCM) of a UTF8 message /// using a key derived from a password (PBKDF2) /// </summary> /// <param name="encryptedMessage">The encrypted message.</param> /// <param name="password">The password.</param> /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param> /// <returns> /// Decrypted Message /// </returns> /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception> /// <remarks> /// Significantly less secure than using random binary keys. /// </remarks> public static string SimpleDecryptWithPassword(string encryptedMessage, string password, int nonSecretPayloadLength = 0) { if (string.IsNullOrWhiteSpace(encryptedMessage)) throw new ArgumentException("Encrypted Message Required!","encryptedMessage"); var cipherText = Convert.FromBase64String(encryptedMessage); var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength); return plainText == null ? null : Encoding.UTF8.GetString(plainText); } public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] key, byte[] nonSecretPayload = null) { //User Error Checks if (key == null || key.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize),"key"); if (secretMessage == null || secretMessage.Length == 0) throw new ArgumentException("Secret Message Required!","secretMessage"); //Non-secret Payload Optional nonSecretPayload = nonSecretPayload ?? new byte[] { }; //Using random nonce large enough not to repeat var nonce = new byte[NonceBitSize / 8]; Random.NextBytes(nonce, 0, nonce.Length); var cipher = new GcmBlockCipher(new AesFastEngine()); var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload); cipher.Init(true, parameters); //Generate Cipher Text With Auth Tag var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)]; var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0); cipher.DoFinal(cipherText, len); //Assemble Message using (var combinedStream = new MemoryStream()) { using (var binaryWriter = new BinaryWriter(combinedStream)) { //Prepend Authenticated Payload binaryWriter.Write(nonSecretPayload); //Prepend Nonce binaryWriter.Write(nonce); //Write Cipher Text binaryWriter.Write(cipherText); } return combinedStream.ToArray(); } } public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] key, int nonSecretPayloadLength = 0) { //User Error Checks if (key == null || key.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize),"key"); if (encryptedMessage == null || encryptedMessage.Length == 0) throw new ArgumentException("Encrypted Message Required!","encryptedMessage"); using (var cipherStream = new MemoryStream(encryptedMessage)) using (var cipherReader = new BinaryReader(cipherStream)) { //Grab Payload var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength); //Grab Nonce var nonce = cipherReader.ReadBytes(NonceBitSize / 8); var cipher = new GcmBlockCipher(new AesFastEngine()); var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload); cipher.Init(false, parameters); //Decrypt Cipher Text var cipherText = cipherReader.ReadBytes(encryptedMessage.Length - nonSecretPayloadLength - nonce.Length); var plainText = new byte[cipher.GetOutputSize(cipherText.Length)]; try { var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0); cipher.DoFinal(plainText, len); } catch (InvalidCipherTextException) { //Return null if it doesn't authenticate return null; } return plainText; } } public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null) { nonSecretPayload = nonSecretPayload ?? new byte[] {}; //User Error Checks if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength) throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength),"password"); if (secretMessage == null || secretMessage.Length == 0) throw new ArgumentException("Secret Message Required!","secretMessage"); var generator = new Pkcs5S2ParametersGenerator(); //Use Random Salt to minimize pre-generated weak password attacks. var salt=new byte[SaltBitSize / 8]; Random.NextBytes(salt); generator.Init( PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()), salt, Iterations); //Generate Key var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize); //Create Full Non Secret Payload var payload = new byte[salt.Length + nonSecretPayload.Length]; Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length); Array.Copy(salt,0, payload,nonSecretPayload.Length, salt.Length); return SimpleEncrypt(secretMessage, key.GetKey(), payload); } public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0) { //User Error Checks if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength) throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength),"password"); if (encryptedMessage == null || encryptedMessage.Length == 0) throw new ArgumentException("Encrypted Message Required!","encryptedMessage"); var generator = new Pkcs5S2ParametersGenerator(); //Grab Salt from Payload var salt=new byte[SaltBitSize / 8]; Array.Copy(encryptedMessage, nonSecretPayloadLength, salt, 0, salt.Length); generator.Init( PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()), salt, Iterations); //Generate Key var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize); return SimpleDecrypt(encryptedMessage, key.GetKey(), salt.Length + nonSecretPayloadLength); } } } |
下面是一个使用RSA的示例。
重要提示:可以使用RSA加密(
用RSA密钥替换您的u rsa u密钥。
1 2 3 4 5 6 7 8 | var provider = new System.Security.Cryptography.RSACryptoServiceProvider(); provider.ImportParameters(your_rsa_key); var encryptedBytes = provider.Encrypt( System.Text.Encoding.UTF8.GetBytes("Hello World!"), true); string decryptedTest = System.Text.Encoding.UTF8.GetString( provider.Decrypt(encryptedBytes, true)); |
有关详细信息,请访问msdn-rsacryptoServiceProvider
如果您使用的是ASP.NET,则现在可以在.NET 4.0及更高版本中使用内置功能。
系统.web.security.machinekey
.NET 4.5有
.NET 4.0有
在ASP.NET之外,这个类似乎在每次重新启动应用程序时都会生成一个新的键,因此不起作用。通过在ilspy中快速浏览,在我看来,如果缺少合适的app.settings,它会生成自己的默认值。因此,您实际上可以在ASP.NET之外设置它。
我在System.Web命名空间之外找不到非ASP.NET等效项。
BouncyCastle是一个很好的.NET加密库,它可以作为nuget包安装到您的项目中。我比System.Security.Cryptography库中现有的更喜欢它。它在可用算法方面为您提供了更多的选项,并为这些算法提供了更多的模式。
这是一个twofish实现的例子,它是由BruceSchneier(英雄)为我们这些偏执的人写的。这是一个对称的算法,就像Rijndael一样(AKA AES)。它是AES标准的三个入围者之一,也是布鲁斯施耐尔(BruceSchneier)写的另一个著名算法"河豚"的兄弟。
使用BouncyCastle的第一件事是创建一个加密程序类,这将使在库中实现其他块密码更加容易。下面的Encryptor类接受一个泛型参数t,其中t实现IBlockCipher并具有一个默认构造函数。
更新:由于流行的需求,我决定实现生成一个随机的IV,并在这个类中包含一个HMAC。尽管从风格的角度来看,这违背了单一责任的坚实原则,因为这个类的本质我重新定义了什么。这个类现在将采用两个通用参数,一个用于密码,另一个用于摘要。它使用RNGCryptoServiceProvider自动生成IV以提供良好的RNG熵,并允许您使用从BouncyCastle生成MAC所需的任何摘要算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | using System; using System.Security.Cryptography; using System.Text; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Paddings; using Org.BouncyCastle.Crypto.Parameters; public sealed class Encryptor<TBlockCipher, TDigest> where TBlockCipher : IBlockCipher, new() where TDigest : IDigest, new() { private Encoding encoding; private IBlockCipher blockCipher; private BufferedBlockCipher cipher; private HMac mac; private byte[] key; public Encryptor(Encoding encoding, byte[] key, byte[] macKey) { this.encoding = encoding; this.key = key; this.Init(key, macKey, new Pkcs7Padding()); } public Encryptor(Encoding encoding, byte[] key, byte[] macKey, IBlockCipherPadding padding) { this.encoding = encoding; this.key = key; this.Init(key, macKey, padding); } private void Init(byte[] key, byte[] macKey, IBlockCipherPadding padding) { this.blockCipher = new CbcBlockCipher(new TBlockCipher()); this.cipher = new PaddedBufferedBlockCipher(this.blockCipher, padding); this.mac = new HMac(new TDigest()); this.mac.Init(new KeyParameter(macKey)); } public string Encrypt(string plain) { return Convert.ToBase64String(EncryptBytes(plain)); } public byte[] EncryptBytes(string plain) { byte[] input = this.encoding.GetBytes(plain); var iv = this.GenerateIV(); var cipher = this.BouncyCastleCrypto(true, input, new ParametersWithIV(new KeyParameter(key), iv)); byte[] message = CombineArrays(iv, cipher); this.mac.Reset(); this.mac.BlockUpdate(message, 0, message.Length); byte[] digest = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()]; this.mac.DoFinal(digest, 0); var result = CombineArrays(digest, message); return result; } public byte[] DecryptBytes(byte[] bytes) { // split the digest into component parts var digest = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()]; var message = new byte[bytes.Length - digest.Length]; var iv = new byte[this.blockCipher.GetBlockSize()]; var cipher = new byte[message.Length - iv.Length]; Buffer.BlockCopy(bytes, 0, digest, 0, digest.Length); Buffer.BlockCopy(bytes, digest.Length, message, 0, message.Length); if (!IsValidHMac(digest, message)) { throw new CryptoException(); } Buffer.BlockCopy(message, 0, iv, 0, iv.Length); Buffer.BlockCopy(message, iv.Length, cipher, 0, cipher.Length); byte[] result = this.BouncyCastleCrypto(false, cipher, new ParametersWithIV(new KeyParameter(key), iv)); return result; } public string Decrypt(byte[] bytes) { return this.encoding.GetString(DecryptBytes(bytes)); } public string Decrypt(string cipher) { return this.Decrypt(Convert.FromBase64String(cipher)); } private bool IsValidHMac(byte[] digest, byte[] message) { this.mac.Reset(); this.mac.BlockUpdate(message, 0, message.Length); byte[] computed = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()]; this.mac.DoFinal(computed, 0); return AreEqual(digest,computed); } private static bool AreEqual(byte [] digest, byte[] computed) { if(digest.Length != computed.Length) { return false; } int result = 0; for (int i = 0; i < digest.Length; i++) { // compute equality of all bytes before returning. // helps prevent timing attacks: // https://codahale.com/a-lesson-in-timing-attacks/ result |= digest[i] ^ computed[i]; } return result == 0; } private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, ICipherParameters parameters) { try { cipher.Init(forEncrypt, parameters); return this.cipher.DoFinal(input); } catch (CryptoException) { throw; } } private byte[] GenerateIV() { using (var provider = new RNGCryptoServiceProvider()) { // 1st block byte[] result = new byte[this.blockCipher.GetBlockSize()]; provider.GetBytes(result); return result; } } private static byte[] CombineArrays(byte[] source1, byte[] source2) { byte[] result = new byte[source1.Length + source2.Length]; Buffer.BlockCopy(source1, 0, result, 0, source1.Length); Buffer.BlockCopy(source2, 0, result, source1.Length, source2.Length); return result; } } |
接下来,只需调用新类上的加密和解密方法,下面是使用twofish的示例:
1 2 3 4 | var encrypt = new Encryptor<TwofishEngine, Sha1Digest>(Encoding.UTF8, key, hmacKey); string cipher = encrypt.Encrypt("TEST"); string plainText = encrypt.Decrypt(cipher); |
用另一个类似三重密码的分组密码来代替密码也很容易:
1 2 3 4 | var des = new Encryptor<DesEdeEngine, Sha1Digest>(Encoding.UTF8, key, hmacKey); string cipher = des.Encrypt("TEST"); string plainText = des.Decrypt(cipher); |
最后,如果要将AES与SHA256 HMAC一起使用,可以执行以下操作:
1 2 3 4 | var aes = new Encryptor<AesEngine, Sha256Digest>(Encoding.UTF8, key, hmacKey); cipher = aes.Encrypt("TEST"); plainText = aes.Decrypt(cipher); |
加密最困难的部分实际上是处理密钥而不是算法。你得考虑把钥匙放在哪里,如果必须的话,还要考虑如何交换钥匙。这些算法都经得起时间的考验,极难破解。想要从你那里窃取信息的人不会花很长时间对你的信息进行密码分析,他们会试图找出你的密钥是什么或在哪里。所以1明智地选择您的密钥,2将它们存储在安全的地方,若您使用web.config和IIS,则可以加密web.config的部分内容,最后,若您必须交换密钥,请确保用于交换密钥的协议是安全的。
更新2更改比较方法以减轻定时攻击。更多信息请访问http://codahale.com/a-lesson-in-timing-attacks/。还更新为pkcs7 padding的默认值,并添加了新的构造函数,以允许最终用户选择要使用的padding。谢谢@codesinchaos的建议。
免责声明:此解决方案只能用于不公开的静态数据(例如配置文件或数据库)。只有在这种情况下,快速而肮脏的解决方案才能被认为比@jbtule的解决方案更好,因为维护成本较低。
原始职位:我发现jbtule的答案对于一个快速而肮脏的安全aes字符串加密来说有点复杂,而brett的答案有一个错误,初始化向量是一个固定值,使其容易受到填充攻击,所以我修复了brett的代码,并添加了一个随机的iv,添加到芯片字符串中,每次都创建不同的加密值。相同值的Y加密:
加密:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public static string Encrypt(string clearText) { byte[] clearBytes = Encoding.Unicode.GetBytes(clearText); using (Aes encryptor = Aes.Create()) { byte[] IV = new byte[15]; rand.NextBytes(IV); Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV); encryptor.Key = pdb.GetBytes(32); encryptor.IV = pdb.GetBytes(16); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write)) { cs.Write(clearBytes, 0, clearBytes.Length); cs.Close(); } clearText = Convert.ToBase64String(IV) + Convert.ToBase64String(ms.ToArray()); } } return clearText; } |
解密:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public static string Decrypt(string cipherText) { byte[] IV = Convert.FromBase64String(cipherText.Substring(0, 20)); cipherText = cipherText.Substring(20).Replace("","+"); byte[] cipherBytes = Convert.FromBase64String(cipherText); using (Aes encryptor = Aes.Create()) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV); encryptor.Key = pdb.GetBytes(32); encryptor.IV = pdb.GetBytes(16); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write)) { cs.Write(cipherBytes, 0, cipherBytes.Length); cs.Close(); } cipherText = Encoding.Unicode.GetString(ms.ToArray()); } } return cipherText; } |
用您的密钥替换encryptionkey。在我的实现中,该键被保存在配置文件(web.configapp.config)中,因为您不应该将其硬编码保存。配置文件也应该加密,这样密钥就不会以明文形式保存在其中。
1 2 3 4 5 6 7 8 9 10 11 12 13 | protected static string _Key =""; protected static string EncryptionKey { get { if (String.IsNullOrEmpty(_Key)) { _Key = ConfigurationManager.AppSettings["AESKey"].ToString(); } return _Key; } } |
加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public string EncryptString(string inputString) { MemoryStream memStream = null; try { byte[] key = { }; byte[] IV = { 12, 21, 43, 17, 57, 35, 67, 27 }; string encryptKey ="aXb2uy4z"; // MUST be 8 characters key = Encoding.UTF8.GetBytes(encryptKey); byte[] byteInput = Encoding.UTF8.GetBytes(inputString); DESCryptoServiceProvider provider = new DESCryptoServiceProvider(); memStream = new MemoryStream(); ICryptoTransform transform = provider.CreateEncryptor(key, IV); CryptoStream cryptoStream = new CryptoStream(memStream, transform, CryptoStreamMode.Write); cryptoStream.Write(byteInput, 0, byteInput.Length); cryptoStream.FlushFinalBlock(); } catch (Exception ex) { Response.Write(ex.Message); } return Convert.ToBase64String(memStream.ToArray()); } |
解密:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public string DecryptString(string inputString) { MemoryStream memStream = null; try { byte[] key = { }; byte[] IV = { 12, 21, 43, 17, 57, 35, 67, 27 }; string encryptKey ="aXb2uy4z"; // MUST be 8 characters key = Encoding.UTF8.GetBytes(encryptKey); byte[] byteInput = new byte[inputString.Length]; byteInput = Convert.FromBase64String(inputString); DESCryptoServiceProvider provider = new DESCryptoServiceProvider(); memStream = new MemoryStream(); ICryptoTransform transform = provider.CreateDecryptor(key, IV); CryptoStream cryptoStream = new CryptoStream(memStream, transform, CryptoStreamMode.Write); cryptoStream.Write(byteInput, 0, byteInput.Length); cryptoStream.FlushFinalBlock(); } catch (Exception ex) { Response.Write(ex.Message); } Encoding encoding1 = Encoding.UTF8; return encoding1.GetString(memStream.ToArray()); } |
通过在C中对字符串进行加密和解密,我找到了一个很好的解决方案:
1 2 3 | static readonly string PasswordHash ="P@@Sw0rd"; static readonly string SaltKey ="S@LT&KEY"; static readonly string VIKey ="@1B2c3D4e5F6g7H8"; |
用于加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public static string Encrypt(string plainText) { byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8); var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros }; var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey)); byte[] cipherTextBytes; using (var memoryStream = new MemoryStream()) { using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) { cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); cryptoStream.FlushFinalBlock(); cipherTextBytes = memoryStream.ToArray(); cryptoStream.Close(); } memoryStream.Close(); } return Convert.ToBase64String(cipherTextBytes); } |
解密用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static string Decrypt(string encryptedText) { byte[] cipherTextBytes = Convert.FromBase64String(encryptedText); byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8); var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None }; var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey)); var memoryStream = new MemoryStream(cipherTextBytes); var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); byte[] plainTextBytes = new byte[cipherTextBytes.Length]; int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); memoryStream.Close(); cryptoStream.Close(); return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount).TrimEnd("\0".ToCharArray()); } |
支持马特曼的回答。下面是一个使用machinekey类加密/解密URL安全值的示例。
要记住的是,正如前面提到的,这将使用机器配置设置(https://msdn.microsoft.com/en-us/library/ff649308.aspx)。您可以在web.config文件中手动设置加密和解密密钥/算法(如果您的站点在多个服务器上运行,则可能需要特别设置)。您可以从IIS生成密钥(请参阅:https://blogs.msdn.microsoft.com/vijaysk/2009/05/13/iis-7-tip-10-you-can-generate-machine-keys-from-the-iis-manager/)或使用在线计算机密钥生成器,如:http://www.developerfusion.com/tools/generate machine key/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | private static readonly UTF8Encoding Encoder = new UTF8Encoding(); public static string Encrypt(string unencrypted) { if (string.IsNullOrEmpty(unencrypted)) return string.Empty; try { var encryptedBytes = MachineKey.Protect(Encoder.GetBytes(unencrypted)); if (encryptedBytes != null && encryptedBytes.Length > 0) return HttpServerUtility.UrlTokenEncode(encryptedBytes); } catch (Exception) { return string.Empty; } return string.Empty; } public static string Decrypt(string encrypted) { if (string.IsNullOrEmpty(encrypted)) return string.Empty; try { var bytes = HttpServerUtility.UrlTokenDecode(encrypted); if (bytes != null && bytes.Length > 0) { var decryptedBytes = MachineKey.Unprotect(bytes); if(decryptedBytes != null && decryptedBytes.Length > 0) return Encoder.GetString(decryptedBytes); } } catch (Exception) { return string.Empty; } return string.Empty; } |
对于AES-GCM加密,BouncyCastle的一个替代方法是libnadium网。它包围了libnadium C库。一个很好的优点是它在CPU中使用AES-NI扩展来实现非常快速的加密。缺点是,如果CPU没有扩展名,它就根本无法工作。没有软件退步。
如果您在这里寻找pgp加密,在下面关于如何通过BouncyCastle使用pgp加密的示例的注释中,
http://blogs.microsoft.co.il/kim/2009/01/23/pgp-zip-encrypted-files-with-c/comment-611002
粘贴时间太长,稍作修改:https://gist.github.com/zaus/c0ea1fd8dad5d9590af1
下面是使用带有随机IV和HMAC以及密码派生密钥的AES CBC模式对C中的字符串进行加密的简单示例,以显示基本的移动部件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | private byte[] EncryptBytes(byte[] key, byte[] plaintext) { using (var cipher = new RijndaelManaged { Key = key }) { using (var encryptor = cipher.CreateEncryptor()) { var ciphertext = encryptor.TransformFinalBlock(plaintext, 0, plaintext.Length); // IV is prepended to ciphertext return cipher.IV.Concat(ciphertext).ToArray(); } } } private byte[] DecryptBytes(byte[] key, byte[] packed) { using (var cipher = new RijndaelManaged { Key = key }) { int ivSize = cipher.BlockSize / 8; cipher.IV = packed.Take(ivSize).ToArray(); using (var encryptor = cipher.CreateDecryptor()) { return encryptor.TransformFinalBlock(packed, ivSize, packed.Length - ivSize); } } } private byte[] AddMac(byte[] key, byte[] data) { using (var hmac = new HMACSHA256(key)) { var macBytes = hmac.ComputeHash(data); // HMAC is appended to data return data.Concat(macBytes).ToArray(); } } private bool BadMac(byte[] found, byte[] computed) { int mismatch = 0; // Aim for consistent timing regardless of inputs for (int i = 0; i < found.Length; i++) { mismatch += found[i] == computed[i] ? 0 : 1; } return mismatch != 0; } private byte[] RemoveMac(byte[] key, byte[] data) { using (var hmac = new HMACSHA256(key)) { int macSize = hmac.HashSize / 8; var packed = data.Take(data.Length - macSize).ToArray(); var foundMac = data.Skip(packed.Length).ToArray(); var computedMac = hmac.ComputeHash(packed); if (this.BadMac(foundMac, computedMac)) { throw new Exception("Bad MAC"); } return packed; } } private List<byte[]> DeriveTwoKeys(string password) { var salt=new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; var kdf = new Rfc2898DeriveBytes(password, salt, 10000); var bytes = kdf.GetBytes(32); // Two keys 128 bits each return new List<byte[]> { bytes.Take(16).ToArray(), bytes.Skip(16).ToArray() }; } public byte[] EncryptString(string password, String message) { var keys = this.DeriveTwoKeys(password); var plaintext = Encoding.UTF8.GetBytes(message); var packed = this.EncryptBytes(keys[0], plaintext); return this.AddMac(keys[1], packed); } public String DecryptString(string password, byte[] secret) { var keys = this.DeriveTwoKeys(password); var packed = this.RemoveMac(keys[1], secret); var plaintext = this.DecryptBytes(keys[0], packed); return Encoding.UTF8.GetString(plaintext); } public void Example() { var password ="correcthorsebatterystaple"; var secret = this.EncryptString(password,"Hello World"); Console.WriteLine("secret:" + BitConverter.ToString(secret)); var recovered = this.DecryptString(password, secret); Console.WriteLine(recovered); } |
这是布雷特安排的班级。但是,我做了一个轻微的编辑,因为我在使用它对URL字符串进行加密和解密时接收到错误"base 64字符数组的长度无效"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | public class CryptoURL { private static byte[] _salt=Encoding.ASCII.GetBytes("Catto_Salt_Enter_Any_Value99"); /// <summary> /// Encrypt the given string using AES. The string can be decrypted using /// DecryptStringAES(). The sharedSecret parameters must match. /// The SharedSecret for the Password Reset that is used is in the next line /// string sharedSecret ="OneUpSharedSecret9"; /// </summary> /// <param name="plainText">The text to encrypt.</param> /// <param name="sharedSecret">A password used to generate a key for encryption.</param> public static string EncryptString(string plainText, string sharedSecret) { if (string.IsNullOrEmpty(plainText)) throw new ArgumentNullException("plainText"); if (string.IsNullOrEmpty(sharedSecret)) throw new ArgumentNullException("sharedSecret"); string outStr = null; // Encrypted string to return RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data. try { // generate the key from the shared secret and the salt Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); // Create a RijndaelManaged object aesAlg = new RijndaelManaged(); aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); // Create a decryptor to perform the stream transform. ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); // Create the streams used for encryption. using (MemoryStream msEncrypt = new MemoryStream()) { // prepend the IV msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int)); msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length); using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { //Write all data to the stream. swEncrypt.Write(plainText); } } outStr = HttpServerUtility.UrlTokenEncode(msEncrypt.ToArray()); //outStr = Convert.ToBase64String(msEncrypt.ToArray()); // you may need to add a reference. right click reference in solution explorer =>"add Reference" => .NET tab => select"System.Web" } } finally { // Clear the RijndaelManaged object. if (aesAlg != null) aesAlg.Clear(); } // Return the encrypted bytes from the memory stream. return outStr; } /// <summary> /// Decrypt the given string. Assumes the string was encrypted using /// EncryptStringAES(), using an identical sharedSecret. /// </summary> /// <param name="cipherText">The text to decrypt.</param> /// <param name="sharedSecret">A password used to generate a key for decryption.</param> public static string DecryptString(string cipherText, string sharedSecret) { if (string.IsNullOrEmpty(cipherText)) throw new ArgumentNullException("cipherText"); if (string.IsNullOrEmpty(sharedSecret)) throw new ArgumentNullException("sharedSecret"); // Declare the RijndaelManaged object // used to decrypt the data. RijndaelManaged aesAlg = null; // Declare the string used to hold // the decrypted text. string plaintext = null; byte[] inputByteArray; try { // generate the key from the shared secret and the salt Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); // Create the streams used for decryption. //byte[] bytes = Convert.FromBase64String(cipherText); inputByteArray = HttpServerUtility.UrlTokenDecode(cipherText); using (MemoryStream msDecrypt = new MemoryStream(inputByteArray)) { // Create a RijndaelManaged object // with the specified key and IV. aesAlg = new RijndaelManaged(); aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); // Get the initialization vector from the encrypted stream aesAlg.IV = ReadByteArray(msDecrypt); // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(csDecrypt)) // Read the decrypted bytes from the decrypting stream // and place them in a string. plaintext = srDecrypt.ReadToEnd(); } } } catch (System.Exception ex) { return"ERROR"; //throw ex; } finally { // Clear the RijndaelManaged object. if (aesAlg != null) aesAlg.Clear(); } return plaintext; } static string ConvertStringArrayToString(string[] array) { // // Concatenate all the elements into a StringBuilder. // StringBuilder builder = new StringBuilder(); foreach (string value in array) { builder.Append(value); builder.Append('.'); } return builder.ToString(); } private static byte[] ReadByteArray(Stream s) { byte[] rawLength = new byte[sizeof(int)]; if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length) { throw new SystemException("Stream did not contain properly formatted byte array"); } byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)]; if (s.Read(buffer, 0, buffer.Length) != buffer.Length) { throw new SystemException("Did not read byte array properly"); } return buffer; } } |
加密是编程中非常常见的问题。我认为最好安装一个包来为您完成这项任务。可能是一个简单的开源nuget项目,比如简单的AES加密
密钥在配置文件中,因此在生产环境中很容易更改,我看不到任何缺点。
1 2 3 | <MessageEncryption> <EncryptionKey KeySize="256" Key="3q2+796tvu/erb7v3q2+796tvu/erb7v3q2+796tvu8="/> </MessageEncryption> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | using System; using System.IO; using System.Security.Cryptography; using System.Text; public class Program { public static void Main() { var key = Encoding.UTF8.GetBytes("SUkbqO2ycDo7QwpR25kfgmC7f8CoyrZy"); var data = Encoding.UTF8.GetBytes("testData"); //Encrypt data var encrypted = CryptoHelper.EncryptData(data,key); //Decrypt data var decrypted = CryptoHelper.DecryptData(encrypted,key); //Display result Console.WriteLine(Encoding.UTF8.GetString(decrypted)); } } public static class CryptoHelper { public static byte[] EncryptData(byte[] data, byte[] key) { using (var aesAlg = Aes.Create()) { aesAlg.Mode = CipherMode.CBC; using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV)) { using (var msEncrypt = new MemoryStream()) { msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length); using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) csEncrypt.Write(data, 0, data.Length); return msEncrypt.ToArray(); } } } } public static byte[] DecryptData(byte[] encrypted, byte[] key) { var iv = new byte[16]; Buffer.BlockCopy(encrypted, 0, iv, 0, iv.Length); using (var aesAlg = Aes.Create()) { aesAlg.Mode = CipherMode.CBC; using (var decryptor = aesAlg.CreateDecryptor(key, iv)) { using (var msDecrypt = new MemoryStream(encrypted, iv.Length, encrypted.Length - iv.Length)) { using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (var resultStream = new MemoryStream()) { csDecrypt.CopyTo(resultStream); return resultStream.ToArray(); } } } } } } } |
以下是最初由ASP代码段生成的简单代码段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | using System.Text; using System.Security.Cryptography; using System.IO; private string Encrypt(string clearText) { string EncryptionKey ="yourkey"; byte[] clearBytes = Encoding.Unicode.GetBytes(clearText); using (Aes encryptor = Aes.Create()) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); encryptor.Key = pdb.GetBytes(32); encryptor.IV = pdb.GetBytes(16); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write)) { cs.Write(clearBytes, 0, clearBytes.Length); cs.Close(); } clearText = Convert.ToBase64String(ms.ToArray()); } } return clearText; } private string Decrypt(string cipherText) { string EncryptionKey ="yourkey"; cipherText = cipherText.Replace("","+"); byte[] cipherBytes = Convert.FromBase64String(cipherText); using (Aes encryptor = Aes.Create()) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); encryptor.Key = pdb.GetBytes(32); encryptor.IV = pdb.GetBytes(16); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write)) { cs.Write(cipherBytes, 0, cipherBytes.Length); cs.Close(); } cipherText = Encoding.Unicode.GetString(ms.ToArray()); } } return cipherText; } |
从我这里的答案中复制了一个类似的问题:C的简单双向加密。
基于多个答案和评论。
- 加密文本前的随机初始化向量(@jbtule)
- 使用transformFinalBlock()而不是memoryStream(@renniepet)
- 没有预先填充的密钥以避免任何人复制和粘贴灾难
- 正确处置和使用模式
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | /// <summary> /// Simple encryption/decryption using a random initialization vector /// and prepending it to the crypto text. /// </summary> /// <remarks>Based on multiple answers in https://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp </remarks> public class SimpleAes : IDisposable { /// <summary> /// Initialization vector length in bytes. /// </summary> private const int IvBytes = 16; /// <summary> /// Must be exactly 16, 24 or 32 characters long. /// </summary> private static readonly byte[] Key = Convert.FromBase64String("FILL ME WITH 16, 24 OR 32 CHARS"); private readonly UTF8Encoding _encoder; private readonly ICryptoTransform _encryptor; private readonly RijndaelManaged _rijndael; public SimpleAes() { _rijndael = new RijndaelManaged {Key = Key}; _rijndael.GenerateIV(); _encryptor = _rijndael.CreateEncryptor(); _encoder = new UTF8Encoding(); } public string Decrypt(string encrypted) { return _encoder.GetString(Decrypt(Convert.FromBase64String(encrypted))); } public void Dispose() { _rijndael.Dispose(); _encryptor.Dispose(); } public string Encrypt(string unencrypted) { return Convert.ToBase64String(Encrypt(_encoder.GetBytes(unencrypted))); } private byte[] Decrypt(byte[] buffer) { // IV is prepended to cryptotext byte[] iv = buffer.Take(IvBytes).ToArray(); using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv)) { return decryptor.TransformFinalBlock(buffer, IvBytes, buffer.Length - IvBytes); } } private byte[] Encrypt(byte[] buffer) { // Prepend cryptotext with IV byte[] inputBuffer = _rijndael.IV.Concat(buffer).ToArray(); return _encryptor.TransformFinalBlock(inputBuffer, IvBytes, buffer.Length); } } |
下面的示例演示如何加密和解密示例数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | // This constant is used to determine the keysize of the encryption algorithm in bits. // We divide this by 8 within the code below to get the equivalent number of bytes. private const int Keysize = 128; // This constant determines the number of iterations for the password bytes generation function. private const int DerivationIterations = 1000; public static string Encrypt(string plainText, string passPhrase) { // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text // so that the same Salt and IV values can be used when decrypting. var saltStringBytes = GenerateBitsOfRandomEntropy(16); var ivStringBytes = GenerateBitsOfRandomEntropy(16); var plainTextBytes = Encoding.UTF8.GetBytes(plainText); using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) { var keyBytes = password.GetBytes(Keysize / 8); using (var symmetricKey = new RijndaelManaged()) { symmetricKey.BlockSize = 128; symmetricKey.Mode = CipherMode.CBC; symmetricKey.Padding = PaddingMode.PKCS7; using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes)) { using (var memoryStream = new MemoryStream()) { using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) { cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); cryptoStream.FlushFinalBlock(); // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes. var cipherTextBytes = saltStringBytes; cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray(); cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray(); memoryStream.Close(); cryptoStream.Close(); return Convert.ToBase64String(cipherTextBytes); } } } } } } public static string Decrypt(string cipherText, string passPhrase) { // Get the complete stream of bytes that represent: // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText] var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText); // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes. var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray(); // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes. var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray(); // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string. var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray(); using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) { var keyBytes = password.GetBytes(Keysize / 8); using (var symmetricKey = new RijndaelManaged()) { symmetricKey.BlockSize = 128; symmetricKey.Mode = CipherMode.CBC; symmetricKey.Padding = PaddingMode.PKCS7; using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes)) { using (var memoryStream = new MemoryStream(cipherTextBytes)) { using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) { var plainTextBytes = new byte[cipherTextBytes.Length]; var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); memoryStream.Close(); cryptoStream.Close(); return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount); } } } } } } private static byte[] GenerateBitsOfRandomEntropy(int size) { // 32 Bytes will give us 256 bits. // 16 Bytes will give us 128 bits. var randomBytes = new byte[size]; using (var rngCsp = new RNGCryptoServiceProvider()) { // Fill the array with cryptographically secure random bytes. rngCsp.GetBytes(randomBytes); } return randomBytes; } |
AES算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | public static class CryptographyProvider { public static string EncryptString(string plainText, out string Key) { if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException("plainText"); using (Aes _aesAlg = Aes.Create()) { Key = Convert.ToBase64String(_aesAlg.Key); ICryptoTransform _encryptor = _aesAlg.CreateEncryptor(_aesAlg.Key, _aesAlg.IV); using (MemoryStream _memoryStream = new MemoryStream()) { _memoryStream.Write(_aesAlg.IV, 0, 16); using (CryptoStream _cryptoStream = new CryptoStream(_memoryStream, _encryptor, CryptoStreamMode.Write)) { using (StreamWriter _streamWriter = new StreamWriter(_cryptoStream)) { _streamWriter.Write(plainText); } return Convert.ToBase64String(_memoryStream.ToArray()); } } } } public static string DecryptString(string cipherText, string Key) { if (string.IsNullOrEmpty(cipherText)) throw new ArgumentNullException("cipherText"); if (string.IsNullOrEmpty(Key)) throw new ArgumentNullException("Key"); string plaintext = null; byte[] _initialVector = new byte[16]; byte[] _Key = Convert.FromBase64String(Key); byte[] _cipherTextBytesArray = Convert.FromBase64String(cipherText); byte[] _originalString = new byte[_cipherTextBytesArray.Length - 16]; Array.Copy(_cipherTextBytesArray, 0, _initialVector, 0, _initialVector.Length); Array.Copy(_cipherTextBytesArray, 16, _originalString, 0, _cipherTextBytesArray.Length - 16); using (Aes _aesAlg = Aes.Create()) { _aesAlg.Key = _Key; _aesAlg.IV = _initialVector; ICryptoTransform decryptor = _aesAlg.CreateDecryptor(_aesAlg.Key, _aesAlg.IV); using (MemoryStream _memoryStream = new MemoryStream(_originalString)) { using (CryptoStream _cryptoStream = new CryptoStream(_memoryStream, decryptor, CryptoStreamMode.Read)) { using (StreamReader _streamReader = new StreamReader(_cryptoStream)) { plaintext = _streamReader.ReadToEnd(); } } } } return plaintext; } } |
下面是如何使用BouncyCastle包进行AES-GCM加密/解密的示例。
我在golang
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | const ( gcmBlockSize = 16 // this is key size gcmTagSize = 16 // this is mac gcmStandardNonceSize = 12 // this is nonce ) func encrypt(data []byte, passphrase string) []byte { block, _ := aes.NewCipher([]byte(createHash(passphrase))) gcm, err := cipher.NewGCM(block) if err != nil { panic(err.Error()) } nonce := make([]byte, gcm.NonceSize()) if _, err = io.ReadFull(rand.Reader, nonce); err != nil { panic(err.Error()) } ciphertext := gcm.Seal(nonce, nonce, data, nil) return ciphertext } |
.NET示例的工作方式与键(256位)、Mac(128位)和nonce(96位)类似。
下面的代码是Ghazal对类似问题答案的改进版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | public class EncryptionHelper { private Aes aesEncryptor; public EncryptionHelper() { } private void BuildAesEncryptor(string key) { aesEncryptor = Aes.Create(); var pdb = new Rfc2898DeriveBytes(key, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); aesEncryptor.Key = pdb.GetBytes(32); aesEncryptor.IV = pdb.GetBytes(16); } public string EncryptString(string clearText, string key) { BuildAesEncryptor(key); var clearBytes = Encoding.Unicode.GetBytes(clearText); using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, aesEncryptor.CreateEncryptor(), CryptoStreamMode.Write)) { cs.Write(clearBytes, 0, clearBytes.Length); } var encryptedText = Convert.ToBase64String(ms.ToArray()); return encryptedText; } } public string DecryptString(string cipherText, string key) { BuildAesEncryptor(key); cipherText = cipherText.Replace("","+"); var cipherBytes = Convert.FromBase64String(cipherText); using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, aesEncryptor.CreateDecryptor(), CryptoStreamMode.Write)) { cs.Write(cipherBytes, 0, cipherBytes.Length); } var clearText = Encoding.Unicode.GetString(ms.ToArray()); return clearText; } } } |
为了简单起见,我为自己设计了一个用于非加密目的的函数:用密码替换"yourpassphrase"…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; using System.IO; namespace My { public class strCrypto { // This constant string is used as a"salt" value for the PasswordDeriveBytes function calls. // This size of the IV (in bytes) must = (keysize / 8). Default keysize is 256, so the IV must be // 32 bytes long. Using a 16 character string here gives us 32 bytes when converted to a byte array. private const string initVector ="r5dm5fgm24mfhfku"; private const string passPhrase ="yourpassphrase"; // email password encryption password // This constant is used to determine the keysize of the encryption algorithm. private const int keysize = 256; public static string encryptString(string plainText) { //if the plaintext is empty or null string just return an empty string if (plainText =="" || plainText == null ) { return""; } byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector); byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null); byte[] keyBytes = password.GetBytes(keysize / 8); RijndaelManaged symmetricKey = new RijndaelManaged(); symmetricKey.Mode = CipherMode.CBC; ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes); MemoryStream memoryStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write); cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); cryptoStream.FlushFinalBlock(); byte[] cipherTextBytes = memoryStream.ToArray(); memoryStream.Close(); cryptoStream.Close(); return Convert.ToBase64String(cipherTextBytes); } public static string decryptString(string cipherText) { //if the ciphertext is empty or null string just return an empty string if (cipherText =="" || cipherText == null ) { return""; } byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector); byte[] cipherTextBytes = Convert.FromBase64String(cipherText); PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null); byte[] keyBytes = password.GetBytes(keysize / 8); RijndaelManaged symmetricKey = new RijndaelManaged(); symmetricKey.Mode = CipherMode.CBC; ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes); MemoryStream memoryStream = new MemoryStream(cipherTextBytes); CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); byte[] plainTextBytes = new byte[cipherTextBytes.Length]; int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); memoryStream.Close(); cryptoStream.Close(); return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount); } } |
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; // This is for password validation using System.Security.Cryptography; using System.Configuration; // This is where the hash functions reside namespace BullyTracker.Common { public class HashEncryption { //public string GenerateHashvalue(string thisPassword) //{ // MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); // byte[] tmpSource; // byte[] tmpHash; // tmpSource = ASCIIEncoding.ASCII.GetBytes(thisPassword); // Turn password into byte array // tmpHash = md5.ComputeHash(tmpSource); // StringBuilder sOutput = new StringBuilder(tmpHash.Length); // for (int i = 0; i < tmpHash.Length; i++) // { // sOutput.Append(tmpHash[i].ToString("X2")); // X2 formats to hexadecimal // } // return sOutput.ToString(); //} //public Boolean VerifyHashPassword(string thisPassword, string thisHash) //{ // Boolean IsValid = false; // string tmpHash = GenerateHashvalue(thisPassword); // Call the routine on user input // if (tmpHash == thisHash) IsValid = true; // Compare to previously generated hash // return IsValid; //} public string GenerateHashvalue(string toEncrypt, bool useHashing) { byte[] keyArray; byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt); System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader(); // Get the key from config file string key = (string)settingsReader.GetValue("SecurityKey", typeof(String)); //System.Windows.Forms.MessageBox.Show(key); if (useHashing) { MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider(); keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key)); hashmd5.Clear(); } else keyArray = UTF8Encoding.UTF8.GetBytes(key); TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider(); tdes.Key = keyArray; tdes.Mode = CipherMode.ECB; tdes.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = tdes.CreateEncryptor(); byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); tdes.Clear(); return Convert.ToBase64String(resultArray, 0, resultArray.Length); } /// <summary> /// DeCrypt a string using dual encryption method. Return a DeCrypted clear string /// </summary> /// <param name="cipherString">encrypted string</param> /// <param name="useHashing">Did you use hashing to encrypt this data? pass true is yes</param> /// <returns></returns> public string Decrypt(string cipherString, bool useHashing) { byte[] keyArray; byte[] toEncryptArray = Convert.FromBase64String(cipherString); System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader(); //Get your key from config file to open the lock! string key = (string)settingsReader.GetValue("SecurityKey", typeof(String)); if (useHashing) { MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider(); keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key)); hashmd5.Clear(); } else keyArray = UTF8Encoding.UTF8.GetBytes(key); TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider(); tdes.Key = keyArray; tdes.Mode = CipherMode.ECB; tdes.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = tdes.CreateDecryptor(); byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); tdes.Clear(); return UTF8Encoding.UTF8.GetString(resultArray); } } } |
安全散列数据的一个好算法是bcrypt:
Besides incorporating a salt to protect against rainbow table attacks,
bcrypt is an adaptive function: over time, the iteration count can be
increased to make it slower, so it remains resistant to brute-force
search attacks even with increasing computation power.
BCrypt有一个很好的.NET实现,它也可以作为nuget包提供。
我想给你我的贡献,我的代码为aes
我希望这能帮助任何人!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | using System; using System.IO; using System.Security.Cryptography; using System.Text; public static class Crypto { private static readonly byte[] IVa = new byte[] { 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x11, 0x12, 0x13, 0x14, 0x0e, 0x16, 0x17 }; public static string Encrypt(this string text, string salt) { try { using (Aes aes = new AesManaged()) { Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt)); aes.Key = deriveBytes.GetBytes(128 / 8); aes.IV = aes.Key; using (MemoryStream encryptionStream = new MemoryStream()) { using (CryptoStream encrypt = new CryptoStream(encryptionStream, aes.CreateEncryptor(), CryptoStreamMode.Write)) { byte[] cleanText = Encoding.UTF8.GetBytes(text); encrypt.Write(cleanText, 0, cleanText.Length); encrypt.FlushFinalBlock(); } byte[] encryptedData = encryptionStream.ToArray(); string encryptedText = Convert.ToBase64String(encryptedData); return encryptedText; } } } catch { return String.Empty; } } public static string Decrypt(this string text, string salt) { try { using (Aes aes = new AesManaged()) { Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt)); aes.Key = deriveBytes.GetBytes(128 / 8); aes.IV = aes.Key; using (MemoryStream decryptionStream = new MemoryStream()) { using (CryptoStream decrypt = new CryptoStream(decryptionStream, aes.CreateDecryptor(), CryptoStreamMode.Write)) { byte[] encryptedData = Convert.FromBase64String(text); decrypt.Write(encryptedData, 0, encryptedData.Length); decrypt.Flush(); } byte[] decryptedData = decryptionStream.ToArray(); string decryptedText = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length); return decryptedText; } } } catch { return String.Empty; } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | using System; using System.Data; using System.Configuration; using System.Text; using System.Security.Cryptography; namespace Encription { class CryptorEngine { public static string Encrypt(string ToEncrypt, bool useHasing) { byte[] keyArray; byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(ToEncrypt); //System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader(); string Key ="Bhagwati"; if (useHasing) { MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider(); keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(Key)); hashmd5.Clear(); } else { keyArray = UTF8Encoding.UTF8.GetBytes(Key); } TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider(); tDes.Key = keyArray; tDes.Mode = CipherMode.ECB; tDes.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = tDes.CreateEncryptor(); byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); tDes.Clear(); return Convert.ToBase64String(resultArray, 0, resultArray.Length); } public static string Decrypt(string cypherString, bool useHasing) { byte[] keyArray; byte[] toDecryptArray = Convert.FromBase64String(cypherString); //byte[] toEncryptArray = Convert.FromBase64String(cypherString); //System.Configuration.AppSettingsReader settingReader = new AppSettingsReader(); string key ="Bhagwati"; if (useHasing) { MD5CryptoServiceProvider hashmd = new MD5CryptoServiceProvider(); keyArray = hashmd.ComputeHash(UTF8Encoding.UTF8.GetBytes(key)); hashmd.Clear(); } else { keyArray = UTF8Encoding.UTF8.GetBytes(key); } TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider(); tDes.Key = keyArray; tDes.Mode = CipherMode.ECB; tDes.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = tDes.CreateDecryptor(); try { byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length); tDes.Clear(); return UTF8Encoding.UTF8.GetString(resultArray,0,resultArray.Length); } catch (Exception ex) { throw ex; } } } } |
必须使用System.Security.Cryptography来使用命名空间;而useHashing是一个bool类型,可以是true,也可以是false。对于加密和解密,字符串变量"key"应该相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | //Encryption public string EncryptText(string toEncrypt, bool useHashing) { try { byte[] keyArray; byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt); string key ="String Key Value"; //Based on this key stirng is encrypting //System.Windows.Forms.MessageBox.Show(key); //If hashing use get hashcode regards to your key if (useHashing) { MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider(); keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key)); //Always release the resources and flush data //of the Cryptographic service provide. Best Practice hashmd5.Clear(); } else keyArray = UTF8Encoding.UTF8.GetBytes(key); TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider(); //set the secret key for the tripleDES algorithm tdes.Key = keyArray; //mode of operation. there are other 4 modes. We choose ECB(Electronic code Book) tdes.Mode = CipherMode.ECB; //padding mode(if any extra byte added) tdes.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = tdes.CreateEncryptor(); //transform the specified region of bytes array to resultArray byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); //Release resources held by TripleDes Encryptor tdes.Clear(); //Return the encrypted data into unreadable string format return Convert.ToBase64String(resultArray, 0, resultArray.Length); } catch (Exception e) { throw e; } } //Decryption public string DecryptText(string cipherString, bool useHashing) { try { byte[] keyArray; //get the byte code of the string byte[] toEncryptArray = Convert.FromBase64String(cipherString); string key ="String Key Value"; //Based on this key string is decrypted if (useHashing) { //if hashing was used get the hash code with regards to your key MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider(); keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key)); //release any resource held by the MD5CryptoServiceProvider hashmd5.Clear(); } else { //if hashing was not implemented get the byte code of the key keyArray = UTF8Encoding.UTF8.GetBytes(key); } TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider(); //set the secret key for the tripleDES algorithm tdes.Key = keyArray; //mode of operation. there are other 4 modes. //We choose ECB(Electronic code Book) tdes.Mode = CipherMode.ECB; //padding mode(if any extra byte added) tdes.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = tdes.CreateDecryptor(); byte[] resultArray = cTransform.TransformFinalBlock (toEncryptArray, 0, toEncryptArray.Length); //Release resources held by TripleDes Encryptor tdes.Clear(); //return the Clear decrypted TEXT return UTF8Encoding.UTF8.GetString(resultArray); } catch (Exception ex) { throw ex; } } |