Triple DES Encrypt C# - Decrypt in Java

2020-03-08 06:35发布

问题:

I'm getting a Triple DES decrypted string from the clients server, which has been coded in c# (see below):

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

class Program
{
    static void Main()
    {
        Console.WriteLine("Hello, World!");

    var encryption = TripleDESEncrypt("12345678901234", "C9AF269DF8A78A06D1216BFFF8F0536A");
      Console.WriteLine(encryption);

    }

      public static string TripleDESEncrypt(string strClearText, string strKey)
        {
            byte[] bytClearText;
            byte[] bytClearTextChunk = new byte[8];
            byte[] bytEncryptedChunk = new byte[8];
            int BytesCount = 0;
            int nArrayPosition = 0;
            string strEncryptedChar;
            string strEncryptedText = "";

            ArrayList Input = new ArrayList();
            ArrayList Output = new ArrayList();

            TripleDESCryptoServiceProvider tdes = (TripleDESCryptoServiceProvider)TripleDESCryptoServiceProvider.Create();

            tdes.Key = HexToByteArray(strKey);
            tdes.Mode = CipherMode.ECB;

            ICryptoTransform tdesEncrypt = tdes.CreateEncryptor();

            bytClearText = ASCIIEncoding.ASCII.GetBytes(strClearText);
            BytesCount = bytClearText.Length;

            for (int i = 0; i < BytesCount; i++)
            {
                if (nArrayPosition == 8)
                {
                    Input.Add(bytClearTextChunk);
                    bytClearTextChunk = new byte[8];
                    nArrayPosition = 0;
                }
                bytClearTextChunk[nArrayPosition] = bytClearText[i];
                nArrayPosition++;
            }

            if (nArrayPosition != 0)
                Input.Add(bytClearTextChunk);


            foreach (byte[] Cbyte in Input)
            {
                tdesEncrypt.TransformBlock(Cbyte, 0, 8, bytEncryptedChunk, 0);
                Output.Add(bytEncryptedChunk);
                bytEncryptedChunk = null;
                bytEncryptedChunk = new byte[8];
            }


            foreach (byte[] Cbyte in Output)
            {
                foreach (byte BByte in Cbyte)
                {
                    strEncryptedChar = BByte.ToString("X");
                    strEncryptedChar = strEncryptedChar.PadLeft(2, Convert.ToChar("0"));
                    strEncryptedText += strEncryptedChar;
                }
            }

            return strEncryptedText;
        }
        private static byte[] HexToByteArray(string strHex)
        {
            byte[] bytArray = new byte[strHex.Length / 2];
            int positionCount = 0;

            for (int i = 0; i < strHex.Length; i += 2)
            {
                bytArray[positionCount] = byte.Parse(strHex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
                positionCount++;
            }
            return bytArray;
        }
}

I am then trying to Triple DES decrypt it in Java using this key: C9AF269DF8A78A06D1216BFFF8F0536A

Here is my code to decrypt:

public String DesDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {

        String UNICODE_FORMAT = "UTF8";
        String decryptedPinText = null;

        byte[] hexConvert = hexStringtoByteArray(encryptKey);

        SecretKey desKey = null;
        byte[] tdesKey = new byte[24];
        System.arraycopy(hexConvert, 0, tdesKey, 0,16);
        System.arraycopy(hexConvert, 0, tdesKey, 0,8);

        byte[] encryptKeyBytes = encryptKey.getBytes(UNICODE_FORMAT);

        KeySpec desKeySpec = new DESedeKeySpec(tdesKey);
        Cipher desCipher;
        SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
        desCipher = Cipher.getInstance("DESede/ECB/NoPadding");
        try {
            desKey = skf.generateSecret(desKeySpec);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }

        desCipher.init(Cipher.DECRYPT_MODE, desKey);

       byte[] decryptPin = desCipher.doFinal(pin.getBytes());
        decryptedPinText = new String(decryptPin, "UTF-8");
        return decryptedPinText;
    }

The sample out put would be input/output would be "12345678901234" however, I'm getting jumbled nonsense returned e.g ��0�8��/0��

So something is getting lost between c# and java... This is a follow on from a previous question I asked here

I'd appreciate help on this

changes to code

