0
votes

I have an android project in which am getting an Triple DES encrypted piece of text from my web services. I need to Triple DES decrypt.

However, I am getting invalid key exceptions. My key is converted to HEX format and I got an error: W/System.err﹕ java.security.InvalidKeyException: DES key too long - should be 8 bytes I found here a forum explaining that hex may cause issue

"DES keys are 56 bits normally packaged in 8 bytes so the chances are that the 16 bytes/characters they have given you are the hex encoded bytes of the key. You can get a hex decoder"

So I converted my hex string to a byte array using

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

and passed that in to the cipher and I get an error:

W/System.err﹕ java.security.InvalidKeyException
W/System.err﹕ at javax.crypto.spec.DESedeKeySpec.

here is my decrypt method. I would appreciate if someone could shine some light on where I might be going wrong.

 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;
    KeySpec desKeySpec = new DESedeKeySpec(hexConvert); // Exception HERE
    Cipher desCipher;
    SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
    desCipher = Cipher.getInstance("DES/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;
}

My key is C9AF269DF8A78A06D1216BFFF8F0536A.

I have checked with the client and the key is correct, so the same key is being used for encryption.

Encryption Code

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

Here is an example of the decrypted text with 14 chars: 12345678901234

3
Well how long is it and what character sets are used? Without any information about the key, this is just a guessing game. I don't like games.Artjom B.
C9AF269DF8A78A06D1216BFFF8F0536A is the keyDJ-DOO
Can't you add this to your question? Btw, what is the line where the error is thrown?Artjom B.
@ArtjomB. W/System.err﹕ java.security.InvalidKeyException W/System.err﹕ at javax.crypto.spec.DESedeKeySpec. error is thrown at the line KeySpec desKeySpec = new DESedeKeySpec(hexConvert);DJ-DOO

3 Answers

4
votes

DES would expect an 8 byte key (with parity). So Triple DES expects a 24 byte key (with parity). Since you only have a 16 byte key, you have to replicate some of it, to get the final key. Oftentimes the first and last 8 bytes are the same. You can try two variants:

byte[] tdesKey = new byte[24];
System.arraycopy(hexConvert, 0, tdesKey, 0, 16);
System.arraycopy(hexConvert, 0, tdesKey, 16, 8);
// tdesKey := K1 || K2 || K1

or

byte[] tdesKey = new byte[24];
System.arraycopy(hexConvert, 8, tdesKey, 0, 8);
System.arraycopy(hexConvert, 0, tdesKey, 8, 16);
// tdesKey := K2 || K1 || K2

when

hexConvert := K1 || K2
1
votes

Maybe you must to use this cipher:

public byte[] encTripleDes (String txt, byte [] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeySpecException{
    DESedeKeySpec keySpec = new DESedeKeySpec(key);
    SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
    SecretKey ky = keyfactory.generateSecret(keySpec);

    Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, ky);
    return cipher.doFinal(txt.getBytes("UTF-8"));

}

And for decrypting:

public byte[] uncTripleDes (byte [] encryptedTextBytes, byte [] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeySpecException{
    DESedeKeySpec keySpec = new DESedeKeySpec(key);
    SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
    SecretKey ky = keyfactory.generateSecret(keySpec);

    Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, ky);
    return cipher.doFinal(encryptedTextBytes);

}

Look that I use a "PKCS5Padding" in the instance of the cipher.

Notice that the padding is used for give the same size at all the blocks (for example if the last block is 788 instead of 1024).

For create the key, in my solution (it's not the only one), I calculate a sha-256 hash and then I get the necessary bytes for the Des key:

Calculating the hash:

public byte[] sumCalc (){ 
    String key = "anyKey";
    byte[] hashedKey = null;
    try {
        byte [] byteKey = key.getBytes("UTF-8");
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        hashedKey = md.digest(byteKey);
    }catch (Exception ex){
        System.err.println("Error generant clau" + ex);  
    }
    return hashedKey;
}

Finally get only the 128 bytes that we need for the Des key:

bytedKey = Arrays.copyOf(bytedKey, 16 ); // 16 use only first 128 bit. if 32 use only 256

It's my solution but not the only one!

0
votes

You instantiate your Cipher as a (singel) DES Cipher with:

desCipher = Cipher.getInstance("DES/ECB/NoPadding");

But your key is a 16 byte 3Des key, and thuse you get the error

DES key too long - should be 8 bytes

Try instantiating your cipher as a 3DES cipher with:

desCipher = Cipher.getInstance("DESede/ECB/NoPadding");