RSA Encryption, getting bad length - c#

When calling the following function :
byte[] bytes = rsa.Encrypt(System.Text.UTF8Encoding.UTF8.GetBytes(stringToEncrypt), true);
I am now getting the error: bad length.
With a smaller string it works, any ideas what the problem could be the string I am passing is under 200 characters.

RSA encryption is only mean for small amounts of data, the amount of data you can encrypt is dependent on the size of the key you are using, for example for 1024 bit RSA keys, and PKCS # 1 V1.5 padding, you can encrypt 117 bytes at most, with a 2048 RSA key, you can encrypt 245 bytes.
There's a good reason for this, asymmetric encryption is computationally expensive. If you want to encrypt large amounts of data you should be using symmetric encryption. But what if you want non-repudiation? Well what you then do is use both. You create a symmetric key and exchange it using asymmetric encryption, then that safely exchanged symmetric key to encrypt your large amounts of data. This is what SSL and WS-Secure use underneath the covers.

For future searches regarding RSA bad length exceptions...
You can calculate the max number of bytes which can be encrypted with a particular key size with the following:
((KeySize - 384) / 8) + 37
However, if the optimal asymmetric encryption padding (OAEP) parameter is true, as it is in the original post, the following can be used to calculate the max bytes:
((KeySize - 384) / 8) + 7
The legal key sizes are 384 thru 16384 with a skip size of 8.

As explained above, the solution to the 'bad length' type exceptions is to hybridize the use of symmetric and asymmetric encryption, so that the size of the text you are encrypting is not constrained by the key size. You basically use RSA encryption to asymmetrically encrypt the random key .
For encryption:
Generate a random key of the length required for symmetrical encryption technique such as AES or Rijndael.
Symmetrically encrypt your text/data using AES/Rijndael using the random key generated in step 1.
Using RSA, asymmetrically encrypt the random key generated in step 1.
For decryption:
First decrypt the AES/Rijndael-generated random key using your private RSA key.
Then decrypt the original text/data using the RSA-decrypted random key
For a demonstration, you may wish to have a look this following example in C#:
http://www.technical-recipes.com/2013/using-rsa-to-encrypt-large-data-files-in-c/

I faced the same challenge while doing 2048 RSA encryption of plain text having less than 200 characters.
In my opinion, we can achieve the target without getting into complexity of Symmetric or Asymmetric encryption, with following simple steps;
By doing so I managed to encrypt and decrypt 40x larger text
Encryption:
Compress the plain text by using *Zip() method and convert into array of bytes
Encrypt with RSA
Decryption:
Decrypt cypher text with RSA
un-compress decrypted data by using **Unzip() method
*byte[] bytes = Zip(stringToEncrypt); // Zip() method copied below
**decryptedData = Unzip(decryptedBytes); // Unzip() method copied below
public static byte[] Zip(string str)
{
var bytes = System.Text.Encoding.UTF8.GetBytes(str);
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(mso, CompressionMode.Compress))
{
CopyTo(msi, gs);
}
return mso.ToArray();
}
}
public static string Unzip(byte[] bytes)
{
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(msi, CompressionMode.Decompress))
{
CopyTo(gs, mso);
}
return System.Text.Encoding.UTF8.GetString(mso.ToArray());
}
}
public static void CopyTo(Stream src, Stream dest)
{
byte[] bytes = new byte[4096];
int cnt;
while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0)
{
dest.Write(bytes, 0, cnt);
}
}

Related

AES Encryption using C#

