javax.crypto.BadPaddingException:给定的最终块未正确填充

Posted

技术标签:

【中文标题】javax.crypto.BadPaddingException:给定的最终块未正确填充【英文标题】:javax.crypto.BadPaddingException:Given final block not properly padded 【发布时间】:2012-05-12 17:19:13 【问题描述】:

我必须解密我服务器上的一个框架。加密帧通过套接字上的 GPRS 来自客户端设备。加密是使用 TripleDes 和给定的密钥完成的。我在服务器端使用相同的算法和密钥。 Frame 是 Hex 和 Ascii 字符串的组合。现在的问题是:当我用零填充字节数组时,出现以下异常。

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

以下是我的代码:

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

这是我在代码中使用的函数:

    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代码。

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

我是 java Cryptography 的新手。请告诉我该怎么做?任何人都可以发布可以正常工作的代码来解密我的框架。提前致谢。

【问题讨论】:

【参考方案1】:

(3)DES 加密/解密 8 字节的块。由于并非所有文本都恰好是 8 个字节,因此最后一个块必须包含非纯文本原始字节。

技巧是找出哪个是纯文本的最后一个字符。有时纯文本的长度是事先知道的 - 然后填充字符可以是任何东西。

如果纯文本的长度未知,则必须使用确定性填充算法,例如PKCS5填充。 PKCS5Padding 总是执行填充,即使明文是 N * blocksize(以字节为单位)。原因很简单:否则不知道最后一个字节是纯文本还是填充。

稍后我会尝试提供一个工作代码...必须对其进行测试。同时尝试使用填充算法。

【讨论】:

谢谢。我已经与 PKCS5Padding 合作过,但在我的情况下我没有看到任何区别。【参考方案2】:

我查看了您的 stringToHex 方法,它似乎不正确。试试这个:

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

我还发现了这个TripleDes with Padding 示例;您可以尝试使用示例使用的算法和转换。

【讨论】:

【参考方案3】:

如果文档没有告诉您在传入的密文上使用了什么填充,则使用“NoPadding”解密,它将接受最后一个块的任何类型的填充。然后看看你最后一个块的十六进制。这将告诉您在加密端使用了什么填充。修改您的代码以期望正确的填充类型。 here 涵盖了不同类型的填充。

【讨论】:

【参考方案4】:

您的代码的主要问题是您使用默认的 PKCS5Padding 进行解密。 "TripleDES" 将在内部产生 "TripleDES/ECB/PKCS5Padding"。这是在 Sun JCE 提供程序中实现的;大多数其他提供商都复制此默认设置。

您似乎期望零填充,这意味着您应该改用"DESede/ECB/NoPadding"。之后,您可以使用外部函数来计算纯文本大小(如果您不小心,删除零填充可能会删除最后的零值纯文本)。

其他问题:

试图在解密前填充数据(你应该unpad数据解密之后) 编码和字符编码问题,例如试图用"0"的字符值填充,这可能是错误的

我已经指出"ECB",因为我不知道实际使用的模式。如果你能找到的话,你可以用正确的模式和填充算法修改你的问题。如果 ECB 不起作用,您可能也想尝试 CBC 模式。

请注意,除了非常特殊的情况外,使用 ECB 模式并不安全。使用随机 IV 的 CBC 是最低要求。

【讨论】:

非常感谢。它成功了。现在我告诉客户发送带有填充的加密帧。我使用了 DES/CBC/NoPadding,它解密得很好。

以上是关于javax.crypto.BadPaddingException:给定的最终块未正确填充的主要内容,如果未能解决你的问题,请参考以下文章