Cannot AES decrypt data in C# that was enccrypted in Objective-C - c#

I have an iOS app that sends encrypted data that is later decrypted in C#. I have checked that the hex key and data received is same, but I still get Bad PKCS7 padding. Invalid length 0.
my Objective-C call is
+(NSData*) encryptData: (NSData*) data
key: (NSString*) key
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[data bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
my keysize is 256, blocksize is 128, padding is pkcs7, IV is null, mode is CBC (default).
My C# code to decrypt is
using (MemoryStream memoryStream = new MemoryStream(outputBytes))
{
AesManaged algo = GetCryptoAlgorithm(GetRawBrokerKey());
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, algo.CreateDecryptor(), CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(cryptoStream))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
private static AesManaged GetCryptoAlgorithm()
{
return GetCryptoAlgorithm(null);
}
private static AesManaged GetCryptoAlgorithm(byte[] key)
{
AesManaged algorithm = new AesManaged();
//set the mode, padding and block size
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 256;
algorithm.BlockSize = 128;
if (key != null)
{
algorithm.Key = key;
}
algorithm.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
return algorithm;
}
I tried encryption using C# and see a different hex output using the same key.
c# encryption - 42AC7494606333309287768F47DFB35B
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] key)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
byte[] encrypted;
AesManaged algorithm = new AesManaged();
//set the mode, padding and block size
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 256;
algorithm.BlockSize = 128;
if (key != null)
{
algorithm.Key = key;
}
algorithm.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = algorithm.CreateEncryptor();
// Create the streams used for encryption.
using (MemoryStream 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);
}
encrypted = msEncrypt.ToArray();
}
}
string hex = BitConverter.ToString(encrypted);
Console.WriteLine("c# encryption - " + hex.Replace("-", ""));
// Return the encrypted bytes from the memory stream.
return encrypted;
}
Any ideas what could be going wrong? I seem to be following all the online advice around defaults for mode and IV, I think.

in GetCryptoAlgorithm, you need to pass the private key to GetCryptoAlgorithm.

Related

AesCryptoServiceProvider "The input data is not a complete block"

I wrote two programs in C#. One does the encryption, and the other one does the decryption. The decryption program throws exception of "The input data is not a complete block" with data from the encryption program. However the decryption program works fine with the encrypted data from a Unix encryption program, using PKCS5_PBKDF2_HMAC_SHA1().
Encrypted data is base 64 encoded, and saved in a text file. The first 8 bytes is a salt, the next 16 bytes is an IV, and the rest is the application text.
I would appreciate very much if anyone could help.
public static string DecryptText(string cipherData)
{
if (string.IsNullOrEmpty(_passwd))
return null;
var decodedBytes = Convert.FromBase64String(cipherData);
// First 8 bytes contain the salt used for key derivation. Use the password from the passwd.dat
// file and the salt to derive the key used to encode the credential.
//
var salt = new byte[8];
Buffer.BlockCopy(decodedBytes, 0, salt, 0, 8);
var derivedBytes = new Rfc2898DeriveBytes(_passwd, salt, 1000);
var key = derivedBytes.GetBytes(32);
// Next 16 bytes contain the initialization vector used to encrypt
//
var ivBytes = new byte[16];
Buffer.BlockCopy(decodedBytes, 8, ivBytes, 0, ivBytes.Length);
// Remaining bytes contain the credential cipher text
//
var cipherBytes = new byte[decodedBytes.Length - 8 - 16];
Buffer.BlockCopy(decodedBytes, 8 + 16, cipherBytes, 0, cipherBytes.Length);
string decryptedData = null;
try
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = key;
aes.IV = ivBytes;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
decryptedData = Encoding.UTF8.GetString(ms.ToArray());
}
}
}
catch (Exception ex)
{
Console.Write("Caught exception while decryption: {0}: ", ex.Message);
}
return decryptedData;
}
public static string EncryptText(string plainData)
{
if (string.IsNullOrEmpty(_passwd))
return null;
var rfc2898db = new Rfc2898DeriveBytes(_passwd, 8, 1000);
byte[] salt = new byte[8];
Buffer.BlockCopy(rfc2898db.Salt, 0, salt, 0, 8);
byte[] key = new byte[32];
Buffer.BlockCopy(rfc2898db.GetBytes(32), 0, key, 0, 32);
string cipherData;
try
{
var aes = new AesCryptoServiceProvider
{
Key = key,
KeySize = 256,
BlockSize = 128,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
};
aes.GenerateIV();
byte[] encrypted;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
ms.Write(aes.IV, 0, aes.IV.Length);
ms.Write(salt, 0, 8);
cs.Write(Encoding.UTF8.GetBytes(plainData), 0, plainData.Length);
cs.Close();
}
encrypted = ms.ToArray();
}
byte[] encryptedBytes = new byte[SaltLength + IvLength + encrypted.Length];
Buffer.BlockCopy(salt, 0, encryptedBytes, 0, SaltLength);
Buffer.BlockCopy(aes.IV, 0, encryptedBytes, SaltLength, IvLength);
Buffer.BlockCopy(encrypted, 0, encryptedBytes, SaltLength + IvLength, encrypted.Length);
cipherData = Convert.ToBase64String(encryptedBytes);
}
catch (Exception ex)
{
Console.Write("Caught exception while encryption: {0}", ex.Message);
return null;
}
return cipherData;
}

