2016-04-14 76 views
1

我想使用Java的加密库来实现AES加密使用GCM模式(因为它是一个AEAD计划,我想确保数据完好无损),但我想喜欢使用用户友好的密码。让程序编译没有错误是一个困难,直到我几次抨击我的头墙砖墙,并阅读this other stackoverflow response.无法获取AES/GCM与密码

现在,不幸的是程序编译,但它返回的密文是字节数量的一小部分消息(我怀疑它只是GCM使用的标题,实际的消息根本没有被写入)。有人可以帮忙吗?

这里有一些程序变量:

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; 
} 
+0

你可能想看看我的[这个答案](http://stackoverflow.com/a/15712409/589259)关于加密相关的异常。 –

回答

3

你不应该忽视的cipher.update(messageBytes);返回值。此外,GCM使用下面的CTR模式。 CTR模式不需要填充,因此您应该使用"AES/GCM/NoPadding"

您可能刚刚获得了密文和认证标记的最后部分(即尾部而不是头部)。

+0

所以我应该将返回值分配给cipher.doFinal()方法吗?或者,我可以跳过该行,只需调用cipher.doFinal(messageBytes);我认为这是一样的。 – SupremeMitchell

+0

是的,工作,谢谢! – SupremeMitchell