JAVA实现SHA-1算法

一、实验目的

5. 学习SHA-1算法的基本原理和特点;
6. 学习SHA-1算法的实现方法;
7. 掌握Java标准库中SHA-1算法的使用方法。

二、实验要求

  1. 了解SHA-1算法的原理及过程;
  2. 掌握Java标准库中密码学算法的使用方法;
  3. 根据SHA的算法,能使用MD5, SHA-256, SHA-512。
    拓展:计算文件的Hash值

三、开发环境

JDK1.8,Eclipse Oxygen.3 Release (4.7.3)

【1-1】SHA-1算法的基本原理和特点

SHA1是一种密码散列函数,由美国国家安全局设计,主要适用于数字签名标准里面定义的数字签名算法。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。
应用:可以对文本和文件进行SHA运算,比较2个文件是否被损坏过。
SHA1哈希算法流程
对于任意长度的明文,SHA1首先对其进行分组,使得每一组的长度为512位,然后对这些明文分组反复重复处理。对于每个明文分组的摘要生成过程如下:
1、将512位的明文分组划分为16个子明文分组,每个子明文分组为32位。
2、申请5个32位的链接变量,记为A、B、C、D、E。
3、16份子明文分组扩展为80份。
4、80份子明文分组进行4轮运算。
5、链接变量与初始链接变量进行求和运算。
6、链接变量作为下一个明文分组的输入重复进行以上操作。
7、 最后,5个链接变量里面的数据就是SHA1摘要。
SHA1算法的分组过程
对于任意长度的明文,SHA1的明文分组过程与MD5相类似,首先需要对明文添加位数,使明文总长度为448(mod512)位。在明文后添加位的方法是第一个添加位是l,其余都是0。然后将真正明文的长度(没有添加位以前的明文长度)以64位表示,附加于前面已添加过位的明文后,此时的明文长度正好是512位的倍数。与MD5不同的是SHA1的原始报文长度不能超过2的64次方,另外SHA1的明文长度从低位开始填充。
经过添加位数处理的明文,其长度正好为512位的整数倍,然后按512位的长度进行分组(block),可以划分成L份明文分组,我们用Y0,Y1,……YL-1表示这些明文分组。对于每一个明文分组,都要重复反复的处理,这些与MD5是相同的。
对于512位的明文分组,SHA1将其再分成16份子明文分组(sub-block),每份子明文分组为32位,我们使用M[k](k= 0, 1,……15)来表示这16份子明文分组。之后还要将这16份子明文分组扩充到80份子明文分组,我们记为W[k](k= 0, 1,……79),扩充的方法如下。

SHA1有4轮运算,每一轮包括20个步骤(一共80步),最后产生160位摘要,这160位摘要存放在5个32位的链接变量中,分别标记为A、B、C、D、E。这5个链接变量的初始值以16进制位表示如下。
A=0x67452301
B=0xEFCDAB89
C=0x98BADCFE
D=0x10325476
E=0xC3D2E1F0
SHA1的4轮运算
SHA1有4轮运算,每一轮包括20个步骤,一共80步,当第1轮运算中的第1步骤开始处理时,A、B、C、D、E五个链接变量中的值先赋值到另外5个记录单元A′,B′,C′,D′,E′中。这5个值将保留,用于在第4轮的最后一个步骤完成之后与链接变量A,B,C,D,E进行求和操作。
SHA1的4轮运算,共80个步骤使用同一个操作程序,如下:

其中 ft(B,C,D)为逻辑函数,Wt为子明文分组W[t],Kt为固定常数。这个操作程序的意义为:
a、将[(A<<<5)+ ft(B,C,D)+E+Wt+Kt]的结果赋值给链接变量A;
b、将链接变量A初始值赋值给链接变量B;
c、将链接变量B初始值循环左移30位赋值给链接变量C;
d、将链接变量C初始值赋值给链接变量D;
e、将链接变量D初始值赋值给链接变量E。
我们同样举一个例子来说明SHA1哈希算法中的每一步是怎样进行的,比起MD5算法,SHA1相对简单,假设W[1]=0x12345678,此时链接变量的值分别为A=0x67452301、B=0xEFCDAB89、C=0x98BADCFE、D=0x10325476、E=0xC3D2E1F0,那么第1轮第1步的运算过程如下。
(1) 将链接变量A循环左移5位,得到的结果为:0xE8A4602C。
(2) 将B,C,D经过相应的逻辑函数:
(3) 将第(1)步,第(2)步的结果与E,W[1],和K[1]相加得:

