> 技术文档 > java SHA加密/解密(附带源码)

java SHA加密/解密(附带源码)


目录

  1. 项目背景与动机

  2. 哈希算法概述

    1. 哈希函数基本特性

    2. SHA 系列演进

  3. SHA 算法数学原理

    1. 压缩函数与分组迭代

    2. 初始向量与常量表

    3. 消息填充与附加长度

  4. Java 环境与依赖

    1. JDK 版本与安全策略

    2. Maven/Gradle 配置

  5. 项目结构与模块设计

    1. 包结构

    2. 模块职责

  6. 核心代码实现(SHA-1/SHA-256/SHA-512)

    1. 通用哈希工具类

    2. 各种 SHA 算法实现方法

    3. 带盐哈希与多次迭代

    4. HMAC-SHA 实现

  7. 代码整合示例(详注版)

  8. 方法解读

    1. hash(byte[])

    2. hashHex(String)

    3. hashWithSalt(String, byte[])

    4. verify(String, String)

  9. 进阶话题

    1. 摘要长度与安全性

    2. 碰撞攻击与防范

    3. 密钥派生函数(PBKDF2)

    4. 高性能实现与并行化

  10. 性能优化与安全实践

    1. 缓存与复用 MessageDigest 实例

    2. 防止时序攻击

    3. 安全随机数源

    4. 密码存储最佳实践

  11. 项目实战:Web 登录系统中的 SHA 应用

    1. Spring Boot 集成

    2. 前端密码散列示例

  12. CI/CD 与自动化测试

    1. 单元测试覆盖

    2. 集成测试

    3. 安全扫描

  13. 项目总结与展望

  14. 参考资料与延伸阅读


1. 项目背景与动机

在信息安全领域,数据完整性身份验证是核心需求之一。与非对称加密不同,哈希算法是一种单向函数,无法从输出反推输入,广泛用于密码存储、消息摘要、数字签名前置处理、数据完整性校验等场景。本项目旨在使用 Java 原生 API(JCA/JCE)及少量手写逻辑,全方位实现 SHA 系列哈希(SHA-1、SHA-256、SHA-512)以及基于 HMAC 的消息认证,提供高可用、易扩展、可测试的工具类,帮助读者:

  • 深入理解 SHA 家族算法原理及实现细节

  • 掌握 Java 中 MessageDigest 的使用与优化

  • 学会带盐(Salted)哈希、多次迭代和 HMAC 的正确姿势

  • 在 Web 系统中安全地存储与校验密码

  • 了解密码学攻击手段及防范最佳实践


2. 哈希算法概述

2.1 哈希函数基本特性

  1. 单向性:给定消息 M,易于计算 H(M);但仅凭 H(M),无法高效恢复 M。

  2. 抗碰撞:难以找到 M1≠M2 且 H(M1)=H(M2)。

  3. 抗篡改:对消息进行微小修改,哈希值发生巨大变化(雪崩效应)。

  4. 效率:对任意长度输入都能快速计算。

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 < iterations; i++) { md.reset(); result = md.digest(result); } return DatatypeConverter.printHexBinary(result).toLowerCase(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(\"不支持的哈希算法: \" + algorithm, e); } } /** * 生成随机 salt,默认长度 16 字节 */ public static byte[] generateSalt(int length) { SecureRandom sr = new SecureRandom(); byte[] salt = new byte[length]; sr.nextBytes(salt); return salt; }}

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 < a.length; i++) { result |= a[i] ^ b[i]; } return result == 0;}

10.3 安全随机数源

  • 强制使用 SecureRandom.getInstanceStrong(),在高安全需求下确保熵的质量

10.4 密码存储最佳实践

  • 使用带盐、多次迭代或 PBKDF2/HMAC/KDF 存储密码

  • 永不保存明文密码


11. 项目实战:Web 登录系统中的 SHA 应用

11.1 Spring Boot 集成

  1. 配置:在 application.properties 中存储盐长度和迭代次数

  2. 服务层:注册用户时生成 salt 并存储 hash(salt+password);登录时取出 salt 重新哈希比对

  3. 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 MessageDigestMac 等 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

妊娠纹产品推荐