I'm a newbie in cryptography and to learn it I tried to encrypt/decrypt with AES in C#.
Sadly I realized, that it isn't as easy as I thought.
So I was looking for a simpler solution.
Later I found a couple of code snippets including some explanation.
I copied the code and tried to implement it into a small application.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace aes
{
class Program
{
public static string passwd = null;
public static string content = null;
public static string encryptedcontent = null;
public static byte[] IV = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
public static int BlockSize = 128;
static void Encrypt()
{
if (passwd == "") return;
//Content to Byte Array
byte[] bytes = Encoding.Unicode.GetBytes(content);
//Encrypt
//Init AES
SymmetricAlgorithm crypt = Aes.Create();
//Init md5 hash
HashAlgorithm hash = MD5.Create();
//AES blocksize (AES 192 etc.) (min 128)
crypt.BlockSize = BlockSize;
//Generating Key
crypt.Key = hash.ComputeHash(Encoding.Unicode.GetBytes(passwd));
//Initialize Vectors
crypt.IV = IV;
//CryptoStram is used for encryption
//The required Encryptor is based on the algorithm above
//Cryptostream sends data of the encrypted byte array to Memorystream
//The memory stream is then converted into a Base64 string and made readable
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream =
new CryptoStream(memoryStream, crypt.CreateEncryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(bytes, 0, bytes.Length);
}
encryptedcontent = Convert.ToBase64String(memoryStream.ToArray());
}
}
static void Main(string[] args)
{
//Set Password
Console.WriteLine("Passwort angeben");
Console.Write("> ");
passwd = Console.ReadLine();
//Set content to encrypt (String)
Console.WriteLine("Zu verschlüsselner Text angeben");
Console.Write("> ");
content = Console.ReadLine();
Encrypt();
Console.WriteLine(encryptedcontent);
Console.ReadLine();
}
}
}
Subsequently I wanted to try the programm with some testdata.
I actually got a seemingly encrypted string.
PW: supersecretpassword Content: I like to keep my secrets Result: SEEc1sLMIyfVFsoHPFRIcl437+yjUC5uFMgco3iO+oWSgJWQOwKhoDhUbFJREeqiIvaY2DBR+Ih4OJeGAc6JZQ==
I tried to use some online tools to decrypt and check my result.
Sadly most of the Webtools were not able to decrypt my result.
And if I encrypt the sentence I like to keep my secrets with that online tools I get results like:
7IWuebm0T8HdrGdtkBjt5zgjbdEqYfidNZVvfgtOjH4=
My result SEEc1sLMIyfVFsoHPFRIcl437+yjUC5uFMgco3iO+oWSgJWQOwKhoDhUbFJREeqiIvaY2DBR+Ih4OJeGAc6JZQ==
As you can see, the two results are different.
Unfortunately I have no idea why this could be the case.
Thanks for you help
Jonas
P.S Somehow I deleted some of rows written in this question. I hope the new words can clarify what my problem is.
You don't say what online tools did, or did not, succeed in replicating your results, so this is a general answer, instead of specific.
//AES blocksize (AES 192 etc.) (min 128)
crypt.BlockSize = BlockSize;
The BlockSize of AES is 128. Always (contrast with the original algorithm, Rijndael, which allows the BlockSize to change).
AES-128/AES-192/AES-256 are about the KeySize, not the BlockSize.
crypt.Key = hash.ComputeHash(Encoding.Unicode.GetBytes(passwd));
You're using MD5(UTF16(password)) as your Key Deriviation Function (KDF). Maybe you can find an online sample that is using this, but they're more likely to be using MD5(UTF8(password)) (which would come from Encoding.UTF8, vs Encoding.Unicode). A better answer would be to use a proper password-based Key Derivation Function, like PBKDF2 (which is called Rfc2898DeriveBytes in .NET for... reasons).
[When I encrypt I like to keep my secrets I get an answer that is twice as long as online tools.]
You're encrypting the UTF-16 representation of that string. The string is comprised of 25 Unicode codepoint values, all from the US-ASCII range. Therefore the UTF-16 representation is just the codepoint length * 2 (50 bytes).
50 bytes breaks down into 3 16-byte (128-bit) blocks, plus 2 bytes left over. Add padding, that becomes 4 blocks of AES-CBC-PKCS#7 output (64 bytes). 64 bytes converts to Base64 as 21 full values (of 3 bytes -> 4 chars) with 1 byte remaining, so the Base64 value ends in 2 = padding characters with a total length of 88 characters. This matches your description, hooray :).
If, on the other hand, you used the UTF-8 encoding, you'd have 25 bytes into encryption, which becomes 2 blocks of output (32 bytes) which turns into 10 full base64 conversions with 2 bytes remaining, so one = at a total of 44 characters... which looks a lot like what the online tools are using.
You also should produce a new IV for every time you encrypt with the same key. The IV isn't a key, but changing the IV causes the same secret input to get encrypted differently, so someone who can see your encrypted data can't tell that you sent the same message that you just sent. (At least, that's the purpose in CBC block mode, in other block modes it has sometimes more important purposes). The IV can be transmitted with the message... in fact it should be, unless you have some other way of both sides agreeing (without hard-coding it).
And, of course, you should dispose all of your disposable objects. Changing your encoding to UTF-8, but not changing your KDF, would better be
private static string Encrypt(string content, string password)
{
byte[] bytes = Encoding.UTF8.GetBytes(content);
using (SymmetricAlgorithm crypt = Aes.Create())
using (HashAlgorithm hash = MD5.Create())
using (MemoryStream memoryStream = new MemoryStream())
{
crypt.Key = hash.ComputeHash(Encoding.UTF8.GetBytes(password));
// This is really only needed before you call CreateEncryptor the second time,
// since it starts out random. But it's here just to show it exists.
crypt.GenerateIV();
using (CryptoStream cryptoStream = new CryptoStream(
memoryStream, crypt.CreateEncryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(bytes, 0, bytes.Length);
}
string base64IV = Convert.ToBase64String(crypt.IV);
string base64Ciphertext = Convert.ToBase64String(memoryStream.ToArray());
return base64IV + "!" + base64Ciphertext;
}
}
Some issues that I see is a self defined IV and odd blocksize, edit: and you probably have the wrong value for the password in mind when comparing to online tools where you do have to fill in the password as calculated by the ComputeHash function.
Check out this simple MSDN Example

Porting PHP decryption AES-256-CBC to C#

i have the following PHP decryption routine which is working flawlessly and need help converting it to c#. i have tried many approaches but none of them is working.
i have managed to match the hash function output between c# and php.
also matched the output of the conversion from and to base64.
PHP Code:
function decrypt($encrypted_txt, $secret_key, $secret_iv)
{
$encrypt_method = "AES-256-CBC";
// hash
$key = hash('sha256', $secret_key);
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
$output = openssl_decrypt(base64_decode($encrypted_txt), $encrypt_method, $key, 0, $iv);
return $output;
}
secret_key= "t-3zafRa";
secret_key_hash = "d03a4d94b29e7f55c80726f1152dcebc9f03f4c698470f72083af967cf786b6b";
the problem is that the key hash is a 64 bytes which is invalid for the AES-256 but i am not sure how it's working in php and how the openssl_decrypt php function is dealing with the keys.
i have also tried to pass the MD5 of the key hash but also failed to decrypt.
byte[] asciiBytes = ASCIIEncoding.ASCII.GetBytes(keyhash);
byte[] hashedBytes = MD5CryptoServiceProvider.Create().ComputeHash(asciiBytes);
string keymd5 = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); //To match with PHP MD5 output
C# Hashing function:
static string sha256(string randomString)
{
var crypt = new System.Security.Cryptography.SHA256Managed();
var hash = new System.Text.StringBuilder();
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
foreach (byte theByte in crypto)
{
hash.Append(theByte.ToString("x2"));
}
return hash.ToString();
}
C# Decryption routine:
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;
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None, 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();
}
}
}
return plaintext;
}
any help or ideas are highly appreciated.
openssl_decrypt simply takes as many bytes for the key as required for the algorithm. As your algorithm is "AES-256-CBC" is uses 32 bytes (256 bits), as AES-256 is defined to as AES with a 256 bit key (and 14 rounds, rather than 10 or 12).
The way PHP does this is either by adding 00 valued bytes to the right in case the key is too small, or - as in your case - by simply ignoring the bytes after the 32st one. That's not a good way to behave of any kind of cryptographic library, especially for a high level language like PHP, but the OpenSSL wrapper library does it anyway.
So you have to extract the first 32 bytes from the hex encoded key and use that as key in C# to be compatible. Using different hash functions is of course not going to work, MD5 and SHA-256 are entirely incompatible (by design). Of course, you now have 16 hex encoded bytes left, which means you are using AES-256 with 128 bit keys, leaving you with 128 bit security. And yes, you need to use PKCS#7 padding in C#.
Note that using CBC with a static IV is not secure. Using CBC mode for transport mode security is not secure. Using SHA-256 or any normal hash over a password or key with little entropy is not secure. Storing keys in strings is generally not secure.
Getting crypto working is hard enough; getting it secure is much harder and requires understanding what the heck you're doing in the first place. You need to start off with a good protocol for your specific use case (and that's skipping a few steps already).