(4) 将B循环左移30位得:(B<<<30)=0x7BF36AE2。
(5) 将第3步结果赋值给A,A(这里是指A的原始值)赋值给B,步骤4的结果赋值给C,C的原始值赋值给D,D的原始值赋值给E。
(6) 最后得到第1轮第1步的结果:
A = 0xB1E8EF2B
B = 0x67452301
C = 0x7BF36AE2
D = 0x98BADCFE
E = 0x10325476
按照这种方法,将80个步骤进行完毕。
第四轮最后一个步骤的A,B,C,D,E输出,将分别与记录单元A′,B′,C′,D′,E′中的数值求和运算。其结果将作为输入成为下一个512位明文分组的链接变量A,B,C,D,E,当最后一个明文分组计算完成以后,A,B,C,D,E中的数据就是最后散列函数值。

【1-3】Java代码

封装工具类:HashUtils

package xxx;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.MessageDigest;

/**
 * Hash 工具
 */
public class HashUtils {
	private HashUtils() {
		
	}
	
	private static class HashUtilsInner{
		private static volatile HashUtils INSTANCE = new HashUtils();
	}
	
	public static HashUtils getInstance() {
		return HashUtilsInner.INSTANCE;
	}

    /**
     * @Description:封装SHA-1方法
     * @param data 需要加密的数据
     * @Return:String 返回结果
     */
    public String SHA1(String text) {
    	try {
    		byte[] data = text.getBytes("UTF-8");
			return encrypt(data,"SHA-1");
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return "fasle";
    }
    
    
    /**
     * 计算文本(byte数据)的哈希的总方法
     * 根据指定的算法加密任意长度的数据, 返回固定长度的十六进制小写哈希值
     * @param data 需要加密的数据
     * @param algorithm 加密算法, 例如: MD5, SHA-1, SHA-256, SHA-512 等
     */
    private String encrypt(byte[] data, String algorithm) throws Exception {
        // 1. 根据算法名称获实现了算法的加密实例
        MessageDigest digest = MessageDigest.getInstance(algorithm);
        // 2. 加密数据, 计算数据的哈希值
        byte[] cipher = digest.digest(data);
        
        // 3. 将结果转换为十六进制小写
        return bytes2Hex(cipher);
    }
    

    /**
     * @Description:对文件进行SHA1
     * @param ptah 文件的路径
     * @Return:String
     */
    public String SHA12File(String ptah) {
    	try {
			return encrypt(new File(ptah), "SHA-1");
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return "fasle";
    }
    
    /**
     * 计算文件的哈希
     * 根据指定的算法加密文件数据, 返回固定长度的十六进制小写哈希值
     * @param file 需要加密的文件
     * @param algorithm 加密算法, 例如: MD5, SHA-1, SHA-256, SHA-512 等
     */
    private String encrypt(File file, String algorithm) throws Exception {
        InputStream in = null;
        try {
            // 1. 根据算法名称获实现了算法的加密实例
            MessageDigest digest = MessageDigest.getInstance(algorithm);
            in = new FileInputStream(file);
            byte[] buf = new byte[1024];
            int len = -1;
            while ((len = in.read(buf)) != -1) {
                // 2. 文件数据通常比较大, 使用 update() 方法逐步添加
                digest.update(buf, 0, len);
            }
            // 3. 计算数据的哈希值, 添加完数据后 digest() 方法只能被调用一次
            byte[] cipher = digest.digest();
            // 4. 将结果转换为十六进制小写
            return bytes2Hex(cipher);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    // nothing
                }
            }
        }
    }

    /*
	 * 映射十六进制数组 
	 * 目的时把经过处理后的byte数值映射成16进制
	 */
    private final char[] HEXES = {
            '0', '1', '2', '3',
            '4', '5', '6', '7',
            '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f'
    };
    
    /**
     * byte数组转换为 16 进制数据
     */
    private String bytes2Hex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(HEXES[(b >> 4) & 0x0F]);
            sb.append(HEXES[b & 0x0F]);
// 说明: x mod n 的时候,如果n满足是2的幂次减1时,采用位移运算
// 可去分析hashMap源码。这儿分2段是对8字节的前和后4位分别运算再拼凑
        }
        return sb.toString();
    }
}

测试代码:SHA1Test

package xxx;

import org.junit.Test;
/*
 * 测试类
 */
public class SHA1Test {

	@Test
	public void testEncText() {
		String text = "Hello world!";
	       
        String test = HashUtils.getInstance().SHA1(text);
        System.out.println(test);
	@Test
	public void testEncFile() {
		String path = "/Users/mulming/Desktop/kb.rtf";
		String file = HashUtils.getInstance().SHA12File(path);
        System.out.println(file);
	}
}

	}

【1-4】对MD5, SHA-256, SHA-512的使用

只需要将如下部分中的XXX改成相应的参数即可:
参数范围:MD5, SHA-256, SHA-512

public String SHA12File(String ptah) {
    	try {
			return encrypt(new File(ptah), "XXX");
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return "fasle";
    }

public String SHA12File(String ptah) {
    	try {
			return encrypt(new File(ptah), " XXX");
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return "fasle";
    }

你可能感兴趣的:(java,网络空间安全)