摘要的作用?
1.保证数据的完整性:例如你发送一个100M的文件给你的B,但是你不知道B收到的是否是完整的文件;此时你首先使用摘要算法,如MD5,计算了一个固定长度的摘要,将这个摘要和文件一起发送给B,B接收完文件之后,同样使用MD5计算摘要,如果B计算的结果和你发送给他的摘要结果一致,说明B接收的文件是完整的。
2.数字签名:数字签名里面的签名都是使用摘要算法计算的,目前(2017年),数字签名的摘要算法已经使用SHA-256作为规范
JAVA如何产生摘要
JDK包含一个security包,这个包已经实现主流的摘要算法,没有特殊需求直接拿过来使用就好:(java.security.MessageDigest)
加密方式:MD5、SHA-1、SHA-256、SHA-512、HmacMD5、HmacSHA1 、HmacSHA256、HmacSHA512
public final class MessageDigestUtil {
/**
* 使用指定哈希算法计算摘要信息
* @param content 内容
* @param algorithm 哈希算法
* @return 内容摘要
*/
public static String getMD5Digest(String content,String algorithm){
try {
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
messageDigest.update(content.getBytes("utf-8"));
return bytesToHexString(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将字节数组转换成16进制字符串
* @param bytes 即将转换的数据
* @return 16进制字符串
*/
private static String bytesToHexString(byte[] bytes){
StringBuffer sb = new StringBuffer(bytes.length);
String temp = null;
for (int i = 0;i< bytes.length;i++){
temp = Integer.toHexString(0xFF & bytes[i]);
if (temp.length() <2){
sb.append(0);
}
sb.append(temp);
}
return sb.toString();
}
}
调用:
MessageDigestUtil.getMD5Digest("test","sha-256");
输出:
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
API调取
方法 | 补充说明 |
---|---|
getSHA1 | 获得SHA1摘要 |
getSHA256 | 获得SHA256摘要 |
getSHA512 | 获得SHA512摘要 |
getHmacMD5 | 获得HmacMD5摘要 |
getHmacSHA1 | 获得HmacSHA1摘要 |
getHmacSHA256 | 获得HmacSHA256摘要 |
getHmacSHA512 | 获得HmacSHA512摘要 |
getPBKDF2 | 默认迭代54次,产生32位密钥 |
getRandomSalt | 获得一个定长的盐 |
注意:
- 不适用于SHA384等非2i 2^i2 长度的方法。如有需要,请重写toHex()方法
- 默认加盐方式为"msg"+“salt”,如需自定义请重写addSalt方法
- 精简版代码中只包含MD5相关算法,可以参照该方法填充其它数字摘要算法。参考此处:完整摘要算法工具类Java代码封装
精简版的JAVA代码
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
public class MDBuilder {
// 散列算法
public static String getMD5(String msg, String salt){
return MDBuilder.buildMD(msg, "MD5", salt);
}
// 哈希算法
public static String getHmacMD5(String msg, String key, String salt){
return MDBuilder.buildHmacMD(msg, "HmacMD5", key, salt);
}
// 产生具体的编码
private static String buildMD(String msg, String algs, String salt){
String retult="";
try{
MessageDigest md = MessageDigest.getInstance(algs);
byte[] buff = md.digest(addSalt(msg,salt).getBytes());
retult = toHex(buff);
}catch (Exception e){
e.printStackTrace();
}
return retult;
}
private static String buildHmacMD(String msg, String algs, String key, String salt) {
String result="";
try{
SecretKey sk = new SecretKeySpec(key.getBytes(),algs);
Mac mac = Mac.getInstance(algs);
mac.init(sk);
byte[] buff = mac.doFinal(addSalt(msg, salt).getBytes());
result = toHex(buff);
}catch (Exception e){
e.printStackTrace();
}
return result;
}
// 对盐的添加方式
private static String addSalt(String msg, String salt){
return msg+salt;
}
// 迭代散列
private static final int PBKDF2_ITERATIONS = 54;
private static final int HASH_BIT_SIZE = 32 * 4;
/** @link https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory */
private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";
public static String getPBKDF2(String msg, String salt, int iterations){
String result="";
try{
KeySpec spec = new PBEKeySpec(msg.toCharArray(), salt.getBytes(), iterations, HASH_BIT_SIZE);
SecretKeyFactory f = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
result = toHex(f.generateSecret(spec).getEncoded());
}catch (Exception e){
e.printStackTrace();
}
return result;
}
//将128位的二进制序列转为32位的16进制编码
private static String toHex(byte[] bytes) {
StringBuilder md5str = new StringBuilder();
for (byte aByte : bytes) {
int temp = aByte;
if (temp < 0) temp += 256; // 0x8* 在经过toHexString转化时,会被转为ffffff8*,需要+256保证其正值
if (temp < 16) md5str.append("0"); // 0x05 转化会变成 5,缺少一位0
md5str.append(Integer.toHexString(temp));
}
return md5str.toString();
}
// 产生一个指定种子的Strong盐
public static String getRandomSalt(int length, Long seed){
String salt = "default";
try{
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
if(seed!=null)random.setSeed(seed);
byte[] bytes = new byte[length / 2];
random.nextBytes(bytes);
//将byte数组转换为16进制的字符串
salt = DatatypeConverter.printHexBinary(bytes);
}catch (Exception e){
e.printStackTrace();
}
return salt;
}
}