如何在C#中加密和解密字符串?
jbtule
,在本Bouncy Castle AES-GCM
小节下面有最佳答案。如果你不相信我们,至少信任的专家在美国国家安全局(NSA Suite B的@ nsa.gov/ia/programs/suiteb_cryptography/index.shtml:The Galois/Counter Mode (GCM) is the preferred AES mode.
)
如何在C#中加密和解密字符串?
jbtule
,在本Bouncy Castle AES-GCM
小节下面有最佳答案。如果你不相信我们,至少信任的专家在美国国家安全局(NSA Suite B的@ nsa.gov/ia/programs/suiteb_cryptography/index.shtml:The Galois/Counter Mode (GCM) is the preferred AES mode.
)
Answers:
EDIT 2013年10月:尽管我已经为解决缺点而逐步编辑了此答案,但是请参阅jbtule的答案以获取更可靠,更明智的解决方案。
https://stackoverflow.com/a/10366194/188474
原始答案:
这是从“ RijndaelManaged Class”文档和MCTS Training Kit派生的工作示例。
编辑2012年4月:对该答案进行了编辑,以根据每个小建议的建议在IV之前添加,如下所示:
http://msdn.microsoft.com/zh-CN/library/system.security.cryptography.aesmanaged%28v=vs.95%29.aspx
祝好运!
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),这需要更多的开销和更多的密钥。
第二个示例使用开放源代码Bouncy Castle(通过nuget)使用AES256- GCM的更简单实践。
这两个示例都具有一个主要功能,该功能接受秘密消息字符串,密钥和可选的非秘密有效负载,并可选地在非秘密数据之前添加返回和已认证的加密字符串。理想情况下,您可以将它们与随机生成的see的256位密钥一起使用NewKey()
。
这两个示例还具有使用字符串密码生成密钥的辅助方法。提供这些辅助方法是为了方便与其他示例匹配,但是它们的安全性要差得多,因为密码的强度将远小于256位密钥。
更新:
添加了byte[]
重载,并且由于StackOverflow答案限制,只有Gist才能使用4个缩进和api文档的完整格式。
.NET内置加密(AES)-然后MAC(HMAC)[要点]
/*
* 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 [要点]
/*
* 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加密可以加密的数据大小是有限制的KeySize - MinimumPadding
。例如256个字节(假设2048位密钥)-42个字节(最小OEAP填充)= 214个字节(最大明文大小)
用您的RSA密钥替换your_rsa_key。
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
RSA
可能是一个很好的选择。非对称加密有其用途,但它不是默认加密的正确选择。对于较长的字符串,您的示例代码将失败,因为RSA类不是为通用加密而设计的。如果需要非对称功能,则应使用RSA加密对称密钥,并使用该对称密钥加密实际数据。因此,我仍然相信您的答案是错误的建议。
如果使用的是ASP.Net,则现在可以使用.Net 4.0及更高版本中的内置功能。
System.Web.Security.MachineKey
.Net 4.5具有MachineKey.Protect()
和MachineKey.Unprotect()
。
.Net 4.0具有MachineKey.Encode()
和MachineKey.Decode()
。您只需将MachineKeyProtection设置为“ All”。
在ASP.Net之外,此类在每次重新启动应用程序时都会生成一个新密钥,因此无法正常工作。快速浏览一下ILSpy,在我看来,如果缺少适当的app.settings,它将生成自己的默认值。因此,您实际上可以在ASP.Net外部进行设置。
我无法在System.Web命名空间之外找到非ASP.Net等效项。
BouncyCastle是适用于.NET的出色加密库,可以作为Nuget软件包使用,可以安装到项目中。我喜欢它比System.Security.Cryptography库中当前提供的东西更多。就可用算法而言,它为您提供了更多选择,并为这些算法提供了更多模式。
这是TwoFish的实现示例,该实现由Bruce Schneier(对我们所有人持偏执狂的英雄)撰写。像Rijndael(aka AES)这样的对称算法。它是AES标准的三项入围者之一,并且是Bruce Schneier编写的另一种著名算法BlowFish的兄弟。
bouncycastle的第一件事是创建一个加密器类,这将使在库中实现其他分组密码更加容易。以下加密程序类接受通用参数T,其中T实现IBlockCipher并具有默认构造函数。
更新: 由于流行的需求,我决定实现生成随机IV并将HMAC包含在此类中。尽管从样式的角度来看,这违反了单一责任的SOLID原则,但是由于我不喜欢此类的性质,因此。此类现在将采用两个通用参数,一个用于密码,一个用于摘要。它使用RNGCryptoServiceProvider自动生成IV,以提供良好的RNG熵,并允许您使用BouncyCastle想要的任何摘要算法来生成MAC。
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;
}
}
接下来,只需在新类上调用crypto和解密方法,这是使用twofish的示例:
var encrypt = new Encryptor<TwofishEngine, Sha1Digest>(Encoding.UTF8, key, hmacKey);
string cipher = encrypt.Encrypt("TEST");
string plainText = encrypt.Decrypt(cipher);
替换另一个分组密码(如TripleDES)也很容易:
var des = new Encryptor<DesEdeEngine, Sha1Digest>(Encoding.UTF8, key, hmacKey);
string cipher = des.Encrypt("TEST");
string plainText = des.Decrypt(cipher);
最后,如果要将AES与SHA256 HMAC一起使用,则可以执行以下操作:
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填充,并添加了新的构造函数,以使最终用户能够选择他们要使用的填充。感谢@CodesInChaos的建议。
SequenceEqual
。这避免了定时侧信道泄漏了所呈现的MAC的前缀和实际的MAC匹配多长时间。
免责声明:此解决方案仅应用于未公开的静态数据(例如-配置文件或DB)。仅在这种情况下,由于维护量较低,因此可以认为快速和肮脏的解决方案比@jbtule的解决方案更好。
原始帖子:我发现jbtule的答案对于快速,肮脏的安全AES字符串加密有点复杂,Brett的答案存在一个错误,因为Initialization Vector是固定值,因此容易受到填充攻击,因此我修复了Brett的代码,添加了一个随机IV,该IV被添加到切细后的字符串中,每个相同值的加密都创建一个不同的加密值:
加密:
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;
}
解密:
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.config \ app.config)中,因为您不应该对其进行硬编码。配置文件也应该加密,这样密钥就不会保存为明文。
protected static string _Key = "";
protected static string EncryptionKey
{
get
{
if (String.IsNullOrEmpty(_Key))
{
_Key = ConfigurationManager.AppSettings["AESKey"].ToString();
}
return _Key;
}
}
Encrypt
即使使用相同的纯文本,您的方法也会为每个调用生成不同的值,但是每次Substring(20)
都会相同,对吗?
Encrypt
每次都会生成不同的IV。由于某种原因,我一直认为每次IV都是相同的,这基本上使它毫无意义。
加密
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());
}
解密:
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#中的“加密和解密字符串”的参考,我找到了一个很好的解决方案:
static readonly string PasswordHash = "P@@Sw0rd";
static readonly string SaltKey = "S@LT&KEY";
static readonly string VIKey = "@1B2c3D4e5F6g7H8";
加密
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);
}
解密
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());
}
Encoding.ASCII.GetBytes
对于旨在向人类选择的事物添加熵的数据,其熵将远低于预期,这是一个非常新手的错误。这些都是很容易纠正的东西,但实际上并非如此,由于安全隐患,我的大胆警告仍然存在。
支持mattmanser的答案。这是一个使用MachineKey类加密/解密URL安全值的示例。
如前所述,请记住,这将使用计算机配置设置(https://msdn.microsoft.com/zh-cn/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/generatemachinekey/
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;
}
下面的示例演示如何加密和解密示例数据:
// 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 CBC模式,随机IV和HMAC和密码派生密钥在C#中对字符串进行加密,以显示基本的移动部分:
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);
}
mismatch += found[i]^computed[i]
。3)您使用的PBKDF2-HMAC-SHA-1字节超过20个,这会使KDF降低2倍,而不会降低攻击者的速度
用于AES-GCM加密的BouncyCastle的替代方法是libsodium-net。它包装了libsodium C库。一个不错的优点是,它使用CPU中的AES-NI扩展来进行非常快速的加密。不利的一面是,如果CPU没有扩展名,它将根本无法工作。没有软件退回。
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;
}
}
}
这是布雷特在这里布置的课。但是,我进行了少许编辑,因为在将其用于URL字符串加密和解密时收到错误“ Base-64 char数组的长度无效”。
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;
}
}
ConvertStringArrayToString()
使用的方法是什么?
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();
}
}
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;
using System.IO;
using System.Text;
/// <summary>
/// Summary description for Encryption
/// </summary>
public class Encryption
{
public TripleDES CreateDES(string key)
{
MD5 md5 = new MD5CryptoServiceProvider();
TripleDES des = new TripleDESCryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
des.IV = new byte[des.BlockSize / 8];
return des;
}
public byte[] Encryptiondata(string PlainText)
{
TripleDES des = CreateDES("DreamMLMKey");
ICryptoTransform ct = des.CreateEncryptor();
byte[] input = Encoding.Unicode.GetBytes(PlainText);
return ct.TransformFinalBlock(input, 0, input.Length);
}
public string Decryptiondata(string CypherText)
{
string stringToDecrypt = CypherText.Replace(" ", "+");
int len = stringToDecrypt.Length;
byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt);
byte[] b = Convert.FromBase64String(CypherText);
TripleDES des = CreateDES("DreamMLMKey");
ICryptoTransform ct = des.CreateDecryptor();
byte[] output = ct.TransformFinalBlock(b, 0, b.Length);
return Encoding.Unicode.GetString(output);
}
public string Decryptiondataurl(string CypherText)
{
string newcyperttext=CypherText.Replace(' ', '+');
byte[] b = Convert.FromBase64String(newcyperttext);
TripleDES des = CreateDES("DreamMLMKey");
ICryptoTransform ct = des.CreateDecryptor();
byte[] output = ct.TransformFinalBlock(b, 0, b.Length);
return Encoding.Unicode.GetString(output);
}
#region encryption & Decription
public string Encrypt(string input, string key)
{
byte[] inputArray = UTF8Encoding.UTF8.GetBytes(input);
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
tripleDES.Key = UTF8Encoding.UTF8.GetBytes(key);
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tripleDES.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
tripleDES.Clear();
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
public string Decrypt(string input, string key)
{
byte[] inputArray = Convert.FromBase64String(input);
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
tripleDES.Key = UTF8Encoding.UTF8.GetBytes(key);
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tripleDES.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
tripleDES.Clear();
return UTF8Encoding.UTF8.GetString(resultArray);
}
public string encrypt(string encryptString)
{
string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
byte[] clearBytes = Encoding.Unicode.GetBytes(encryptString);
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();
}
encryptString = Convert.ToBase64String(ms.ToArray());
}
}
return encryptString;
}
public string Decrypt(string cipherText)
{
string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
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;
}
#endregion
}
加密是编程中非常普遍的事情。我认为最好安装一个软件包来为您完成任务。也许是像Simple Aes Encryption这样的简单开源Nuget项目
关键在配置文件中,因此很容易在生产环境中进行更改,并且我看不到任何缺点
<MessageEncryption>
<EncryptionKey KeySize="256" Key="3q2+796tvu/erb7v3q2+796tvu/erb7v3q2+796tvu8="/>
</MessageEncryption>
基于多个答案和评论。
码:
/// <summary>
/// Simple encryption/decryption using a random initialization vector
/// and prepending it to the crypto text.
/// </summary>
/// <remarks>Based on multiple answers in /programming/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);
}
}
这是最初由ASP Snippets编写的简单代码段
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;
}
AES算法:
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;
}
}
Encryption
方法的多次执行之间改变,并且不会持续存在,因此除了出于演示目的之外,根本不需要使用此方法。3)没有对密文进行身份验证,因此攻击者可能会在未检测到密文的情况下对其进行操作(请参阅:padding oracle Attack)。
_iv
类变量中,而不写入密文中。那么,您认为接收方将如何知道密钥和IV?它们将不得不以其他方式分发。由于IV不应被认为是秘密的,因此应为每种加密随机生成IV,并将其与密文一起分发。
这是使用Bouncy Castle包如何完成AES-GCM加密/解密的示例。
我在Google搜索该示例时发现有可能从GOlang crypto/aes
API 解密数据:
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位)的超级按钮。
很好的例子,如何做到这一点使用PGPCore与BouncyCastle的,很简单的解决方案: https://blog.bitscry.com/2018/07/05/pgp-encryption-and-decryption-in-c/
我尝试了不同的解决方案,但这对我来说最有效,有些有错误,但这对我来说是完美的。
using (PGP pgp = new PGP())
{
// Generate keys
pgp.GenerateKey(@"C:\TEMP\keys\public.asc", @"C:\TEMP\keys\private.asc", "email@email.com", "password");
// Encrypt file
pgp.EncryptFile(@"C:\TEMP\keys\content.txt", @"C:\TEMP\keys\content__encrypted.pgp", @"C:\TEMP\keys\public.asc", true, true);
// Encrypt and sign file
pgp.EncryptFileAndSign(@"C:\TEMP\keys\content.txt", @"C:\TEMP\keys\content__encrypted_signed.pgp", @"C:\TEMP\keys\public.asc", @"C:\TEMP\keys\private.asc", "password", true, true);
// Decrypt file
pgp.DecryptFile(@"C:\TEMP\keys\content__encrypted.pgp", @"C:\TEMP\keys\content__decrypted.txt", @"C:\TEMP\keys\private.asc", "password");
// Decrypt signed file
pgp.DecryptFile(@"C:\TEMP\keys\content__encrypted_signed.pgp", @"C:\TEMP\keys\content__decrypted_signed.txt", @"C:\TEMP\keys\private.asc", "password");
// Encrypt stream
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\keys\content.txt", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\keys\content__encrypted2.pgp"))
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\keys\public.asc", FileMode.Open))
pgp.EncryptStream(inputFileStream, outputFileStream, publicKeyStream, true, true);
// Decrypt stream
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\keys\content__encrypted2.pgp", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\keys\content__decrypted2.txt"))
using (Stream privateKeyStream = new FileStream(@"C:\TEMP\keys\private.asc", FileMode.Open))
pgp.DecryptStream(inputFileStream, outputFileStream, privateKeyStream, "password");
}
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);
}
}
}
为简单起见,我为自己创建了我用于非加密目的的此功能:将“ yourpassphrase”替换为您的密码...
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);
}
}
}
encryptor.TransformFinalBlock
比使用那些内存和加密流更简单。
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;
}
}
}
}
我想用我的AES代码Rfc2898DeriveBytes
(在此为文档)algorhytm(用C#(.NET Framework 4)编写,并且也可以在有限的平台上完全正常工作,例如用于Windows Phone 7.0+的.NET Compact Framework)为我做出贡献。平台支持.NET框架的所有加密方法!)。
我希望这可以帮助任何人!
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;
}
}
}
}
IVa
IV而不是密码的变量?2)为什么要设置IV = Key?每种加密都需要一个新的随机IV。3)缺少MAC会
您必须通过System.Security.Cryptography使用名称空间;useHashing是布尔类型true或false。字符串变量“ key”对于加密和解密应该相同
//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;
}
}
一个安全地对数据进行哈希处理的好算法是BCrypt:
除了结合盐以防止彩虹表攻击外,bcrypt还具有自适应功能:随着时间的流逝,迭代计数可以增加以使其变慢,因此即使计算能力提高,它仍然可以抵抗暴力搜索攻击。
BCrypt有一个不错的.NET实现,也可以作为NuGet包提供。