java中常⽤的对称加密算法
⼀常⽤的对称加密算法
对称加密算法简单来讲就是加密和解密使⽤同⼀个密钥,并且加密解密互为逆运算,如加法和减法,先加密再解密与先解密后加密都能得到原结果,常⽤的对称加密算法有DES;3DES(⼆倍长,三倍长);AES;
3DES是DES扩展,3DES使⽤DES转换:
3DES 2倍长密钥长度16个字节,使⽤前⼋个字节对数据des加密,后⼋个字节对数据des解密,再⽤前⼋个字节对数据des加密
3DES 3倍长密钥长度24个字节,使⽤前⼋个字节对数据des加密,中间⼋个字节对数据des解密,再⽤后⼋个字节对数据des加密
算法具体实现推荐⽂章:
1>分组模式与填充
1:分组模式
<1>ECB模式:
对数据按照block长度(des,3DES为8个字节,AES16字节)分成多组b1,b2……bn(不⾜按设置的填充模式填充),再⽤密钥对每组分别加密
<2>CBC模式:
对数据按照block长度(des,3DES为8个字节,AES 16字节)分成多组b1,b2……bn(不⾜按设置的填充模式填充),⽤给定IV向量与第⼀组异或运算,对异或结果加密⽣成密⽂块1,再⽤密⽂块与第⼆明⽂块异或运算对异或结果加密⽣成密⽂块2,以此类推
两种分组模式⽐较:
1》ECB模式速度⽐CBC模式块。—>ECB不⽤等上⼀个块的计算结果,可以多线程计算
2》ECB模式不会传递误差。—>⼀组计算错误不会影响其他组
3》ECB模式容易被攻击。—>每组加密因⼦⼀样,加密实例多,易被破解
2: 填充
<1>NoPadding:
数据不填充,需要满⾜条件(n*block)
<2>PKCS5Padding:
如:des/3des block 长度为8个字节
数据长度为8n末尾填充8个08
数据长度为8n+m(0<m<8)末尾填充8-m个8-m 如:0102030405060708 09 --->0102030405060708 0907070707070707
<3>ISO10126Padding
如:aes block 长度为16个字节
数据长度为16n末尾填充15个随机字节和⼀个长度10(16进制10⼗进位16)
数据长度为16n+m(0<m<16)末尾填充16-m个字节,16-m-1个随机字节和⼀个长度字节
如:0102030405060708 0910 --->0102030405060708 0910+ 5个随机字节+06
JCE中AES⽀持五中模式:CBC,CFB,ECB,OFB,PCBC;⽀持三种填充:NoPadding,PKCS5Padding,ISO10126Padding。不⽀持SSL3Padding。不⽀持“NONE”模式。
其他分组模式 :
2>具体实现与应⽤
/**
* 对称加密解密
*
* @param alg    算法
*              "DES/ECB/NoPadding" "DES/ECB/PKCS5Padding" "DES/CBC/NoPadding" "DES/CBC/NoPadding"
*              "DESede/ECB/NoPadding" "DESede/ECB/PKCS5Padding" "DESede/CBC/PKCS5Padding"  "DESede/CBC/PKCS5Padding"
*              "AES/ECB/NOPADDING" "AES/ECB/PKCS5Padding" "AES/CBC/NOPADDING" "AES/CBC/PKCS5Padding"
*              "AES/ECB/ISO10126Padding"
* @param key    密钥长度需要按照对应加密算法
* @param opMode 模式,加密还是解密 {@link  Cipher#ENCRYPT_MODE }{@link Cipher#DECRYPT_MODE}
* @param data  待加密的数据
* @param iv    CBC模式向量
* @return 加密后的数据
*/
public static byte[]SymmetricAlg(String alg,byte[] key,int opMode,byte[] data,byte[] iv)throws Exception {
IvParameterSpec ivParameterSpec = null;
if(iv != null && iv.length !=0){
ivParameterSpec =new IvParameterSpec(iv);
}
SecretKey secretKey =new SecretKeySpec(key, alg);
Cipher cipher = Instance(alg);
cipher.init(opMode, secretKey, ivParameterSpec);
return cipher.doFinal(data);
}
alg 上⾯的不全还有其他的分组模式和填充
注意点:
1》加密解密需要对应的分组模式和填充要⼀致
2》alg 传 “des”等不传分组模式和填充时都有⼀个默认值,不同平台默认值可能不同,最好是显⽰传参数
3》密钥长度问题:des 的密钥长度为8字节;DESede(3des) 的密钥长度为16个字节或者24个字节(2倍长或者3倍长);AES的密钥长度为16个字节,24个字节或者32个字节都可以(AES-128,AES-19
2或者AE-256)
4》数据长度 NoPadding时,des/3des 为8n,aes 为16n
⼆字符编码
1>常⽤的字符编码有Unicode、ASCII、GBK、GB2312、UTF-8 还有⼀个特殊的编码 base64 ,区别如UTF-8等编码都是与字符集对应的,如果不在编码表内就会乱码,⽽Base64编码是从⼆进制到字符的过程,还都是可打印字符,不会出现乱码,所以常在加密后使⽤Base64处理显⽰⽂本,还有⼀种是直接显⽰对应字节数组的16进制字符串
2>加密和base64
String test ="对称加密AES";
byte[] input = Bytes("utf-8");
byte[] key = BytesUtils.hexStr2Bytes("111111111111111111111111111222221111111111122222");
byte[] result =SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.ENCRYPT_MODE, input, null);
String base64Out = deToString(result, Base64.DEFAULT);
Log.e(TAG,"base64:"+ base64Out);
input = Base64.decode(base64Out, Base64.DEFAULT);
result = SymmetricEncryptionAlgUtils.SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.DECRYPT_MODE, input, null);        Log.e(TAG,"base64:"+new String(result,"utf-8"));
打印结果:
base64:gMb8YHWx8mfaAKYzvkROEA==
原数据:对称加密AES
3>加密和显⽰16进制字符串
String test ="对称加密AES";
byte[] input = Bytes("utf-8");
byte[] key = BytesUtils.hexStr2Bytes("111111111111111111111111111222221111111111122222");
byte[] result =SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.ENCRYPT_MODE, input, null);
String hexOut = BytesUtils.byte2HexStr(result);
Log.e(TAG,"hex:"+ hexOut);
input = BytesUtils.hexStr2Bytes(hexOut);
result = SymmetricEncryptionAlgUtils.SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.DECRYPT_MODE, input, null);        Log.e(TAG,"原数据:"+new String(result,"utf-8"));
打印结果:
hex:80C6FC6075B1F267DA00A633BE444E10
原数据:对称加密AES
4>对密钥的处理
上⾯密钥都是⼆进制的不利于记忆,⼀般处理是对字符串密钥做摘要(md5)当做计算密钥
String keyStr = "我的密钥";
String test = "对称加密AES";
byte[] input = Bytes("utf-8");
byte[] key = MessageDigestUtils.Bytes());
byte[] result = SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.ENCRYPT_MODE, input, null);
String base64Out = deToString(result, Base64.DEFAULT);
Log.e(TAG, "base64:" + base64Out);
input = Base64.decode(base64Out, Base64.DEFAULT);
result = SymmetricEncryptionAlgUtils.SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.DECRYPT_MODE, input, null);        Log.e(TAG, "原数据:" + new String(result, "utf-8"));
}
打印结果:
base64:cfnxIg2ngoORziek5SX/kA==
原数据:对称加密AES
BytesUtils ⼯具类将16进制字符串装换成对应字节数组,和字节数组转化成对应16进制字符串
public class BytesUtils {
/**
* 字节数组转换成⼗六进制⼤写字符串
*
* @param b
* @return
*/
public static String byte2HexStr(byte[] b) {
StringBuilder hexSb = new StringBuilder();
String stmp = "";
int len = b.length;
for (int n = 0; n < len; n++) {
stmp = (HexString(b[n] & 0xFF));
if (stmp.length() == 1)
hexSb.append("0" + stmp);
else
hexSb.append(stmp);
}
String().toUpperCase();
}
/
**
* ⼗六进制字符串转换成字节数组
*
* @param hexStr
* @return
*/
public static byte[] hexStr2Bytes(String hexStr) {
int len = hexStr.length();
if (len % 2 != 0) {
throw new RuntimeException("⼗六进制字符串长度不对");        }
byte[] b = new byte[hexStr.length() / 2];
int bLen = b.length;
int j = 0;
for (int i = 0; i < bLen; i++) {
char c0 = hexStr.charAt(j++);
char c1 = hexStr.charAt(j++);
b[i] = (byte) ((parse(c0) << 4) | parse(c1));
}
return b;
}
/**
* ⼗六进制字符串转换成字节数组
*
* @param hexStr
* @return
*/
public static byte[] hexStr2Bytes1(String hexStr) {
int len = hexStr.length();
if (len % 2 != 0) {
throw new RuntimeException("⼗六进制字符串长度不对");        }
byte[] b = new byte[hexStr.length() / 2];
int bLen = b.length;
int j = 0;
String tem="";
for (int i = 0; i < bLen; i++) {
java加密方式有哪些tem=hexStr.substring(2*i,2*i+2);
b[i]= (byte) Integer.parseInt(tem,16);
}
return b;
}
private static int parse(char c) {
if (c >= 'a')
return (c - 'a' + 10) & 0x0f;
if (c >= 'A')
return (c - 'A' + 10) & 0x0f;
return (c - '0') & 0x0f;
}
}
三总结
密钥:⾃⼰⽣成,与服务器协商,对⽤户输⼊密码做摘要(md5),需要注意长度
加密算法:选取其中⼀种加密算法,分组模式和填充 (具体选择看需求)
需要加密数据:获取对应加密数据的编码,需要注意长度,加密的分组模式和填充
加密后的数据显⽰:base64编码显⽰和16进制显⽰
解密回原数据:将加密后显⽰的数据转换成对应加秘数据,对加密后数据解密(注意分组和填充模式),解密后转换成原来编码数据(注意编码字符集)
此⽂要是对你有帮助,如果⽅便⿇烦点个赞,谢谢

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。