14
votes

I have to decrypt a frame on my server. Encrypted frame comes from client device through GPRS on socket.Encryption is done with TripleDes and with a given key.I use same algorithm and key on server side. Frame is a combination of Hex and Ascii String. Now the problem is : When I pad my byte array with zeros I get the following exception.

javax.crypto.BadPaddingException: Given final block not properly padded

Following is my code :

byte[] key = new byte[]{31, 30, 31, 36, 32, 11, 11, 11, 22, 26,
               30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30};
myKeySpec = new DESedeKeySpec(key);
mySecretKeyFactory = SecretKeyFactory.getInstance("TripleDES");
de = mySecretKeyFactory.generateSecret(myKeySpec);

    Cipher c = Cipher.getInstance("TripleDES");
c.init(Cipher.DECRYPT_MODE, key);

    int l = completeHexStr.length();

    if (l%8==1){
        completeHexStr = completeHexStr + "0000000";
    }else if (l%8==7){
        completeHexStr = completeHexStr + "0";
    }
byte decordedValue[] =completeHexString.getBytes();
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
System.out.println("decryptedValue= " + decryptedValue);

Here are the functions which I am using inside the code:

    public String stringToHex(String base) {
            StringBuffer buffer = new StringBuffer();
            int intValue = 0;
            for (int x = 0; x < base.length(); x++) {
                intValue = base.charAt(x);
                String hex = Integer.toHexString(intValue);
                if (hex.length() == 1) {
                    buffer.append("0" + hex + "");
                } else {
                    buffer.append(hex + "");
                }
            }
            return buffer.toString();
        }
    public String byteToAscii(byte[] b, int length) {
            String returnString = "";
            for (int i = 0; i < length; i++) {
                returnString += (char) (b[i] & 0xff);
            }
            return returnString;
        }

this is the code in c used for encryption at client side.

