最早系统学习密码学的知识是在软考过程中,当时也是半瓶子醋晃悠,我记得最近技术分享中也分享过一些相关的知识,可惜也没有细细分析,老大说要在数据在B/S传输过程中要加密,所以自己就先研究的AES前端和后端的加解密以及对接。这是两个过程:前端加密—传输到后台—后台解密、后台加密—传输—前台解密。
AES的基本要求是,采用对称分组密码体制,密钥长度的最少支持为128、192、256,分组长度128位,算法应易于各种硬件和软件实现
加密模式
AES分为几种模式,比如ECB,CBC,CFB等等,这些模式除了ECB由于没有使用IV而不太安全,其他模式差别并没有太明显,大部分的区别在IV和KEY来计算密文的方法略有区别。具体可参考WIKI的说明。另外,AES分为AES128,AES256等,表示期待秘钥的长度,比如AES256秘钥的长度应该是256/8的32字节,一些语言的库会进行自动截取,让人以为任何长度的秘钥都可以。而这其实是有区别的。
IV的作用
IV称为初始向量,不同的IV加密后的字符串是不同的,加密和解密需要相同的IV,既然IV看起来和key一样,却还要多一个IV的目的,对于每个块来说,key是不变的,但是只有第一个块的IV是用户提供的,其他块IV都是自动生成。
IV的长度为16字节。超过或者不足,可能实现的库都会进行补齐或截断。但是由于块的长度是16字节,所以一般可以认为需要的IV是16字节。
PADDING
AES块加密说过,PADDING是用来填充最后一块使得变成一整块,所以对于加密解密两端需要使用同一的PADDING模式,大部分PADDING模式为PKCS5, PKCS7, NOPADDING。
加密解密端
对于加密端,应该包括:加密秘钥长度,秘钥,IV值,加密模式,PADDING方式。
对于解密端,应该包括:解密秘钥长度,秘钥,IV值,解密模式,PADDING方式。
前端加解密:
<!DOCTYPEhtml>
<html>
<head>
<title>aes demo</title>
<meta charset="utf-8"/>
<style>
*{margin:0;padding:0}
.demo-wrap{width: 400px;height:50px;margin: 50px auto auto auto}
</style>
<scriptsrc="./rollups/aes.js"></script>
<!-- <scriptsrc="./components/pad-zeropadding.js"></script>-->
</head>
<body>
<divclass="demo-wrap">
<input type="text"id="data-ipt"/>
<buttonοnclick="getAES();">AES加密</button>
<buttonοnclick="getDAes();">AES解密</button>
<br/>
加密后的数据:
<p id ="encrypted"></p>
解密后的数据:
<pid="decrypted"></p>
</div>
<script>
function getAesString(data,key,iv){//加密
var key = CryptoJS.enc.Utf8.parse(key);
//alert(key);
var iv = CryptoJS.enc.Utf8.parse(iv);
var encrypted =CryptoJS.AES.encrypt(data,key,
{
iv:iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
});
return encrypted.toString(); //返回的是base64格式的密文
}
function getDAesString(encrypted,key,iv){//解密
var key = CryptoJS.enc.Utf8.parse(key);
var iv = CryptoJS.enc.Utf8.parse(iv);
var decrypted =CryptoJS.AES.decrypt(encrypted,key,
{
iv:iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
});
returndecrypted.toString(CryptoJS.enc.Utf8); //
}
function getAES(){ //加密
var data =document.getElementById("data-ipt").value;//明文
var key = 'abcdefgabcdefg12'; //密钥
var iv = 'abcdefgabcdefg12';
var encrypted =getAesString(data,key,iv); //密文
var encrypted1 =CryptoJS.enc.Utf8.parse(encrypted);
document.getElementById("encrypted").innerHTML = encrypted;
}
function getDAes(){//解密
var encrypted =document.getElementById("encrypted").innerHTML; //密文
var key = 'abcdefgabcdefg12';
var iv = 'abcdefgabcdefg12';
var decryptedStr =getDAesString(encrypted,key,iv);
alert(decryptedStr);
document.getElementById("decrypted").innerHTML = decryptedStr;
}
</script>
</body>
</html>
吃亏的地方在编码格式的问题上,用 crypto.js加密后的密文是base64格式的,而且解密的数据也必须是base64格式的, 这样才能和后台做对接。
现在来看后台代码:
packagecom.test.aes;
importjava.io.UnsupportedEncodingException;
importjavax.crypto.Cipher;
importjavax.crypto.spec.IvParameterSpec;
importjavax.crypto.spec.SecretKeySpec;
importorg.apache.commons.codec.binary.Base64;
public classAesCBC {
public static void main(String[] args)throws UnsupportedEncodingException {
Stringcontent="12345678";
Stringkey="abcdefgabcdefg12";
Stringiv="abcdefgabcdefg12";
//加密
byte[ ]encrypted=AES_CBC_Encrypt(content.getBytes(), key.getBytes(), iv.getBytes());
//解密
byte[ ]decrypted=AES_CBC_Decrypt(encrypted, key.getBytes(), iv.getBytes());
System.out.println("解密后:"+byteToHexString(decrypted));
System.out.println(byteToString(decrypted));
}
public static String byteToString(byte[ ]byte1){
returnnew String(byte1);
}
public static byte[] AES_CBC_Encrypt(byte[]content, byte[] keyBytes, byte[] iv){
try{
SecretKeySpec key = newSecretKeySpec(keyBytes, "AES");
Ciphercipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,key, new IvParameterSpec(iv));
byte[]result=cipher.doFinal(content);
return result;
}catch (Exception e) {
System.out.println("exception:"+e.toString());
}
return null;
}
public static byte[] AES_CBC_Decrypt(byte[]content, byte[] keyBytes, byte[] iv){
try{
SecretKeySpec key = newSecretKeySpec(keyBytes, "AES");
Ciphercipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,key, new IvParameterSpec(iv));
byte[]result=cipher.doFinal(content);
return result;
}catch (Exception e) {
// TODO Auto-generated catchblock
System.out.println("exception:"+e.toString());
}
return null;
}
/**
* 字符串装换成base64
*
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptBASE64(Stringkey) throws Exception {
returnBase64.decodeBase64(key.getBytes());
}
/**
*二进制装换成base64
*
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(byte[]key) throws Exception {
returnnew String(Base64.encodeBase64(key));
}
}
前端和后端是分开来做的但是都在AES在线生成器中加密过,得到的都是相同的密文。
AES收获很多,这里可能有很多坑,大家踩一踩,然后交流交流。做到最后老大说用RSA算法,今天研究的是RSA也算有进度,等待分享。
附录
http://www.cnblogs.com/vmax-tam/p/4624032.html
http://blog.csdn.net/chence19871/article/details/27653805
http://blog.csdn.net/vieri_32/article/details/48345023
http://blog.csdn.net/defonds/article/details/42775183
http://blog.csdn.net/cfl20121314/article/details/49749275
http://www.cjjjs.com/paper/xmkf/2016629102258195.aspx
http://www.cjjjs.com/paper/xmkf/201663012403485.aspx