web开发工具之:二、加密和解密工具类,学习加密算法和非加密算法(哈希算法)知识,Java支持MD5和SHA系列的哈希算法。使用UUID作为盐进行增强哈希算法加密的数据完整性验证

文章目录

  • 前言
  • 一、加密算法/非加密算法-了解和学习为主
    • 1、加密算法和秘钥
      • a、介绍
      • b、常用加密算法-对称加密算法
      • c、常用加密算法-对称加密算法
    • 2、非加密算法:哈希算法(MD5、SHA系列)
      • a、哈希算法介绍
      • b、MD5和SHA系列介绍
  • 二、哈希算法应用场景 概念介绍
    • 1. 数据完整性验证
    • 2. 密码存储(借助 数据完整性验证 来进行密码存储)
    • 3. 数字签名
    • 4. 总结
  • 三、注册和登录-采用哈希算法进行密码存储和验证流程
    • 1. 加密过程(注册和登录)
    • 2. 加盐的加密
    • 3. java 支持的MD5/SHA算法加密code
      • MD5哈希算法
      • SHA-256哈希算法(更安全-推荐)
      • 总结
  • 四、登录注册的密码加密工具类
    • 1. 逻辑介绍
    • 2. PasswordUtils.java 加密和验证工具类
    • 3. PasswordEncoder.java 加密的具体实施类

前言

本文介绍了加密算法和非加密算法的基本概念,重点讲解了加密算法中的对称加密和非对称加密,以及常用的加密算法如AES、DES、RSA等。同时,介绍了哈希算法,尤其是MD5和SHA系列,并阐述了其在数据完整性验证、密码存储和数字签名等场景的应用。文章还详细描述了在用户注册和登录过程中,如何使用哈希算法进行密码加密和验证,提供了Java代码示例,并通过工具类PasswordUtils和PasswordEncoder实现了密码的安全存储和验证流程。强调了在密码安全领域,应使用更安全的算法如SHA-256替代MD5。

一、加密算法/非加密算法-了解和学习为主

1、加密算法和秘钥

a、介绍

为了保证信息的安全性,就需要采用信息加密技术对信息进行伪装,使得信息非法窃取者无法理解信息的真实合义,信息的合法拥有者可以利用特征码对信息的完整性进行校验。采用加密算法对信息使用者的身份进行认证、识别和确认,以对信息的使用进行控制。

加密技术 包括两个元素:算法秘钥 ,密钥加密技术的密码体制分为 对称秘钥体制非对称加密体制 两种。相应地,对数据加密的技术分为两类,即对称加密(私人密钥加密)和非对称加密(公开密钥加密)

  • 对称加密以数据加密标准DES(data encryption standard, ) 算法为典型代表非对称加密通常RSA(rivest shamir adleman)算法为代表
  • 对称加密的加密秘钥和解密秘钥相同,而非对称加密的加密密钥和解密密钥不同,加密秘钥可以公开而解密密密钥需要保密。

b、常用加密算法-对称加密算法

对称加密算法(也称为“私钥加密”)是指加密和解密使用同一密钥的算法。它们的特点是加密和解密过程使用相同的密钥,因此密钥的保护至关重要。如果密钥泄露,整个系统的安全性就会受到威胁。

  1. AES (Advanced Encryption Standard)
    特点:AES 是一种广泛使用的对称加密算法,属于现代加密算法中的标准之一。它具有非常高的安全性和效率,适用于加密大数据。
    密钥长度:AES 支持 128、192 和 256 位密钥。
    应用场景:AES 常用于文件加密、数据传输加密(如 SSL/TLS 协议)、磁盘加密(如 BitLocker)等。
  2. DES (Data Encryption Standard)
    特点:DES 是较早的对称加密算法,已不再安全。它使用 56 位的密钥长度,易受到暴力破解攻击(例如,通过穷举法攻击)。
    应用场景:由于其不再安全,DES 已被许多更安全的算法(如 AES)所取代,但它仍然存在于一些历史遗留系统中。
  3. 3DES (Triple DES)
    特点:3DES 是对 DES 的改进,它通过三次加密(每次使用不同的密钥)来增强 DES 的安全性。虽然比 DES 安全,但仍然被认为相对较慢且不够安全。
    密钥长度:使用三个 56 位的密钥,总共 168 位密钥。
    应用场景:3DES 曾被广泛用于支付系统和银行交易加密,但如今已逐步被 AES 所替代。
  4. RC4 (Rivest Cipher 4)
    特点:RC4 是一种流加密算法,通过生成伪随机数流与明文进行异或运算来加密数据。由于其简单高效,曾被广泛应用于 SSL/TLS 协议中。
    缺点:RC4 存在严重的安全漏洞(例如对密钥的弱性),因此它已不再被推荐使用。
    应用场景:曾用于 Web 安全(SSL/TLS)和 WEP 无线加密协议,但现在已经被更安全的算法替代。

