Search code examples
javasecurityaes

First 8 bytes is truncating in AES decryption in java where encryption is done in c#


I am trying to decrypt the string which is encrypted in c# with same key length and vector.

JAVA CODE

public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        String cum006333 = decrypt("h+VJ5or2yBFQYawNWXET11nekUzWYYVWk0/O2fHxoLNm60l3d3qQo2NJjGr3+zZP", "MYPRIVATE@KEY");
        System.out.println(cum006333);

    }

    public static String decrypt(String cipherText, String passphrase) {
        String s = null;
        try {

            Cipher   cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeyFactory  factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            
            byte[] bytes = Base64.getDecoder().decode(cipherText);
            int keySize = 256;
            int iterCount = 1000;

            int ivLength = 16;
            byte[] saltByte = new byte[]{0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};
            byte[] ivB = Arrays.copyOf(bytes, ivLength);
            byte[] data = Arrays.copyOfRange(bytes,ivLength, bytes.length);

            KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), saltByte, iterCount, keySize);
            SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
            IvParameterSpec iv = new IvParameterSpec(ivB);
            cipher.init(Cipher.DECRYPT_MODE, key, iv);
            byte[] original = cipher.doFinal(data);
            s = new String(original);
            System.out.println(s);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return s;
    }

    
}

expexted output is - Duplicate Request ID

output coming is - e Request Id

I need to encrypt the request in java and send to api where they will decrypt it and use it. So i want java equivalent of the below C# code..

please find C# code used for encrypting and decrypting below

public static string EncryptResponse(string clearText)
    {
        string EncryptionKey = System.Configuration.ConfigurationManager.AppSettings["SECRETMSG"].ToString();
        byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes rfcdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = rfcdb.GetBytes(32);
            encryptor.IV = rfcdb.GetBytes(16);
            using (MemoryStream mem = new MemoryStream())
            {
                using (CryptoStream cryp = new CryptoStream(mem, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cryp.Write(clearBytes, 0, clearBytes.Length);
                    cryp.Close();
                }
                clearText = Convert.ToBase64String(mem.ToArray());
            }
        }
        return clearText;
    }


    public static string DecryptResponse(string cipherText)
    {
        string EncryptionKey = System.Configuration.ConfigurationManager.AppSettings["SECRETMSG"].ToString();
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes rfcdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = rfcdb.GetBytes(32);
            encryptor.IV = rfcdb.GetBytes(16);

            using (MemoryStream mem = new MemoryStream())
            {
                using (CryptoStream cryp = new CryptoStream(mem, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cryp.Write(cipherBytes, 0, cipherBytes.Length);
                    cryp.Close();
                }
                cipherText = Encoding.Unicode.GetString(mem.ToArray());
            }
        }
        return cipherText;
    }


Solution

  • As I don't like to mix answers I'm writing a second answer with the final solution for your question.

    Thanks for providing the C#-code as it answered 2 questions - what is the encoding of the plaintext and what data is included in the ciphertext.

    Using this line on C# shows us that the encoding is UNICODE (=UTF 16) as I argued in my first answer:

    byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
    

    To get identical results in Java you need to decode any decryption result with

    s = new String(original, StandardCharsets.UTF_16LE);
    

    The second question - what is included in the ciphertext is answered very simple - just the ciphertext itself (no IV). The neccessary IV is derived by the

    Rfc2898DeriveBytes...
    encryptor.Key = rfcdb.GetBytes(32);
    encryptor.IV = rfcdb.GetBytes(16);
    

    Putting both information together I can decrypt the ciphertext with the given key as follows:

    First 8 bytes is truncating in AES decryption in java where encryption is done in c#
    cipherByte length: 48 data: 87e549e68af6c8115061ac0d597113d759de914cd6618556934fced9f1f1a0b366eb4977777a90a363498c6af7fb364f
    keyDerived length: 32 data: e35b88684a86fe06ae80eab429b2c9c242ab64f225649aa484ced8f53e5de146
    ivB        length: 16 data: 19b9e11cb1e6c6aa69060fbdfe10484c
    original   length: 40 data: 4400750070006c006900630061007400650020005200650071007500650073007400200049004400
    Duplicate Request ID
    Duplicate Request ID
    

    Here is the complete 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.security.spec.KeySpec;
    import java.util.Arrays;
    import java.util.Base64;
    
    public class mainSo2 {
    
        public static void main(String[] args) {
            System.out.println("First 8 bytes is truncating in AES decryption in java where encryption is done in c#");
            String cipherstring = "h+VJ5or2yBFQYawNWXET11nekUzWYYVWk0/O2fHxoLNm60l3d3qQo2NJjGr3+zZP";
            byte[] cipherByte = Base64.getDecoder().decode(cipherstring);
            System.out.println("cipherByte length: " + cipherByte.length + " data: " + bytesToHex(cipherByte));
            String cum006333 = decrypt(cipherstring, "KEY@CRISIL123");
            System.out.println(cum006333);
        }
    
        public static String decrypt(String cipherText, String passphrase) {
            String s = null;
            try {
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
                byte[] bytes = Base64.getDecoder().decode(cipherText);
                int keySize = 256;
                int iterCount = 1000;
                int ivLength = 16;
                byte[] saltByte = new byte[]{0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};
                KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), saltByte, iterCount, (keySize + (8 * ivLength))); // 32 + 16
                byte[] rawKey = factory.generateSecret(spec).getEncoded(); // 48 bytes
                byte[] keyDerived = Arrays.copyOf(rawKey, (keySize / 8)); // first 32 bytes
                byte[] ivB = Arrays.copyOfRange(rawKey, (keySize / 8), rawKey.length); // last 16 bytes
                System.out.println("keyDerived length: " + keyDerived.length + " data: " + bytesToHex(keyDerived));
                System.out.println("ivB        length: " + ivB.length + " data: " + bytesToHex(ivB));
                SecretKey key = new SecretKeySpec(keyDerived, "AES");
                IvParameterSpec iv = new IvParameterSpec(ivB);
                cipher.init(Cipher.DECRYPT_MODE, key, iv);
                //byte[] original = cipher.doFinal(data);
                byte[] original = cipher.doFinal(bytes);
                System.out.println("original   length: " + original.length + " data: " + bytesToHex(original));
                s = new String(original, StandardCharsets.UTF_16LE);
                System.out.println(s);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return s;
        }
    
        private static String bytesToHex(byte[] bytes) {
            StringBuffer result = new StringBuffer();
            for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
            return result.toString();
        }
    }