没有正确加密和解​​密

我写的以下AES类用于加密和解密,这有点时髦。 当我复制AES对象并且我选择加密纯文本,然后我立即尝试解密我刚刚加密的文本时,它不会完全解密(并且每次都有不同的方式)。

例如,我正在使用这样的简单JSP初始化它:

 <% String hexMessage = "0xe800a86d90d2074fbf339aa70b6d0f62f047db15ef04c86b488a1dda3c6c4f2f2bbb444a8c709bbb4c29c7ff1f1e"; String keyText = "12345678abcdefgh";//*/ AES e = new AES(); //e.setKey(keyText); String plaintext = "This should decode & encode!"; String ciphertext = e.encrypt(plaintext); out.println(ciphertext); out.println("
"); out.println(e.decrypt(ciphertext)); %>

输出因页面加载而异:一次:

 0x663D64E6A0AE455AB3D25D5AF2F77C72202627EBA068E6DEBE5F22C31 This should decoÁdìmèåV4ÉkÓ 

另一个:

 0x5F5CF31961505F01EA9D5B7D7BFC656BD3117725D2EA041183F48 This s2??XêêÈ&ÀܧF?ÒDÒ? 

等等:

 0xC7178A34C59F74E5D68F7CE5ED655B670A0B4E715101B4DDC2122460E8 Tà@¼R×ËÖ?_U?xÎÚ?Ba?b4r!©F 

我创建的课程如下:

 package com.myclass.util; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Security; import java.util.regex.Pattern; import javax.crypto.*; import javax.crypto.spec.*; public class AES { private static String provider = "AES/CTR/NoPadding"; private static String providerkey = "AES"; private static int size = 128; private SecretKeySpec key; private Cipher cipher; private byte[] ivBytes = new byte[size/8]; private IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); public AES() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException{ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); KeyGenerator kgen = KeyGenerator.getInstance(providerkey); kgen.init(size); // 192 and 256 bits may not be available SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); key = new SecretKeySpec(raw, providerkey); cipher = Cipher.getInstance(provider); for(int x = 0; x < (size/8); x++) ivBytes[x] = 00; ivSpec = new IvParameterSpec(ivBytes); } public void setKey(String keyText){ byte[] bText = new byte[size/8]; bText = keyText.getBytes(); key = new SecretKeySpec(bText, providerkey); } public void setIV(String ivText){ setIV(ivText.getBytes()); } public void setIV(byte[] ivByte){ byte[] bText = new byte[size/8]; bText = ivByte; ivBytes = bText; ivSpec = new IvParameterSpec(ivBytes); } public String encrypt(String message) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException{ cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); byte[] encrypted = cipher.doFinal(message.getBytes()); return byteArrayToHexString(encrypted); } public String decrypt(String hexCiphertext) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException{ cipher.init(Cipher.DECRYPT_MODE, key, ivSpec); byte[] dec = hexStringToByteArray(hexCiphertext); byte[] decrypted = cipher.doFinal(dec); return new String(decrypted); } private static String byteArrayToHexString( byte [] raw ) { String hex = "0x"; String s = new String(raw); for(int x = 0; x < s.length(); x++){ char[] t = s.substring(x, x + 1).toCharArray(); hex += Integer.toHexString((int) t[0]).toUpperCase(); } return hex; } private static byte[] hexStringToByteArray(String hex) { Pattern replace = Pattern.compile("^0x"); String s = replace.matcher(hex).replaceAll(""); byte[] b = new byte[s.length() / 2]; for (int i = 0; i < b.length; i++){ int index = i * 2; int v = Integer.parseInt(s.substring(index, index + 2), 16); b[i] = (byte)v; } return b; } } 

基于不同的结果,我想知道是否某些东西在某种程度上与IV搞砸了,但我真的不明白为什么……

[编辑]看起来它不是IV,如果我硬编码解密仍然变化。 如果我硬编码密钥它会停止变化,但仍然没有正确解密文本:-(。

——————— ===================== ——– ————-

根据owlstead的代码和建议添加我在下面创建的最终解决方案。 它执行以下操作:
1)有一个随机密钥和iv初始化。
2)允许您将键或iv指定为常规字符串或hex编码字符串。
3)自动截断或删除任何给定键或iv的填充以使其适当长度。

注意:项目#3可以被视为非常不安全,因为它允许你做一些愚蠢的事情。 为了我的目的,我需要它,但请谨慎使用。 如果为空键填充键的短字符串,则您的内容不会非常安全。

