这个可是真的坑。。。弄了几个小时,必须mark一下
本篇文章主要是针对web前端和后端之间aes加解密通讯的部分(!!!仅写了aes)
先上代码,再说坑
首先是我的dynamic web project 的文件结构
准备好以下类:
java的aes工具类
package Utils;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class AESUtil {
private static String iv = "0123456789ABCDEF";//偏移量字符串必须是16位 当模式是CBC的时候必须设置偏移量
private static String Algorithm = "AES";
private static String AlgorithmProvider = "AES/CBC/PKCS5Padding"; //算法/模式/补码方式
public static byte[] generatorKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm);
keyGenerator.init(256);//默认128,获得无政策权限后可为192或256
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
public static IvParameterSpec getIv() throws UnsupportedEncodingException {
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes("utf-8"));
System.out.println("偏移量:" + byteToHexString(ivParameterSpec.getIV()));
return ivParameterSpec;
}
public static String encrypt(String src, byte[] key)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
String IV = "face0123456789ai";
IvParameterSpec iv = new IvParameterSpec(IV.getBytes("utf-8"));
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8")));
System.out.println(byteToHexString(cipherBytes));
return byteToHexString(cipherBytes);
}
public static byte[] decrypt(String src, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
String IV = "face0123456789ai";
IvParameterSpec iv = new IvParameterSpec(IV.getBytes("utf-8"));
//IvParameterSpec ivParameterSpec = getIv();
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] hexBytes = hexStringToBytes(src);
byte[] plainBytes = cipher.doFinal(hexBytes);
return plainBytes;
}
/**
}
* 将byte转换为16进制字符串
* @param src
* @return
*/
public static String byteToHexString(byte[] src) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xff;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
sb.append("0");
}
sb.append(hv);
}
return sb.toString();
}
/**
* 将16进制字符串装换为byte数组
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] b = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return b;
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
}
需要引入CryptoJS(我是后下的这个,所以之前有的aes.js,md5.js都下重了,懒得删了,之前有依赖,比较麻烦)
1.java加密,js解密
js代码如下:
<script type="text/javascript">
function aesEncrypt() {
var password1 = document.getElementById("password").value;
var key = CryptoJS.enc.Utf8.parse(hex_md5(password1));
var iv = CryptoJS.enc.Utf8.parse("face0123456789ai");
console.log('key:', key);
var src=document.getElementById("password_t").value;
src=hex_md5(src);
console.log('src:', src);
var enc = CryptoJS.AES.encrypt(src ,key,{
iv:iv,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
})
document.getElementById("password_t").value=enc.ciphertext.toString();
var loginForm = document.getElementById("form_t");
loginForm.submit();
}
</script>
java代码如下:
byte key[] =pw_data.getBytes("utf-8");
String newpw1 =new String(AESUtil.decrypt(pw, key), "utf-8");
1.java解密,js加密
js代码如下:
function aesDecrypt(cipher,key) {
var key_f = CryptoJS.enc.Utf8.parse(key);
console.log(key_f);
var iv = CryptoJS.enc.Utf8.parse("face0123456789ai");
console.log('cipher:'+cipher);
var encryptedHexStr = CryptoJS.enc.Hex.parse(cipher);
var srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
var dec = CryptoJS.AES.decrypt(srcs,key_f,{
iv:iv,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
})
var data=dec.toString(CryptoJS.enc.Utf8);
document.getElementById("showcode").value=data;
console.log(document.getElementById("showcode").value);
}
java代码如下:
String newpw1 =AESUtil.encrypt(string, key.getBytes("utf-8"));
说一下我遇到的坑吧,就在AESUtil的加密函数中
byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8")));
System.out.println(byteToHexString(cipherBytes));
return byteToHexString(cipherBytes);
我尝试拿parse base64编码的加密结果在网页端解密,但是怎么调试也网页端也解不了密,总是输出null,我调试的时候发现key和cipher在进入解密函数后都变成了空,不明所以,,,后来发现原来网页中aes加密后返回的结果是hex格式的,我就换成了这个,发现可以用(AESUtil的解密函数用的也是这个),个人觉得应该是这个原理:
网页端进行加密或者解密的密文参数应该是hex编码的,所以在后端进行数据交互时应该注意发送给网页端的加密数据应该使用hex编码,当然,我们也可以在hex编码后加上一层base64编码,只是注意在传入解密/加密函数时parse hex一下
如果上述说法有误,欢迎指正,多谢
如需源码请留邮箱
题外话:我在调试的时候曾经想过是否是aes的密钥有要求,但是后来发现并不是这个问题,根据SecretKey secretKey = new SecretKeySpec(key, Algorithm);密钥生成函数可以依据参数密钥生成合法密钥。