Java AES加密和解密教程
在本教程中,我们将看到如何使用JDK中的Java密码体系结构(JCA)来实现AES加密和解密。对称密钥块密码在数据加密中起重要作用。这意味着同一密钥可用于加密和解密。高级加密标准(AES)是一种广泛使用的对称密钥加密算法。
AES算法是一种迭代的对称密钥块密码,它支持128、192和256位的加密密钥(秘密密钥),以对128位的块中的数据进行加密和解密。
在AES中生成密钥的方法有两种:从随机数生成或从给定密码生成。
在第一种方法中,应该从像SecureRandom类这样的加密安全(伪)随机数生成器生成秘密密钥。为了生成密钥,我们可以使用KeyGenerator类。让我们定义一种用于生成大小为n(128、192和256)位的AES密钥的方法:
publicstaticSecretKeygenerateKey(intn)throwsNoSuchAlgorithmException{
KeyGeneratorkeyGenerator=KeyGenerator.getInstance("AES");
keyGenerator.init(n);
SecretKeykey=keyGenerator.generateKey();
returnkey;
}
在第二种方法中,可以使用基于密码的密钥派生功能(例如PBKDF2)从给定的密码派生AES秘密密钥。下面方法可通过65,536次迭代和256位密钥长度从给定密码生成AES密钥:
publicstaticSecretKeygetKeyFromPassword(Stringpassword,Stringsalt)
throwsNoSuchAlgorithmException,InvalidKeySpecException{
SecretKeyFactoryfactory=SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpecspec=newPBEKeySpec(password.toCharArray(),salt.getBytes(),65536,256);
SecretKeysecret=newSecretKeySpec(factory.generateSecret(spec)
.getEncoded(),"AES");
returnsecret;
}
加密字符串
要实现输入字符串加密,我们首先需要根据上一节生成密钥和初始化向量IV:
IV是伪随机值,其大小与加密的块相同。我们可以使用SecureRandom类生成随机IV。
让我们定义一种生成IV的方法:
publicstaticIvParameterSpecgenerateIv(){
byte[]iv=newbyte[16];
newSecureRandom().nextBytes(iv);
returnnewIvParameterSpec(iv);
}
下一步,我们使用getInstance()方法从Cipher类创建一个实例。
此外,我们使用带有秘密密钥,IV和加密模式的init()方法配置密码实例。最后,我们通过调用doFinal()方法对输入字符串进行加密。此方法获取输入字节并以字节为单位返回密文:
publicstaticStringencrypt(Stringalgorithm,Stringinput,SecretKeykey,
IvParameterSpeciv)throwsNoSuchPaddingException,NoSuchAlgorithmException,
InvalidAlgorithmParameterException,InvalidKeyException,
BadPaddingException,IllegalBlockSizeException{
Ciphercipher=Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE,key,iv);
byte[]cipherText=cipher.doFinal(input.getBytes());
returnBase64.getEncoder()
.encodeToString(cipherText);
}
为了解密输入字符串,我们可以使用DECRYPT_MODE初始化密码来解密内容:
publicstaticStringdecrypt(Stringalgorithm,StringcipherText,SecretKeykey,
IvParameterSpeciv)throwsNoSuchPaddingException,NoSuchAlgorithmException,
InvalidAlgorithmParameterException,InvalidKeyException,
BadPaddingException,IllegalBlockSizeException{
Ciphercipher=Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE,key,iv);
byte[]plainText=cipher.doFinal(Base64.getDecoder()
.decode(cipherText));
returnnewString(plainText);
}
编写一个用于加密和解密字符串输入的测试方法:
@Test
voidgivenString_whenEncrypt_thenSuccess()
throwsNoSuchAlgorithmException,IllegalBlockSizeException,InvalidKeyException,
BadPaddingException,InvalidAlgorithmParameterException,NoSuchPaddingException{
Stringinput="baeldung";
SecretKeykey=AESUtil.generateKey(128);
IvParameterSpecivParameterSpec=AESUtil.generateIv();
Stringalgorithm="AES/CBC/PKCS5Padding";
StringcipherText=AESUtil.encrypt(algorithm,input,key,ivParameterSpec);
StringplainText=AESUtil.decrypt(algorithm,cipherText,key,ivParameterSpec);
Assertions.assertEquals(input,plainText);
}
加密文件
现在,让我们使用AES算法加密文件。步骤是相同的,但是我们需要一些IO类来处理文件。让我们加密一个文本文件:
publicstaticvoidencryptFile(Stringalgorithm,SecretKeykey,IvParameterSpeciv,
FileinputFile,FileoutputFile)throwsIOException,NoSuchPaddingException,
NoSuchAlgorithmException,InvalidAlgorithmParameterException,InvalidKeyException,
BadPaddingException,IllegalBlockSizeException{
Ciphercipher=Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE,key,iv);
FileInputStreaminputStream=newFileInputStream(inputFile);
FileOutputStreamoutputStream=newFileOutputStream(outputFile);
byte[]buffer=newbyte[64];
intbytesRead;
while((bytesRead=inputStream.read(buffer))!=-1){
byte[]output=cipher.update(buffer,0,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
voidgivenFile_whenEncrypt_thenSuccess()
throwsNoSuchAlgorithmException,IOException,IllegalBlockSizeException,
InvalidKeyException,BadPaddingException,InvalidAlgorithmParameterException,
NoSuchPaddingException{
SecretKeykey=AESUtil.generateKey(128);
Stringalgorithm="AES/CBC/PKCS5Padding";
IvParameterSpecivParameterSpec=AESUtil.generateIv();
Resourceresource=newClassPathResource("inputFile/baeldung.txt");
FileinputFile=resource.getFile();
FileencryptedFile=newFile("classpath:baeldung.encrypted");
FiledecryptedFile=newFile("document.decrypted");
AESUtil.encryptFile(algorithm,key,ivParameterSpec,inputFile,encryptedFile);
AESUtil.decryptFile(
algorithm,key,ivParameterSpec,encryptedFile,decryptedFile);
assertThat(inputFile).hasSameTextualContentAs(decryptedFile);
}
基于密码加密解密
我们可以使用从给定密码派生的密钥进行AES加密和解密。
为了生成密钥,我们使用getKeyFromPassword()方法。加密和解密步骤与字符串输入部分中显示的步骤相同。然后,我们可以使用实例化的密码和提供的密钥来执行加密。
让我们写一个测试方法:
@Test
voidgivenPassword_whenEncrypt_thenSuccess()
throwsInvalidKeySpecException,NoSuchAlgorithmException,
IllegalBlockSizeException,InvalidKeyException,BadPaddingException,
InvalidAlgorithmParameterException,NoSuchPaddingException{
StringplainText="www.baeldung.com";
Stringpassword="baeldung";
Stringsalt="12345678";
IvParameterSpecivParameterSpec=AESUtil.generateIv();
SecretKeykey=AESUtil.getKeyFromPassword(password,salt);
StringcipherText=AESUtil.encryptPasswordBased(plainText,key,ivParameterSpec);
StringdecryptedCipherText=AESUtil.decryptPasswordBased(
cipherText,key,ivParameterSpec);
Assertions.assertEquals(plainText,decryptedCipherText);
}
加密对象
为了加密Java对象,我们需要使用SealedObject类。该对象应可序列化。让我们从定义学生类开始:
publicclassStudentimplementsSerializable{
privateStringname;
privateintage;
//standardsettersandgetters
}
接下来,让我们加密Student对象:
publicstaticSealedObjectencryptObject(Stringalgorithm,Serializableobject,
SecretKeykey,IvParameterSpeciv)throwsNoSuchPaddingException,
NoSuchAlgorithmException,InvalidAlgorithmParameterException,
InvalidKeyException,IOException,IllegalBlockSizeException{
Ciphercipher=Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE,key,iv);
SealedObjectsealedObject=newSealedObject(object,cipher);
returnsealedObject;
}
稍后可以使用正确的密码解密加密的对象:
publicstaticSerializabledecryptObject(Stringalgorithm,SealedObjectsealedObject,
SecretKeykey,IvParameterSpeciv)throwsNoSuchPaddingException,
NoSuchAlgorithmException,InvalidAlgorithmParameterException,InvalidKeyException,
ClassNotFoundException,BadPaddingException,IllegalBlockSizeException,
IOException{
Ciphercipher=Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE,key,iv);
SerializableunsealObject=(Serializable)sealedObject.getObject(cipher);
returnunsealObject;
}
让我们写一个测试用例:
@Test
voidgivenObject_whenEncrypt_thenSuccess()
throwsNoSuchAlgorithmException,IllegalBlockSizeException,InvalidKeyException,
InvalidAlgorithmParameterException,NoSuchPaddingException,IOException,
BadPaddingException,ClassNotFoundException{
Studentstudent=newStudent("Baeldung",20);
SecretKeykey=AESUtil.generateKey(128);
IvParameterSpecivParameterSpec=AESUtil.generateIv();
Stringalgorithm="AES/CBC/PKCS5Padding";
SealedObjectsealedObject=AESUtil.encryptObject(
algorithm,student,key,ivParameterSpec);
Studentobject=(Student)AESUtil.decryptObject(
algorithm,sealedObject,key,ivParameterSpec);
assertThat(student).isEqualToComparingFieldByField(object);
}
可以在GitHub上获得本文的完整源代码 。
以上就是JavaAES加密和解密教程的详细内容,更多关于JavaAES加密和解密的资料请关注毛票票其它相关文章!