Java BadPaddingException:GCM中的mac签入失败
我正在尝试使用AES-GCM和JDK 1.8 CipherOutputStream进行加密/解密,但在解密过程中遇到BadPaddingException。我在加密和解密期间使用相同的IV和密钥,但不确定出了什么问题。请参阅下面的代码:Java BadPaddingException:GCM中的mac签入失败,java,cryptography,bouncycastle,aes-gcm,badpaddingexception,Java,Cryptography,Bouncycastle,Aes Gcm,Badpaddingexception,我正在尝试使用AES-GCM和JDK 1.8 CipherOutputStream进行加密/解密,但在解密过程中遇到BadPaddingException。我在加密和解密期间使用相同的IV和密钥,但不确定出了什么问题。请参阅下面的代码: static String AES_GCM_MODE = "AES/GCM/NoPadding"; SecretKey secretKey; public SymmetricFileEncryption(){ Securi
static String AES_GCM_MODE = "AES/GCM/NoPadding";
SecretKey secretKey;
public SymmetricFileEncryption(){
Security.insertProviderAt( new BouncyCastleProvider(), 1);
setSecretKey();
}
public static void main(String[] args) throws Exception {
File inputFile = new File("test.txt");
File outputFile = new File("test-crypt.txt");
File out = new File("test-decrypt.txt");
SymmetricFileEncryption sym = new SymmetricFileEncryption();
sym.encrypt(inputFile, outputFile);
sym.decrypt(outputFile, out);
}
public Cipher getEncryptionCipher() throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
Cipher cipher = Cipher.getInstance(AES_GCM_MODE, "BC");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, getInitializationVector());
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), new IvParameterSpec(getInitializationVector()) );
return cipher;
}
private Cipher getDecryptionCipher(File inputFile) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, IOException, NoSuchProviderException {
//initialize cipher
Cipher cipher = Cipher.getInstance(AES_GCM_MODE, "BC");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, getInitializationVector());
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(),new IvParameterSpec(getInitializationVector()) );
return cipher;
}
public void encrypt(File inputFile, File outputFile) throws Exception {
Cipher cipher = getEncryptionCipher();
FileOutputStream fos = null;
CipherOutputStream cos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(inputFile);
fos = new FileOutputStream(outputFile);
cos = new CipherOutputStream(fos, cipher);
byte[] data = new byte[16];
int read = fis.read(data);
while (read != -1) {
cos.write(data, 0, read);
read = fis.read(data);
}
cos.flush();
}catch (Exception e){
e.printStackTrace();
}
finally {
fos.close();
cos.close();
fis.close();
}
String iv = new String(cipher.getIV());
}
public void decrypt(File inputFile, File outputFile) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, IOException, NoSuchProviderException {
Cipher cipher = getDecryptionCipher(inputFile);
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
CipherInputStream cipherInputStream = null;
try{
inputStream = new FileInputStream(inputFile);
cipherInputStream = new CipherInputStream(inputStream, cipher);
outputStream = new FileOutputStream(outputFile);
byte[] data = new byte[16];
int read = cipherInputStream.read(data);
while(read != -1){
outputStream.write(data);
read = cipherInputStream.read(data);
}
outputStream.flush();
}catch (Exception e){
e.printStackTrace();
}
finally {
cipherInputStream.close();
inputStream.close();
outputStream.close();
}
}
public void setSecretKey(){
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
secretKey = new SecretKeySpec(key, "AES");
}
public SecretKey getSecretKey(){
return secretKey;
}
public byte[] getInitializationVector(){
String ivstr = "1234567890ab"; //12 bytes
byte[] iv = ivstr.getBytes();//new byte[12];
return iv;
}
上述代码在第行解密期间导致以下错误
int read=cipherInputStream.read(数据)
- 加密无法正常工作:在
中,必须在加密
之前调用。这是因为文件输出流#关闭
调用生成标记并将其附加到密文。如果尚未调用CipherOutputStream#close
,则此部分只能写入FileOutputStream#close
实例。顺便说一下,不需要调用FileOutputStream
CipherOutputStream#flush
- 解密也有一个问题:在
中,decrypt
必须替换为outputStream.write(data)
。否则,通常会将太多数据写入outputStream.write(data,0,read)
-实例FileOutputStream
- 类和可能执行身份验证误报,因此不适用于GCM模式,例如来自
CipherInputStream的文档(Java 12): 此类可以捕获BadPaddingException和解密期间完整性检查失败引发的其他异常。不会重新引发这些异常,因此可能不会通知客户端完整性检查失败。由于这种行为,此类可能不适合在经过身份验证的操作模式(例如GCM)中与解密一起使用。需要经过身份验证的加密的应用程序可以直接使用Cipher API作为使用此类的替代方法 因此,应按照文档中的建议直接使用Cipher API,或使用BouncyCastle实现,例如用于加密:
模拟解密 请注意,即使身份验证失败,也会执行解密,因此开发人员必须确保丢弃结果,并且在这种情况下不使用结果import org.bouncycastle.crypto.io.CipherInputStream; import org.bouncycastle.crypto.io.CipherOutputStream; import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; ... public void encrypt(File inputFile, File outputFile) throws Exception { AEADBlockCipher cipher = getEncryptionCipher(); // Following code as before (but with fixes described above) ... } public AEADBlockCipher getEncryptionCipher() throws Exception { AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine()); cipher.init(true, // encryption new AEADParameters( new KeyParameter(getSecretKey().getEncoded()), 128, // tag length getInitializationVector(), "Optional Associated Data".getBytes())); return cipher; } ...
import org.bouncycastle.crypto.io.CipherInputStream;
import org.bouncycastle.crypto.io.CipherOutputStream;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
...
public void encrypt(File inputFile, File outputFile) throws Exception {
AEADBlockCipher cipher = getEncryptionCipher();
// Following code as before (but with fixes described above)
...
}
public AEADBlockCipher getEncryptionCipher() throws Exception {
AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
cipher.init(true, // encryption
new AEADParameters(
new KeyParameter(getSecretKey().getEncoded()),
128, // tag length
getInitializationVector(),
"Optional Associated Data".getBytes()));
return cipher;
}
...