一、编码算法
编码是直接给每个字符指定一个若干字节表示的整数。
常见的编解码算法有:URL编码、Base64编码。
1、URL编码是浏览器发送数据给服务器时使用的编码。
特点:把数据编码成前缀为%表示的字符串。
URL编码对A(a)-Z(z),0-9, - , _ , . , * 这些字符不进行编码,除此以外的字符则需要进行编码。
URLEncoder类:对URL进行斑马操作的。
有编码必然有解码,URLDecoder类:对URL进行解码操作的。
2.Base64编码:将字节数组编码成字符串或将字符串解码为字节数组
特点:把字符编码成%xx表示的形式。
二、哈希算法
1、哈希算法又称摘要算法,作用是对任意一组输入数据进行计算,得到一个固定长度的输出摘要。
目的:验证原始数据是否被篡改。
2、哈希碰撞是指:两个不同的输入得到了相同的输出。
哈希碰撞不能避免,因为输出的字节长度是固定的,而且最多有4294967296中输出,但是有无限种输入。
3、常见的哈希算法:MD5算法,SHA-1(20字节),SHA-256(32字节),SHA-512(64字节),RipeMD-160算法(Java标准类库中没有提供RipeMD-160算法)。
MD5算法(长度:16字节)
需要编写一个hash算法的工具类,定义bytesToHex()方法,将字节数组转换为十六进制字符串。
SHA-1算法:与MD5使用方法一样,只用在实例化MessageDigest对象时将传入的参数改为"SHA-1"即可。
RipeMD160算法
public class Demo03 {
//使用第三方开源库提供的RipeMD160消息摘要算法实现
public static void main(String[] args) throws NoSuchAlgorithmException {
//注册BouncyCastleBouncyCastleProvider通知类
//将提供的消息摘要算法注册至Security
Security.addProvider(new BouncyCastleProvider());
//获取RipeMD160算法的“消息摘要对象”(加密对象)
MessageDigest ripeMd160 = MessageDigest.getInstance("RipeMD160");
//更新原始数据
ripeMd160.update("wbjxxmy".getBytes());
//获取消息摘要(加密)
byte[] result = ripeMd160.digest();
//消息摘要的字节长度和内容
System.out.println("加密结果(字节长度):"+result.length);//160位=20字节
System.out.println("加密结果(字节内容):"+Arrays.toString(result));
//16进制内容字符串
String hex = new BigInteger(1,result).toString(16);
System.out.println("加密结果(字符串长度):"+hex.length());
System.out.println("加密结果(字符串内容):"+hex);
}
}
4.存储用户密码
如果直接将用户的密码不做任何处理直接放入数据库中,会存在很大的安全风险。所以改进方法是存储用户密码的哈希。
什么是彩虹表攻击?例如:使用MD5。就是将常用口令和它们MD5制作成一张表(这个表足够庞大),这个表就是彩虹表。为了防止彩虹表攻击,对每个口令前面追加一些额外的数字,这就是盐值。
5.解决MD5算法与SHA-1算法代码冗余
//hash算法工具类
public class HashTools {
//定义全局变量,消息摘要对象
private static MessageDigest digest;
//构造方法私有
private HashTools() {}
//按照MD5进行消息摘要算法(哈希计算)
public static String digestByMD5(String source) throws NoSuchAlgorithmException {
//基于MD5算法
digest = MessageDigest.getInstance("MD5");
return handler(source);
}
//按照SHA-1进行消息摘要算法(哈希计算)
public static String digestBySHA1(String source) throws NoSuchAlgorithmException {
//基于SHA-1算法
digest = MessageDigest.getInstance("SHA-1");
return handler(source);
}
//通过消息摘要对象,处理加密内容
private static String handler(String source) {
//更新原始数据
digest.update(source.getBytes());
//进行加密处理
byte[] bytes = digest.digest();
//将字节数组转换成十六进制
String hash = bytesToHex(bytes);
return hash;
}
//将字节数组转换为16进制字符串
public static String bytesToHex(byte[] bytes) {
StringBuilder ret = new StringBuilder();
for(Byte b : bytes) {
//将字节值转换为2位十六进制字符串
ret.append(String.format("%02x", b));
}
return ret.toString();
}
}
三、Hmac算法
Hmac算法:基于秘钥的消息摘要算法.也是一种防御彩虹表攻击的算法,它比"加盐"这种方式的安全性更高。
1、使用秘钥生成器生成秘钥,进行加密处理
public class Demo01 {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String password = "wbjxxmy";
//1.产生秘钥
//获取HmacMD5秘钥生成器
KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
//生成密钥
SecretKey key = keyGen.generateKey();
System.out.println("秘钥:"+Arrays.toString(key.getEncoded()));
System.out.println("秘钥长度(64字节):"+key.getEncoded().length);
System.out.println("秘钥:"+HashTools.bytesToHex(key.getEncoded()));
//2.使用秘钥,进行加密
//获取HMac加密算法
//创建Mac对象,传入"HmacMD5"算法
Mac mac = Mac.getInstance("HmacMD5");
//初始化秘钥
mac.init(key);
//更新原始加密内容
mac.update(password.getBytes());
//加密处理,并获取加密结果
byte[] bytes = mac.doFinal();
//加密结果处理成16进制字符串
String result = HashTools.bytesToHex(bytes);
System.out.println("加密结果16进制字符串:"+result);
System.out.println("加密结果(字节长度16字节):"+bytes.length);
System.out.println("加密结果(字符长度32字节):"+result.length());
}
}
2、给定秘钥字节数组,恢复Hmac秘钥
给定一个秘钥字节数组,先将其恢复成秘钥对象,再进行加密处理。
//秘钥(字节数组)
byte[] keyBytes = {51, 8, 87, 91, 106, -78, 70, -119, 5, -75, 78, -52, -62, -33,
-64, -102, 80, 56, 7, -43, -115, 57, 29, 69, -61, 121, -37, -47, -95,
66, -82, -116, -22, 19, -8, -42, 127, 67, 18, -109, -9, 89, 7, 6, 59,
-63, 51, 35, 0, 45, -102, -20, -82, -82, 29, -105, 10, 0, -110, -70, 76,
-102, -57, 50};
try {
//恢复秘钥(字节数组)
SecretKey key = new SecretKeySpec(keyBytes, "HmacMD5");
//创建Hmac加密算法对象
Mac mac = Mac.getInstance("HmacMD5");
//初始化秘钥
mac.init(key);
//更新原始加密内容
mac.update(password.getBytes());
//加密结果处理成16进制字符串
String result = HashTools.bytesToHex(mac.doFinal());
//输出
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
3、按照"字符串",恢复Hmac秘钥
先将字符串转换成字节数组再进行后续步骤。
//秘钥(字符串)
String keyStr = "1dbb7361d48949df91e3ff0c5fab8666c5358a8daf965d6"
+ "bbd84ff90e17c3a365b2c6613ac257ae961d75402e88d47c6a0854de60cde"
+ "00.070982b3e437e4dec91af";
//用于保存秘钥,秘钥长度为64字节
byte[] keyBytes = new byte[64];
//遍历字符串,将其保存进字节数组keyBytes中
for(int i = 0,k = 0;i < keyStr.length();i += 2,k++) {
//每次截取两位
String s = keyStr.substring(i,i+2);
//将截取的两位字符转换成16进制,放入字节数组中
keyBytes[k] = (byte) Integer.parseInt(s,16);
}
try {
//恢复秘钥(字节数组)
SecretKey key = new SecretKeySpec(keyBytes, "HmacMD5");
//创建Hmac加密算法对象
Mac mac = Mac.getInstance("HmacMD5");
//初始化秘钥
mac.init(key);
//更新原始加密内容
mac.update(password.getBytes());
//加密结果处理成16进制字符串
String result = HashTools.bytesToHex(mac.doFinal());
//输出
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}