File encryption in C# with AES, decryption with phpseclib

I am working on a project for secure file transfer which encrypts files using c# client on the customer side. i need to decrypt the files on server side using php and maybe phpseclib. The code here i copied from a msdn example. But i cant work out the decrypt function in php.
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = passwordBytes;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.KeySize = 256;
AES.BlockSize = 256;
AES.Mode = CipherMode.CBC;
AES.Padding = PaddingMode.Zeros;
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
using (CryptoStream cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
This is the php code which doesnt work:
$pw = "this_is_my_pw";
$aes = new Crypt_AES(CRYPT_AES_MODE_CBC);
$aes->setKey($pw);
$aes->setKeyLength(256);
$aes->disablePadding();
$file = "enc.txt";
$fh = fopen($file, "r");
$contents = trim(fread($fh, filesize($file)));
fclose($fh);
//echo "Encoded: \n\n" . $contents;
$contents = $aes->decrypt($contents);
#$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
#$padding = $block - (strlen($clear) % $block);
#$dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $pw, base64_decode($contents), MCRYPT_MODE_CBC, $pw);
echo "Decoded: \n\n" . $contents;
Can someone help me fixing this or give me a hint what i do wrong?
No initialization vector used when decrypting. You need to send the initialization vector (IV) along with the data - your PHP code is never calling $aes->setIV from phpseclib, so it will never be able to decrypt the text because phpseclib uses an IV of all zeros if one is not set according to the docs. I would personally recommend generating a secure random IV from C# using RijndaelManaged.GenerateIV, but apparently it's considered acceptable to derive the IV from a PBKDF2 key. PBKDF2 (specified in RFC 2898) is the key-stretching algorithm Rfc2898DeriveBytes implements. Regardless, you need to re-produce the IV on the PHP side, whether that means transmitting the IV with the encrypted data (which is completely fine) or re-deriving the IV on the PHP side.
Using the password as the salt is a REALLY BAD IDEA. The salt needs to be of sufficient length and cryptographically randomly generated. Using the password as the salt completely defeats the point of having a salt. MSDN has some sample code that shows how to generate a cryptographically random salt in conjunction with using Rfc2898DeriveBytes, but the important part is here:
byte[] saltBytes = new byte[8];
using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with a random value.
rngCsp.GetBytes(salt1);
}
The salt must be transmitted with the encrypted data. You need to send the PBKDF2 salt bytes along with the IV bytes and encrypted data. phpseclib will need all of those to properly initialize itself and decrypt the data. You'll probably want to use phpseclib's setPassword to do this, like so:
$salt = ...; // get the salt to your PHP code somehow
$iv = ...; // get the IV to your PHP code
$pw = "this_is_my_pw";
$aes = new Crypt_AES(CRYPT_AES_MODE_CBC);
$aes->setPassword($pw, 'pbkdf2' /* key extension algorithm */,
'sha1' /* hash algorithm */, $salt /* generated salt from C# */,
1000 /* number of iterations, must be same as C# code */,
256 / 8 /* key size in bytes, 256 bit key / 8 bits per byte */
);
$aes->setIV($iv);
Keep the other answers in mind about blocksize. 128 bits is the standard AES blocksize, so make sure both C# and phpseclib can function correctly with a larger blocksize, or just use the AES standard for both.
If you are trying to use AES set the block size to 128-bits, that is the only block size that is supported. Using a different block size means you are using Rijndael encryption which is not well supported cross platform.
AES supports multiple key sizes of 128, 192 and 256 bits. Sometimes there is confusion when using a Rijndael implementation to use AES encryption.
In the Java code I see AES.BlockSize = 256;. Technically, AES has a fixed block size of 128 bits. Rijndael supports variable block sizes but AES doesn't. If you want to make use of variable block sizes in PHP with phpseclib you'd need to do this:
$pw = "this_is_my_pw";
$aes = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CBC);
$aes->setKey($pw);
$aes->setKeyLength(256);
$aes->setBlockLength(256);
$aes->disablePadding();
Also, your key is 13 bytes long. AES keys need to be either 16 bytes (128 bits) long, 24 bytes (192 bits) long or 32 bytes (256 bits) long. idk what js lib you're using but phpseclib 1.0/2.0 null pads keys if they're not long enough. The newest version of phpseclib - currently under development - throws exceptions.
Or maybe you mean to be using a password based key derivation function? phpseclib provides two that can be utilized via setPassword() but if that were the case you'd need to know what method and parameters were being utilized by the js lib.

