欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SHA 消息摘要算法应用详解

程序员文章站 2024-03-14 14:25:10
...

  SHA 简介:

  SHA(Secure Hash Algorithm,安全散列算法)是消息摘要算法的一种,被广泛的认为是MD5的替代者。SHA算法家族目前有SHA-1、SHA-224、SHA-256、SHA-384和SHA-512五种算法,通常将后四种称为SHA-2算法。

  SHA家族算法是在MD4算法的基础上演进而来,通过SHA算法同样能够得到一个固定长度的摘要信息。与MD系列算法不同的是:若输入的消息不同,则与其相对应的摘要信息的差异概率很高。SHA算法是FIPS所认证的五种安全杂凑算法。

  SHA算法的安全体现在:

  · 由消息摘要反推输入信息,从理论上来说是非常困难的。

  · 想要找到两组不同输入信息对应到相同的摘要信息,从理论上来说是非常困难的。

  · 输入信息任何变动,都有很高的几率导致其产生的摘要消息差异很大。

  SHA 发展史:

  · SHA-0 算法:

  1993年,NIST公布了SHA算法家族的第一个版本SHA-0,SHA-0在公布不久后即被撤回,原因是SHA-0算法中含有降低密码安全性的错误。

  · SHA-1 算法:

  1995年,继SHA-0夭折之后,NIST发布了SHA算法家族的第二个版本SHA-1,SHA-1在很多安全协议中得到广泛应用,包括TSL/SSL、PGP、SSH、S/MIME和IPsec。SHA-0和SHA-1算法可对最大长度为264的字节信息做摘要处理,得到一个160位的摘要信息。如将160位的摘要信息转换为十六进制,是一个40位的字符串。

  · SHA-2 算法:

  SHA算法家族除了SHA-1算法外,还有SHA-224、SHA-256、SHA-384和SHA-512四种SHA算法的变体,算法的命名代表其摘要信息长度。摘要信息长度是SHA-1和SHA-2算法的最大不同。

  应用场景:

  · 软件校验

  很多软件,尤其是安全性要求较高的软件,会在官网上公布软件的SHA值,用户下载软件后,可以自行计算软件SHA值,然后与官网公布的SHA值进行比较,确认软件是否被篡改过。

  · 基于口令的加密

  SHA也被用于基于口令的加密(Password Based Encryption,PBE),PBE的原理是将口令和盐(salt)混合后计算其SHA值,然后将这个散列值用作加密的秘钥。PBE可以防御针对口令的字典攻击。

  · 消息认证码

  消息认证码是将“发送者和接收者之间的共享秘钥”和“消息”进行混合后计算出的SHA。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装。

  · 数字签名

  数字签名是现实社会中的签名和盖章这样的行为在数字世界中的实现。数字签名的处理过程非常耗时,因此一般不会对整个消息内容施加数字签名,而是先取消息内容的SHA值,对SHA值进行数字签名。

  · 安全协议

  SHA算法在许多安全协议中广为使用,包括TSL/SSL、PGP、SSH、S/MIME和IPsec,同时在TSL/SSL安全协议数字证书中,也有SHA的影子,例如证书指纹一般都是通过SHA-1来实现的。

  演示示例:

  JDK提供了SHA-1、SHA-256、SHA-384、SHA-512的实现。Bouncy Castle作为JDK的补充,提供了SHA-224的实现。Commons Codec对JDK的SHA算法进行了封装,提供了简洁易用的API。

  JDK原生示例:

package com.securitit.serialize.sha;

import java.security.MessageDigest;

import org.apache.commons.codec.binary.Hex;

public class JdkShaTester {

	public static void main(String[] args) throws Exception {
		String plainText = "Hello SHA! This is my SHA test program!";
		byte[] sha1Bts = encodeSHA1(plainText.getBytes("UTF-8"));
		System.out.println("SHA-1散列值:" + new String(Hex.encodeHex(sha1Bts)));
		byte[] sha256Bts = encodeSHA256(plainText.getBytes("UTF-8"));
		System.out.println("SHA-256散列值:" + new String(Hex.encodeHex(sha256Bts)));
		byte[] sha384Bts = encodeSHA384(plainText.getBytes("UTF-8"));
		System.out.println("SHA-384散列值:" + new String(Hex.encodeHex(sha384Bts)));
		byte[] sha512Bts = encodeSHA512(plainText.getBytes("UTF-8"));
		System.out.println("SHA-512散列值:" + new String(Hex.encodeHex(sha512Bts)));
	}
	
	/**
	 * SHA-1 算法.
	 * @param plainBts 原文.
	 * @return 摘要信息.
	 * @throws Exception .
	 */
	public static byte[] encodeSHA1(byte[] plainBts) throws Exception {
		// 初始化MessageDigest.
		MessageDigest sha1 = MessageDigest.getInstance("SHA");
		// 获取消息摘要.
		return sha1.digest(plainBts);
	}
	
