js和java中的AES加密和解密
2015-11-23 12:58
435 查看
每次都要在这个问题上耗费一天的时间,所以这次留下记录免得以后麻烦。
JS端使用CryptoJS,服务端bouncy castle提供的AES算法。
AES算法采用“AES/CBC/PKCS7Padding”,这个在JS和JAVA中都支持。Java默认的加密算法中,不支持PKCS7Padding,只支持PKCS5Padding,bouncy castle支持PKCS7Padding;CryptoJS中没有Pkcs5,只有Pkcs7。所以最后才选择js部分用CryptoJS和java部分用bouncy castle的实现。
Java部分的“AES/CBC/PKCS7Padding”描述的内容是这样的,AES是加密解密算法;CBC是加密过程中如何分块,还有加密各个块的时候如何更换密钥;PKCS7Padding是加密数据不够一块的时候如何填补剩余空间的。AES+CBC+PKCS7Padding这样的组合是CryptoJS的默认设置。
java的AES工具
在java中采用相同的密码和iv就可以解密js的密文了。
解密代码如下:
java端加密,用于返回
js端解密
java加密的过程没有直接贴工具上来,因为拆成好几个部分写了,涉及其它东西太多。这样应该能复原了。
JS端使用CryptoJS,服务端bouncy castle提供的AES算法。
AES算法采用“AES/CBC/PKCS7Padding”,这个在JS和JAVA中都支持。Java默认的加密算法中,不支持PKCS7Padding,只支持PKCS5Padding,bouncy castle支持PKCS7Padding;CryptoJS中没有Pkcs5,只有Pkcs7。所以最后才选择js部分用CryptoJS和java部分用bouncy castle的实现。
Java部分的“AES/CBC/PKCS7Padding”描述的内容是这样的,AES是加密解密算法;CBC是加密过程中如何分块,还有加密各个块的时候如何更换密钥;PKCS7Padding是加密数据不够一块的时候如何填补剩余空间的。AES+CBC+PKCS7Padding这样的组合是CryptoJS的默认设置。
//JsonFormatter是用来把加密结果格式化的工具。如果不设置,加密结果toString()之后是base64编码的密文。 var JsonFormatter = { stringify: function (cipherParams) { // create json object with ciphertext var jsonObj = { ct: cipherParams.ciphertext.toString(CryptoJS.enc.Hex) }; // optionally add iv and salt if (cipherParams.iv) { jsonObj.iv = cipherParams.iv.toString(); } if (cipherParams.salt) { jsonObj.s = cipherParams.salt.toString(); } // stringify json object return JSON.stringify(jsonObj); }, parse: function (jsonStr) { // parse json string var jsonObj = JSON.parse(jsonStr); // extract ciphertext from json object, and create cipher params object var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Hex.parse(jsonObj.ct) }); // optionally extract iv and salt if (jsonObj.iv) { cipherParams.iv = CryptoJS.enc.Hex.parse(jsonObj.iv) } if (jsonObj.s) { cipherParams.salt = CryptoJS.enc.Hex.parse(jsonObj.s) } return cipherParams; } }; var pwd = CryptoJS.enc.Hex.parse("00000000000000000000000000000000");//密码必须用Hex或其他方式处理为byte数组,如果直接使用字符串,CryptoJS会用加盐的方法重新生成密码,而且加的盐是随机数,这样在java端就没法解秘了。 var iv = CryptoJS.enc.Hex.parse('11111111111111111111111111111111');//iv在java中必须为16byte长,所以js中也必须设置为相同的长度,否则加密后的结果在java中无法解密。 var setting={iv:iv, //mode:CryptoJS.mode.CBC, //默认值,可以不设置 //padding:CryptoJS.pad.Pkcs7,//同上 format: JsonFormatter}; var mi=CryptoJS.AES.encrypt(args.data,pwd, setting); mi=JSON.parse(mi.toString());//mi本身是个对象,而且内部属性循环引用,所以不用直接用JSON处理,需要先toString()。因为我们设置过format,所以得到一个son字符串,这样可以获得密文和iv。 args.data=mi.ct;//ct是密文,iv是参数向量,s是盐(密码为字符串的时候出现,否则不出现)
java的AES工具
Security.addProvider(new BouncyCastleProvider());//添加BouncyCastle的实现, 放static块中 //下面是一些常量 /** * IV大小. */ private static final int IV_SIZE = 16; /** * BC包中AES算法名. */ public static final String ALGORITHM_LONG_NAME = "AES/CBC/PKCS7Padding"; /** * BC包中AES算法名. */ public static final String ALGORITHM_SHORT_NAME = "AES"; /** * BC Provider名称. */ public static final String PROVIDER_NAME = "BC"; //获得加密器的函数 private static Cipher generateCipher(final int mode, final byte[] key, final byte[] ivp) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { Cipher res = null; final SecretKey secretkey = new SecretKeySpec(key, ALGORITHM_SHORT_NAME); final IvParameterSpec ivparameter = new IvParameterSpec(ivp); res = Cipher.getInstance(ALGORITHM_LONG_NAME, PROVIDER_NAME); res.init(mode, secretkey, ivparameter); return res; } //java安全加密的部分对随机数又要求,普通的随机数是不行的,需要特殊处理,应该是长度、算法上有区别,而且好像存储也不一样。使用的方法如下: /** * 获得密钥. * @return 密钥. */ public static byte[] generateKey() { byte[] res = null; KeyGenerator keyGen = null; SecretKey key = null; try { keyGen = KeyGenerator.getInstance(ALGORITHM_SHORT_NAME, PROVIDER_NAME); keyGen.init(new SecureRandom()); key = keyGen.generateKey(); res = key.getEncoded(); } catch (NoSuchAlgorithmException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (NoSuchProviderException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } return res; } //Java的cipher可以完成加密和解密两种功能,处理过程如下 /** * 处理加密解密过程. * @param input * 输入. * @param cipher * cipher. * @return 结果. */ private static byte[] process(final byte[] input, final Cipher cipher) { byte[] res = null; ByteArrayOutputStream bOut = new ByteArrayOutputStream(); CipherOutputStream cOut = new CipherOutputStream(bOut, cipher); try { cOut.write(input); cOut.flush(); cOut.close(); res = bOut.toByteArray(); } catch (IOException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } return res; } //加密和解密接口 /** * 加密. * @param data * 加密的数据. * @param key * 密钥. * @param iv * CBC算法所需初始矩阵. * @return 加密结果. */ public static byte[] encrypt( final byte[] data, final byte[] key, final byte[] iv ) { byte[] res = null; try { res = process(data, generateCipher(Cipher.ENCRYPT_MODE, key, iv)); } catch (InvalidKeyException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (NoSuchAlgorithmException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (NoSuchProviderException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (NoSuchPaddingException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (InvalidAlgorithmParameterException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } return res; } /** * 解密. * @param data * 解密的数据. * @param key * 密钥. * @param iv * CBC算法所需初始矩阵. * @return 解密结果. */ public static byte[] decrypt( final byte[] data, final byte[] key, final byte[] iv ) { byte[] res = null; try { res = process(data, generateCipher(Cipher.DECRYPT_MODE, key, iv)); } catch (InvalidKeyException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (NoSuchAlgorithmException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (NoSuchProviderException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (NoSuchPaddingException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } catch (InvalidAlgorithmParameterException e) { staticLogger(AesUtil.class).error(e.getMessage(), e); } return res; }
在java中采用相同的密码和iv就可以解密js的密文了。
解密代码如下:
byte[] key = Hex.decodeHex("000000000...000000000000000".toCharArray());//注意密码长度 byte[] iv = Hex.decodeHex("11111111111111...111".toCharArray());//注意iv长度 byte[] ct = Hex.decodeHex(new String(ct).toCharArray()); byte[] res = AesUtil.decrypt(ct, key, iv);
java端加密,用于返回
byte[] key = Hex.decodeHex("111...111".toCharArray()); byte[] iv = Hex.decodeHex("000...000".toCharArray()); byte[] ct = AesUtil.encrypt(ct, key, iv); byte[] output = Hex.encodeHexString(ct).getBytes();
js端解密
//data是写回的数据 var msg=CryptoJS.enc.Hex.parse(data); msg=CryptoJS.enc.Base64.stringify(msg);//CryptJS的解密方法输入密文数据必须为Base64编码 var decrypted = CryptoJS.AES.decrypt(msg, pwd, { iv:iv}); //密码和iv同加密过程 var txt = (CryptoJS.enc.Utf8.stringify(decrypted).toString());//获得明文
java加密的过程没有直接贴工具上来,因为拆成好几个部分写了,涉及其它东西太多。这样应该能复原了。
相关文章推荐
- JDK动态代理
- eclipse中Git的配置
- Spring(一)
- Eclipse关联android.support.v4包
- java 反射异常和 找不action的解决方案
- Java中的数组
- Java初步(OOP)
- Java中的线程
- Java画图程序设计
- java课程设计-文本编辑器
- Mybatis 和 Spring配置
- Java Font类
- 传智播客javase总结 5
- java_SSH继续充电_struts2_第一天
- 百度ueditor struts2图片上传问题解决方案
- struts2的工作原理
- 《深入理解java虚拟机》学习笔记之虚拟机字节码执行引擎
- Spring事务配置
- Hibernate持久化对象存在三种状态
- Myeclipse快捷键