Unity C# AES - Can't send IV

I am having a problem with my code and cannot figure out the problem.
With fullMsg set to false it works.
If I change fullMsg to true it no longer works even if I set it back to false.
I have no idea what is going on as the byte[] array remains the same in all the cases.
private void sendIndividualMsg(string msg){
//Debug.Log (msg);
byte[] toSend = PackageMsg (msg);
byte[] decoded = UnPackageMsg (toSend);
Debug.Log( System.Text.Encoding.ASCII.GetString(decoded) );
}
private byte[] UnPackageMsg(byte[] msg){
var toDecrypt = msg;
if (fullMsg) {
toDecrypt = new byte[msg.Length - 16];
var decryptIV = new byte[16];
System.Buffer.BlockCopy (msg, 0, decryptIV, 0, 16);
System.Buffer.BlockCopy (msg, 16, toDecrypt, 0, (msg.Length - 16));
//myAes.IV = decryptIV;
}
PrintByteArray (toDecrypt, "ToDecrypt");
byte[] plaintext;
// Create the streams used for decryption.
{
ICryptoTransform decryptor = myAes.CreateDecryptor (myAes.Key, myAes.IV);
MemoryStream msDecrypt = new MemoryStream ();
CryptoStream csDecrypt = new CryptoStream (msDecrypt, decryptor, CryptoStreamMode.Write);
csDecrypt.Write(msg, 0, msg.Length);
csDecrypt.FlushFinalBlock();
csDecrypt.Close();
plaintext = msDecrypt.ToArray();
msDecrypt.Close ();
decryptor.Dispose();
}
if (plaintext.Length > 16) {
byte[] hash = new byte[16];
byte[] hashCalc = new byte[16];
byte[] payload = new byte[plaintext.Length - 16 ];
System.Buffer.BlockCopy (plaintext, 0, hash, 0, 16);
System.Buffer.BlockCopy (plaintext, 16, payload, 0, plaintext.Length - 16);
hashCalc = md5.ComputeHash(payload);
if(ByteArrayCompare(hash, hashCalc)){
return payload;
}else{
return new byte[0];
}
} else {
return new byte[0];
}
}
private byte[] PackageMsg(string msg){
System.Text.ASCIIEncoding ue = new System.Text.ASCIIEncoding();
byte[] bytes = ue.GetBytes(msg);
byte[] hashBytes = md5.ComputeHash(bytes);
byte[] toEncrypt = new byte[bytes.Length + hashBytes.Length];
System.Buffer.BlockCopy(hashBytes, 0, toEncrypt, 0, hashBytes.Length);
System.Buffer.BlockCopy(bytes, 0, toEncrypt, hashBytes.Length, bytes.Length);
//myAes.GenerateIV();
byte[] encrypted;
{
ICryptoTransform encryptor = myAes.CreateEncryptor (myAes.Key, myAes.IV);
MemoryStream msEncrypt = new MemoryStream ();
CryptoStream csEncrypt = new CryptoStream (msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
csEncrypt.Close();
encrypted = msEncrypt.ToArray();
msEncrypt.Close ();
encryptor.Dispose();
}
PrintByteArray (encrypted, "Encrypted");
//SessionBytes + IV + ENCRYPTED( hash + msg)
byte[] finalMsg = new byte[myAes.IV.Length + encrypted.Length ];
System.Buffer.BlockCopy(myAes.IV, 0, finalMsg, 0 , myAes.IV.Length);
System.Buffer.BlockCopy(encrypted, 0, finalMsg, myAes.IV.Length , encrypted.Length);
if (!fullMsg) {
finalMsg = encrypted;
}
return finalMsg;
}
(I want to send the IV through each message, but until I get this working their is no point.)
Basic explanation of the algorithm for encrypting:
If fullMsg is set the encryption is [session][iv][encryptedmsg]
If fullMsg is not set the encryption is [encryptedmsg]
For Decryption the first part of the code is too extract just the encryptedmsg part.
As show by the screenshot of the logs, in both modes the bytes are the same.
--

Length of the data to decrypt is invalid when decrypting

I need to encrypt an image, return the string of the encrypted data, then decrypt it
Here's my encryption code :
string plainText = ASCIIEncoding.ASCII.GetString(Imagebytes);
byte[] encrypted;
byte[] key = Encoding.UTF8.GetBytes("M02cnQ51Ji97vwT4");;
// Create an AesCryptoServiceProvider object
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.Key = key;
aesAlg.BlockSize = 128;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream 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);
}
encrypted = msEncrypt.ToArray();
}
}
}
using (var aesAlg = new AesManaged())
{
aesAlg.Key = new UTF8Encoding().GetBytes("M02cnQ51Ji97vwT4");
aesAlg.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
return UTF8Encoding.UTF8.GetString(encryptor.TransformFinalBlock(Imagebytes, 0, Imagebytes.Length)).Length;
}
My decryption work fine ( because i can receive image / video perfectly )
here's the code:
const string BLOB_KEY = "TTAyY25RNTFKaTk3dndUNA==";
using (RijndaelManaged rm = new RijndaelManaged())
{
rm.Mode = CipherMode.ECB;
rm.Key = Convert.FromBase64String(BLOB_KEY);
rm.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
rm.Padding = PaddingMode.Zeros;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, rm.CreateDecryptor(),
CryptoStreamMode.Write))
{
cs.Write(image, 0, image.Length);
return ms.ToArray();
}
}
}
What's wrong with my encryption code ?
Your very initial line of code is already wrong:
string plainText = ASCIIEncoding.ASCII.GetString(Imagebytes);
An image is not a string, and plaintext is not necessarily a string either. Both consist of bytes. So you should not be using a StreamWriter either, just a normal stream.
It is likely that data is lost during conversion.
Furthermore, you are writing to a decryption stream and you are using ECB mode at one side and CBC mode at the other.
I would strongly advice you to read into the material and start over.
Thank you, i've changed my code to :
var aesAlg = new AesManaged
{
KeySize = 128,
Key = key,
BlockSize = 128,
Mode = CipherMode.ECB,
Padding = PaddingMode.Zeros,
IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
return encryptor.TransformFinalBlock(Imagebytes, 0, Imagebytes.Length);
And it works fine !

AES Encryption C#

EDIT: Added my hash code to the bottom of this.
I am having some problems creating a message integrity key for a solution I am creating. In order for this to be correct I need to use the following settings.
Mode: ECB
KeySize: 256
BlockSize: 128
Padding: PKCS7
I am using a 32 byte key which is generated from a file and also a blank IV as I understand ECB does not require one.
My problem I am expecting a 48 byte output from this before the encoding however I am receiving a 64 byte output.
I have shown some code below about how am I am trying to achieve this but I am not having much success.
public static string Encrypt(string hash) {
// Create a new instance of the AesManaged
// class. This generates a new key and initialization
// vector (IV).
using (AesManaged myAes = new AesManaged()) {
myAes.Key = File.ReadAllBytes("keyfile");
myAes.Mode = CipherMode.ECB;
myAes.IV = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
myAes.KeySize = 256;
myAes.BlockSize = 128;
myAes.Padding = PaddingMode.PKCS7;
// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes_Aes(hash, myAes.Key, myAes.IV);
// Decrypt the bytes to a string.
string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);
//Display the original data and the decrypted data.
Console.WriteLine("Original: {0}", hash);
Console.WriteLine("Round Trip: {0}", roundtrip);
// Encode
string encoded = Convert.ToBase64String(encrypted);
Console.WriteLine("Encoded: {0}", encoded);
return encoded;
}
}
static byte[] EncryptStringToBytes_Aes(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("Key");
byte[] encrypted;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged()) {
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream 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);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
static string DecryptStringFromBytes_Aes(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("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.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();
}
}
}
}
return plaintext;
}
public static string getHashSha256(string text) {
byte[] bytes = Encoding.UTF8.GetBytes(text);
SHA256Managed hashstring = new SHA256Managed();
byte[] hash = hashstring.ComputeHash(bytes);
string hashString = string.Empty;
foreach (byte x in hash) {
hashString += String.Format("{0:x2}", x);
}
return hashString;
}
PKCS #7 padding is defined such that padding is added in all cases. When the plaintext is a multiple of the block size, a whole block of padding is added. This is why the ciphertext is 64 bytes long when the plaintext is 48 bytes long.

