使用.NET类的OpenSSL加密

我正在寻找创建一个使用与OpenSSL兼容的.NET库的类。 我知道有一个OpenSSL.Net包装器,但我宁愿避免引用第三方\非托管代码。 我不是在寻找关于这是否是正确选择的讨论,但有理由。

目前我有以下内容,我认为应该与OpenSSL兼容 – 它有效地完成了我认为OpenSSL从OpenSSL文档中所做的事情。 但是,即使只使用此类进行加密和解密,我也会收到以下错误:

[CryptographicException] Padding is invalid and cannot be removed. 

我已经逐步完成了代码并validation了salt \ key \ iv在加密和解密过程中都是一样的。

请参阅下面的示例类和调用加密解密。 欢迎任何想法或指示。

 public class Protection { public string OpenSSLEncrypt(string plainText, string passphrase) { // generate salt byte[] key, iv; byte[] salt = new byte[8]; RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); rng.GetNonZeroBytes(salt); DeriveKeyAndIV(passphrase, salt, out key, out iv); // encrypt bytes byte[] encryptedBytes = EncryptStringToBytesAes(plainText, key, iv); // add salt as first 8 bytes byte[] encryptedBytesWithSalt = new byte[salt.Length + encryptedBytes.Length]; Buffer.BlockCopy(salt, 0, encryptedBytesWithSalt, 0, salt.Length); Buffer.BlockCopy(encryptedBytes, 0, encryptedBytesWithSalt, salt.Length, encryptedBytes.Length); // base64 encode return Convert.ToBase64String(encryptedBytesWithSalt); } public string OpenSSLDecrypt(string encrypted, string passphrase) { // base 64 decode byte[] encryptedBytesWithSalt = Convert.FromBase64String(encrypted); // extract salt (first 8 bytes of encrypted) byte[] salt = new byte[8]; byte[] encryptedBytes = new byte[encryptedBytesWithSalt.Length - salt.Length]; Buffer.BlockCopy(encryptedBytesWithSalt, 0, salt, 0, salt.Length); Buffer.BlockCopy(encryptedBytesWithSalt, salt.Length, encryptedBytes, 0, encryptedBytes.Length); // get key and iv byte[] key, iv; DeriveKeyAndIV(passphrase, salt, out key, out iv); return DecryptStringFromBytesAes(encryptedBytes, key, iv); } private static void DeriveKeyAndIV(string passphrase, byte[] salt, out byte[] key, out byte[] iv) { // generate key and iv List concatenatedHashes = new List(48); byte[] password = Encoding.UTF8.GetBytes(passphrase); byte[] currentHash = new byte[0]; MD5 md5 = MD5.Create(); bool enoughBytesForKey = false; // See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM while (!enoughBytesForKey) { int preHashLength = currentHash.Length + password.Length + salt.Length; byte[] preHash = new byte[preHashLength]; Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length); Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length); Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length); currentHash = md5.ComputeHash(preHash); concatenatedHashes.AddRange(currentHash); if (concatenatedHashes.Count >= 48) enoughBytesForKey = true; } key = new byte[32]; iv = new byte[16]; concatenatedHashes.CopyTo(0, key, 0, 32); concatenatedHashes.CopyTo(32, iv, 0, 16); md5.Clear(); md5 = null; } static byte[] EncryptStringToBytesAes(string plainText, byte[] key, byte[] iv) { // Check arguments. if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException("plainText"); if (key == null || key.Length <= 0) throw new ArgumentNullException("key"); if (iv == null || iv.Length <= 0) throw new ArgumentNullException("iv"); // Declare the stream used to encrypt to an in memory // array of bytes. MemoryStream msEncrypt; // Declare the RijndaelManaged object // used to encrypt the data. RijndaelManaged aesAlg = null; try { // Create a RijndaelManaged object // with the specified key and IV. aesAlg = new RijndaelManaged { Key = key, IV = iv, Mode = CipherMode.CBC, KeySize = 256, BlockSize = 256 }; // Create an encryptor to perform the stream transform. ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); // Create the streams used for encryption. msEncrypt = new MemoryStream(); using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { //Write all data to the stream. swEncrypt.Write(plainText); swEncrypt.Flush(); swEncrypt.Close(); } } } finally { // Clear the RijndaelManaged object. if (aesAlg != null) aesAlg.Clear(); } // Return the encrypted bytes from the memory stream. return msEncrypt.ToArray(); } static string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv) { // Check arguments. if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException("cipherText"); if (key == null || key.Length <= 0) throw new ArgumentNullException("key"); if (iv == null || iv.Length <= 0) throw new ArgumentNullException("iv"); // Declare the RijndaelManaged object // used to decrypt the data. RijndaelManaged aesAlg = null; // Declare the string used to hold // the decrypted text. string plaintext; try { // Create a RijndaelManaged object // with the specified key and IV. aesAlg = new RijndaelManaged { Key = key, IV = iv, Mode = CipherMode.CBC, KeySize = 256, BlockSize = 256}; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); // Create the streams used for decryption. using (MemoryStream msDecrypt = new MemoryStream(cipherText)) { 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(); srDecrypt.Close(); } } } } finally { // Clear the RijndaelManaged object. if (aesAlg != null) aesAlg.Clear(); } return plaintext; } } 

然后我调用它来测试它:

 Protection protection = new Protection(); const string passphrase = ""; string encrypted = protection.OpenSSLEncrypt(jobid, passphrase); string decrypted = protection.OpenSSLDecrypt(encrypted, passphrase); 

最后想出了这一个。 如果有人需要在不使用openssl包装器的情况下集成openssl和.NET,我将在此处分享结果。

1)我的原始代码(如问题中)的主要问题是,在设置密钥或IV之前,必须在RijndaelManaged实例上初始化BlockSize和KeySize。

2)我也将BlockSize设置为256,它应该只有128

