在实现对称加密算法之前,先让我们了解一下对称加密的过程,假设我们有一组数据要加密那么我们可以使用一个或一组密钥对数据进行加密解密,但存在一个问题对称加密算法的密钥长度不尽相同,如DES的密钥长度为64 bit,而AES的长度可以为128bit、192bit或256 bit,难道要我们hard code每种算法的密钥长度吗?能不能动态地产生对应算法的密钥呢?
其实.NET已经提供我们根据不同的对称算法生成对应密钥的方法了,并且把这些方法都封装在PasswordDeriveBytes和Rfc2898DeriveBytes类中。
首先让我们看一下PasswordDeriveBytes类包含两个方法CryptDeriveKey和GetBytes用来产生对应算法的密钥,现在让我们看一下它们如何产生密钥。
CryptDeriveKey:
// The sample function. public void Encrypt() { // The size of the IV property must be the same as the BlockSize property. // Due to the RC2 block size is 64 bytes, so iv size also is 64 bytes. var iv = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 }; var pdb = new PasswordDeriveBytes("pwd", null); // Set the encrypted algorithm and export key algorithm. // Then get the key base on encrypt algorithm. byte[] key = pdb.CryptDeriveKey("RC2", "SHA1", 128, iv); Console.WriteLine(key.Length * 8); Console.WriteLine(new RC2CryptoServiceProvider().BlockSize); // Creates an RC2 object to encrypt with the derived key var rc2 = new RC2CryptoServiceProvider { Key = key, IV = new byte[] { 21, 22, 23, 24, 25, 26, 27, 28 } }; // now encrypt with it byte[] plaintext = Encoding.UTF8.GetBytes("NeedToEncryptData"); using (var ms = new MemoryStream()) { var cs = new CryptoStream( ms, rc2.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(plaintext, 0, plaintext.Length); cs.Close(); byte[] encrypted = ms.ToArray(); } }
示意例子一:我们使用SHA1哈希算法为RC2加密算法生成128bit的密钥,这样我们就可以根据不同对称加密算法获取相应长度的密钥了,注意我们并没用动态地生成初始化向量iv,这是为了简单起见实际中不应该这样获取初始化向量。
接下来让我们看一下通过PBKDF1和PBKDF2s算法生成密钥的实现。
PBKDF1
GetBytes:PasswordDeriveBytes的GetBytes()方法实现了PBKDF1(Password Based Key Derivation Function)。
PBKDF1算法过程:
1.拼接密钥和盐:R0 = Pwd + Salt
2.哈希加密过程:R1 = Hash(R2-1)
……..
3.哈希加密过程:Rn = Hash(Rn - 1)
4.n是迭代的次数(参考PBKDF1规范请点这里)
现在我们对PBKDF1算法的原理有了初步的了解,接下来我们将通过GetBytes()调用该算法生成密钥。
/// <summary> /// Uses the PBKDF1 to genernate key, /// then use it to encrypt plain text. /// </summary> public void PBKDF1() { byte[] salt = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 }; // Creates an RC2 object to encrypt with the derived key var pdb = new PasswordDeriveBytes("pwd", salt) {IterationCount = 23, HashName = "SHA1"}; // Gets the key and iv. byte[] key = pdb.GetBytes(16); byte[] iv = pdb.GetBytes(8); var rc2 = new RC2CryptoServiceProvider { Key = key, IV = iv }; byte[] plaintext = Encoding.UTF8.GetBytes("NeedToEncryptData"); using (var ms = new MemoryStream()) { // Encrypts data. var cs = new CryptoStream( ms, rc2.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(plaintext, 0, plaintext.Length); cs.Close(); byte[] encrypted = ms.ToArray(); } }
示意例子二:我们使用PBKDF1算法为RC2加密算法生成128 bit的密钥和64 bit的初始化向量,要注意的是PasswordDeriveBytes的GetBytes()方法已经过时了,而它的替代项就是接下来要介绍的Rfc2898DeriveBytes的GetBytes()方法。
PBKDF2
GetBytes:由于Rfc2898DeriveBytes的GetBytes()方法实现了PBKDF2算法,而且它也替代了PBKDF1过时的GetBytes()方法,所以我们推荐使用Rfc2898DeriveBytes的GetBytes()方法。
/// <summary> /// Uses the PBKDF2 to genernate key, /// then use it to encrypt plain text. /// </summary> public void PBKDF2() { byte[] salt = new byte[] { 23, 21, 32, 33, 46, 59, 60, 74 }; var rfc = new Rfc2898DeriveBytes("pwd", salt, 23); // generate key and iv. byte[] key = rfc.GetBytes(16); byte[] iv = rfc.GetBytes(8); // Creates an RC2 object to encrypt with the derived key var rc2 = new RC2CryptoServiceProvider { Key = key, IV = iv }; // Encrypts the data. byte[] plaintext = Encoding.UTF8.GetBytes("NeedToEncryptData"); using (var ms = new MemoryStream()) { var cs = new CryptoStream( ms, rc2.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(plaintext, 0, plaintext.Length); cs.Close(); byte[] encrypted = ms.ToArray(); } }