示意例子三:我们发现PBKDF2()方法和之前的PBKDF1()方法没有什么区别,就是无需指定加密密钥的哈希算法(参考PBKDF2规范请点这里)。
前面通过三种方法来动态的生成加密密钥,而且我们将使用Rfc2898DeriveBytes的GetBytes()方法来获取密钥,那么接下来让我们使用该方法实现通用的对称加密算法吧!
图5 对称算法加密过程
首先我们对加密的平文进行编码,这里默认使用UTF8对平文进行编码,也可以使用其他编码方式,接着使用相应加密算法对编码后的平文进行加密,最后把加密后的Byte数组转换为Base64格式字符串返回。
/// <summary> /// Encrypts with specified symmetric algorithm. /// Can be Aes, DES, RC2, Rijndael and TripleDES. /// </summary> /// <param>The symmertric algorithm (Aes, DES, RC2, Rijndael and TripleDES).</param> /// <param>The plain text need to be encrypted.</param> /// <param>The secret key to encrypt plain text.</param> /// <param>The iv should be 16 bytes.</param> /// <param>Salt to encrypt with.</param> /// <param>The number of iterations for plain text.</param> /// <param>Size of the key.</param> /// <param>The cipher mode.</param> /// <param>The padding mode.</param> /// <returns></returns> public static byte[] Encrypt(SymmetricAlgorithm algorithm, byte[] plainText, string key, string iv, string salt, int pwdIterations, int keySize, CipherMode cipherMode, PaddingMode paddingMode) { if (null == plainText) throw new ArgumentNullException("plainText"); if (null == algorithm) throw new ArgumentNullException("algorithm"); if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key"); if (String.IsNullOrEmpty(iv)) throw new ArgumentNullException("iv"); if (String.IsNullOrEmpty(salt)) throw new ArgumentNullException("salt"); // Note the salt should be equal or greater that 64bit (8 byte). var rfc = new Rfc2898DeriveBytes(key, salt.ToByteArray(), pwdIterations); using (SymmetricAlgorithm symmAlgo = algorithm) { symmAlgo.Mode = cipherMode; //symmAlgo.Padding = paddingMode; byte[] cipherTextBytes = null; using (var encryptor = symmAlgo.CreateEncryptor( rfc.GetBytes(keySize / 8), iv.ToByteArray())) { using (var ms = new MemoryStream()) { using (var cs = new CryptoStream( ms, encryptor, CryptoStreamMode.Write)) { cs.Write(plainText, 0, plainText.Length); cs.FlushFinalBlock(); cipherTextBytes = ms.ToArray(); ms.Close(); cs.Close(); } } symmAlgo.Clear(); return cipherTextBytes; } } }
图5 对称算法解密过程
通过上图的解密过程,我们发现解密过程恰恰是加密的反向,首先把Base64格式的密文转换为Byte数组,接着使用对应的解密算法解密密文,最后对解密后的数据进行编码返回平文(默认使用UTF8)。
/// <summary> /// Decrypts the specified algorithm. /// Can be Aes, DES, RC2, Rijndael and TripleDES. /// </summary> /// <param>The symmertric algorithm (Aes, DES, RC2, Rijndael and TripleDES).</param> /// <param>The cipher text.</param> /// <param>The secret key to decrypt plain text.</param> /// <param>The iv should be 16 bytes.</param> /// <param>Salt to decrypt with.</param> /// <param>The number of iterations for plain text.</param> /// <param>Size of the key.</param> /// <param>The cipher mode.</param> /// <param>The padding mode.</param> /// <returns></returns> public static byte[] Decrypt(SymmetricAlgorithm algorithm, byte[] cipherText, string key, string iv, string salt, int pwdIterations, int keySize, CipherMode cipherMode, PaddingMode paddingMode) { if (null == cipherText) throw new ArgumentNullException("cipherText"); if (null == algorithm) throw new ArgumentNullException("algorithm"); if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key"); if (String.IsNullOrEmpty(iv)) throw new ArgumentNullException("iv"); if (String.IsNullOrEmpty(salt)) throw new ArgumentNullException("salt"); // Note the salt should be equal or greater that 64bit (8 byte). var rfc = new Rfc2898DeriveBytes(key, salt.ToByteArray(), pwdIterations); using (SymmetricAlgorithm symmAlgo = algorithm) { symmAlgo.Mode = cipherMode; //symmAlgo.Padding = paddingMode; byte[] plainTextBytes = new byte[cipherText.Length]; int cnt = -1; using (var encryptor = symmAlgo.CreateDecryptor( rfc.GetBytes(keySize / 8), iv.ToByteArray())) { using (var ms = new MemoryStream(cipherText)) { using (var cs = new CryptoStream( ms, encryptor, CryptoStreamMode.Read)) { cnt = cs.Read(plainTextBytes, 0, plainTextBytes.Length); ms.Close(); cs.Close(); } } } symmAlgo.Clear(); Array.Resize(ref plainTextBytes, cnt); return plainTextBytes; } }
在前面的加密和解密方法,我们通过Rfc2898DeriveBytes获取密码、salt 值和迭代次数,然后通过调用GetBytes方法生成密钥。