OpenSSL encryption using .NET classes

I'm looking to create a class that uses the .NET libraries that is compatible with OpenSSL. I'm aware there is an OpenSSL.Net wrapper, but I would prefer to avoid referencing 3rd party\unmanaged code. I'm not looking for a discussion of whether this is the right choice, but there are reasons for it.
Currently I have the following, which I believe should be compatible with OpenSSL - it effectively does what I believe OpenSSL does from the OpenSSL documentation. However even when just using this class to do both the encryption and decryption, I'm getting the following error:
[CryptographicException] Padding is invalid and cannot be removed.
I have stepped through the code and verified that the salt\key\iv are all the same during the encryption and decryption process.
See below for sample class and call to do encrypt decrypt. Any ideas or pointers would be welcome.
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<byte> concatenatedHashes = new List<byte>(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;
}
}
I then call this to test it:
Protection protection = new Protection();
const string passphrase = "<passphrase>";
string encrypted = protection.OpenSSLEncrypt(jobid, passphrase);
string decrypted = protection.OpenSSLDecrypt(encrypted, passphrase);
Finally figured this one out. In the event someone needs to integrate openssl and .NET without using the openssl wrappers, I'll share the results here.
1) The main issue with my original code (as in the question) is that you must initialize the BlockSize and KeySize on your RijndaelManaged instance BEFORE setting the key or IV.
2) I also had BlockSize set to 256 when it should only be 128
3) The remainder of my issue came to the fact that openssl puts and expects "Salted__" onto the front of the salt before appending the encrypted string and then base64 encoding it. (I saw this initially in the openssl documentation with respect to file encryption but didn't think it did it when doing it directly through commandline - Apparently I was wrong!! Note also the capitalization of the S in Salted!)
With that all in mind, here is my "fixed" code:
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<byte> concatenatedHashes = new List<byte>(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;
}
}
Afraid there are issues with this latest code as well as OpenSSLDecrypt results in an error:
Padding is invalid and cannot be removed.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for >more information about the error and where it originated in the code.
Exception Details: System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed.
It occurs at the closen paren of this code:
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))" in 'static string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv)
I had no idea it would be this hard to encrypt a blob of text from one computer, then send it for storage and decryption to another.
10 years after, I opened a similar question
It is possible to decrypt AES password protected file in C# / dotNet 5 encrypted by openssl enc -k?
and Evk did some minor changes of the above answer (SHA256 instead of MD5, deal with the Salted__string, use byte[]) to make it work as a replacement of openSSL 1.1.1 (a to l)

Categories