Aes Encryption in Java and Decryption in C#

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.

Encrypt in java and Decrypt in C# For AES 256 bit

I believe the blockSize should be 128 and the keysize be 256. The keyStr should be 32 characters long and the IVstr should be 16 characters long. This may help as it describes why 128 bits have to be used for block size and what the key sizes can be. csrc.nist.gov/publications/fips/fips197/fips-197.pdf

You have this in the decrypt method.

    aesEncryption.Padding = PaddingMode.None; 

I believe you need to put that in the encrypt method also.

Also why not use this method for the key and IV.

    aes.Key = ASCIIEncoding.ASCII.GetBytes(keyStr); 
aes.IV = ASCIIEncoding.ASCII.GetBytes(ivStr);

AES decrypt text in java, encrypted in C#

Here is Java code to decode encoded data from C#:

public static void main(String[] args) throws Exception {

String encText = "EAAAADE2ODA2NjQya2JNN2M1ISShgi+Oi5tbsPgOz5KsCHj0";
final String password = "KJH#$@kds32@!kjhdkftt";
byte[] salt = new byte[] { 34, (byte) 134, (byte) 145, 12, 7, 6, (byte) 243, 63, 43, 54, 75, 65, 53, 2, 34, 54,
45, 67, 64, 64, 32, (byte) 213 };

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256);
SecretKey tmp = factory.generateSecret(spec);

SecretKeySpec secret = new SecretKeySpec(tmp.getEncoded(), "AES");

byte[] data = Base64.getDecoder().decode("EAAAADE2ODA2NjQya2JNN2M1ISShgi+Oi5tbsPgOz5KsCHj0");
// skip first 4 bytes (the length of IV) and get IV byte array
byte[] iv = Arrays.copyOfRange(data, 4, 20);

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
// skip IV length (4 bytes) and IV (16 bytes)
cipher.update(data, 20, data.length - 20);
String plaintext = new String(cipher.doFinal(), "UTF-8");
System.out.println(plaintext);
}

Below is simplified example, just in case if someone has access to both sources:

C#:

private static byte[] salt = new byte[] { 34, 134, 145, 12, 7, 6, 243, 63, 43, 54, 75, 65, 53, 2, 34, 54, 45, 67, 64, 64, 32, 213 };
private static byte[] vector = Encoding.ASCII.GetBytes("16806642kbM7c5!$");
private static string cleartext = "8212093345";

static void Main(string[] args)
{
var kdf = new Rfc2898DeriveBytes("KJH#$@kds32@!kjhdkftt", salt);
using (var aes = new RijndaelManaged())
{
aes.Key = kdf.GetBytes(aes.KeySize / 8);
aes.IV = vector;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
// don't use StreamWriter, it just makes things more complicated
var bytes = Encoding.ASCII.GetBytes(cleartext);
cs.Write(bytes, 0, bytes.Length);
}
Console.WriteLine(Convert.ToBase64String(ms.ToArray()));
// outputs: oYIvjoubW7D4Ds+SrAh49A==
}
}
}

Java:

public static void main(String[] args) throws Exception {

String encText = "EAAAADE2ODA2NjQya2JNN2M1ISShgi+Oi5tbsPgOz5KsCHj0";
final String password = "KJH#$@kds32@!kjhdkftt";
final String iv = "16806642kbM7c5!$";
byte[] salt = new byte[] { 34, (byte) 134, (byte) 145, 12, 7, 6, (byte) 243, 63, 43, 54, 75, 65, 53, 2, 34, 54,
45, 67, 64, 64, 32, (byte) 213 };

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256);
SecretKey tmp = factory.generateSecret(spec);

SecretKeySpec secret = new SecretKeySpec(tmp.getEncoded(), "AES");
IvParameterSpec ivs = new IvParameterSpec(iv.getBytes(StandardCharsets.US_ASCII));

// your code use PKCS7, but I use PKCS5 because it shows exception in my case
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, ivs);

// base64 string from C# output
String plaintext = new String(cipher.doFinal(Base64.getDecoder().decode("oYIvjoubW7D4Ds+SrAh49A==")), "UTF-8");
System.out.println(plaintext);
}

AES Encrypt in C# (Help) and Decrypt in Java (Done)

See answers to this question: Using AES encryption in C#

C# AES encrypt to java 8 decrypt