c、常用加密算法-对称加密算法

非对称加密算法(也称为“公钥加密”)使用一对密钥:公钥(公开的密钥)和私钥(秘密的密钥)。公钥用于加密,私钥用于解密。非对称加密算法的安全性基于数学难题(如大数分解或离散对数问题),其最大优点是能够实现密钥的公开分发。

  1. RSA (Rivest-Shamir-Adleman)
    特点:RSA 是最著名的非对称加密算法,广泛应用于数据加密、数字签名等场景。它的安全性基于大数分解问题的难度。
    密钥长度:RSA 的密钥长度通常为 2048 位或 4096 位,密钥越长,安全性越高。
    应用场景:RSA 常用于数字签名、SSL/TLS 协议、电子邮件加密等场景。
  2. ECC (Elliptic Curve Cryptography)
    特点:ECC 是一种基于椭圆曲线数学的非对称加密算法,能够提供与 RSA 相同级别的安全性,但使用更小的密钥长度,从而提高效率。
    密钥长度:通常 ECC 采用 256 位密钥,安全性等同于 3072 位 RSA。
    应用场景:ECC 由于其高效性和安全性,广泛应用于现代加密协议,如 SSL/TLS、移动设备安全、比特币等。
  3. ElGamal
    特点:ElGamal 是基于离散对数问题的非对称加密算法,主要用于加密和数字签名。其主要优点是其加密过程相对简单,但解密过程较慢。
    应用场景:常用于数字签名和公钥加密。
  4. DSA (Digital Signature Algorithm)
    特点:DSA 是一种用于数字签名的非对称加密算法,通常与 SHA 哈希算法一起使用。它用于生成数字签名,证明数据的真实性和完整性。
    应用场景:主要用于数字签名,广泛应用于政府、军事和金融领域。

2、非加密算法:哈希算法(MD5、SHA系列)

a、哈希算法介绍

哈希算法不是加密算法,但它在加密和信息安全中起着重要的作用。哈希算法将输入数据映射到固定长度的哈希值,用于数据完整性验证、数字签名等。

哈希算法的目的是将任意长度的输入(通常称为“消息”)映射到固定长度的输出(通常称为“哈希值”或“摘要”)。哈希算法具有以下特点:

  1. 不可逆性:
    • 哈希是单向的操作:给定输入,你可以生成输出(哈希值),但无法从输出逆向恢复原始输入。也就是说,哈希函数没有解密的过程。
    • 这与加密算法不同,加密算法的目的是将数据加密,通常是为了保证数据的保密性,且可以通过密钥进行解密还原。
  2. 固定长度输出:
    • 无论输入数据的大小如何,哈希算法都会生成一个固定长度的输出。比如:
      • MD5 输出 128 位(16 字节)的哈希值。
      • SHA-1 输出 160 位(20 字节)的哈希值。
      • SHA-256 输出 256 位(32 字节)的哈希值。
  3. 相同输入产生相同输出:
    • 哈希算法总是给定相同的输入时产生相同的哈希值。
  4. 冲突性:
    • 理论上,不同的输入数据可能会产生相同的哈希值(即哈希碰撞)。虽然现代哈希算法已经尽量减少了这种情况的发生,但这仍然是哈希算法的一个潜在问题。
  5. 主要用途:
    • 数据完整性验证:确保数据在传输过程中没有被篡改。
    • 数字签名:用来保证数据的真实性。
    • 密码存储:通过哈希存储密码,使得即使数据库泄露,攻击者无法直接看到原始密码。
    • 快速查找:哈希值可用于快速查找或比较数据(例如哈希表)。

b、MD5和SHA系列介绍

