简单的Java AESencryption/解密示例
下面的例子有什么问题?
问题是解密string的第一部分是无稽之谈。 但是,其余的很好,我得到…
Result: `£eB6O geS i are you? Have a nice day.
@Test public void testEncrypt() { try { String s = "Hello there. How are you? Have a nice day."; // Generate key KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128); SecretKey aesKey = kgen.generateKey(); // Encrypt cipher Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey); // Encrypt ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, encryptCipher); cipherOutputStream.write(s.getBytes()); cipherOutputStream.flush(); cipherOutputStream.close(); byte[] encryptedBytes = outputStream.toByteArray(); // Decrypt cipher Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded()); decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec); // Decrypt outputStream = new ByteArrayOutputStream(); ByteArrayInputStream inStream = new ByteArrayInputStream(encryptedBytes); CipherInputStream cipherInputStream = new CipherInputStream(inStream, decryptCipher); byte[] buf = new byte[1024]; int bytesRead; while ((bytesRead = cipherInputStream.read(buf)) >= 0) { outputStream.write(buf, 0, bytesRead); } System.out.println("Result: " + new String(outputStream.toByteArray())); } catch (Exception ex) { ex.printStackTrace(); } }
包括我自己在内的许多人在面对诸如忘记转换为Base64,初始化向量,字符集等信息时面临着许多问题。所以我想到了一个完整的代码。
希望这对大家都有用:编译你需要额外的Apache Commons Codec jar,可以在这里find: http : //commons.apache.org/proper/commons-codec/download_codec.cgi
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; public class Encryptor { public static String encrypt(String key, String initVector, String value) { try { IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(value.getBytes()); System.out.println("encrypted string: " + Base64.encodeBase64String(encrypted)); return Base64.encodeBase64String(encrypted); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static String decrypt(String key, String initVector, String encrypted) { try { IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); return new String(original); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static void main(String[] args) { String key = "Bar12345Bar12345"; // 128 bit key String initVector = "RandomInitVector"; // 16 bytes IV System.out.println(decrypt(key, initVector, encrypt(key, initVector, "Hello World"))); } }
在我看来,你不能正确处理你的初始化vector(IV)。 自从我上次读到AES,IV和块链以来,已经有很长一段时间了,但是你的路线
IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());
似乎并不确定。 在AES的情况下,可以将初始化向量视为密码实例的“初始状态”,这种状态是一些信息,不能从密钥中获得,而是从encryption密码的实际计算中获得。 (可以争辩说,如果IV可以从密钥中提取出来,那么这将是没有用的,因为在初始阶段密钥已经被提供给密码实例)。
因此,您应该在encryption结束时从密码实例获取IV作为byte []
cipherOutputStream.close(); byte[] iv = encryptCipher.getIV();
你应该使用这个byte []来初始化DECRYPT_MODE
的Cipher
:
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
那么,你的解密应该是确定的。 希望这可以帮助。
这里没有Apache Commons Codec
的Base64
:
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; public class AdvancedEncryptionStandard { private byte[] key; private static final String ALGORITHM = "AES"; public AdvancedEncryptionStandard(byte[] key) { this.key = key; } /** * Encrypts the given plain text * * @param plainText The plain text to encrypt */ public byte[] encrypt(byte[] plainText) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(plainText); } /** * Decrypts the given byte array * * @param cipherText The data to decrypt */ public byte[] decrypt(byte[] cipherText) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(cipherText); } }
用法示例:
byte[] encryptionKey = "MZygpewJsCpRrfOr".getBytes(StandardCharsets.UTF_8); byte[] plainText = "Hello world!".getBytes(StandardCharsets.UTF_8); AdvancedEncryptionStandard advancedEncryptionStandard = new AdvancedEncryptionStandard( encryptionKey); byte[] cipherText = advancedEncryptionStandard.encrypt(plainText); byte[] decryptedCipherText = advancedEncryptionStandard.decrypt(cipherText); System.out.println(new String(plainText)); System.out.println(new String(cipherText)); System.out.println(new String(decryptedCipherText));
打印:
Hello world! դ; LA+ ߙb* Hello world!
你用来解密的IV是不正确的。 replace这个代码
//Decrypt cipher Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded()); decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
用这个代码
//Decrypt cipher Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptCipher.getIV()); decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
这应该可以解决你的问题。
下面包含了Java中一个简单的AES类的例子。 我不推荐在生产环境中使用这个类,因为它可能不会满足您的应用程序的所有特定需求。
import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class AES { public static byte[] encrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException { return AES.transform(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, messageBytes); } public static byte[] decrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException { return AES.transform(Cipher.DECRYPT_MODE, keyBytes, ivBytes, messageBytes); } private static byte[] transform(final int mode, final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException { final SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); final IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); byte[] transformedBytes = null; try { final Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); cipher.init(mode, keySpec, ivSpec); transformedBytes = cipher.doFinal(messageBytes); } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); } return transformedBytes; } public static void main(final String[] args) throws InvalidKeyException, InvalidAlgorithmParameterException { //Retrieved from a protected local file. //Do not hard-code and do not version control. final String base64Key = "ABEiM0RVZneImaq7zN3u/w=="; //Retrieved from a protected database. //Do not hard-code and do not version control. final String shadowEntry = "AAECAwQFBgcICQoLDA0ODw==:ZtrkahwcMzTu7e/WuJ3AZmF09DE="; //Extract the iv and the ciphertext from the shadow entry. final String[] shadowData = shadowEntry.split(":"); final String base64Iv = shadowData[0]; final String base64Ciphertext = shadowData[1]; //Convert to raw bytes. final byte[] keyBytes = Base64.getDecoder().decode(base64Key); final byte[] ivBytes = Base64.getDecoder().decode(base64Iv); final byte[] encryptedBytes = Base64.getDecoder().decode(base64Ciphertext); //Decrypt data and do something with it. final byte[] decryptedBytes = AES.decrypt(keyBytes, ivBytes, encryptedBytes); //Use non-blocking SecureRandom implementation for the new IV. final SecureRandom secureRandom = new SecureRandom(); //Generate a new IV. secureRandom.nextBytes(ivBytes); //At this point instead of printing to the screen, //one should replace the old shadow entry with the new one. System.out.println("Old Shadow Entry = " + shadowEntry); System.out.println("Decrytped Shadow Data = " + new String(decryptedBytes, StandardCharsets.UTF_8)); System.out.println("New Shadow Entry = " + Base64.getEncoder().encodeToString(ivBytes) + ":" + Base64.getEncoder().encodeToString(AES.encrypt(keyBytes, ivBytes, decryptedBytes))); } }
请注意,AES与编码无关,这就是为什么我select单独处理而不需要任何第三方库。
依靠标准库提供的解决scheme通常是个好主意:
private static void stackOverflow15554296() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { // prepare key KeyGenerator keygen = KeyGenerator.getInstance("AES"); SecretKey aesKey = keygen.generateKey(); String aesKeyForFutureUse = Base64.getEncoder().encodeToString( aesKey.getEncoded() ); // cipher engine Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // cipher input aesCipher.init(Cipher.ENCRYPT_MODE, aesKey); byte[] clearTextBuff = "Text to encode".getBytes(); byte[] cipherTextBuff = aesCipher.doFinal(clearTextBuff); // recreate key byte[] aesKeyBuff = Base64.getDecoder().decode(aesKeyForFutureUse); SecretKey aesDecryptKey = new SecretKeySpec(aesKeyBuff, "AES"); // decipher input aesCipher.init(Cipher.DECRYPT_MODE, aesDecryptKey); byte[] decipheredBuff = aesCipher.doFinal(cipherTextBuff); System.out.println(new String(decipheredBuff)); }
这将打印“文本进行编码”。
解决scheme是基于Java密码体系结构参考指南和https://stackoverflow.com/a/20591539/146745的答案。;