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加密和解密的資料請關注我們其它相關文章!