MD5 和 SHA(如 SHA-1、SHA-256)是哈希算法(Hashing Algorithms)。哈希算法和加密算法有着不同的设计目的和用途,虽然它们在某些方面有相似性(如都处理数据并生成固定长度的输出),但它们在工作原理和用途上有本质的区别。

  1. SHA (Secure Hash Algorithm)
    特点:SHA 是一组常用的哈希算法,包括 SHA-1、SHA-2(如 SHA-256)和 SHA-3(如 SHA-512)。SHA-1 已经被证明不够安全,因此现在主要使用 SHA-256 和 SHA-3。
    应用场景:常用于数据完整性验证、数字签名、密码存储等。
  2. MD5 (Message Digest Algorithm 5)
    特点:MD5 是一种非常流行的哈希算法,输出 128 位(16 字节)的哈希值。然而,MD5 已经被发现存在严重的安全漏洞(碰撞攻击),因此不再推荐用于安全用途。
    应用场景:曾广泛用于文件完整性检查但现已被 SHA-256 取代

二、哈希算法应用场景 概念介绍

1. 数据完整性验证

目的:确保数据在传输或存储过程中没有被篡改。

如何工作:通过对数据计算哈希值,在数据传输或存储时记录哈希值,接收方或读取方可以重新计算数据的哈希值,并与原先的哈希值进行对比。如果哈希值一致,说明数据未被篡改;如果不一致,说明数据可能已被修改。

示例:
你下载一个文件,比如一个软件包。官网提供了这个文件的SHA-256哈希值。下载后,你计算文件的哈希值并与官网的哈希值进行比较。如果两者一致,说明文件未被篡改。如果不一致,说明文件可能在下载过程中被篡改或损坏。

2. 密码存储(借助 数据完整性验证 来进行密码存储)

目的:确保密码在存储时不会暴露,保护用户隐私,即使数据库泄露也能避免密码泄漏。

如何工作:通过哈希算法(如SHA-256、bcrypt等)将密码转换为哈希值。哈希算法是单向的,即无法从哈希值逆向推导出原始密码。为进一步增强安全性,通常会为密码加盐(即在密码前后添加随机字符串)以避免相同密码的哈希值相同。

示例:
用户注册时输入密码 “password123”。系统对这个密码应用哈希算法(如SHA-256),并把哈希值(例如 abc123…)存储在数据库中。即使数据库泄露,攻击者也无法直接获得用户的密码。
如果密码存储中使用了盐(salt),每个用户的密码哈希值都会不同,即使密码相同,哈希值也会不同。

3. 数字签名

目的:保证数据的来源真实性、完整性和不可否认性。即确保数据没有被篡改,且确实是由发送方发出的。

如何工作:发送方使用 私钥 对数据的哈希值进行加密(即生成数字签名),接收方用发送方的公钥解密签名并验证数据是否被篡改。如果数据哈希值一致,证明数据是未被篡改的且确实来自发送方。

示例:
你发送一份重要合同文件给某人,你用你的私钥对合同文件的哈希值签名。接收方收到文件后,会用你的公钥解密签名并计算文件的哈希值。如果解密后的哈希值与计算值一致,接收方可以确认文件未被篡改,并且确认是由你发送的(你是签名者)。
在区块链中,每个区块都包含上一个区块的哈希值,保证整个链条的数据完整性,并确保每个区块的来源和数据没有被篡改。

4. 总结

概念 目的 关键技术 应用示例
数据完整性验证 确保数据未被篡改或损坏 哈希算法(如MD5、SHA-256) 文件下载时验证文件是否被篡改
密码存储 确保用户密码安全,即使数据库泄露也不暴露密码 哈希算法 + 盐(salt) 存储用户密码时进行哈希处理
数字签名 确保数据的真实性、完整性,并防止否认发送者 公钥加密与私钥解密(对数据的哈希值签名) 电子邮件或文件的签名验证

这三者都依赖于哈希算法和公钥/私钥加密技术,但它们分别用于数据完整性验证、密码保护和验证数据来源与完整性,各有不同的应用场景和技术实现。

三、注册和登录-采用哈希算法进行密码存储和验证流程

1. 加密过程(注册和登录)

明文密码(比如123456)->通过 哈希算法 ->产生一串字符串,这就是密文密码。

  • 注册:java在用户注册时,可以将用户从前端传过来的明文密码(最好从前端就加密了,这里不讨论前端加密情况),在java后端进行秘钥加密,生成密文密码,存在数据库。任何人都不能看,哪怕是管理员。
  • 登录:后端接收前端传过来的用户名和密码,根据用户名从数据库获取用户信息(如果有,如果没有直接报警告),然后拿着从前端获取的的明文密码,根据加密算法进行加密,获取密文密码,然后与从数据库获取的密文密码,如果相等则登录成功,如果不相等则登录失败即可

