1. 程式人生 > 程式設計 >Java AES加密和解密教程

Java AES加密和解密教程

在本教程中,我們將看到如何使用JDK中的Java密碼體系結構(JCA)來實現AES加密和解密。對稱金鑰塊密碼在資料加密中起重要作用。這意味著同一金鑰可用於加密和解密。高階加密標準(AES)是一種廣泛使用的對稱金鑰加密演算法。

AES演算法是一種迭代的對稱金鑰塊密碼,它支援128、192和256位的加密金鑰(祕密金鑰),以對128位的塊中的資料進行加密和解密。

在AES中生成金鑰的方法有兩種:從隨機數生成或從給定密碼生成。

在第一種方法中,應該從像SecureRandom類這樣的加密安全(偽)隨機數生成器生成祕密金鑰。為了生成金鑰,我們可以使用KeyGenerator類。讓我們定義一種用於生成大小為n(128、192和256)位的AES金鑰的方法:

public static SecretKey generateKey(int n) throws NoSuchAlgorithmException {
  KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
  keyGenerator.init(n);
  SecretKey key = keyGenerator.generateKey();
  return key;
}

在第二種方法中,可以使用基於密碼的金鑰派生功能(例如PBKDF2)從給定的密碼派生AES祕密金鑰。下面方法可通過65,536次迭代和256位金鑰長度從給定密碼生成AES金鑰:

public static SecretKey getKeyFromPassword(String password,String salt)
  throws NoSuchAlgorithmException,InvalidKeySpecException {
 
  SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
  KeySpec spec = new PBEKeySpec(password.toCharArray(),salt.getBytes(),65536,256);
  SecretKey secret = new SecretKeySpec(factory.generateSecret(spec)
    .getEncoded(),"AES");
  return secret;
}

加密字串

要實現輸入字串加密,我們首先需要根據上一節生成金鑰和初始化向量IV:

IV是偽隨機值,其大小與加密的塊相同。我們可以使用SecureRandom類生成隨機IV。

讓我們定義一種生成IV的方法:

public static IvParameterSpec generateIv() {
  byte[] iv = new byte[16];
  new SecureRandom().nextBytes(iv);
  return new IvParameterSpec(iv);
}

下一步,我們使用getInstance()方法從Cipher類建立一個例項。

此外,我們使用帶有祕密金鑰,IV和加密模式的init()方法配置密碼例項。最後,我們通過呼叫doFinal()方法對輸入字串進行加密。此方法獲取輸入位元組並以位元組為單位返回密文:

public static String encrypt(String algorithm,String input,SecretKey key,IvParameterSpec iv) throws NoSuchPaddingException,NoSuchAlgorithmException,InvalidAlgorithmParameterException,InvalidKeyException,BadPaddingException,IllegalBlockSizeException {
 
  Cipher cipher = Cipher.getInstance(algorithm);
  cipher.init(Cipher.ENCRYPT_MODE,key,iv);
  byte[] cipherText = cipher.doFinal(input.getBytes());
  return Base64.getEncoder()
    .encodeToString(cipherText);
}

為了解密輸入字串,我們可以使用DECRYPT_MODE初始化密碼來解密內容:

public static String decrypt(String algorithm,String cipherText,IllegalBlockSizeException {
 
  Cipher cipher = Cipher.getInstance(algorithm);
  cipher.init(Cipher.DECRYPT_MODE,iv);
  byte[] plainText = cipher.doFinal(Base64.getDecoder()
    .decode(cipherText));
  return new String(plainText);
}

編寫一個用於加密和解密字串輸入的測試方法:

@Test
void givenString_whenEncrypt_thenSuccess()
  throws NoSuchAlgorithmException,IllegalBlockSizeException,NoSuchPaddingException {
 
  String input = "baeldung";
  SecretKey key = AESUtil.generateKey(128);
  IvParameterSpec ivParameterSpec = AESUtil.generateIv();
  String algorithm = "AES/CBC/PKCS5Padding";
  String cipherText = AESUtil.encrypt(algorithm,input,ivParameterSpec);
  String plainText = AESUtil.decrypt(algorithm,cipherText,ivParameterSpec);
  Assertions.assertEquals(input,plainText);
}

加密檔案

現在,讓我們使用AES演算法加密檔案。步驟是相同的​​,但是我們需要一些IO類來處理檔案。讓我們加密一個文字檔案:

public static void encryptFile(String algorithm,IvParameterSpec iv,File inputFile,File outputFile) throws IOException,NoSuchPaddingException,iv);
  FileInputStream inputStream = new FileInputStream(inputFile);
  FileOutputStream outputStream = new FileOutputStream(outputFile);
  byte[] buffer = new byte[64];
  int bytesRead;
  while ((bytesRead = inputStream.read(buffer)) != -1) {
    byte[] output = cipher.update(buffer,bytesRead);
    if (output != null) {
      outputStream.write(output);
    }
  }
  byte[] outputBytes = cipher.doFinal();
  if (outputBytes != null) {
    outputStream.write(outputBytes);
  }
  inputStream.close();
  outputStream.close();
}