To test any C# encryption I added a decryption method to your Java code and run successfully a full round (encryption and decryption).

For the C# part I was too lazy to check your code (as @Topaco did it) and used my own code with your credentials to get an output that you can present to the Java decryption method.

Let's start with a longer Security warning: the codes are using a static initialization vector and a static salt and the iteration count
for PBKDF2 key derivation is much too low (a minimum of 10.000 should be used)
. The codes do not have any exception handling and are for educational purpose only.

Running the C#-code gives a short output:

AES CBC 256 string encryption with PBKDF2 SHA1 key derivation
plaintext: The quick brown fox jumps over the lazy dog
ciphertext: 5HMLSQKEgG+RADgPmf5Eyw0F/GG9sXFuWiHeuZxgpmJP+UoH4MZlvnQDrgnofQy4

Presenting the ciphertext to the Java decryption will give this output:

C# AES encrypt to java 8 decrypt
plaintext: The quick brown fox jumps over the lazy dog
ciphertext: 5HMLSQKEgG+RADgPmf5Eyw0F/GG9sXFuWiHeuZxgpmJP+UoH4MZlvnQDrgnofQy4
decryptedtext: The quick brown fox jumps over the lazy dog

decryption of a ciphertext from C#
ciphertextFromCsharp: 5HMLSQKEgG+RADgPmf5Eyw0F/GG9sXFuWiHeuZxgpmJP+UoH4MZlvnQDrgnofQy4
decryptedtextFromCsharp: The quick brown fox jumps over the lazy dog

Both codes are available for a live self test here (Java: https://repl.it/@javacrypto/JavaAes256EncryptionWithPBKDF2SHA1keyderivation, C#: https://repl.it/@javacrypto/CsharpAes256Pbkdf2Encryption#main.cs).

C#-code:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public class Program {
public static void Main() {
Console.WriteLine("AES CBC 256 string encryption with PBKDF2 SHA1 key derivation");
// credentials
string plaintext = "The quick brown fox jumps over the lazy dog";
string password = "myPassword";
string saltString = "salt_sixteen1234";
var iterationsCount = 100;
string ivString = "sixteen_value_12";

Encoding enc = Encoding.UTF8;
byte[] saltBytes = enc.GetBytes(saltString);
byte[] iv = enc.GetBytes(ivString);
byte[] key;

try {
// pbkdf2 sha1 key derivation
using (var pbkdf2 = new Rfc2898DeriveBytes(
password,
saltBytes,
iterationsCount,
HashAlgorithmName.SHA1))
{
key = pbkdf2.GetBytes(32);
}
Console.WriteLine("plaintext: {0}", plaintext);
string ciphertext = encrypt(key, iv, plaintext);
Console.WriteLine("ciphertext: {0}", ciphertext);
}
catch(Exception e) {
Console.WriteLine("Error: {0}", e.Message);
}
}

static string encrypt(byte[] key, byte[] IV, string data) {
byte[] encrypted;
using(Aes aesAlg = Aes.Create()) {
aesAlg.Key = key;
aesAlg.IV = IV;
aesAlg.Mode = CipherMode.CBC;
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// create the streams used for encryption.
using(var msEncrypt = new MemoryStream()) {
using(var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
using(var swEncrypt = new StreamWriter(csEncrypt)) {
//Write all data to the stream.
swEncrypt.Write(data);
}
encrypted = msEncrypt.ToArray();
}
}
}
return Convert.ToBase64String(encrypted);
}
}