	/**
	 * SHA-256 算法.
	 * @param plainBts 原文.
	 * @return 摘要信息.
	 * @throws Exception .
	 */
	public static byte[] encodeSHA256(byte[] plainBts) throws Exception {
		// 初始化MessageDigest.
		MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
		// 获取消息摘要.
		return sha256.digest(plainBts);
	}
	
	/**
	 * SHA-384 算法.
	 * @param plainBts 原文.
	 * @return 摘要信息.
	 * @throws Exception .
	 */
	public static byte[] encodeSHA384(byte[] plainBts) throws Exception {
		// 初始化MessageDigest.
		MessageDigest sha384 = MessageDigest.getInstance("SHA-384");
		// 获取消息摘要.
		return sha384.digest(plainBts);
	}
	
	/**
	 * SHA-512 算法.
	 * @param plainBts 原文.
	 * @return 摘要信息.
	 * @throws Exception .
	 */
	public static byte[] encodeSHA512(byte[] plainBts) throws Exception {
		// 初始化MessageDigest.
		MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
		// 获取消息摘要.
		return sha512.digest(plainBts);
	}
	
}

  输出结果:

SHA-1散列值:e73fecc0e62b18e974027322fdd7951c5939566a
SHA-256散列值:e39e4f9ceeadd7cb6ddc2bd970c794dec4a7eeabe49dafeaf2e7a644f904abd1
SHA-384散列值:581e0080891bfd65acf895bf2ad4ae2dcf99ab72633c17e0792f957bab9f71da6478baca2cf813e8135dee7e1814450e
SHA-512散列值:d087a7ebe3833ee909b46495f5756ecc5ffb8a8b342f616bfc35af3dab8284fee26fbd869d570e4aceffbf0f32a5d04d53d6ce6c540531cea379582f91b62df9

  Bouncy Castle示例:

  第三方加密组件包Bouncy Castle是对JDK的补充,弥补了JDK未提供SHA-224算法的空白。

package com.securitit.serialize.sha;

import java.security.MessageDigest;
import java.security.Security;

import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class BouncyCastleShaTester {

	public static void main(String[] args) throws Exception {
		String plainText = "Hello SHA! This is my SHA test program!";
		byte[] sha224Bts = encodeSHA224(plainText.getBytes("UTF-8"));
		System.out.println("SHA-224散列值:" + new String(Hex.encodeHex(sha224Bts)));
	}
	
	/**
	 * SHA-224 算法.
	 * @param plainBts 原文.
	 * @return 摘要信息.
	 * @throws Exception .
	 */
	public static byte[] encodeSHA224(byte[] plainBts) throws Exception {
		// 加入BouncyCastleProvider支持.
		Security.addProvider(new BouncyCastleProvider());
		// 初始化MessageDigest.
		MessageDigest sha224 = MessageDigest.getInstance("SHA-224");
		// 获取消息摘要.
		return sha224.digest(plainBts);
	}
	
}

  输出结果:

SHA-224散列值:4d7fa6c9eee45853f04d4fb51a7f7c9352168fa0bdb6c20b483a9d10

  Commons Codec示例:

  Apache Commons Codec提供了消息摘要工具类DigestUtils,DigestUtils类对JDK提供的MessageDigest进行了封装,使得API的使用更加简洁。在日常开发中建议通过Commons Codec来使用SHA算法。

package com.securitit.serialize.sha;

import org.apache.commons.codec.digest.DigestUtils;

public class CommonsCodecShaTester {

	public static void main(String[] args) throws Exception {
		String plainText = "Hello SHA! This is my SHA test program!";
		System.out.println("SHA-1散列值:" + DigestUtils.sha1Hex(plainText.getBytes("UTF-8")));
		System.out.println("SHA-256散列值:" + DigestUtils.sha256Hex(plainText.getBytes("UTF-8")));
		System.out.println("SHA-384散列值:" + DigestUtils.sha384Hex(plainText.getBytes("UTF-8")));
		System.out.println("SHA-512散列值:" + DigestUtils.sha512Hex(plainText.getBytes("UTF-8")));
	}
	
}

  输出结果:

SHA-1散列值:e73fecc0e62b18e974027322fdd7951c5939566a
SHA-256散列值:e39e4f9ceeadd7cb6ddc2bd970c794dec4a7eeabe49dafeaf2e7a644f904abd1
SHA-384散列值:581e0080891bfd65acf895bf2ad4ae2dcf99ab72633c17e0792f957bab9f71da6478baca2cf813e8135dee7e1814450e
SHA-512散列值:d087a7ebe3833ee909b46495f5756ecc5ffb8a8b342f616bfc35af3dab8284fee26fbd869d570e4aceffbf0f32a5d04d53d6ce6c540531cea379582f91b62df9

  从上面三种方式示例结果对比可以看出,无论使用哪种方式,同一个字符串的SHA值都是一样的。