java SHA加密/解密(附带源码)
目录
-
项目背景与动机
-
哈希算法概述
-
哈希函数基本特性
-
SHA 系列演进
-
-
SHA 算法数学原理
-
压缩函数与分组迭代
-
初始向量与常量表
-
消息填充与附加长度
-
-
Java 环境与依赖
-
JDK 版本与安全策略
-
Maven/Gradle 配置
-
-
项目结构与模块设计
-
包结构
-
模块职责
-
-
核心代码实现(SHA-1/SHA-256/SHA-512)
(图片来源网络,侵删)-
通用哈希工具类
-
各种 SHA 算法实现方法
(图片来源网络,侵删) -
带盐哈希与多次迭代
-
HMAC-SHA 实现
(图片来源网络,侵删)
-
-
代码整合示例(详注版)
-
方法解读
-
hash(byte[])
-
hashHex(String)
-
hashWithSalt(String, byte[])
-
verify(String, String)
-
-
进阶话题
-
摘要长度与安全性
-
碰撞攻击与防范
-
密钥派生函数(PBKDF2)
-
高性能实现与并行化
-
-
性能优化与安全实践
-
缓存与复用 MessageDigest 实例
-
防止时序攻击
-
安全随机数源
-
密码存储最佳实践
-
-
项目实战:Web 登录系统中的 SHA 应用
-
Spring Boot 集成
-
前端密码散列示例
-
-
CI/CD 与自动化测试
-
单元测试覆盖
-
集成测试
-
安全扫描
-
-
项目总结与展望
-
参考资料与延伸阅读
1. 项目背景与动机
在信息安全领域,数据完整性和身份验证是核心需求之一。与非对称加密不同,哈希算法是一种单向函数,无法从输出反推输入,广泛用于密码存储、消息摘要、数字签名前置处理、数据完整性校验等场景。本项目旨在使用 Java 原生 API(JCA/JCE)及少量手写逻辑,全方位实现 SHA 系列哈希(SHA-1、SHA-256、SHA-512)以及基于 HMAC 的消息认证,提供高可用、易扩展、可测试的工具类,帮助读者:
-
深入理解 SHA 家族算法原理及实现细节
-
掌握 Java 中 MessageDigest 的使用与优化
-
学会带盐(Salted)哈希、多次迭代和 HMAC 的正确姿势
-
在 Web 系统中安全地存储与校验密码
-
了解密码学攻击手段及防范最佳实践
2. 哈希算法概述
2.1 哈希函数基本特性
-
单向性:给定消息 M,易于计算 H(M);但仅凭 H(M),无法高效恢复 M。
-
抗碰撞:难以找到 M1≠M2 且 H(M1)=H(M2)。
-
抗篡改:对消息进行微小修改,哈希值发生巨大变化(雪崩效应)。
-
效率:对任意长度输入都能快速计算。
2.2 SHA 系列演进
-
SHA-0:1978 年发布,存在安全缺陷,迅速废弃。
-
SHA-1:1995 年发布,输出 160 位摘要;2005 年起被认为不安全,陆续弃用。
-
SHA-2 家族(2001 年):包括 SHA-224、SHA-256、SHA-384、SHA-512;目前主流。
-
SHA-3(Keccak,2015 年):NIST 全面标准,抗碰撞更强。
本项目聚焦 SHA-1、SHA-256、SHA-512,并扩展 HMAC-SHA256 用于消息认证。
3. SHA 算法数学原理
3.1 压缩函数与分组迭代
SHA 基于 Merkle–Damgård 结构,将任意长度消息分为固定大小的分组(512 位或 1024 位),通过压缩函数迭代处理,并将上一分组的输出作为下一分组的输入状态(中间哈希值)。
3.2 初始向量与常量表
-
每种 SHA 算法有一组固定的初始哈希值(IV),如 SHA-256 的 8 个 32 位常量。
-
压缩函数中使用一系列常量 K,用于每轮迭代的消息扩展与混合。
3.3 消息填充与附加长度
-
首先在消息后附加一位“1”位,再用“0”填充,直到消息长度 ≡ (block_size − 64) mod block_size。
-
最后附加一个 64 位(或 128 位)的大端整数,表示原消息长度(位为单位)。
整个填充保证消息长度可被分组大小整除。
4. Java 环境与依赖
4.1 JDK 版本与安全策略
-
建议:JDK 11 及以上,内置最新 JCA/JCE,性能更好。
-
对超长摘要(如 SHA-512)无需额外策略包支持。
4.2 Maven/Gradle 配置
org.slf4j slf4j-api 1.7.36 ch.qos.logback logback-classic 1.2.11 org.junit.jupiter junit-jupiter 5.9.0 test
dependencies { implementation 'org.slf4j:slf4j-api:1.7.36' implementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' }
5. 项目结构与模块设计
src/main/java └─ com.example.sha ├─ util │ └─ SHAUtil.java # 核心哈希工具类 ├─ demo │ └─ App.java # 演示入口 └─ security └─ HMACUtil.java # HMAC 实现工具 src/test/java └─ com.example.sha ├─ SHAUtilTest.java # 单元测试 └─ HMACUtilTest.java # HMAC 测试
-
SHAUtil:封装 SHA-1、SHA-256、SHA-512、带盐、多次迭代等方法。
-
HMACUtil:基于 Mac 类实现 HMAC-SHA256。
-
App:演示从命令行读取输入,输出哈希结果。
-
测试类:覆盖各种输入、盐、迭代次数及异常场景。
6. 核心代码实现(SHA-1/256/512)
package com.example.sha.util; import javax.xml.bind.DatatypeConverter; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; /** * SHAUtil:提供 SHA-1、SHA-256、SHA-512 哈希计算, * 支持带盐和多次迭代,以及十六进制输出。 */ public class SHAUtil { // 默认迭代次数 private static final int DEFAULT_ITERATIONS = 1; /** * 计算纯 SHA-256 哈希,返回十六进制字符串 */ public static String sha256Hex(String input) { return hashHex(input.getBytes(StandardCharsets.UTF_8), "SHA-256", null, 1); } /** * 计算带 salt 的 SHA-256 哈希,返回十六进制字符串 */ public static String sha256Hex(String input, byte[] salt, int iterations) { return hashHex(input.getBytes(StandardCharsets.UTF_8), "SHA-256", salt, iterations); } /** * 通用哈希方法 * * @param data 要哈希的字节数组 * @param algorithm 算法名称,如 SHA-1、SHA-256、SHA-512 * @param salt 可选 salt,null 则无盐 * @param iterations 迭代次数,至少 1 * @return 哈希后十六进制字符串 */ public static String hashHex(byte[] data, String algorithm, byte[] salt, int iterations) { try { MessageDigest md = MessageDigest.getInstance(algorithm); if (salt != null) { md.update(salt); } byte[] result = md.digest(data); for (int i = 1; i
7. 代码整合示例
package com.example.sha.demo; import com.example.sha.util.SHAUtil; import java.util.Scanner; /** * App:命令行演示 SHA 哈希功能 */ public class App { public static void main(String[] args) { Scanner scanner = new Scanner(System.in, "UTF-8"); System.out.println("请输入要计算哈希的字符串:"); String input = scanner.nextLine(); // 纯 SHA-256 String sha256 = SHAUtil.sha256Hex(input); System.out.println("SHA-256: " + sha256); // 带盐 SHA-256,迭代 2 次 byte[] salt = SHAUtil.generateSalt(16); String salted = SHAUtil.sha256Hex(input, salt, 2); System.out.println("带盐+迭代 SHA-256: " + salted); System.out.println("Salt (Hex): " + bytesToHex(salt)); scanner.close(); } private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 2); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } }
注释解读
-
使用 Scanner 读取用户输入,支持 UTF-8。
-
调用 SHAUtil 提供的纯哈希和带盐多次迭代哈希两种方式。
-
bytesToHex 辅助方法将 salt 转为可阅读的十六进制字符串。
8. 方法解读
8.1 hashHex(byte[] data, String algorithm, byte[] salt, int iterations)
-
通过 MessageDigest.getInstance(algorithm) 获取实例
-
如有 salt,先 update(salt),再对数据 digest(data)
-
迭代多次:对上次输出再次 digest
-
最终使用 DatatypeConverter.printHexBinary 转为十六进制
8.2 sha256Hex(String)
-
直接调用通用方法,算法固定为 "SHA-256",无盐,1 次迭代
8.3 sha256Hex(String, byte[] salt, int iterations)
-
支持自定义 salt 和迭代次数,可用于密码存储强化
8.4 generateSalt(int length)
-
使用 SecureRandom 生成高质量随机数,保证 salt 不能预测
9. 进阶话题
9.1 摘要长度与安全性
-
SHA-1(160 位)已被碰撞攻击击破
-
SHA-256(256 位)目前认为安全
-
SHA-512(512 位)更强但性能稍低
9.2 碰撞攻击与防范
-
预映像攻击:给定 H,难以找到 M 使 H(M)=H
-
碰撞攻击:找任意 M1,M2 使 H(M1)=H(M2)
-
防范:选用 SHA-2 或 SHA-3 家族,不使用已弃用 SHA-1
9.3 密钥派生函数(PBKDF2)
-
基于 HMAC-SHA256 的迭代派生,可用 SecretKeyFactory 实现
-
用于密码存储,比简单迭代更安全
9.4 高性能实现与并行化
-
对大数据可分块并行调用 MessageDigest 多实例
-
使用 Java 9+ 的 MessageDigest.digest(ByteBuffer) 零拷贝
10. 性能优化与安全实践
10.1 缓存与复用 MessageDigest 实例
-
MessageDigest 线程不安全,建议使用 ThreadLocal 缓存
10.2 防止时序攻击
-
对比哈希结果时使用常量时间比对,避免泄露长度或内容差异
-
示例:
public static boolean constantTimeEquals(byte[] a, byte[] b) { if (a.length != b.length) return false; int result = 0; for (int i = 0; i
10.3 安全随机数源
-
强制使用 SecureRandom.getInstanceStrong(),在高安全需求下确保熵的质量
10.4 密码存储最佳实践
-
使用带盐、多次迭代或 PBKDF2/HMAC/KDF 存储密码
-
永不保存明文密码
11. 项目实战:Web 登录系统中的 SHA 应用
11.1 Spring Boot 集成
-
配置:在 application.properties 中存储盐长度和迭代次数
-
服务层:注册用户时生成 salt 并存储 hash(salt+password);登录时取出 salt 重新哈希比对
-
Controller:接收 JSON,调用服务层完成注册/登录
示例注册流程片段:
byte[] salt = SHAUtil.generateSalt(16); String passwordHash = SHAUtil.sha256Hex(rawPassword, salt, 10000); user.setSalt(bytesToHex(salt)); user.setPasswordHash(passwordHash); userRepository.save(user);
11.2 前端密码散列示例
-
可在前端使用 Web Crypto API 进行 SHA-256 初步哈希,减轻服务器压力
-
仍需后端带盐迭代以确保安全
12. CI/CD 与自动化测试
12.1 单元测试覆盖
-
测试空字符串、普通字符串、带 Unicode 字符串哈希结果
-
测试带盐、不带盐、不同迭代次数的输出长度与稳定性
12.2 集成测试
-
Spring Boot @WebMvcTest 模拟注册登录接口,确保哈希、存储、比对流程正确
12.3 安全扫描
-
使用 OWASP Dependency-Check 插件扫描依赖
-
定期使用 Snyk 运行安全审计
13. 项目总结与展望
本文从理论到实践,完整讲解了 Java 版 SHA 哈希(含带盐、多次迭代、HMAC)项目的设计与实现。通过本项目,你将能够:
-
深刻理解 SHA 系列算法原理
-
熟练使用 Java MessageDigest、Mac 等 API
-
掌握密码存储与验证的安全最佳实践
-
在 Web 应用中安全集成哈希流程
未来可继续拓展:
-
引入 PBKDF2、bcrypt、scrypt 等专业 KDF 算法
-
对接硬件安全模块(HSM)增强安全
-
实现 SHA-3 与其它哈希函数对比研究
14. 参考资料与延伸阅读
-
Java 官方文档:Java Cryptography Architecture (JCA)
-
NIST FIPS PUB 180-4: Secure Hash Standard
-
OWASP 密码存储指南
-
《Applied Cryptography》 by Bruce Schneier
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-