1

我需要一些帮助来验证下面的代码片段,以使用 CBC、PKCS5Padding 和 IV 进行 Java AES 加密。

我测试了代码并且能够加密和解密。我有一些疑问,如下所述。

  1. 作为一个好的约定,密码应该存储在哪里?
  2. 将盐和 IV 字节附加/检索到密文的方式是否正常?
  3. 任何其他意见高度赞赏,谢谢!
public class Encryption {

    private static int iterations = 65536;
    private static int keySize = 128;
    private static char[] password = "password".toCharArray();
    private static String algorithm= "PBKDF2WithHmacSHA1";


    private static final String SEPARATOR = "~";


     public static void main(String []args) throws Exception {

         String filePath = "test.xml";

         String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));

         String encrMesg = encrypt(fileContent);

         System.out.println("Encrypted: " + encrypt(encrMesg)); 

         System.out.println("Decrypted: " + decrypt(encrMesg)); 
     }


    public static String encrypt(String plaintext) throws Exception {


        byte[] saltBytes = getSalt().getBytes();

        SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
        PBEKeySpec spec = new PBEKeySpec(password, saltBytes, iterations, keySize);
        SecretKey secretKey = skf.generateSecret(spec);
        SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
        AlgorithmParameters params = cipher.getParameters();

        byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
        byte[] cipherText = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));

        return DatatypeConverter.printBase64Binary(ivBytes)+SEPARATOR+DatatypeConverter.printBase64Binary(saltBytes)
        +SEPARATOR+DatatypeConverter.printBase64Binary(cipherText);
    }

    public static String decrypt(String encryptedText) throws Exception {

        System.out.println(encryptedText);

        String[] encryptedArr = encryptedText.split(SEPARATOR);

        byte[] ivBytes = DatatypeConverter.parseBase64Binary(new String(encryptedArr[0]));

        byte[] salt = DatatypeConverter.parseBase64Binary(new String(encryptedArr[1]));

        byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedArr[2]));

        SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
        PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keySize);
        SecretKey secretKey = skf.generateSecret(spec);
        SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));

        byte[] decryptedTextBytes = null;

        try {
            decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return new String(decryptedTextBytes);

    }

    public static String getSalt() throws Exception {

        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[20];
        sr.nextBytes(salt);
        return new String(salt);
    }

}
4

1 回答 1

1

查询

作为一个好的约定,密码应该存储在哪里?

对称密钥最好存放在保险库中。否则,他们应该继续使用密钥库,但是您会遇到保护密钥库密码的问题。

将 Salt 和 IV 字节附加/检索到密文的方式是否可以?

盐应该通过以下方式生成:

SecureRandom random = SecureRandom.getInstanceStrong();

否则,您将使用较弱的熵池(即 Linux 中的 /dev/urandom)来生成安全数字,这会导致更容易被破解的弱密钥。

任何其他意见高度赞赏,谢谢!

在处理字符串转换时,您应该始终使用相同的编码,即.getBytes("UTF-8")避免出现问题。例如,在转换盐时不要使用它。

于 2017-05-10T10:02:30.100 回答