#include <svc_sec.h>
const unsigned char fixed_key[] = { 0x31, 0x30, 0x31, 0x36, 0x32, 0x11, 0x11, 0x11, 0x22, 0x26, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
int Comm_Encrypt_Data(unsigned char *Test_Input_Data, int Len_Input_Data)
{
int Count_Input_Data, Counter_Input_Data;
unsigned long Timer_1;
unsigned char Init_Vector[8];
int Counter_Init_Vector, Temp_Byte_Count;
unsigned char *Temp_Dst_Ptr, *Temp_Src_Ptr;
unsigned char Temp_Input_Frame[9], Temp_Output_Frame[9];
unsigned char Test_Output_Data[500];
unsigned char Test_Key_Arr[9];

memset(&Init_Vector[0], '\0', sizeof(Init_Vector));
memset(Test_Key_Arr, '0', sizeof(Test_Key_Arr));
memcpy(Test_Key_Arr, &fixed_key[0], 8);
Test_Key_Arr[sizeof(Test_Key_Arr)-1] = '\0';

Display_Data("KEY: ", Test_Key_Arr, sizeof(Test_Key_Arr)-1);

memset(Test_Output_Data, '\0', sizeof(Test_Output_Data));
memcpy(Test_Output_Data, Test_Input_Data, 48);

Count_Input_Data = Len_Input_Data -48 -3; //minus Data before payload, 3 bytes of '|' and CRC
Counter_Input_Data = 0;
while(Counter_Input_Data < Count_Input_Data)
{
Temp_Byte_Count = Count_Input_Data- Counter_Input_Data;
if(Temp_Byte_Count > 8)
Temp_Byte_Count = 8;

memcpy(Temp_Input_Frame, &Test_Input_Data[48+Counter_Input_Data], Temp_Byte_Count);
//succeeding bytes to be 0
if(Temp_Byte_Count < 8)
{
memset(&Temp_Input_Frame[Temp_Byte_Count], '0', (8-Temp_Byte_Count));

}

Display_Data("InPut Data Before Init",Temp_Input_Frame, Temp_Byte_Count);

//============Initialize the data
Temp_Dst_Ptr = (unsigned char *)Temp_Input_Frame;
Temp_Src_Ptr = (unsigned char *)&Init_Vector[0];
for(Counter_Init_Vector =0;Counter_Init_Vector < 8; Counter_Init_Vector++)
*Temp_Dst_Ptr++ ^= *Temp_Src_Ptr++;
//============Initializing data ends

DES(DESE, (unsigned char *)&Test_Key_Arr[0],
(unsigned char *)&Temp_Input_Frame[0], (unsigned char *)&Temp_Output_Frame[0]);
//DES(TDES3KE, (unsigned char *)&Test_Key_Arr[0],
// (unsigned char *)&Temp_Input_Frame[0], (unsigned char *)&Temp_Output_Frame[0]);
Display_Data("AFTER DES::::", Temp_Output_Frame, Temp_Byte_Count);

memcpy(&Test_Output_Data[48+Counter_Input_Data], Temp_Output_Frame, Temp_Byte_Count);
Counter_Input_Data += Temp_Byte_Count;

if(Counter_Input_Data < Count_Input_Data)
{
memcpy(Init_Vector, Temp_Output_Frame, 8);

}
}

{
memset(Test_Input_Data, '\0', Len_Input_Data);
memcpy(&Test_Input_Data[0], &Test_Output_Data[48], Counter_Input_Data); //1 Separator + 2 CRCs
}
Display_Data("Final Output Frame", Test_Input_Data, Counter_Input_Data);
return Counter_Input_Data;
}

I am new in java Cryptography. Please tell me how to do it? Can anybody post the code which can work properly to decrypt my frame. Thanks in advance.

4

4 Answers

10
votes

The main issue with your code is that you are decrypting using a default of PKCS5Padding. "TripleDES" will result in "TripleDES/ECB/PKCS5Padding" internally. This is as it is implemented in the Sun JCE provider; most other providers copy this default.

It seems you are expecting zero padding, which means you should use "DESede/ECB/NoPadding" instead. After that you can use an external function to calculate the plain text size (removing zero padding may remove zero valued plain text at the end if you are not careful).

Other issues:

  • trying to pad data before decryption (you should unpad data after decryption)
  • encoding and character encoding issues, such as trying to pad with the character value of "0", which is probably wrong

I've indicated "ECB" because I don't know the actual mode used. You could amend your question with the right mode and padding algorithm if you can find out. You might want to try CBC mode as well if ECB does not work.

Note that ECB mode is not safe to use except for very specific circumstances. Using CBC with a randomized IV is a minimal requirement.

5
votes

(3)DES encrypts/decrypts blocks of 8 bytes. As not all texts are precisely 8 bytes, the last block must contain bytes that are not original from the plain text.

Trick is to find out which one is the last character of the plain text. Sometimes the length of the plain text is known beforehand - then the padding characters can be anything really.

If the length of the plain text is not known then a deterministic padding algorithm must be used, e.g. PKCS5Padding. PKCS5Padding always performs padding, even if the plaintext is N * blocksize in bytes. The reason for this is simple: otherwise it doesn't know if the last byte is plain text or padding.

I will try to come with a working code later...have to test it. In the meantime try using the padding algorithms.

4
votes

If the documentation does not tell you what padding is being used on the incoming cyphertext, then decrypt with "NoPadding", which will accept any sort of padding on the last block. Then have a look at the hex of your last block. That will tell you what padding is being used at the encrypting end. Amend your code to expect the correct type of padding. The different types of padding are covered here.

3
votes

I've taken a look at your stringToHex method and it seems to be incorrect. Try this one instead:

        StringBuilder rep = new StringBuilder();
        for (byte b : base.getBytes) {
            rep.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        }
        System.out.println(rep);

Also I found this TripleDes with Padding example; you could try with the algorithm and transformation the example uses.