Reputation: 231
I am a newbie to cryptography. My requirement is to decrypt/encrypt the text that is encrypted/decrypted using openssl. The algorithm that we are using is aes-256-cbc in the Openssl. So, I am trying to implement the same functionality in my application. so far after a lot of googling all i was able to do is..
private static string Encryptor(string TextToEncrypt)
{
//Turn the plaintext into a byte array.
byte[] PlainTextBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(TextToEncrypt);
//Setup the AES providor for our purposes.
AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider();
aesProvider.BlockSize = 128;
aesProvider.KeySize = 256;
//My key and iv that i have used in openssl
aesProvider.Key = System.Text.Encoding.ASCII.GetBytes(strKey);
aesProvider.IV = System.Text.Encoding.ASCII.GetBytes(strIV);
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.Mode = CipherMode.CBC;
ICryptoTransform cryptoTransform = aesProvider.CreateEncryptor(aesProvider.Key, aesProvider.IV);
byte[] EncryptedBytes = cryptoTransform.TransformFinalBlock(PlainTextBytes, 0, PlainTextBytes.Length);
return Convert.ToBase64String(EncryptedBytes);
}
private static string Decryptor(string TextToDecrypt)
{
byte[] EncryptedBytes = Convert.FromBase64String(TextToDecrypt);
//Setup the AES provider for decrypting.
AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider();
//aesProvider.Key = System.Text.Encoding.ASCII.GetBytes(strKey);
//aesProvider.IV = System.Text.Encoding.ASCII.GetBytes(strIV);
aesProvider.BlockSize = 128;
aesProvider.KeySize = 256;
//My key and iv that i have used in openssl
aesProvider.Key = System.Text.Encoding.ASCII.GetBytes(strKey);
aesProvider.IV = System.Text.Encoding.ASCII.GetBytes(strIV);
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.Mode = CipherMode.CBC;
ICryptoTransform cryptoTransform = aesProvider.CreateDecryptor(aesProvider.Key, aesProvider.IV);
byte[] DecryptedBytes = cryptoTransform.TransformFinalBlock(EncryptedBytes, 0, EncryptedBytes.Length);
return System.Text.Encoding.ASCII.GetString(DecryptedBytes);
}
My openssl command is
openssl aes-256-cbc -e -nosalt -a -in inputfile.txt -out output.txt -k key -iv ivkey
My key length is 32digits and iv is 16digits
Thnx ...
Upvotes: 5
Views: 17998
Reputation: 4358
In my case I needed to decrypt a file which was generated with following openssl command -
openssl enc -aes-256-cbc -salt -in DATA.zip -out DATA.zip.enc -pass file:key.txt
In this configuration, openssl generates a random 8-byte salt and uses the passphrase and salt to derive key and IV for AES encryption using its default algorithm1. The algorithm is defined in their manual as follows -
The key and IV is derived by concatenating D_1, D_2, etc until enough data is available for the key and IV. D_i is defined as:
D_i = HASH^count(D_(i-1) || data || salt)
where || denotes concatenation, D_0 is empty, HASH is the digest algorithm in use, HASH^1(data) is simply HASH(data), HASH^2(data) is HASH(HASH(data)) and so on.
The initial bytes are used for the key and the subsequent bytes for the IV.
Newer openssl versions (from 1.0.0 maybe) use SHA256 as Hash function. I ended up with the following code for key and IV derivation -
void DeriveKeyAndIV(string passphrase, byte[] salt, out byte[] key, out byte[] iv)
{
var pass = Encoding.UTF8.GetBytes(passphrase);
var hash1 = SHA256.HashData([.. pass, .. salt]);
var hash2 = SHA256.HashData([.. hash1, .. pass, .. salt]);
key = hash1;
iv = hash2[0..16];
}
In my case key length was 256 bit and IV was 128 bit. The pass phrase was 64 characters long.
openssl prepends the encrypted bytes with 16bytes containing the following information
Salted__XXXXXXXX<encrypted data bytes.....>
Here XXXXXXXX
are the salt bytes. To decrypt the payload we have to first read the bytes 8 to 15 (0-indexed) and use it with passphrase to derive the key and IV. Below is the code for decrypting the payload -
public static void DecryptFile(string encryptedFilePath, string passphrase)
{
byte[] encfilebytes = File.ReadAllBytes(encryptedFilePath);
byte[] salt = encfilebytes[8..16];
DeriveKeyAndIV(passphrase, salt, out byte[] key, out byte[] iv);
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using var fsEncrypted = new MemoryStream(encfilebytes[16..]);
using var decryptor = aes.CreateDecryptor();
using var fsDecrypted = new FileStream(encryptedFilePath.Replace(".enc", ""), FileMode.Create, FileAccess.Write);
using var csDecrypt = new CryptoStream(fsEncrypted, decryptor, CryptoStreamMode.Read);
csDecrypt.CopyTo(fsDecrypted);
fsDecrypted.Flush();
fsDecrypted.Close();
}
P.S. These codes are targeting dotnet 8.
Upvotes: 0
Reputation: 31799
First, read man enc
for openssl. -iv
is ignored when -k
is used. You probably want capital -K
. Second, the key and iv values are hexadecimal when used with the openssl tool, if your C# is using the same string as the command line then you need to do appropriate conversions rather than Encoding.ASCII.GetBytes
(a 7 bit encoding is never the right answer anyway).
For your plain text, you might as well use Encoding.UTF8.GetBytes/GetString
since it is backwards compatible with ASCII.
If for some reason you actually want to use lowercase -k
, a password to generate both the key and iv, that is much more difficult as openssl uses it's own key derivation scheme. Also, it is dangerous to use with the -nosalt
flag.
-nosalt: doesn't use a salt in the key derivation routines. This option SHOULD NOT be used except for test purposes or compatibility with ancient versions of OpenSSL and SSLeay.
One of the reasons this is dangerous, is due to the fact that IV's should not be predictable or reused for AES-CBC and if you don't use a salt, the passphrase will always produce the same key with the same IV that opens you up to several attacks and can leak info about the plaintext.
You can find out how to derive from passphrase, the same key and IV as openssl from this blog post Decrypting OpenSSL AES files in C# although it is specifically for AES-128 the comments lead you to how to modify for aes-256, from man EVP_BytesToKey
:
Hash0 = ''
Hash1 = MD5(Hash0 + Password + Salt)
Hash2 = MD5(Hash1 + Password + Salt)
Hash3 = MD5(Hash2 + Password + Salt)
Key = Hash1 + Hash2
IV = Hash3
Upvotes: 2