不管文件大小如何,每个解密的文件都会附加32个字节的附加字符。我可以剪掉32个字节,但是它们来自哪里,如何避免在输出文件中出现它们?
这是我的源代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
public class EtAesCrypto {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final int KEY_LENGTH = 256;
private static final int SALT_LENGTH = 16;
private static final int IV_LENGTH = 12;
private static final int AUT_TAG_LENGTH = 128;
private static final int ITERATIONS = 100;
private static final String ALGORITHM = "AES";
private static final String SECRET_KEY_ALGORITHM = "PBKDF2WithHmacSHA256";
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private String msg;
public void encrypt(String path2Original, String path2Encrypted, String password) {
try (FileOutputStream out = new FileOutputStream(path2Encrypted)) {
byte[] salt = new byte[SALT_LENGTH];
byte[] iv = new byte[IV_LENGTH];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_ALGORITHM);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec skey = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);
secureRandom.nextBytes(iv);
logger.trace("IV length: {}", iv.length);
out.write(salt);
out.write(iv);
Cipher ci = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec parameterSpec = new GCMParameterSpec(AUT_TAG_LENGTH, iv);
ci.init(Cipher.ENCRYPT_MODE, skey, parameterSpec);
try (FileInputStream in = new FileInputStream(path2Original)) {
processStream(ci, in, out);
}
} catch (Exception e) {
throw new RuntimeException("Encryption of file with id failed.");
}
}
public void decrypt(String path2Encrypted, OutputStream os, String password, String fileId) {
try (FileInputStream in = new FileInputStream(path2Encrypted)) {
doDecryption(in, os, password);
} catch (Exception e){
msg = String.format("Decryption of file with id '%s' failed.", fileId);
logger.warn("Decryption of file '{}' with id '{}' failed: {}", path2Encrypted, fileId, e.getMessage(), e);
throw new RuntimeException(msg);
}
}
public void decrypt(String path2Encrypted, String path2Decrypted, String password, String fileId) {
try (FileInputStream in = new FileInputStream(path2Encrypted)) {
try (FileOutputStream os = new FileOutputStream(path2Decrypted)) {
doDecryption(in, os, password);
}
} catch (Exception e){
msg = String.format("Decryption of file with id '%s' failed.", fileId);
logger.warn("Decryption of file '{}' with id '{}' failed: {}", path2Encrypted, fileId, e.getMessage(), e);
throw new RuntimeException(msg);
}
}
private void doDecryption(InputStream in, OutputStream out, String password) throws Exception {
byte[] salt = new byte[SALT_LENGTH];
byte[] iv = new byte[IV_LENGTH];
int saltBytes = in.read(salt);
int ivBytes = in.read(iv);
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_ALGORITHM);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec skey = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);
Cipher ci = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec parameterSpec = new GCMParameterSpec(AUT_TAG_LENGTH, iv);
ci.init(Cipher.ENCRYPT_MODE, skey, parameterSpec);
processStream(ci, in, out);
}
private void processStream(Cipher ci, InputStream in, OutputStream out) throws Exception {
byte[] inBuffer = new byte[1024];
int len;
while ((len = in.read(inBuffer)) != -1) {
byte[] outBuffer = ci.update(inBuffer, 0, len);
if (outBuffer != null)
out.write(outBuffer);
}
byte[] outBuffer = ci.doFinal();
if (outBuffer != null)
out.write(outBuffer);
}
}
答案 0 :(得分:2)
解密时应使用Cipher.DECRYPT_MODE
。
其他字节是GCM标签(MAC)。它是在加密期间创建的,并在解密期间进行了检查。
在GCM模式下,加密和解密的过程是相同的(XOR),这就是为什么使用Cipher.ENCRYPT_MODE
进行解密(除了MAC部分)似乎可以正常工作的原因。