   public String DesDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {

        String UNICODE_FORMAT = "UTF8";
        String decryptedPinText = null;



        SecretKey desKey = null;
        byte[] encryptKeyBytes = EncodingUtils.getAsciiBytes(encryptKey);
        byte[] tdesKey = new byte[24];
        System.arraycopy(encryptKeyBytes, 8, tdesKey, 0, 8);
        System.arraycopy(encryptKeyBytes, 0, tdesKey, 8, 16);
        KeySpec desKeySpec = new DESedeKeySpec(tdesKey);
        Cipher desCipher;
        SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
        desCipher = Cipher.getInstance("DESede/ECB/NoPadding");
        try {
            desKey = skf.generateSecret(desKeySpec);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        desCipher.init(Cipher.DECRYPT_MODE, desKey);

        byte[] decryptPin = desCipher.doFinal(EncodingUtils.getAsciiBytes(pin));

        decryptedPinText = new String(decryptPin, "ASCII");
        return decryptedPinText;
    }

c# decrypt code

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

class Program
{
    static void Main()
    {
        Console.WriteLine("Hello, World!");

        var encryption = TripleDESDecrypt("1D30CC3DE1641D7F5E821D13FC1200C3", "C9AF269DF8A78A06D1216BFFF8F0536A");
        Console.WriteLine(encryption);

    }

      public static string TripleDESDecrypt(string strEncryptedText, string strKey)
        {
            string errorMessage = "";
            int errorCode = 0;
            string strDecryptedText = "";

            try
            {
                byte[] bytEncryptedChunk = new byte[8];
                byte[] bytClearTextChunk = new byte[8];
                byte[] _bytesEmpty = new byte[8];
                int BytesCount = 0;
                int positionCount = 0;


                ArrayList Input = new ArrayList();
                ArrayList Output = new ArrayList();

                TripleDESCryptoServiceProvider tdes = (TripleDESCryptoServiceProvider)TripleDESCryptoServiceProvider.Create();

                tdes.Key = HexToByteArray(strKey);
                tdes.Mode = CipherMode.ECB;

                ICryptoTransform tdesDecrypt = tdes.CreateDecryptor();

                BytesCount = strEncryptedText.Length;

                for (int i = 0; i < BytesCount; i += 2)
                {
                    if (positionCount == 8)
                    {
                        positionCount = 0;
                        Input.Add(bytEncryptedChunk);
                        bytEncryptedChunk = new byte[8];
                    }

                    bytEncryptedChunk[positionCount] = byte.Parse(strEncryptedText.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
                    positionCount++;
                }

                if (positionCount != 0)
                {
                    Input.Add(bytEncryptedChunk);
                }

                foreach (byte[] Cbyte in Input)
                {
                    tdesDecrypt.TransformBlock(Cbyte, 0, 8, _bytesEmpty, 0);
                    tdesDecrypt.TransformBlock(Cbyte, 0, 8, bytClearTextChunk, 0);
                    Output.Add(bytClearTextChunk);
                    bytClearTextChunk = null;
                    bytClearTextChunk = new byte[8];
                }

                foreach (byte[] Cbyte in Output)
                {
                    strDecryptedText += ASCIIEncoding.ASCII.GetString(Cbyte);
                }
            }
            catch (Exception ex)
            {
                errorCode = 1;
                errorMessage = ex.Message;


            }
Console.WriteLine(strDecryptedText);
            return strDecryptedText;
        }

        private static byte[] HexToByteArray(string strHex)
        {
            byte[] bytArray = new byte[strHex.Length / 2];
            int positionCount = 0;

            for (int i = 0; i < strHex.Length; i += 2)
            {
                bytArray[positionCount] = byte.Parse(strHex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
                positionCount++;
            }
            return bytArray;
        }
}

This returns what is inputting into the encrypt 12345678901234

回答1:

In your C# code, you use ASCII:

bytClearText = ASCIIEncoding.ASCII.GetBytes(strClearText);

While in Java you use UNICODE:

byte[] encryptKeyBytes = encryptKey.getBytes(UNICODE_FORMAT);

Try to change your C# to use UNICODE or your java code to use ASCII.

Also, since the C# is padding the output :

strEncryptedChar = strEncryptedChar.PadLeft(2, Convert.ToChar("0"));

You probably must check to remove all the '00' in the crypted string, so 1D30CC3DE1641D7F5E821D13FC1200C3 will become 1D30CC3DE1641D7F5E821D13FC12C3

(you must check if it's in the boundaries of an hex expression: 1C01A1 should probably be modified since it got a padding on the second Hexa 1C 01 A1: 1C1A1



回答2:

acording https://stackoverflow.com/a/33768305/1140304 you can use unicode instead of UTF-8 in java code

encrypt in c# :

public static string Encrypt2(string clearText,string key)
{
    try
    {
        string encryptedText = "";
        MD5 md5 = new MD5CryptoServiceProvider();
        TripleDES des = new TripleDESCryptoServiceProvider();
        des.KeySize = 128;
        des.Mode = CipherMode.CBC;
        des.Padding = PaddingMode.PKCS7;

        byte[] md5Bytes = md5.ComputeHash(Encoding.Unicode.GetBytes(key));

        byte[] ivBytes = new byte[8];


        des.Key = md5Bytes;

        des.IV = ivBytes;

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

        ICryptoTransform ct = des.CreateEncryptor();
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(clearBytes, 0, clearBytes.Length);
                cs.Close();
            }
            encryptedText = Convert.ToBase64String(ms.ToArray());
        }
        return encryptedText;
    }
    catch (Exception exception)
    {
        return "";
    }
}

for decode in c# you can use:

public static string Decrypt2(string cipher,string key)
{
    try
    {
        byte[] clearBytes = Convert.FromBase64String(cipher);
        MD5 md5 = new MD5CryptoServiceProvider();
        byte[] md5Bytes = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
        string encryptedText = "";
        TripleDES des = new TripleDESCryptoServiceProvider();
        des.KeySize = 128;
        des.Mode = CipherMode.CBC;
        des.Padding = PaddingMode.PKCS7;
        byte[] ivBytes = new byte[8];
        des.Key = md5Bytes;
        des.IV = ivBytes;
        ICryptoTransform ct = des.CreateDecryptor();
        byte[] resultArray = ct.TransformFinalBlock(clearBytes, 0, clearBytes.Length);
        encryptedText = Encoding.Unicode.GetString(resultArray);
        return encryptedText;
    }
    catch (Exception exception)
    {
        return "";
    }
}

now, for encrypt in java you can use :

private String _encrypt2(String clearText,String key )
{
    try
    {
        /**
         * create md5
         */
        MessageDigest md = MessageDigest.getInstance("md5");
        byte[] digestOfPassword = md.digest(key.getBytes("UTF-16LE"));
        byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
        for (int j = 0, k = 16; j < 8; )
        {
            keyBytes[k++] = keyBytes[j++];
        }


        SecretKey secretKey = new SecretKeySpec(keyBytes, 0, 24, "DESede");
        IvParameterSpec iv = new IvParameterSpec(new byte[8]);
        Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);

        byte[] plainTextBytes = clearText.getBytes("UTF-16LE");
        byte[] cipherText = cipher.doFinal(plainTextBytes);

        String output = Base64.encodeToString(cipherText,Base64.DEFAULT);
        return output;
    }
    catch (Exception ex) {}
    return "";
}

and for decrypt in java :

private String _decrypt2(String encryptText,String key)
{

    MessageDigest md = null;
    byte[] digestOfPassword = null;

    try
    {
        byte[] message = Base64.decode(encryptText.getBytes("UTF-16LE"), Base64.DEFAULT);

        /**
         * make md5
         */
        md = MessageDigest.getInstance("md5");
        digestOfPassword = md.digest(key.getBytes("UTF-16LE"));
        byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
        for (int j = 0, k = 16; j < 8; )
        {
            keyBytes[k++] = keyBytes[j++];
        }

        SecretKey secretKey = new SecretKeySpec(keyBytes, 0, 24, "DESede");
        IvParameterSpec iv = new IvParameterSpec(new byte[8]);
        Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
        byte[] cipherText = cipher.doFinal(message);

        return new String(cipherText, "UTF-16LE");
    }
    catch (NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch (UnsupportedEncodingException e)
    {
        e.printStackTrace();
    }
    catch (InvalidKeyException e)
    {
        e.printStackTrace();
    }
    catch (InvalidAlgorithmParameterException e)
    {
        e.printStackTrace();
    }
    catch (NoSuchPaddingException e)
    {
        e.printStackTrace();
    }
    catch (BadPaddingException e)
    {
        e.printStackTrace();
    }
    catch (IllegalBlockSizeException e)
    {
        e.printStackTrace();
    }
    return "";
}


回答3:

If someone find himself/herself in the same problem like I did, here is a java implementation (android) of the same .NET decrypt function:

 public static byte[] byteArrayConcat(byte[] array1, byte[] array2) {
        byte[] result = new byte[array1.length + array2.length];
        System.arraycopy(array1, 0, result, 0, array1.length);
        System.arraycopy(array2, 0, result, array1.length, array2.length);
        return result;
    }


 private byte[] fGPKeyTo3DESKey(byte[] GPKey) {

        byte[] _3DESKey = new byte[24];
        byte[] tmp = new byte[8];

        arraycopy(GPKey, 0, tmp, 0, 8);

        _3DESKey = DaPlugUtils.byteArrayConcat(GPKey, tmp);

        return _3DESKey;
    }
 private static byte[] hexStringtoByteArray(String hex) {
        int len = hex.length();

        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }
public String desDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {

        int bytesCount = 0;
        int positionCount = 0;


        byte[] bytEncryptedChunk = new byte[8];

        ArrayList<byte[]> Input = new ArrayList();
        bytesCount = pin.length();

        for (int i = 0; i < bytesCount; i += 2) {
            if (positionCount == 8) {
                positionCount = 0;
                Input.add(bytEncryptedChunk);
                bytEncryptedChunk = new byte[8];
            }
            bytEncryptedChunk[positionCount] = (byte) (Integer.parseInt(pin.substring(i, i + 2), 16));
            positionCount++;
        }

        if (positionCount != 0) {
            Input.add(bytEncryptedChunk);
        }


        byte[] _3DESKey = fGPKeyTo3DESKey(hexStringtoByteArray(encryptKey));

        DESedeKeySpec keySpec = new DESedeKeySpec(_3DESKey);
        SecretKey k = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
        Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, k);


        String res = "";

        for (byte[] bs : Input) {
            byte[] decryptPin = cipher.doFinal(bs);
            String a = new String(decryptPin, StandardCharsets.US_ASCII);

            res += a;
        }

        return res.trim();
    }