請注意,不建議嘗試將整個檔案(尤其是大檔案)讀入記憶體。相反,我們一次加密一個緩衝區。

為了解密檔案,我們使用類似的步驟,並使用DECRYPT_MODE初始化密碼,如前所述。

再次,讓我們定義一個用於加密和解密文字檔案的測試方法。在這種方法中,我們從測試資源目錄中讀取baeldung.txt檔案,將其加密為一個名為baeldung.encrypted的檔案,然後將該檔案解密為一個新檔案:

@Test
void givenFile_whenEncrypt_thenSuccess()
  throws NoSuchAlgorithmException,IOException,NoSuchPaddingException {
 
  SecretKey key = AESUtil.generateKey(128);
  String algorithm = "AES/CBC/PKCS5Padding";
  IvParameterSpec ivParameterSpec = AESUtil.generateIv();
  Resource resource = new ClassPathResource("inputFile/baeldung.txt");
  File inputFile = resource.getFile();
  File encryptedFile = new File("classpath:baeldung.encrypted");
  File decryptedFile = new File("document.decrypted");
  AESUtil.encryptFile(algorithm,ivParameterSpec,inputFile,encryptedFile);
  AESUtil.decryptFile(
   algorithm,encryptedFile,decryptedFile);
  assertThat(inputFile).hasSameTextualContentAs(decryptedFile);
}

基於密碼加密解密

我們可以使用從給定密碼派生的金鑰進行AES加密和解密。

為了生成金鑰,我們使用getKeyFromPassword()方法。加密和解密步驟與字串輸入部分中顯示的步驟相同。然後,我們可以使用例項化的密碼和提供的金鑰來執行加密。

讓我們寫一個測試方法:

@Test
void givenPassword_whenEncrypt_thenSuccess()
  throws InvalidKeySpecException,NoSuchPaddingException {
 
  String plainText = "www.baeldung.com";
  String password = "baeldung";
  String salt = "12345678";
  IvParameterSpec ivParameterSpec = AESUtil.generateIv();
  SecretKey key = AESUtil.getKeyFromPassword(password,salt);
  String cipherText = AESUtil.encryptPasswordBased(plainText,ivParameterSpec);
  String decryptedCipherText = AESUtil.decryptPasswordBased(
   cipherText,ivParameterSpec);
  Assertions.assertEquals(plainText,decryptedCipherText);
}

加密物件

為了加密Java物件,我們需要使用SealedObject類。該物件應可序列化。讓我們從定義學生類開始:

public class Student implements Serializable {
  private String name;
  private int age;

  // standard setters and getters
}

接下來,讓我們加密Student物件:

public static SealedObject encryptObject(String algorithm,Serializable object,iv);
  SealedObject sealedObject = new SealedObject(object,cipher);
  return sealedObject;
}

稍後可以使用正確的密碼解密加密的物件:

public static Serializable decryptObject(String algorithm,SealedObject sealedObject,ClassNotFoundException,IOException {
 
  Cipher cipher = Cipher.getInstance(algorithm);
  cipher.init(Cipher.DECRYPT_MODE,iv);
  Serializable unsealObject = (Serializable) sealedObject.getObject(cipher);
  return unsealObject;
}

讓我們寫一個測試用例:

@Test
void givenObject_whenEncrypt_thenSuccess()
  throws NoSuchAlgorithmException,ClassNotFoundException {
 
  Student student = new Student("Baeldung",20);
  SecretKey key = AESUtil.generateKey(128);
  IvParameterSpec ivParameterSpec = AESUtil.generateIv();
  String algorithm = "AES/CBC/PKCS5Padding";
  SealedObject sealedObject = AESUtil.encryptObject(
   algorithm,student,ivParameterSpec);
  Student object = (Student) AESUtil.decryptObject(
   algorithm,sealedObject,ivParameterSpec);
  assertThat(student).isEqualToComparingFieldByField(object);
}

可以在GitHub上獲得本文的完整原始碼。

以上就是Java AES加密和解密教程的詳細內容,更多關於Java AES加密和解密的資料請關注我們其它相關文章!