[英]Java AES/CFB8/NoPadding encryption and Charset
I'm trying to encrypt and decrypt Strings
in Java and PHP, so i use AES/CFB8/NoPadding
that sould work on both sides. 我正在尝试使用Java和PHP加密和解密
Strings
,所以我使用AES/CFB8/NoPadding
,它可以在双方都工作。
Now my Cryptor
works quit well with Latin Characters, as long as i set the Charset to Latin. 现在,只要我将Charset设置为拉丁语,我的
Cryptor
可以很好地放弃拉丁字符。 But when i set it to UTF-8 (which is what i use in my DB), the encryption is not properly done. 但是当我将它设置为UTF-8(这是我在我的数据库中使用的)时,加密没有正确完成。
Here is the output: 这是输出:
/*
* Latin (ISO-8859-1) output:
* Original: MiiiMüäöMeeʞ
* Encoded: rQ¶[ÉÐRíD
* Decoded: MiiiMüäöMee?
*
* UTF-8 output:
* Original: MiiiMüäöMeeʞ
* Encoded: rQ�[�
* Decoded: Mii0SS1])_�ELJI�S�;�W��W?*
*/
Since "ʞ" is not a latin character, I understand it can not be encrypted properly. 由于“ʞ”不是拉丁字符,我理解它无法正确加密。 But why does UTF-8 not work?
但为什么UTF-8不起作用?
public class Cryptor {
private Cipher cipher;
private String secretKey = "1234567890qwertz";
private String iv = "1234567890qwertz";
private SecretKey keySpec;
private IvParameterSpec ivSpec;
private Charset CHARSET = Charset.forName("ISO-8859-1"); // ISO-8859-1 vs. UTF-8
public Cryptor() throws CryptingException {
keySpec = new SecretKeySpec(secretKey.getBytes(CHARSET), "AES");
ivSpec = new IvParameterSpec(iv.getBytes(CHARSET));
try {
cipher = Cipher.getInstance("AES/CFB8/NoPadding");
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(e);
} catch (NoSuchPaddingException e) {
throw new SecurityException(e);
}
}
public String decrypt(String input) throws CryptingException {
try {
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
return new String(cipher.doFinal(input.getBytes(CHARSET)), CHARSET).trim();
} catch (IllegalBlockSizeException e) {
throw new SecurityException(e);
} catch (BadPaddingException e) {
throw new SecurityException(e);
} catch (InvalidKeyException e) {
throw new SecurityException(e);
} catch (InvalidAlgorithmParameterException e) {
throw new SecurityException(e);
}
}
public String encrypt(String input) throws CryptingException {
try {
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
return new String(cipher.doFinal(input.getBytes(CHARSET)), CHARSET).trim();
} catch (InvalidKeyException e) {
throw new SecurityException(e);
} catch (InvalidAlgorithmParameterException e) {
throw new SecurityException(e);
} catch (IllegalBlockSizeException e) {
throw new SecurityException(e);
} catch (BadPaddingException e) {
throw new SecurityException(e);
}
}
public static void main(String Args[]) {
try {
Cryptor c = new Cryptor();
String original = "MiiiMüäöMeeʞ";
System.out.println("Original: " + original);
String encrypted = c.encrypt("MiiiMüäöMeeʞ");
System.out.println("Encoded: " + encrypted);
System.out.println("Decoded: " + c.decrypt(encrypted));
} catch (CryptingException e) {
e.printStackTrace();
}
}
class CryptingException extends RuntimeException {
private static final long serialVersionUID = 7123322995084333687L;
public CryptingException() {
super();
}
public CryptingException(String message) {
super(message);
}
}
}
I think turning the encrypted bytes into a String
is a bad idea. 我认为将加密的字节转换为
String
是个坏主意。 The bytes are not valid for any encoding, they are random. 字节对任何编码都无效,它们是随机的。
You need to encode the resulting byte[]
to base64 to get a consistent outcome. 您需要将生成的
byte[]
编码为base64以获得一致的结果。 See sun.misc.BASE64Encoder
/ sun.misc.BASE64Decoder
. 请参阅
sun.misc.BASE64Encoder
/ sun.misc.BASE64Decoder
。
Here is an example of decoding a base64 String
to a byte[]
, the reverse process is very similar. 下面是将base64
String
解码为byte[]
的示例,反向过程非常相似。
You can declare the decoder/encoder and the top of the class: 您可以声明解码器/编码器和类的顶部:
private final BASE64Decoder base64Decoder = new BASE64Decoder();
private final BASE64Encoder base64Encoder = new BASE64Encoder();
Then in your decypt
method you need to call 然后在你的
decypt
方法中你需要打电话
return new String(cipher.doFinal(base64Decoder.decodeBuffer(input)), CHARSET);
And in your encrypt
method 并在您的
encrypt
方法
return base64Encoder.encode(cipher.doFinal(input.getBytes(CHARSET)));
Output using UTF-8: 使用UTF-8输出:
Original: MiiiMüäöMeeʞ
Encoded: clEUtlv2ALXsKYw4ivOfwQ==
Decoded: MiiiMüäöMeeʞ
One side note is that it's not strictly speaking good practice to use packages from sun.*
as they are not part of the java spec and hence may change/vanish from version to version. 一方面注意,使用
sun.*
包不是严格意义上的好习惯sun.*
因为它们不是java规范的一部分,因此可能会因版本而变化/消失。
Here is a little article on moving to the Apache Commons Codec implementation. 这是关于迁移到Apache Commons Codec实现的一篇小文章。
This is also a similar class in the Guava API. 这也是Guava API中的类似类。
Thanks alot for that answer, it really helped me alot. 非常感谢你的回答,它真的帮了我很多。
Here is the working code i use to encrypt and decrypt JSON stuff in JAVA and PHP: 这是我用来加密和解密JAVA和PHP中的JSON内容的工作代码:
/*-- JAVA --*/ /** * Is used for encrypting and decrypting Strings and JSONObjects. <br> * The JSON Objects can then be sent to a PHP script where they can be encrypted and decrypted with the same algorithm. * @throws CryptingException */ public class Cryptor { private Cipher cipher; private String secretKey = "1234567890qwertz"; private String iv = "1234567890qwertz"; private final String CIPHER_MODE = "AES/CFB8/NoPadding"; private SecretKey keySpec; private IvParameterSpec ivSpec; private Charset CHARSET = Charset.forName("UTF8"); public Cryptor() throws CryptingException { keySpec = new SecretKeySpec(secretKey.getBytes(CHARSET), "AES"); ivSpec = new IvParameterSpec(iv.getBytes(CHARSET)); try { cipher = Cipher.getInstance(CIPHER_MODE); } catch (NoSuchAlgorithmException e) { throw new SecurityException(e); } catch (NoSuchPaddingException e) { throw new SecurityException(e); } } /** * @param input A "AES/CFB8/NoPadding" encrypted String * @return The decrypted String * @throws CryptingException */ public String decrypt(String input) throws CryptingException { try { cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); return new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(input))); } catch (IllegalBlockSizeException e) { throw new SecurityException(e); } catch (BadPaddingException e) { throw new SecurityException(e); } catch (InvalidKeyException e) { throw new SecurityException(e); } catch (InvalidAlgorithmParameterException e) { throw new SecurityException(e); } } /** * @param input Any String to be encrypted * @return An "AES/CFB8/NoPadding" encrypted String * @throws CryptingException */ public String encrypt(String input) throws CryptingException { try { cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); return DatatypeConverter.printBase64Binary(cipher.doFinal(input.getBytes(CHARSET))).trim(); } catch (InvalidKeyException e) { throw new SecurityException(e); } catch (InvalidAlgorithmParameterException e) { throw new SecurityException(e); } catch (IllegalBlockSizeException e) { throw new SecurityException(e); } catch (BadPaddingException e) { throw new SecurityException(e); } } /** * Encrypts the keys and values of a JSONObject with Cryptor.encrypt(String input) * @param o The JSONObject to be encrypted * @return A JSONObject with encrypted keys and values */ public JSONObject jsonObjectEncrypt(JSONObject o) { Iterator<?> keys = o.keys(); JSONObject returnObject = new JSONObject(); while( keys.hasNext() ){ String key = (String)keys.next(); returnObject.put(this.encrypt(key), this.encrypt(o.getString(key))); } return returnObject; } /** * Decrypts the keys and values of a JSONObject with Cryptor.decrypt(String input) * @param o The JSONObject to be decrypted * @return A JSONObject with decrypted keys and values */ public JSONObject jsonObjectDecrypt(JSONObject o) { Iterator<?> keys = o.keys(); JSONObject returnObject = new JSONObject(); while( keys.hasNext() ){ String key = (String)keys.next(); if(key != null && !key.equals("")) { returnObject.put(this.decrypt(key), this.decrypt(o.getString(key))); } } return returnObject; } /** * Encrypts keys and values of every JSONObject in a JSONArray * @param a The JSONArray to be encrypted * @return A JSONArray with encrypted keys and values */ public JSONArray jsonArrayEncrypt(JSONArray a) { JSONArray returnArray = new JSONArray(); for(int i = 0; i < a.length(); i++) { returnArray.put(this.jsonObjectEncrypt((JSONObject)a.get(i))); } return returnArray; } /** * Decrypts keys and values of every JSONObject in a JSONArray * @param a The JSONArray to be decrypted * @return A JSONArray with decrypted keys and values */ public JSONArray jsonArrayDecrypt(JSONArray a) { JSONArray returnArray = new JSONArray(); for(int i = 0; i < a.length(); i++) { returnArray.put(this.jsonObjectDecrypt((JSONObject)a.get(i))); } return returnArray; } public static void main(String Args[]) { try { Cryptor c = new Cryptor(); String original = "MiiiMüäöMeeʞ"; System.out.println("Original: " + original); String encrypted = c.encrypt(original); System.out.println("Encoded: " + encrypted); System.out.println("Decoded: " + c.decrypt(encrypted)); JSONArray arr = new JSONArray("[{\\"id\\"=\\" 1 ʞ3 \\"},{\\"id\\"=\\"4\\"}]"); System.out.println(c.jsonArrayDecrypt(c.jsonArrayEncrypt(arr)).getJSONObject(0).getString("id")); } catch (CryptingException e) { e.printStackTrace(); } } } /*-- PHP ---*/ <html> <meta charset='utf-8'> <?php $cryptor = new Cryptor(); echo $cryptor->decrypt($cryptor->encrypt("MiiiMüäöMeeʞ")); class Cryptor { //Use same as in java Cryptor private $iv = '1234567890qwertz'; private $secretKey = '1234567890qwertz'; function encrypt($input) { return base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $this->secretKey, $input, MCRYPT_MODE_CFB, $this->iv ) ); } function decrypt($input) { return mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $this->secretKey, base64_decode($input), MCRYPT_MODE_CFB, $this->iv ); } function arrayDecrypt($array) { $returnArray = array(); foreach($array as $key=>$value) { $newKey = $this->decrypt($key); $returnArray[$newKey] = $this->decrypt($value); } return $returnArray; } function arrayEncrypt($array) { $returnArray = array(); foreach($array as $key=>$value) { $returnArray[$this->encrypt($key)] = $this->encrypt($value); } return $returnArray; } } ?> </html>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.