Ответ 1
Просто идея: возможно, вы прыгаете между разными провайдерами (Sun/BC/etc)
С уважением.
Я использую javax.crypto.cipher для шифрования и дешифрования некоторых данных. Это хорошо работает. Но иногда, дешифрование faill с badPaddingException. Если я сравниваю успешный вызов с факсимильным вызовом, входные данные, данные для шифрования, одинаковы, и шифр инициализируется таким же образом.
что как я управляю своим шифром
dcipher = Cipher.getInstance("PBEWithMD5AndDES");
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
Как я его использую
dec = Base64.decode(str) ;
byte[] utf8 = dcipher.doFinal(dec);
Исключение возникает в doFinal.
Любая идея?
Спасибо!
О, btw, я использую bouncyCastle в качестве поставщика и добавляю его сверху в список с помощью
Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
Ради полноты и потому, что проблема все же время от времени срабатывает. Вот полный класс.
public class EncryptDecryptUtil {
/** Encryption Cipher */
private static Cipher ecipher;
/** Decription Cipher */
private static Cipher dcipher;
private static Logger logger = Logger.getLogger(EncryptDecryptUtil.class);
/**
* Constructor used to create this object. Responsible for setting and initializing this object encrypter and
* decrypter Cipher instances given a Secret Key and algorithm.
*
* @param key Secret Key used to initialize both the encrypter and decrypter instances.
* @param algorithm Which algorithm to use for creating the encrypter and decrypter instances.
*/
public EncryptDecryptUtil(SecretKey key, String algorithm) {
Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
try {
ecipher = Cipher.getInstance(algorithm);
dcipher = Cipher.getInstance(algorithm);
ecipher.init(Cipher.ENCRYPT_MODE, key);
dcipher.init(Cipher.DECRYPT_MODE, key);
} catch (NoSuchPaddingException e) {
System.out.println("EXCEPTION: NoSuchPaddingException");
} catch (NoSuchAlgorithmException e) {
System.out.println("EXCEPTION: NoSuchAlgorithmException");
} catch (InvalidKeyException e) {
System.out.println("EXCEPTION: InvalidKeyException");
}
}
/**
* Constructor used to create this object. Responsible for setting and initializing this object encrypter and
* decrypter Chipher instances given a Pass Phrase and algorithm.
*
* @param passPhrase Pass Phrase used to initialize both the encrypter and decrypter instances.
*/
public EncryptDecryptUtil(String passPhrase) {
Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
// 8-bytes Salt
byte[] salt = { (byte) 0xB9, (byte) 0x8B, (byte) 0xD8, (byte) 0x31, (byte) 0x55, (byte) 0x24, (byte) 0xF3, (byte) 0x13 };
// Iteration count
int iterationCount = 19;
try {
// Generate the secret key associated to the passphrase.
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
// Get instance of the cipher
ecipher = Cipher.getInstance("PBEWithMD5AndDES");
dcipher = Cipher.getInstance("PBEWithMD5AndDES");
// Prepare the parameters to the cipthers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (InvalidAlgorithmParameterException e) {
logger.error("during encrypter instantiation",e);
} catch (InvalidKeySpecException e) {
logger.error("during encrypter instantiation",e);
} catch (NoSuchPaddingException e) {
logger.error("during encrypter instantiation",e);
} catch (NoSuchAlgorithmException e) {
logger.error("during encrypter instantiation",e);
} catch (InvalidKeyException e) {
logger.error("during encrypter instantiation",e);
}
}
/**
* Takes a single String as an argument and returns an Encrypted version of that String.
*
* @param str String to be encrypted
* @return <code>String</code> Encrypted version of the provided String
*/
public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new String( Base64.encode(enc), "UTF8");
} catch (BadPaddingException e) {
logger.error("during encryption : ",e);
} catch (IllegalBlockSizeException e) {
logger.error("during encryption : ",e);
} catch (UnsupportedEncodingException e) {
logger.error("during encryption : ",e);
}
return new String();
}
/**
* Takes a encrypted String as an argument, decrypts and returns the decrypted String.
*
* @param str Encrypted String to be decrypted
* @return <code>String</code> Decrypted version of the provided String
*/
public String decrypt(String str) {
byte[] dec = new byte[0];
try {
// Decode base64 to get bytes. Not sure to understand why.
dec = Base64.decode(str) ;
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (BadPaddingException e) {
logger.error("error during decryption. String to decode was : "+str + " byte array to decode was : "+ Arrays.toString(dec) ,e);
} catch (IllegalBlockSizeException e) {
logger.error("during decryption : ",e);
} catch (UnsupportedEncodingException e) {
logger.error("during decryption : ",e);
}
return new String();
}
}
Изменить: Я хотел бы подчеркнуть это 2 пункта:
...
for( int i = 0 ; i<1000000000 ; i++){
EncryptDecryptUtil encryptDecript = new EncryptDecryptUtil("pass");
if ( !"YES".equals(encryptDecript.decrypt("Q3qWLKo6yJY="))){
System.out.println("Fail at call " + i);
throw new InvalidParameterException() ;
}
}
Так может быть, это может произойти из-за того, что я использую класс EncryptDecryptUtils? Это поле Spring bean, которое может быть использовано один раз.
com.myStuff.dlm.cryptography.EncryptDecryptUtil error during decryption. String to decode was : Q3qWLKo6yJY= byte array to decode was : [114, 52, -52, -54, 82, 87, 124, 84]
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.SunJCE_ab.b(DashoA13*..)
at com.sun.crypto.provider.PBEWithMD5AndDESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at com.dvidea.dlm.cryptography.EncryptDecryptUtil.decrypt(EncryptDecryptUtil.java:166)
Просто идея: возможно, вы прыгаете между разными провайдерами (Sun/BC/etc)
С уважением.
Некоторые мысли:
Трассировка стека указывает на то, что поставщик BouncyCastle не может быть поднят. Вы можете попытаться передать провайдера явно на Cipher и KeyFactory.
Код, который вы указали, является единственным кодом, который работает, не так ли? В этом процессе нет других потоков?
Интересно, можете ли вы ввести в заблуждение шифр, указав соль и iterationCount как на KeySpec, так и на ParameterSpec. Примеры, которые я вижу (зашифровать PBEWithMD5AndDES в j2me, http://cs.saddleback.edu/rwatkins/CS4B/Crypto/FileEncryptor.html), do не указывать при создании KeySpec.
Возможно, это связано с этой ошибкой: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4414138
Нижняя строка: не используйте String для хранения зашифрованный текст!
Возможно, это может исправить:
dec = Base64.decode(str.getBytes("UTF-8"));
Это немного выстрел в темноте, но попробуйте создать свой шифр следующим образом:
dcipher = Cipher.getInstance("PBEWithMD5AndDES/CBC/PKCS5Padding");
badPaddingException обычно означает, что пароль был неправильным. Использование "wrong_password" в качестве пароля воссоздает ошибку:
EncryptDecryptUtil encryptDecript = new EncryptDecryptUtil("wrong_password");
encryptDecript.decrypt("Q3qWLKo6yJY=");
Результаты:
error during decryption. String to decode was : Q3qWLKo6yJY=
byte array to decode was : [67, 122, -106, 44, -86, 58, -56, 106]
javax.crypto.BadPaddingException: Given final block not properly padded
Я собираюсь выйти на конечность и предположить, что это проблема безопасности потоков.
Попробуйте добавить блоки synchronized
вокруг вызовов ecipher/dcipher#doFinal
, вы можете легко использовать связанный объект Cipher в качестве объекта для синхронизации с.
byte[] enc;
synchronized (ecipher) {
enc = ecipher.doFinal(utf8);
}