Javax.crypto.BadPaddingException: данный конечный блок неправильно заполнен
Мне нужно расшифровать фрейм на моем сервере. Зашифрованный кадр поступает с клиентского устройства через GPRS на сокет. Экранирование выполняется с помощью TripleDes и с заданным ключом. Я использую тот же алгоритм и ключ на стороне сервера. Кадр представляет собой комбинацию Hex и Ascii String. Теперь проблема заключается в следующем: когда я заполняю массив байтов нулями, я получаю следующее исключение.
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
Основная проблема с вашим кодом заключается в том, что вы дешифруете, используя значение по умолчанию PKCS5Padding. "TripleDES"
приведет к "TripleDES/ECB/PKCS5Padding"
внутренне. Это так, как это реализовано в поставщике Sun JCE; большинство других поставщиков копируют это значение по умолчанию.
Кажется, вы ожидаете нулевого заполнения, что означает, что вместо этого вы должны использовать "DESede/ECB/NoPadding"
. После этого вы можете использовать внешнюю функцию для вычисления размера обычного текста (удаление нулевого заполнения может удалять нулевой ценный текст в конце, если вы не будете осторожны).
Другие вопросы:
- пытается выполнить сбор данных перед дешифровкой (вы должны разархивировать данные после дешифрования)
- проблемы кодирования и кодирования символов, такие как попытка поместить с символьным значением
"0"
, что, вероятно, неверно
Я указал "ECB"
, потому что не знаю, какой режим используется. Вы можете изменить свой вопрос с помощью правильного режима и алгоритма заполнения, если сможете это выяснить. Возможно, вы захотите также попробовать режим CBC, если ECB не работает.
Обратите внимание, что режим ECB небезопасен для использования, за исключением особых обстоятельств. Использование CBC с рандомизированным IV является минимальным требованием.
Ответ 2
(3) DES шифрует/дешифрует блоки из 8 байтов. Поскольку не все тексты составляют ровно 8 байт, последний блок должен содержать байты, которые не являются оригинальными из обычного текста.
Трюк - это выяснить, какой из них является последним символом обычного текста. Иногда длина обычного текста известна заранее - тогда символы заполнения могут быть действительно чем-то.
Если длина обычного текста неизвестна, необходимо использовать детерминированный алгоритм заполнения, например. PKCS5Padding. PKCS5Padding всегда выполняет отступы, даже если открытый текст N * блокируется в байтах. Причина этого проста: в противном случае он не знает, является ли последний байт простым текстом или дополнением.
Я попытаюсь прийти с рабочим кодом позже... нужно его протестировать. Тем временем попробуйте использовать алгоритмы заполнения.
Ответ 3
Если в документации не указано, какое дополнение используется для входящего cyphertext, затем расшифруйте с помощью "NoPadding", который будет принимать любые дополнения к последнему блоку. Затем взгляните на шестую часть вашего последнего блока. Это скажет вам, какое дополнение используется в конце шифрования. Измените свой код, чтобы ожидать правильный тип заполнения. Различные типы дополнений покрываются здесь.
Ответ 4
Я взглянул на ваш метод 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; вы можете попробовать с помощью алгоритма и преобразования, используемого в этом примере.