1

My backend server is based on .NET. On the server there is use Rfc2898DeriveBytes encryption

This is the code of .Net

public static string Encrypt(string clearText)
    {
        string EncryptionKey = "abc123";
        byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                clearText = Convert.ToBase64String(ms.ToArray());
            }
        }
        return clearText;
    }

I am writing the client in JAVA. This is the code

try {
        String encryptKey = "abc123";
        byte[] salt = new byte[]{0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(encryptKey.toCharArray(), salt, 1024, 128);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(tmp.getEncoded(), "AES");
        System.out.println("Key:" + Base64.encodeToString(secret.getEncoded(), Base64.DEFAULT));


        String cleartext = "12345";
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        AlgorithmParameters params = cipher.getParameters();
        byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
        byte[] ciphertext = cipher.doFinal(cleartext.getBytes("UTF-8"));
        System.out.println("IV:" + Base64.encodeToString(iv, Base64.DEFAULT));
        System.out.println("Cipher text:" + Base64.encodeToString(ciphertext, Base64.DEFAULT));;
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (InvalidParameterSpecException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

I do not get the same result in java as of .Net. The encrypted value for 12345 is dAQWIrbtHv/eDbu+4oJD0g== on server.

While I get tcvGLK5r99jt6PFLALpRfQ==

What is the fix I need to apply?

WISHY
  • 11,067
  • 25
  • 105
  • 197
  • You need to get the IV from PBKDF2 in Java, but you're always generating a random IV. – Artjom B. Mar 02 '17 at 06:34
  • @ArtjomB. can you share the code if possible? – WISHY Mar 02 '17 at 06:40
  • If you wrote all that code then you can probably make the changes too? Or did you just copy and paste your security code? – Luke Joshua Park Mar 02 '17 at 21:11
  • @LukePark I did copied the code from this website - https://steelmon.wordpress.com/2013/07/01/simple-interoperable-encryption-in-java-and-net/ – WISHY Mar 03 '17 at 06:30
  • @WISHY did u find any solution? – Android dev Jul 17 '18 at 05:03
  • @Androiddev i was able to solve it with some few adjustments also on the .Net code. Have pasted my solution below. – WISHY Jul 19 '18 at 15:56
  • I tried getting keyBytes and ivBytes from java but it returns negative values in byte array, no idea why. – arviman Sep 11 '18 at 08:26
  • @arviman are using the answer which I had shared below or something else? I have also updated my answer if you may have problem with Base64. – WISHY Sep 17 '18 at 12:36
  • I was just being an idiot @WISHY. I was verifying the output with the corresponding c# version where byte is unsigned, so I did have the correct answer in Scala (JVM) as well except byte is signed – arviman Sep 18 '18 at 05:32

2 Answers2

4

The default iteration count for Rfc2898DeriveBytes is 1000, not 1024 (per the source).

And I don't know if the keyLength value for PBEKeySpec is in bytes, or bits, but if it's bits you've asked for 128 in Java and 256 (32-bytes) in C#.

Well, actually, you've asked for 384 bits in C#. Because the first 256 become your cipher key, then the next 128 become your IV (which you seem to let get randomly generated in Java).

So, you probably need to ask for 384 bits, call getEncoded(), split the answer into a 32-byte key and a 16-byte IV, and proceed from there.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
2

I was able to solve the problem with some few adjustments also to the .Net code. Android code is as below

public class AES256Cipher {

private static byte[] encrypt(byte[] ivBytes, byte[] keyBytes, byte[] textBytes)
        throws java.io.UnsupportedEncodingException,
        NoSuchAlgorithmException,
        NoSuchPaddingException,
        InvalidKeyException,
        InvalidAlgorithmParameterException,
        IllegalBlockSizeException,
        BadPaddingException {

    AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
    SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
    Cipher cipher = null;
    cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
    return cipher.doFinal(textBytes);
}

private static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] textBytes)
        throws java.io.UnsupportedEncodingException,
        NoSuchAlgorithmException,
        NoSuchPaddingException,
        InvalidKeyException,
        InvalidAlgorithmParameterException,
        IllegalBlockSizeException,
        BadPaddingException {

    AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
    SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
    return cipher.doFinal(textBytes);
}

final static String key = "0123456789abcdefghijklmnopqrstuv";

final static byte[] ivBytes = new byte[]{0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

public static String encryptData(String plainText) {
    String base64Text = "";
    try {
        byte[] keyBytes = key.getBytes("UTF-8");
        byte[] x = plainText.getBytes("UTF-8");
        byte[] cipherData = encrypt(ivBytes, keyBytes, plainText.getBytes("UTF-8"));
        base64Text = Base64.encodeToString(cipherData, Base64.DEFAULT);
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    }
    return base64Text;
}

//############## Response(decrypt) ##############
public static String decryptData(String base64Text) {
    String plainText = "";
    try {
        byte[] keyBytes = key.getBytes("UTF-8");
        try {
            byte[] cipherData = decrypt(ivBytes, keyBytes, Base64.decode(base64Text.getBytes("UTF-8"), Base64.DEFAULT));
            plainText = new String(cipherData, "UTF-8");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return plainText;
}
}

Simply call as below

String en = AES256Cipher.encryptData("1234");
        Log.e("enc length", en.length() + "");
        Log.e("enc", en);
        String de = AES256Cipher.decryptData(en);
        Log.e("dec", de);

For people using in java, can use the Base64 from package java.util Replace in the encrypt method to this

base64Text =Base64.getEncoder().encodeToString(cipherData);

In decrypt method replace to this

byte[] cipherData = decrypt(ivBytes, keyBytes,Base64.getDecoder().decode(base64Text.getBytes("UTF-8")));
WISHY
  • 11,067
  • 25
  • 105
  • 197