web-dev-qa-db-fra.com

javax.crypto.BadPaddingException: le bloc final donné n'est pas correctement rempli

Je dois décrypter une trame sur mon serveur. La trame cryptée provient du périphérique client via GPRS sur le socket. Le cryptage se fait avec TripleDes et avec une clé donnée. algorithme et clé côté serveur. Frame est une combinaison de Hex et Ascii String. Maintenant, le problème est: lorsque je remplis mon tableau d'octets avec des zéros, j'obtiens l'exception suivante.

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

Voici mon 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);

Voici les fonctions que j'utilise dans le 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;
        }

c'est le code en c utilisé pour le chiffrement côté client.

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

Je suis nouveau en Java Cryptography. S'il vous plaît, dites-moi comment le faire? Quelqu'un peut-il poster le code qui peut fonctionner correctement pour décrypter mon cadre. Merci d'avance.

14
java2485

Le principal problème avec votre code est que vous décryptez en utilisant une valeur par défaut de PKCS5Padding. "TripleDES" aura pour résultat "TripleDES/ECB/PKCS5Padding" en interne. C'est comme il est implémenté dans le fournisseur Sun JCE; la plupart des autres fournisseurs copient cette valeur par défaut.

Il semble que vous vous attendiez à un remplissage nul, ce qui signifie que vous devez utiliser "DESede/ECB/NoPadding" au lieu. Après cela, vous pouvez utiliser une fonction externe pour calculer la taille du texte brut (la suppression du remplissage zéro peut supprimer le texte brut de valeur zéro à la fin si vous ne faites pas attention).

Autres issues:

  • essayer de remplir les données avant le décryptage (vous devez décompresser données après décryptage)
  • problèmes d'encodage et d'encodage de caractères, tels que la tentative de remplissage avec la valeur de caractère "0", ce qui est probablement faux

J'ai indiqué "ECB" car je ne connais pas le mode réel utilisé. Vous pouvez modifier votre question avec le bon mode et l'algorithme de remplissage si vous pouvez le découvrir. Vous pouvez également essayer le mode CBC si la BCE ne fonctionne pas.

Notez que le mode ECB n'est pas sûr à utiliser, sauf dans des circonstances très spécifiques. L'utilisation de CBC avec une IV randomisée est une exigence minimale.

10
Maarten Bodewes

(3) DES crypte/décrypte des blocs de 8 octets. Comme tous les textes ne font pas exactement 8 octets, le dernier bloc doit contenir des octets qui ne sont pas originaires du texte brut.

L'astuce consiste à découvrir lequel est le dernier caractère du texte brut. Parfois, la longueur du texte brut est connue à l'avance - alors les caractères de remplissage peuvent être vraiment n'importe quoi.

Si la longueur du texte brut n'est pas connue, un algorithme de remplissage déterministe doit être utilisé, par ex. PKCS5Padding. PKCS5Padding effectue toujours le remplissage, même si le texte en clair est N * taille de bloc en octets. La raison en est simple: sinon, il ne sait pas si le dernier octet est du texte brut ou du remplissage.

Je vais essayer de venir avec un code de travail plus tard ... je dois le tester. En attendant, essayez d'utiliser les algorithmes de remplissage.

6
Calin Andrei

Si la documentation ne vous indique pas quel remplissage est utilisé sur le texte crypté entrant, alors déchiffrez avec "NoPadding", qui acceptera n'importe quel type de remplissage sur le dernier bloc. Jetez ensuite un œil à l'hex de votre dernier bloc. Cela vous dira quel rembourrage est utilisé à la fin du cryptage. Modifiez votre code pour vous attendre au type de remplissage correct. Les différents types de rembourrage sont couverts ici .

4
rossum

J'ai jeté un œil à votre méthode stringToHex et elle semble incorrecte. Essayez plutôt celui-ci:

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

J'ai également trouvé cet exemple TripleDes with Padding ; vous pouvez essayer avec l'algorithme et la transformation que l'exemple utilise.

3
alain.janinm