3)我的问题的其余部分是这样的事实:openssl在添加加密字符串然后对其进行base64编码之前将“Salted__”置于盐的前面。 (我最初在关于文件加密的openssl文档中看到了这一点,但是当它直接通过命令行执行时却没有想到它 – 显然我错了!!请注意S中Salted的大小写!)

考虑到这一切,这是我的“固定”代码:

 public class Protection { public string OpenSSLEncrypt(string plainText, string passphrase) { // generate salt byte[] key, iv; byte[] salt = new byte[8]; RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); rng.GetNonZeroBytes(salt); DeriveKeyAndIV(passphrase, salt, out key, out iv); // encrypt bytes byte[] encryptedBytes = EncryptStringToBytesAes(plainText, key, iv); // add salt as first 8 bytes byte[] encryptedBytesWithSalt = new byte[salt.Length + encryptedBytes.Length + 8]; Buffer.BlockCopy(Encoding.ASCII.GetBytes("Salted__"), 0, encryptedBytesWithSalt, 0, 8); Buffer.BlockCopy(salt, 0, encryptedBytesWithSalt, 8, salt.Length); Buffer.BlockCopy(encryptedBytes, 0, encryptedBytesWithSalt, salt.Length + 8, encryptedBytes.Length); // base64 encode return Convert.ToBase64String(encryptedBytesWithSalt); } public string OpenSSLDecrypt(string encrypted, string passphrase) { // base 64 decode byte[] encryptedBytesWithSalt = Convert.FromBase64String(encrypted); // extract salt (first 8 bytes of encrypted) byte[] salt = new byte[8]; byte[] encryptedBytes = new byte[encryptedBytesWithSalt.Length - salt.Length - 8]; Buffer.BlockCopy(encryptedBytesWithSalt, 8, salt, 0, salt.Length); Buffer.BlockCopy(encryptedBytesWithSalt, salt.Length + 8, encryptedBytes, 0, encryptedBytes.Length); // get key and iv byte[] key, iv; DeriveKeyAndIV(passphrase, salt, out key, out iv); return DecryptStringFromBytesAes(encryptedBytes, key, iv); } private static void DeriveKeyAndIV(string passphrase, byte[] salt, out byte[] key, out byte[] iv) { // generate key and iv List concatenatedHashes = new List(48); byte[] password = Encoding.UTF8.GetBytes(passphrase); byte[] currentHash = new byte[0]; MD5 md5 = MD5.Create(); bool enoughBytesForKey = false; // See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM while (!enoughBytesForKey) { int preHashLength = currentHash.Length + password.Length + salt.Length; byte[] preHash = new byte[preHashLength]; Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length); Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length); Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length); currentHash = md5.ComputeHash(preHash); concatenatedHashes.AddRange(currentHash); if (concatenatedHashes.Count >= 48) enoughBytesForKey = true; } key = new byte[32]; iv = new byte[16]; concatenatedHashes.CopyTo(0, key, 0, 32); concatenatedHashes.CopyTo(32, iv, 0, 16); md5.Clear(); md5 = null; } static byte[] EncryptStringToBytesAes(string plainText, byte[] key, byte[] iv) { // Check arguments. if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException("plainText"); if (key == null || key.Length <= 0) throw new ArgumentNullException("key"); if (iv == null || iv.Length <= 0) throw new ArgumentNullException("iv"); // Declare the stream used to encrypt to an in memory // array of bytes. MemoryStream msEncrypt; // Declare the RijndaelManaged object // used to encrypt the data. RijndaelManaged aesAlg = null; try { // Create a RijndaelManaged object // with the specified key and IV. aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, KeySize = 256, BlockSize = 128, Key = key, IV = iv }; // Create an encryptor to perform the stream transform. ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); // Create the streams used for encryption. msEncrypt = new MemoryStream(); using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { //Write all data to the stream. swEncrypt.Write(plainText); swEncrypt.Flush(); swEncrypt.Close(); } } } finally { // Clear the RijndaelManaged object. if (aesAlg != null) aesAlg.Clear(); } // Return the encrypted bytes from the memory stream. return msEncrypt.ToArray(); } static string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv) { // Check arguments. if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException("cipherText"); if (key == null || key.Length <= 0) throw new ArgumentNullException("key"); if (iv == null || iv.Length <= 0) throw new ArgumentNullException("iv"); // Declare the RijndaelManaged object // used to decrypt the data. RijndaelManaged aesAlg = null; // Declare the string used to hold // the decrypted text. string plaintext; try { // Create a RijndaelManaged object // with the specified key and IV. aesAlg = new RijndaelManaged {Mode = CipherMode.CBC, KeySize = 256, BlockSize = 128, Key = key, IV = iv}; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); // Create the streams used for decryption. using (MemoryStream msDecrypt = new MemoryStream(cipherText)) { 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(); srDecrypt.Close(); } } } } finally { // Clear the RijndaelManaged object. if (aesAlg != null) aesAlg.Clear(); } return plaintext; } } 

担心这个最新代码存在问题以及OpenSSLDecrypt会导致错误:

填充无效,无法删除。

描述:执行当前Web请求期间发生未处理的exception。 请查看堆栈跟踪以获取有关错误及其源自代码的更多信息。

exception详细信息:System.Security.Cryptography.CryptographicException:填充无效,无法删除。

它发生在这段代码的closen paren:

using(CryptoStream csDecrypt = new CryptoStream(msDecrypt,decryptor,CryptoStreamMode.Read))“in’static string DecryptStringFromBytesAes(byte [] cipherText,byte [] key,byte [] iv)

我不知道从一台计算机加密一段文本,然后将其发送给另一台计算机进行存储和解密是很困难的。