Using Aes Encryption in C#

AES Encryption using C#

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;
}
}

C#.net to Java- Encryption and decryption using AES with Password

Rfc2898DeriveBytes implements PBKDF2, and RijndaelManaged with a block size of 128 bits implements AES. Both seem to be applied correctly in the Java code.

However, there are differences in determining the IV and regarding concatenation: In the C# code, the salt and IV are determined randomly and concatenated with the ciphertext at the end.

In the Java code only the salt is determined randomly, the IV is derived together with the key and the concatenation is missing.

I.e. the encrypt() method in the Java code could be changed for instance as follows, so that a decryption with the C# code is possible:

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

...

byte[] salt = Generate128BitsOfRandomEntropy();
byte[] iv = Generate128BitsOfRandomEntropy();

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, 1000, 128);
SecretKey secretKey = factory.generateSecret(pbeKeySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] ciphertext = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

byte[] saltIvCiphertext = ByteBuffer.allocate(salt.length + iv.length + ciphertext.length).put(salt).put(iv).put(ciphertext).array();
return Base64.getEncoder().encodeToString(saltIvCiphertext);

Note that for PBKDF2, an iteration count of 1000 is generally too low.

How to automate encryption using AES

One option could be to use public-key cryptography. I.e. you would generate a public and private key-pair. The encryption program would generate a random AES key. This key would be encrypted with the public key and stored as part of the data. The rest of the data can then be encrypted using the AES key. The encryption program only need access to the public key, and would have no way for the to decrypt any data once the AES key is overwritten in memory.

The decryption program would work in reverse, using the private key to first decrypt the AES key, and then use said key to decrypt the rest of the data. An potential problem with this process would be to ensure that the private key is accessible in case of a failure. So I would highly recommend testing the restoration process.

See Cryptographic services for more information. Also consider that if an attacker can access your backup server, he likely also has access to the live data. So a backup encryption might have more value if the backups are stored less securely than your live data.

How to symmetrically encrypt & decrypt some data in C# using AES?

I would take a look at the example in the documentation and compare to your code.

Notably when decrypting you are using aes.CreateEncryptor(key, iv). It should probably be aes.CreateDecryptor(key, iv).

The example from the docs also inputs the key and IV when calling CreateEncryptor, but I'm not sure if that is required or not.

You should probably not use sha256 to generate the key from a password. The correct way would be a key derivation algorithm. For example Rfc2898DeriveBytes

C# Example of AES256 encryption using System.Security.Cryptography.Aes

Once I'd discovered all the information of how my client was handling the encryption/decryption at their end it was straight forward using the AesManaged example suggested by dtb.

The finally implemented code started like this:

    try
{
// Create a new instance of the AesManaged class. This generates a new key and initialization vector (IV).
AesManaged myAes = new AesManaged();

// Override the cipher mode, key and IV
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 }; // CRB mode uses an empty IV
myAes.Key = CipherKey; // Byte array representing the key
myAes.Padding = PaddingMode.None;

// Create a encryption object to perform the stream transform.
ICryptoTransform encryptor = myAes.CreateEncryptor();

// TODO: perform the encryption / decryption as required...

}
catch (Exception ex)
{
// TODO: Log the error
throw ex;
}

Encrypting And Decrypting a string using Aes Encryption - C#

In DecryptString method, you forgot to pass cipher parameter to constructor of msDecrypt memory stream as an input, thus method actually deciphers empty input stream, so result is empty too.

Line

using (MemoryStream msDecrypt = new MemoryStream())

should actually be:

using (MemoryStream msDecrypt = new MemoryStream(cipher))

and then everything works fine.



Related Topics



Leave a reply



Submit