2. 加盐的加密

加盐的目的:
当两个用户的密码相同时,单纯使用不加盐的MD5加密方式,会发现数据库中存在相同结构的密码,这样也是不安全的。我们希望即便是两个人的原始密码一样,加密后的结果也不一样。如何做到呢?其实就好像炒菜一样,两道一样的鱼香肉丝,加的盐不一样,炒出来的味道就不一样。MD5加密也是一样,需要进行盐值加密。

流程:
上面注册和登录的流程都没变,就是在使用明文密码加密时,变成了 明文密码+盐 进行加密。这样一来,同样的原文密码,经过加盐的加密之后,存在数据库的密文密码也是不一样的。

3. java 支持的MD5/SHA算法加密code

MD5哈希算法

MD5(Message Digest Algorithm 5)是一个常见的哈希算法,输出128位的哈希值。尽管 MD5 存在碰撞风险,仍然被广泛用于非敏感的校验场景(如文件完整性验证)。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Example {
    public static void main(String[] args) {
        String input = "Hello, world!";
        try {
            // 获取 MD5 摘要实例
            MessageDigest md = MessageDigest.getInstance("MD5");
            
            // 计算输入字符串的哈希值
            byte[] hashBytes = md.digest(input.getBytes());
            
            // 转换成十六进制字符串
            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                hexString.append(String.format("%02x", b));
            }
            
            // 输出哈希值
            System.out.println("MD5 Hash: " + hexString.toString());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }
}

输出案例

MD5 Hash: fc3ff98e8c6a0d3087d515c0473f8677

在这个示例中:

  • MessageDigest.getInstance(“MD5”) 获取 MD5 哈希算法的实例。
  • digest() 方法计算并返回输入字符串的哈希值。
  • 然后通过 String.format 将字节数组转为十六进制的字符串。

SHA-256哈希算法(更安全-推荐)

SHA(Secure Hash Algorithm)是一组哈希算法。SHA-256 是其中一种常见的哈希算法,输出 256 位(32 字节)的哈希值,安全性更高。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class SHA256Example {
    public static void main(String[] args) {
        String input = "Hello, world!";
        try {
            // 获取 SHA-256 摘要实例
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            
            // 计算输入字符串的哈希值
            byte[] hashBytes = md.digest(input.getBytes());
            
            // 转换成十六进制字符串
            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                hexString.append(String.format("%02x", b));
            }
            
            // 输出哈希值
            System.out.println("SHA-256 Hash: " + hexString.toString());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }
}

输出案例

SHA-256 Hash: a591a6d40bf420404a011733cfb7b190d62c65bf0bcdaee23a7c60ab4e61d140

在这个示例中:

  • MessageDigest.getInstance(“SHA-256”) 获取 SHA-256 哈希算法的实例。
  • digest() 方法计算并返回输入字符串的哈希值。
  • 然后通过 String.format 将字节数组转为十六进制的字符串。

总结

  • MD5 和 SHA-256 都是哈希算法,用于将输入(如字符串或文件)转换成固定长度的哈希值。
  • Java 中使用 MessageDigest 类来实现这些哈希算法。
  • 通过 getInstance() 方法指定要使用的哈希算法名称(如 “MD5” 或 “SHA-256”)。
  • digest() 方法计算并返回哈希值,通常通过将字节数组转换成十六进制字符串来显示结果。
    你可以根据需要选择不同的哈希算法,但需要注意的是,MD5 已经不推荐用于安全场合,尤其是涉及密码和敏感数据时。推荐使用更安全的算法,如 SHA-256。

四、登录注册的密码加密工具类

1. 逻辑介绍

使用下面的两个工具类就可以实现对明文密码的加密,PasswordUtils.java 是工具的入口,如果是使用只关注该类即可,里面 有三个方法,如下介绍:

  1. getSalt() :获取盐,使用UUID作为盐,一般也会将UUID作为主键。
  2. encode(String rawPass, String salt) :加密操作。将 明文密码 rawPass 通过 盐 salt,通过 哈希算法(MD5和SHA 在PasswordEncoder.java 中)输出密文密码,并存在数据库。
  3. matches(String salt, String rawPass, String encPass) :进行数据完整性验证。将 盐salt (从数据库获取)、用户传递的明文密码 rawPass、密文密码(从数据库获取),进行验证,本质还是执行 encode()函数,进行 数据完整性验证。

