我正在尝试解密一些文本,但我收到错误:
javax.crypto.BadPaddingException:错误:06065064:数字信封 例程:EVP_DecryptFinal_ex:解密不好 com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(母语 方法)com.android.org.conscrypt.OpenSSLCipher.doFinalInternal(OpenSSLCipher.java:430) com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:466)javax.crypto.Cipher.doFinal(Cipher.java:1340)
import android.util.Base64;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AESEncrypter {
private static final byte[] SALT = {
(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
(byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
};
private static final int ITERATION_COUNT = 65536;
private static final int KEY_LENGTH = 256;
private Cipher ecipher;
private Cipher dcipher;
public AESEncrypter(String passPhrase) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // "AES/CBC/NoPadding"
ecipher.init(Cipher.ENCRYPT_MODE, secret);
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
}
public String encrypt(String encrypt) throws Exception {
encrypt = encrypt.replace("\n", "");
byte[] bytes = encrypt.getBytes("UTF8");
byte[] encrypted = encrypt(bytes);
return Base64.encodeToString(encrypted, Base64.DEFAULT);
}
public byte[] encrypt(byte[] plain) throws Exception {
return ecipher.doFinal(plain);
}
public String decrypt(String encrypt) throws Exception {
encrypt = encrypt.replace("\n", "");
byte[] bytes = Base64.decode(encrypt, Base64.DEFAULT);
byte[] decrypted = decrypt(bytes);
return new String(decrypted, "UTF8");
}
public byte[] decrypt(byte[] encrypt) throws Exception {
return dcipher.doFinal(encrypt);
}
}
有什么建议吗?
答案 0 :(得分:2)
This answer is a complete shot in the dark, but it is plausible.
Currently, you're retrieving the IV as a byte array from the Cipher
instance for encryption and passing the same byte array to the Cipher
instance for decryption. The problem might be that this IV is not actually copied and will reflect the state of the execution of the encryption procedure. A common CBC implementation might use the byte array that is supposed to be the IV as a state for the encryption of every block. This state would change after encryption.
Thus, if the decryption works on a wrong IV and the original plaintext was shorter than 16 bytes, this will likely (~255 in 256) lead to a BadPaddingException. If the plaintext is 16 bytes or longer, then you will not see a BadPaddingException, but the first 16 bytes will look like garbage. I suggest that you look into how CBC mode works.
You need to copy the IV. So this should suffice:
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(Arrays.copyOf(iv, iv.length)));
Keep in mind that this only works if you encrypt and decrypt during one execution of the program. If you want to decrypt in a different execution, you need to store the IV somehow.
Since the IV does not need to be secret, it can be sent along with the ciphertext. It is customary to prepend it to the ciphertext and slice it off before decryption.
The salt should also be random and you can send it along with the ciphertext in the same way as the IV.
The ciphertext is not authenticated. Thus, you cannot detect (malicious) manipulation. It is best to use authenticated modes like GCM/EAX or use an encrypt-then-MAC scheme with a strong MAC like HMAC-SHA256.