Is this wrapper around AesManaged ok?

I need to encrypt/decrypt some strings. I've build my wrapper class according to the msdn documentation but with some changes.
Since I want to encrypt/decrypt data with a given string/passphrase, I don't use AesManaged for creating a key. (The user should be able to encrypt/decrypt with a key he enters, and therefore I cannot use the key from AesManaged and I cannot save the key).
I instead create the key by using Rfc2898DeriveBytes (PBKDF2) with a given salt. The given salt is used since I do not store the key and I think because of this, the salt must be always the same.
I then create an IV, encrypt the given string and concatenate the IV and the encrypted string. This will then eventually got saved in a file. This means the IV gets save together with the encrypted data.
Questions:
Is it ok to store the IV together with the encrypted data?
Is there another way to create the key without using the same salt everytime(Based on a given passphrase)?
Is this encryption done using AES128 or AES256?
Will the IV be always 16 bytes, or can this change?
static void Main(string[] args)
{
const string stringToEncrypt = "String to be encrypted/decrypted. Encryption is done via AesManaged";
const string password = "m1Sup3rS3cre!Password";
string encrypted = EncryptString(stringToEncrypt, password);
string roundtrip = DecryptStringFromBytes_Aes(encrypted, password);
Console.WriteLine("Original: {0}", stringToEncrypt);
Console.WriteLine("Round Trip: {0}", roundtrip);
Console.ReadLine();
}
static string EncryptString(string plainText, string password)
{
string encryptedString;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = PasswordAsByte(password);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
var encrypted = msEncrypt.ToArray();
encryptedString = Encoding.Default.GetString(aesAlg.IV);
encryptedString += Encoding.Default.GetString(encrypted);
}
}
}
return encryptedString;
}
static string DecryptStringFromBytes_Aes(string cipherText, string password)
{
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = PasswordAsByte(password);
aesAlg.IV = Encoding.Default.GetBytes(cipherText).Take(16).ToArray();
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
var encryptedByteArray = Encoding.Default.GetBytes(cipherText).Skip(16).ToArray();
using (MemoryStream msDecrypt = new MemoryStream(encryptedByteArray))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
}
}
private static byte[] PasswordAsByte(string password)
{
byte[] salt = Encoding.Default.GetBytes("foobar42");
Rfc2898DeriveBytes passwordBytes = new Rfc2898DeriveBytes(password, salt);
return passwordBytes.GetBytes(32);
}
No, this is not okay.
1) You're using Encoding.Default in various places. Don't do that - it means you're at the whim of the platform you're on. Always use an explicit encoding, ideally UTF-8 in most cases.
2) You're using Encoding.GetString / Encoding.GetBytes to convert arbitrary binary data to a string and back. That's almost bound to lose data. (It happened to succeed on my machine, but it really depends on the encoding - and it's fundamentally a bad idea.) Encoding is designed for data which is inherently text data, and you're just applying an encoding one way or the other. Your encrypted data is inherently binary data. Use Convert.ToBase64String and Convert.FromBase64String instead.
For your other questions:
Yes, it's okay to store the IV with the encrypted data, as far as I know.
You could use the same approach for the password: generate a different salt each time, and store that with the encrypted text. Not sure whether that's generally recommended or not, I'm afraid.
I believe you're controlling whether the key size is 128 or 256 bits, with your call to passwordBytes.GetBytes(32) - that's a 256-bit key, so it's AES256.
I believe the IV size for AES is always 16 bytes (128 bits)
Normally salt is used together with cryptographic hashing of say passwords to protect against dictionary attacks. To get the same kind of protection for symmetric encryption with AES you should use a random initialization vector. So when you encrypt create a random IV and prepend it to the message (in cleartext). When you decrypt get the IV from the encrypted message and use it to decrypt the message. Then the ciphertext of the same message encrypted with the same key will be different.
So, yes, it is OK to store the IV together with the encrypted data.
You do not need a different salt every time because the purpose of the random IV is similar in how salt makes dictionary attacks on hashes harder.
AES can use key sizes of 128, 192 or 256 bits so to use AES 256 you need a 256 bit key (32 bytes) which is what you use.
AES uses a 128 bit block which requires a 128 bit IV (or 16 bytes).
Is it ok to store the IV together with the encrypted data?
Yes, it is ok. Moreover, you're using AesManaged without explicit setting of Mode - it this case mode is CBC, and in CBC mode IV should preceed cyphertext.
Is there another way to create the key without using the same salt everytime(Based on a given passphrase)?
Rfc2898DeriveBytes is pretty standard way to derive key from text password. There is no need to reinvent way of deriving key from password, just use Rfc2898DeriveBytes as you're doing it now.
Is this encryption done using AES128 or AES256?
It is AES256 since you're using 32-byte password.
Will the IV be always 16byte, or can this change?
The size of the IV property must be the same as the BlockSize property divided by 8. So it is 16 for 128-bit blocks.

C# AES 256-Bit Decrypt given Encrypted Text and Secret

Somebody asked me how I would decrypt a given AES 256-bit encrypted string if I knew the secret key. I'm not very familiar with encryption, so I sat down to look into the problem.
I found this example on MSDN, and tried to modify it to do only the Decrypt:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
internal class AesExample
{
public static void Main()
{
var encryptedString = "U2FsdGVkX1/cHT8XuHCfpw0AV4jpaO8JfLqUeCRJqjY=";
var secret = "SPARKY";
// I know this is not the correct way to get my input byte arrays...
// Just illustrating that I DO need byte arrays.
var encryptedBytes = Encoding.UTF8.GetBytes(encryptedString);
var secretBytes = Encoding.UTF8.GetBytes(secret);
try
{
using (var aes = new AesManaged())
{
aes.Key = secretBytes;
// Decrypt the bytes to a string.
var decryptedString = Decrypt(encryptedBytes, aes.Key, aes.IV);
//Display the original data and the decrypted data.
Console.WriteLine("Encrypted: {0}", encryptedString);
Console.WriteLine("Decrypted: {0}", decryptedString);
}
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
private static string Decrypt(byte[] cipherText, byte[] key, byte[] iv)
{
// Declare the string used to hold
// the decrypted text.
string plaintext;
// Create an AesManaged object
// with the specified key and IV.
using (var aes = new AesManaged())
{
aes.Key = key;
aes.IV = iv;
// Create a decrytor to perform the stream transform.
var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
// Create the streams used for decryption.
using (var msDecrypt = new MemoryStream(cipherText))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
Of course as soon as I hit the following line, a CryptographicExcetion is thrown with the message "Specified key is not a valid size for this algorithm."
==> aes.Key = secretBytes
Someone suggested taking a SHA1 hash of the secret and trimming that to 20 byes. I tried that, and I started getting a new CryptographicException with the message "Length of the data to decrypt is invalid."
So, I have a few questions:
1) Is this even possible given only the encrypted text and secret key?
2) If so, are them some base assumptions one would need to make, like the CipherMode? I was reading that the ECB mode doesn't have a initialization vector. That's why I ask.
3) What would I need to do to put the inputs (encrypted text and secret key) into the correct Byte[] format for the decryption to work?
Thanks!
You probably need more information to make this work. To answer your specific questions:
Yes, except that you don't have the secret key. "SPARKY" is not a valid AES key, as DavidH mentions, though passwords are routinely used to derive secret keys through what are called key derivation functions. You could try running your password through Rfc2898DeriveBytes (a popular KDF in .NET) to derive different AES keys that might work, but it too takes parameters that you apparently don't have. You could also try various SHA hash digests of your password, though again 20 bytes is not a valid AES key - you need a 16, 24 or 32 byte key.
If you don't have an IV, then yes, you'll have to assume the encryption uses ECB. (But note that in general you should never use ECB mode.)
Your encrypted string appears to be encoded using base64. Converting it to a byte array is simple enough in .NET using Convert.FromBase64String(encryptedString);.
This sounds like a fun exercise, but you're probably just going to end up frustrated without a bit more information.
AES key lengths are 128, 192, and 256 bit depending on the cipher you want to use. You must ensure that your string is the appropriate length of bytes.

Categories

Resources