——————— ===================== ——– ————-

 package com.myclass.util; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.spec.InvalidParameterSpecException; import java.util.regex.Pattern; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class AES { private static Charset PLAIN_TEXT_ENCODING = Charset.forName("UTF-8"); private static String CIPHER_TRANSFORMATION = "AES/CTR/NoPadding"; private static String KEY_TYPE = "AES"; private static int KEY_SIZE_BITS = 128; private SecretKey key; private Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION); private byte[] ivBytes = new byte[KEY_SIZE_BITS/8]; public AES() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidParameterSpecException, InvalidKeyException, InvalidAlgorithmParameterException{ KeyGenerator kgen = KeyGenerator.getInstance(KEY_TYPE); kgen.init(KEY_SIZE_BITS); key = kgen.generateKey(); cipher.init(Cipher.ENCRYPT_MODE, key); ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); } public String getIVAsHex(){ return byteArrayToHexString(ivBytes); } public String getKeyAsHex(){ return byteArrayToHexString(key.getEncoded()); } public void setStringToKey(String keyText){ setKey(keyText.getBytes()); } public void setHexToKey(String hexKey){ setKey(hexStringToByteArray(hexKey)); } private void setKey(byte[] bArray){ byte[] bText = new byte[KEY_SIZE_BITS/8]; int end = Math.min(KEY_SIZE_BITS/8, bArray.length); System.arraycopy(bArray, 0, bText, 0, end); key = new SecretKeySpec(bText, KEY_TYPE); } public void setStringToIV(String ivText){ setIV(ivText.getBytes()); } public void setHexToIV(String hexIV){ setIV(hexStringToByteArray(hexIV)); } private void setIV(byte[] bArray){ byte[] bText = new byte[KEY_SIZE_BITS/8]; int end = Math.min(KEY_SIZE_BITS/8, bArray.length); System.arraycopy(bArray, 0, bText, 0, end); ivBytes = bText; } public String encrypt(String message) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivBytes)); byte[] encrypted = cipher.doFinal(message.getBytes(PLAIN_TEXT_ENCODING)); return byteArrayToHexString(encrypted); } public String decrypt(String hexCiphertext) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException { cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivBytes)); byte[] dec = hexStringToByteArray(hexCiphertext); byte[] decrypted = cipher.doFinal(dec); return new String(decrypted, PLAIN_TEXT_ENCODING); } private static String byteArrayToHexString(byte[] raw) { StringBuilder sb = new StringBuilder(2 + raw.length * 2); sb.append("0x"); for (int i = 0; i < raw.length; i++) { sb.append(String.format("%02X", Integer.valueOf(raw[i] & 0xFF))); } return sb.toString(); } private static byte[] hexStringToByteArray(String hex) { Pattern replace = Pattern.compile("^0x"); String s = replace.matcher(hex).replaceAll(""); byte[] b = new byte[s.length() / 2]; for (int i = 0; i < b.length; i++){ int index = i * 2; int v = Integer.parseInt(s.substring(index, index + 2), 16); b[i] = (byte)v; } return b; } 

}

重写,内联评论。 有趣的是,最大的错误是生成hex,所以我重写了这个方法。 它并不完美,但我尽可能地保持原始来源。

 import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Security; import java.util.regex.Pattern; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; /* * Add state handling! Don't allow same key/iv for encrypting different cipher text! */ public class AES { private static Charset PLAIN_TEXT_ENCODING = Charset.forName("UTF-8"); private static String CIPHER_TRANSFORMATION = "AES/CTR/NoPadding"; private static String KEY_TYPE = "AES"; // 192 and 256 bits may not be available private static int KEY_SIZE_BITS = 128; private Cipher cipher; private SecretKey key; private IvParameterSpec iv; static { // only needed if the platform does not contain CTR encryption by default if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { // only needed for some platforms I presume Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } } public AES() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException { // not much use without a getter // final KeyGenerator kgen = KeyGenerator.getInstance(KEY_TYPE); // kgen.init(KEY_SIZE_BITS); // key = kgen.generateKey(); cipher = Cipher.getInstance(CIPHER_TRANSFORMATION); } public void setKeyHex(String keyText) { byte[] bText = hexStringToByteArray(keyText); if (bText.length * Byte.SIZE != KEY_SIZE_BITS) { throw new IllegalArgumentException( "Wrong key size, expecting " + KEY_SIZE_BITS / Byte.SIZE + " bytes in hex"); } key = new SecretKeySpec(bText, KEY_TYPE); } public void setIVHex(String ivText) { byte[] bText = hexStringToByteArray(ivText); if (bText.length != cipher.getBlockSize()) { throw new IllegalArgumentException( "Wrong IV size, expecting " + cipher.getBlockSize() + " bytes in hex"); } iv = new IvParameterSpec(bText); } public String encrypt(String message) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { cipher.init(Cipher.ENCRYPT_MODE, key, iv); byte[] encrypted = cipher.doFinal(message.getBytes(PLAIN_TEXT_ENCODING)); return byteArrayToHexString(encrypted); } public String decrypt(String hexCiphertext) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException { cipher.init(Cipher.DECRYPT_MODE, key, iv); byte[] dec = hexStringToByteArray(hexCiphertext); byte[] decrypted = cipher.doFinal(dec); return new String(decrypted, PLAIN_TEXT_ENCODING); } private static String byteArrayToHexString(byte[] raw) { StringBuilder sb = new StringBuilder(2 + raw.length * 2); sb.append("0x"); for (int i = 0; i < raw.length; i++) { sb.append(String.format("%02X", Integer.valueOf(raw[i] & 0xFF))); } return sb.toString(); } // better add some input validation private static byte[] hexStringToByteArray(String hex) { Pattern replace = Pattern.compile("^0x"); String s = replace.matcher(hex).replaceAll(""); byte[] b = new byte[s.length() / 2]; for (int i = 0; i < b.length; i++) { int index = i * 2; int v = Integer.parseInt(s.substring(index, index + 2), 16); b[i] = (byte) v; } return b; } public static void main(String[] args) { try { String key = "0x000102030405060708090A0B0C0D0E0F"; String iv = "0x000102030405060708090A0B0C0D0E0F"; String text = "Owlsteads answer"; AES aes = new AES(); aes.setKeyHex(key); aes.setIVHex(iv); String cipherHex = aes.encrypt(text); System.out.println(cipherHex); String deciphered = aes.decrypt(cipherHex); System.out.println(deciphered); } catch (GeneralSecurityException e) { throw new IllegalStateException("Something is rotten in the state of Denmark", e); } catch (UnsupportedEncodingException e) { // not always thrown even if decryption fails, add integrity check such as MAC throw new IllegalStateException("Decryption and/or decoding plain text message failed", e); } } } 

你的byteArrayToHexString方法被破坏了。 Integer.toHexString不会添加填充零,但您的代码会假设它。 破坏的转换为hex会破坏加密的字符串,从hex转换回原始字节字符串会进一步破坏它。