Java-code:

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;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Main {
public static void main(String[] args) throws Exception {
System.out.println("C# AES encrypt to java 8 decrypt");

String plaintext = "The quick brown fox jumps over the lazy dog";
String password = "myPassword";
String iv = "sixteen_value_12";
String salt = "salt_sixteen1234";
int iterations = 100;
int keySize = 256;
System.out.println("plaintext: " + plaintext);
String ciphertext = AES_encrypt(plaintext, password, salt, iv, iterations, keySize);
System.out.println("ciphertext: " + ciphertext);
String decryptedtext = AES_decrypt(ciphertext, password, salt, iv, iterations, keySize);
System.out.println("decryptedtext: " + decryptedtext);
System.out.println("\ndecryption of a ciphertext from C#");
String ciphertextFromCsharp = "5HMLSQKEgG+RADgPmf5Eyw0F/GG9sXFuWiHeuZxgpmJP+UoH4MZlvnQDrgnofQy4";
System.out.println("ciphertextFromCsharp: " + ciphertextFromCsharp);
String decryptedtextFromCsharp = AES_decrypt(ciphertextFromCsharp, password, salt, iv, iterations, keySize);
System.out.println("decryptedtextFromCsharp: " + decryptedtextFromCsharp);
}

private static Cipher generateCipher(int mode, String password, String salt, String iv, Integer iterations, Integer keySize) throws Exception {
byte[] saltBytes = salt.getBytes("UTF-8"); byte[]ivBytes = iv.getBytes("UTF-8");
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, iterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//encrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(mode, secret, new IvParameterSpec(ivBytes));
return cipher;
}

public static String AES_encrypt(String plainText, String password, String salt, String iv, Integer iterations, Integer keySize) throws Exception {
Cipher cipher = generateCipher(Cipher.ENCRYPT_MODE, password, salt, iv, iterations, keySize);
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(encryptedTextBytes);
}

public static String AES_decrypt(String cipherText, String password, String salt, String iv, Integer iterations, Integer keySize) throws Exception {
Base64.Decoder decoder = Base64.getDecoder();
Cipher cipher = generateCipher(Cipher.DECRYPT_MODE, password, salt, iv, iterations, keySize);
return new String(cipher.doFinal(decoder.decode(cipherText)), StandardCharsets.UTF_8);
}
}

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

The two methods:

public static byte[] EncryptionToBytes(string str)
{
using (var aes = new AesManaged())
{
aes.Key = new byte[] { 0x72, 0x8f, 0xaf, 0x34, 0xb6, 0x4c, 0xd5, 0x5c, 0x8d, 0x1d, 0x50, 0x02, 0x68, 0x02, 0x6f, 0xfb };
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;

using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] data = Encoding.UTF8.GetBytes(str);
cs.Write(data, 0, data.Length);
}

byte[] encrypted = ms.ToArray();
return encrypted;
}
}
}

public static string DecryptionFromBytes(byte[] encrypted)
{
using (var aes = new AesManaged())
{
aes.Key = new byte[] { 0x72, 0x8f, 0xaf, 0x34, 0xb6, 0x4c, 0xd5, 0x5c, 0x8d, 0x1d, 0x50, 0x02, 0x68, 0x02, 0x6f, 0xfb };
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;

using (var ms2 = new MemoryStream())
{
using (var ms = new MemoryStream(encrypted))
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
cs.CopyTo(ms2);
}

byte[] decrypted = ms2.ToArray();
return Encoding.UTF8.GetString(decrypted);
}
}
}

Note that

System.out.println("Encrypted: " + encrypted);

won't print the byte[], but will print [, followed by a character representing the type of the array's elements (in your case C for char), followed by @ then the "identity hash code" of the array (think of it like you would a "memory address").

Java seems to use (haven't looked for documentation) AES with mode ECB, padding PKCS7 (this confirms my observation)

Note that String.getBytes() encodes using the "default" encoding, that could be different in different computers. Better to use getBytes("UTF-8") and new String(bytes, "UTF-8") (see https://stackoverflow.com/a/5729823/613130). In the code I wrote I used UTF8. You can simulate the Java code by using Encoding.Default.

Encrypt in C# and Decrypt in Java with same secret key

The problem is that, in your Java code, you're only using a 192-bit key, while in the C# version, you're using a 256-bit key.

Your base64-encoded key is 32 characters, which translates to 24 bytes, which is only 192 bits.

These are the offending lines in Java:

public static String key = "Aes Encryption in Java and Decryption in C#bbccccbbAes Encryption in Java and Decryption in C#bbccccbb"; // 32 characters
private static byte[] key_Array = Base64.decode(key, Base64.DEFAULT); // 24 bytes

Just changing the creation of your Java key array will fix things. Something like:

public static String key = "Aes Encryption in Java and Decryption in C#bbccccbbAes Encryption in Java and Decryption in C#bbccccbb"; // 32 characters
private static byte[] key_Array = new byte[32]; // 32 bytes

static {
// copy the 24 base64-decoded bytes to the key array
System.arraycopy(Base64.decode(key, Base64.DEFAULT), 0, key_Array, 0, 24);
}


Related Topics



Leave a reply



Submit