PasswordEncoder.java 是进行加密的主要实现类,PasswordUtils.java是暴露出的接口。

2. PasswordUtils.java 加密和验证工具类

import java.util.UUID;

/**
 * @ClassName: PasswordUtils
 * @Description: 密码工具类
 * @createTime: 2020/2/4 14:44
 * @Author: 冯凡利
 * @UpdateUser: 冯凡利
 * @Version: 0.0.1
 */
public class PasswordUtils {

	/**
	 * 匹配密码
	 * @param salt 盐
	 * @param rawPass 明文 
	 * @param encPass 密文
	 * @return
	 */
	public static boolean matches(String salt, String rawPass, String encPass) {
		return new PasswordEncoder(salt).matches(encPass, rawPass);
	}
	
	/**
	 * 明文密码加密
	 * @param rawPass 明文
	 * @param salt
	 * @return
	 */
	public static String encode(String rawPass, String salt) {
		return new PasswordEncoder(salt).encode(rawPass);
	}

	/**
	 * 获取加密盐
	 * @return
	 */
	public static String getSalt() {
		return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
	}

	public static void main(String[] args) {
		// 给密码 和 salt,看密码密文
		String encode = encode("123456", "d3709b4efe9d47fcaba0");
		System.out.println(encode);
	}
}

3. PasswordEncoder.java 加密的具体实施类

package com.feng.companyframe.utils;

import lombok.extern.slf4j.Slf4j;

import java.security.MessageDigest;

/**
 * @ClassName: PasswordEncoder
 * @Description: 密码加密
 * @createTime: 2020/2/4 14:44
 * @Author: 冯凡利
 * @UpdateUser: 冯凡利
 * @Version: 0.0.1
 */
@Slf4j
public class PasswordEncoder {

	private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
			"e", "f" };

	private final static String MD5 = "MD5";
	private final static String SHA = "SHA";
	
	private Object salt;
	private String algorithm;

	public PasswordEncoder(Object salt) {
		this(salt, MD5);
	}
	
	public PasswordEncoder(Object salt, String algorithm) {
		this.salt = salt;
		this.algorithm = algorithm;
	}

	/**
	 * 密码匹配验证
	 * @param encPass 密文
	 * @param rawPass 明文
	 * @return
	 */
	public boolean matches(String encPass, String rawPass) {
		String pass1 = "" + encPass;
		String pass2 = encode(rawPass);

		return pass1.equals(pass2);
	}

	/**
	 * 密码加密
	 * @param rawPass
	 * @return
	 */
	public String encode(String rawPass) {
		String result = null;
		try {
			MessageDigest md = MessageDigest.getInstance(this.algorithm);
			// 加密后的字符串
			result = byteArrayToHexString(md.digest(mergePasswordAndSalt(rawPass).getBytes("utf-8")));
		} catch (Exception ex) {
			log.error("加密失败{}",ex.getLocalizedMessage());
		}
		return result;
	}

	private String mergePasswordAndSalt(String password) {
		if (password == null) {
			password = "";
		}

		if ((salt == null) || "".equals(salt)) {
			return password;
		} else {
			return password + "{" + salt.toString() + "}";
		}
	}

	/**
	 * 转换字节数组为16进制字串
	 * 
	 * @param b
	 *            字节数组
	 * @return 16进制字串
	 */
	private String byteArrayToHexString(byte[] b) {
		StringBuffer resultSb = new StringBuffer();
		for (int i = 0; i < b.length; i++) {
			resultSb.append(byteToHexString(b[i]));
		}
		return resultSb.toString();
	}

	/**
	 * 将字节转换为16进制
	 * @param b
	 * @return
	 */
	private static String byteToHexString(byte b) {
		int n = b;
		if (n < 0)
			n = 256 + n;
		int d1 = n / 16;
		int d2 = n % 16;
		return hexDigits[d1] + hexDigits[d2];
	}

	public static void main(String[] args) {
		String a=null;
		String b=null;

		if(a!=b||!a.equals(b)){
			System.out.println(a==b);
		}

	}


}

你可能感兴趣的:(web开发,前端,学习,哈希算法)