我正在尝试使用 Java 的密码库来实现使用 GCM 模式的 AES 加密(因为它是一个 AEAD 方案,我想确保数据完好无损地到达)但我想使用密码以方便用户使用。直到我把头撞在砖墙上几次并阅读 this other stackoverflow response. 之前,让程序编译无误是一件困难的事。
现在,不幸的是程序可以编译,但它返回的密文只是消息中字节数的一小部分(我怀疑它只是 GCM 使用的 header ,实际消息根本没有被写入)。有人可以帮忙吗?
下面是一些程序变量:
private static final byte[] SALT = {(byte)0x3b,(byte)0x12,(byte)0x44,(byte)0x61,
(byte)0xec,(byte)0xc0,(byte)0xa1,(byte)0x82,
(byte)0x4d, (byte)0x97, (byte)0x4d, (byte)0x3c,
(byte)0x57, (byte)0xd9, (byte)0x94, (byte)0x52};
private static final String CIPHER_ALG = "AES/GCM/PKCS5Padding";
private static final String SEC_RANDOM_ALG = "SHA1PRNG";
private static final String S_KEY_FACTORY_ALG = "PBKDF2WithHmacSHA1";
private static final int AES_KEY_SIZE = 128; //num bits
private static final int GCM_NONCE_LENGTH = 16; //num bytes
private static final int GCM_TAG_LENGTH = 16; //num bytes
private static final int KEY_SPEC_ROUNDS = 65536;
这里是加密方法:
//FIXME somehow the cipher is truncating the message or something
private byte[] encrypt(File file, String password) throws IOException{
Path path = Paths.get(file.toURI());
byte[] messageBytes = Files.readAllBytes(path);
//Message bytes: 3253
System.out.printf("Message bytes: %d%n", messageBytes.length);
byte[] messageName = path.getFileName().toString().getBytes();
byte[] cipherBytes = {};
byte[] iv = {};
try{
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(S_KEY_FACTORY_ALG);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), SALT, KEY_SPEC_ROUNDS, AES_KEY_SIZE);
SecretKey key = keyFactory.generateSecret(pbeKeySpec);
System.out.printf("Secret key take 1: " + Arrays.toString(key.getEncoded()) + "%n");
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), "AES");
//FIXME this is not encrypting the message correctly
Cipher cipher = Cipher.getInstance(CIPHER_ALG);
final byte[] nonce = new byte[GCM_NONCE_LENGTH];
SecureRandom random = SecureRandom.getInstanceStrong();
random.nextBytes(nonce);
GCMParameterSpec paramSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
iv = paramSpec.getIV();
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
cipher.updateAAD(messageName);
cipher.update(messageBytes);
cipherBytes = cipher.doFinal();
}catch(NoSuchAlgorithmException e){
e.printStackTrace();
}catch(NoSuchPaddingException e){
e.printStackTrace();
}catch(InvalidKeyException e){
e.printStackTrace();
}catch(InvalidAlgorithmParameterException e){
e.printStackTrace();
}catch(InvalidKeySpecException e){
e.printStackTrace();
}catch(BadPaddingException e){
e.printStackTrace();
}catch(IllegalBlockSizeException e){
e.printStackTrace();
}
//IV bytes: 16
System.out.printf("IV bytes: %d%n", iv.length);
//Cipher bytes: 21
System.out.printf("Cipher bytes: %d%n", cipherBytes.length);
//Cipher bytes: [121, 68, 7, -69, -35, -9, -83, 101, -60, -80, 42, 59, -67, 126, 18, -82, 79, -60, 34, -125, 12]
System.out.printf("Cipher bytes: " + Arrays.toString(cipherBytes) + "%n");
//FIXME somehow the cipher is shortening the message or something
return cipherBytes;
}
最佳答案
您不应忽略 cipher.update(messageBytes);
的返回值。此外,GCM 在底层使用 CTR 模式。 CTR 模式不需要填充,因此您应该使用 "AES/GCM/NoPadding"
。
您现在可能只是得到密文的最后一部分和身份验证标签(即尾部而不是头部)。
关于java - 无法让 AES/GCM 使用密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36635576/