Я получил исключение в следующем коде для алгоритма AES в Java.
Код дешифрует зашифрованную строку и возвращает исходную строку.
Помогите мне зафиксировать это.
Код:
public class AES
{
public byte[] encrypted;
public byte[] original;
public String originalString;
public static String asHex (byte buf[])
{
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i; for (i = 0; i < buf.length; i++)
{
if (((int) buf[i] & 0xff) < 0x10) strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public String AESencryptalgo(byte[] text)
{
String newtext="";
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); encrypted = cipher.doFinal(text);
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec); original = cipher.doFinal(encrypted);
originalString = new String(original); System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{ }
finally
{
newtext=new String(encrypted);
System.out.println("ENCRYPTED "+newtext);
//AESdecryptalgo(newtext.getBytes());
return newtext;
}
}
public String AESdecryptalgo(byte[] text)
{
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
original = cipher.doFinal(text); //Exception occurs here
originalString = new String(original);
System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{
System.out.println("exception");
}
finally
{
System.out.println("DECRYPTED "+originalString);
return originalString;
}
}
public static void main(String[] args)
{
AES a=new AES();
a.AESencryptalgo("hello".getBytes());
System.out.println();
}}
`
исключение:
javax.crypto.BadPaddingException: Given final block not properly padded at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..) at
javax.crypto.Cipher.doFinal(DashoA13*..)
Ну, если это ошибка, то длина ввода должна быть кратна 16 при расшифровке с использованием шифра с набивкой. Тогда ответ очевиден, длина вашего буфера должна быть кратна 16. Проверяли ли вы длину buf[]?
Согласно Java™ Cryptography Architecture (JCA) Reference Guide (выделение мое):
Объекты Cipher
получаются при использовании одного изCipher getInstance()
статических фабричных методов. Здесь алгоритм имя немного отличается от других классов движков, в том смысле, что задается не просто имя алгоритма, но и "преобразование". A преобразование - это строка, которая описывает операцию (или набор операций), которая должна быть выполнена на заданным входом для получения некоторого выхода. A преобразование всегда включает имя криптографического алгоритма (например,DES
), за ним может следовать режим и схема набивки.Преобразование имеет вид:
- "algorithm/mode/padding" или
- "algorithm"
Например, следующие преобразования являются допустимыми:
"DES/CBC/PKCS5Padding"
"DES"
Если указано только имя преобразования, то система будет определит, существует ли реализация запрашиваемого преобразования, доступная в среде, и если их несколько одна, возвращает предпочтительную.
Если указаны и имя преобразования, и поставщик пакета указаны, система система определит, существует ли реализация запрашиваемого преобразования в пакете и выбросит исключение, если если нет.
Если режим или прокладка не указаны, используются специфические для поставщика значения по умолчанию для используются значения по умолчанию для режима и схемы прокладки. Например, провайдер
SunJCE
использует.ECB
в качестве режима по умолчанию, иPKCS5Padding
в качестве схемы набивки по умолчанию. дляDES
,DES-EDE
иBlowfish
. шифры. Это означает, что в случае провайдераSunJCE
:Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
и
Cipher c1 = Cipher.getInstance("DES");
являются эквивалентными утверждениями.
Использование режимов таких как CFB и OFB, блочные шифры могут шифровать данные в единицах, меньших, чем фактический размер блока шифра. Когда запросе такого режима, вы можете опционально указать количество битов обрабатываемых за один раз, добавив это число к имени режима, как показано в "DES/CFB8/NoPadding" и "DES/OFB32/PKCS5Padding" преобразования. Если такой номер не указан не указано, используется значение по умолчанию, заданное провайдером используется. (Например,
SunJCE
поставщик использует значение по умолчанию 64 бита для DES.) Таким образом, блочные шифры могут быть превратить в байт-ориентированные потоковые шифры, используя 8-битный режим, такой как CFB8 или OFB8.Приложение А этого документа содержит список стандартных имен, которые могут быть использовать для указания имени алгоритма, режим и компоненты схемы набивки преобразования.
Объекты, возвращаемые фабричными являются неинициализированными и должны быть инициализировать, прежде чем они станут пригодными для использования.
Поскольку в вашем коде не указан режим или padding, используются специфические для провайдера значения по умолчанию. Похоже, что ваш провайдер - SunJCE
, а его padding по умолчанию, вероятно, "NoPadding"
. С помощью этой прокладки вы отвечаете за то, чтобы размер зашифрованного массива байтов был кратен количеству байтов в секретном ключе. Вы можете облегчить себе жизнь, указав режим и padding в своем преобразовании:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
ВНИМАНИЕ: Вы не должны использовать режим ECB в реальном коде. Попробуйте вместо него CBC.
Обновление: Мне показалось нечестным рекомендовать режим CBC, не предложив небольшой пример того, как он работает:
public static void main(String... args) throws Exception {
byte[] data = "hello".getBytes();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
byte[] encryptedData = cipher.doFinal(data);
// When decrypting the encrypted data, you must provide the initialization vector used
// during the encryption phase.
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
if (!Arrays.equals(data, decryptedData)) {
throw new Exception("Data was not decrypted successfully");
}
}
Ваш код умудряется почти во всем ошибаться. Для начала, ваши ошибки включают:
Более того, ваш код работает без исключений для меня.