news 2026/8/26 13:30:42

加密算法笔记(哈希算法、base64,md5,aes,rsa等)、签名校验、bcrypt、数字信封

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
加密算法笔记(哈希算法、base64,md5,aes,rsa等)、签名校验、bcrypt、数字信封

开发中经常要用到加解密,熟悉它很有好处。

文章目录

    • 哈希算法(hash)(消息摘要算法)
    • 单向散列哈希算法
          • 哈希算法可逆吗?
          • 哈希算法的时间复杂度
          • 哈希算法是加密算法吗?
      • md5
      • DigestUtils(spring)实现md5
        • DigestUtils验证密码(登录、修改密码)
        • DigestUtils新增用户时加密
    • 对称加密算法
      • base64
        • BASE64
      • jdk自带base64工具
      • base64有什么好处
        • base64如何不打印出来
        • base64报错 Illegal base64 character 3a
      • aes加密算法
        • 代码
      • AES加密时报错 Cannot find any provider supporting AES/CBC/PKCS7Padding
      • aes加密时报错 Illegal key size or default parameters
    • 非对称加密算法(又称:公开密钥加密算法)
        • rsa每次加密生成的密码不一样是错了吗?
        • 数字信封
    • 签名校验
        • 签名校验时间戳的判断代码
      • bcrypt(spring推荐加密算法)
        • bcrypt引入依赖
        • bcrypt SecurityConfig安全配置类
        • bcrypt在业务代码中使用
          • bcrypt 登录、修改密码时的写法
          • bcrypt 新增用户时的写法

哈希算法(hash)(消息摘要算法)

单向散列哈希算法

摘要算法的特点:
1、无论输入的长度是多少,输出总是固定的长度。
2、如果输入有变化,摘要一定不同。

基于这个原理可以对数据的完整性和一致性进行校验。
由于是提取的不是全部内容,所以无法还原为原始值。

MD5、SHA(128、256)系列

描述单向不可逆
场景用户密码的加密
哈希算法可逆吗?

不可逆。

哈希算法的时间复杂度

是O(1)。# 也就是一次就可以计算出结果(在不考虑碰撞的情况下)

哈希算法是加密算法吗?

当然是。哈希算法又称消息摘要算法,消息摘要算法本身就是加密算法,如md5等。

md5

摘要算法

DigestUtils(spring)实现md5

//md5 单向加密Stringpassword="芝麻开门";Stringdigest=DigestUtils.md5DigestAsHex(password.getBytes());// digest是摘要算法,这里只生成的字符串System.out.println(digest);
DigestUtils验证密码(登录、修改密码)
登录时:StringloginPassword=DigestUtils.md5DigestAsHex(loginDTO.getPassword().getBytes());if(!StringUtils.equals(loginPassword,sysUser.getPassword())){thrownewIllegalArgumentException("旧密码输入错误");}修改密码时: newPassword=DigestUtils.md5DigestAsHex(newPassword.getBytes());if(!StringUtils.equals(newPassword,oldPassword)){thrownewIllegalArgumentException("旧密码输入错误");}
DigestUtils新增用户时加密
Stringpassword=DigestUtils.md5DigestAsHex(rawPassword.getBytes());sysUser.setPassword(password);

对称加密算法

DES、3DES、AES、Blowfish、IDEA、RC5、RC6

base64

首先强调一点,base64属于编码算法,不属于加密算法,概念一定要搞清楚

描述双向可逆加密
场景邮件中的信息
BASE64

base64工具类有很多。

jdk自带base64工具

jdk自带的base64工具就不错。 类路径为:java.util.Base64

// jdk自带的BASE64 加解密工具Stringpassword="芝麻开门";BASE64Encoderbase64Encoder=newBASE64Encoder();Stringencode=base64Encoder.encode(password.getBytes());// 加密System.out.println("加密为字符串: "+encode);BASE64Decoderdecoder=newBASE64Decoder();try{Stringdecode=newString(decoder.decodeBuffer(encode));// 解密System.out.println("解密为: "+decode);}catch(IOExceptione){e.printStackTrace();}

再来一个例子:

// 文件流到base64字符串FilerefereeFile=newFile("D://我的.jpg");byte[]refereeFileOriginalBytes=FileUtils.readFileToByteArray(refereeFile);Stringbase64String=Base64.getEncoder().encodeToString(refereeFileOriginalBytes);logger.info("base64字符串:{}",base64String);//base64字符串到文件byte[]bytes=Base64.getDecoder().decode(base64String);Filefile=newFile("D://base64转换为我的.jpg");FileUtils.writeByteArrayToFile(file,bytes);// 执行完之后去文件系统查看是否有这个文件

字符串直接复制到java,因为太大可能报错,所以最好把字符串写在文件里面再读取,如下例子:

// 文件流到base64字符串FilerefereeFile=newFile("D://base64String.txt");try{Stringbase64String=FileUtils.readFileToString(refereeFile);//base64字符串到文件byte[]bytes=Base64.getDecoder().decode(base64String);Filefile=newFile("D://base64转换为我的.jpg");FileUtils.writeByteArrayToFile(file,bytes);// 执行完之后去文件系统查看是否有这个文件}catch(IOExceptione){e.printStackTrace();}

base64有什么好处

1是不直接显示文件流,稍微安全些。
另外,如果不考虑性能,数据之间的转移也比较方便,就当做是个大点的字符串。

对于需要反复读写的不是特别大的文件,可以把base64放在内存中。省去了io和网络rest的时间。

base64如何不打印出来

方法间传递一般是一个对象,通常会有多个参数,base64String只是其中一项,想要打印其他参数,但是不打印base64String。

其实不应该放在这里,这属于json的序列化问题。
以阿里的fastjson为例,字段加忽略注解即可。

@JSONField(serialize=false)//fastjson 忽略字段打印privateStringbase64String;

还有一种方案,是toString的时候,去掉。

如果是继承的类,调用下父类的toString()方法即可。

还有一种说法是使用lombok时添加如下注解,实测无效。

@ToString(callSuper=true)publicclassA{}
base64报错 Illegal base64 character 3a

一般有两种情况。
1、base64确实不对。
2、前端传的带文件类型头,如:data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD
对于这种,可以替换下头。

Stringbase64=request.getFile().replaceAll(" ","+").replace("data:image/png;base64,","");

aes加密算法

五种加密算法:
ECB
CBC
CTR
OFB
CFB
不用纠结具体区别了,就是起个匹配的作用,和前后端匹配时一致即可。

主要参数:
key
偏移量(懒一点的话,可以设置为和key一样)
算法 例如:AES/CBC/PKCS7Padding

代码

aes属于通用工具,网上代码很多,随便来一段吧。

@Slf4j@ComponentpublicclassAESUtils{// AES密钥算法privatestaticfinalStringKEY_ALGORITHM="AES";// 加密/解密算法/工作模式/填充方式privatestaticfinalStringCIPHER_ALGORITHM="AES/ECB/PKCS5Padding";@Value("${aesKey:kkkkkkkkkkkkkkkk==}")privateStringtempAesKey;publicstaticStringAES_KEY;@PostConstructpublicvoidinit(){log.info("AES工具类初始化开始",tempAesKey);AES_KEY=tempAesKey;log.info("AES工具类初始化完成,aesKey={}",AES_KEY);}// 生成密钥publicstaticStringgenerateAESKey()throwsException{KeyGeneratorkeyGenerator=KeyGenerator.getInstance(KEY_ALGORITHM);keyGenerator.init(128,newSecureRandom());// 192 and 256 bits may not be availableSecretKeysecretKey=keyGenerator.generateKey();returnBase64.getEncoder().encodeToString(secretKey.getEncoded());}// 加密publicstaticStringencrypt(Stringdata,Stringkey)throwsException{Ciphercipher=Cipher.getInstance(CIPHER_ALGORITHM);SecretKeySpecsecretKeySpec=newSecretKeySpec(Base64.getDecoder().decode(key),KEY_ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);byte[]encrypted=cipher.doFinal(data.getBytes());returnBase64.getEncoder().encodeToString(encrypted);}// 解密publicstaticStringdecrypt(Stringdata,Stringkey)throwsException{Ciphercipher=Cipher.getInstance(CIPHER_ALGORITHM);SecretKeySpecsecretKeySpec=newSecretKeySpec(Base64.getDecoder().decode(key),KEY_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE,secretKeySpec);byte[]original=cipher.doFinal(Base64.getDecoder().decode(data));returnnewString(original);}publicstaticvoidmain(String[]args)throwsException{// 这个方法只有第一次用,生成后以后固定用一个值即可 自己编写的key不一定符合位数等要求,所以第一次要自己生成// String key = generateAESKey();Stringkey="kkkkkkkkkkkkkkkk==";StringoriginalText="hello world";StringencryptedText=encrypt(originalText,key);StringdecryptedText=decrypt(encryptedText,key);log.info("key={}",key);log.info("originalText={}",originalText);log.info("encryptedText={}",encryptedText);log.info("decryptedText={}",decryptedText);}}

注:一直以为aes很简单,眼高手低,实际使用的时候还是有些要注意。
例如用默认的方法,每次key都是重新生成,导致解密失败
实际生成key后应该固定用这个key,就可以解密成功了

AES加密时报错 Cannot find any provider supporting AES/CBC/PKCS7Padding

是因为jdk1.8不支持这个算法,需要下载补丁包,放到jdk jar包目录下。(不推荐,因为成本太多,从开发到服务器都要操作)

aes加密时报错 Illegal key size or default parameters

报错解释:

这个错误通常发生在使用Java加密时,尤其是AES加密算法。在JDK 8 Update 161及以后的版本中,引入了新的加密策略限制(称为JEP 261: JDK Cryptography Extension Policy),这意味着默认情况下,Java加密库中的一些加密算法可能被限制使用。Illegal key size or default parameters错误表明你正在使用的加密密钥大小超出了Java加密库的默认限制。

注:这堆内容啰哩吧嗦,知道是版本不对就行了。

解决方法:
1、更新JDK到8u161或更高版本,这样可以使用未受限制的加密策略
2、下载补丁包。
jdk7的略。

https://files-cdn.cnblogs.com/files/zgngg/jce_policy-8.zip # 这个是jdk8的,官网太慢,直接从这里下吧

下载完成后,解压缩下载的文件。你将会得到两个JAR文件:local_policy.jar和US_export_policy.jar。

下载包的readme.txt 有安装说明,即替换
${jdk_home}/jre/lib/security
${jre_home}/lib/security
目录下的local_policy.jarUS_export_policy.jar文件。

自己下载的步骤
步骤 1:下载Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files打开Oracle官方网站,搜索"Java Cryptography Extension Unlimited Strength Jurisdiction Policy Files"。

进入下载页面,找到适用于你的Java版本的文件,并下载。

非对称加密算法(又称:公开密钥加密算法)

如rsa加密。

rsa每次加密生成的密码不一样是错了吗?

当然不是,非对称加密,每次生成的密码本来就不不一样。

数字信封

数字信封是一种方案,原理类似于https,非对称密钥保护对称秘钥,对称密钥加密数据

签名校验

为什么要签名校验呢?
1、只有已注册系统的请求才可以成功。
2、只有一定时间内的请求才可以成功。
3、只有采用相同加密规则的请求才可以成功。

签名校验方式多种多样,至少会一两种套路。
sourceSystem # 来源系统 header中传
salt # 盐值(密钥) 约定好,私下单发
timestamp # 时间戳 header中传
signed # 签名 header中传

校验规则:
sourceSystem必须在自己系统中注册
timestamp误差必须在一分钟内,避免一个签名可以永久使用
signed=约定的加密算法(salt+timestamp*N) # 这里规则自己定义,可以多来几次,外人根本不知道啥规则

这样可以说是相当安全了。

签名校验时间戳的判断代码

挺简单的,也是网上通用的代码了。
这里误差时间设置的是1分钟。

longmyTimestamp=System.currentTimeMillis();longtimestampDiffAbs=Math.abs(myTimestamp-Long.parseLong(timestamp));// 时间误差绝对值booleantimestampFlag=timestampDiffAbs<=signVerifyTimestampOffset*1000*60;log.info(methodName+"操作_时间误差部分完成,timestamp={},myTimestamp={},timestampDiffAbs={},误差范围(秒)={},timestampFlag={}",timestamp,myTimestamp,timestampDiffAbs,signVerifyTimestampOffset,timestampFlag);CustomAssertUtils.isSuccess(timestampFlag,"timestamp误差必须在范围内");

bcrypt(spring推荐加密算法)

md5已经不再安全,推荐bcrypt算法。

生成后的密码如下示例:
$2a$10$X2KuhxgG2eif/GVWx5qGxeDpyodHrlsZxfd.EP3Oox3T4bTmrxK46# 这谁能破解的了。。。

bcrypt引入依赖
组合依赖:<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>也可以引入单个依赖:<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-crypto</artifactId></dependency>
bcrypt SecurityConfig安全配置类
importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;@ConfigurationpublicclassSecurityConfig{@BeanpublicPasswordEncoderpasswordEncoder(){// 默认强度为10,建议生产环境设为 12~14 以平衡安全性与性能returnnewBCryptPasswordEncoder(12);}}
bcrypt在业务代码中使用
importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;@ServicepublicclassUserService{@AutowiredprivatePasswordEncoderpasswordEncoder;publicStringencryptPassword(StringrawPassword){// 对原始密码进行加密,BCrypt 会自动生成随机盐值StringencodedPassword=passwordEncoder.encode(rawPassword);log.info("加密后的密码={}",encodedPassword);returnencodedPassword;}// 密码验证(例如:用户登录)publicbooleanvalidatePassword(StringrawPassword,StringencodedPassword){// 将用户输入的明文密码与数据库中存储的加密密码进行比对booleanmatches=passwordEncoder.matches(rawPassword,encodedPassword);log.info("密码验证结果={}",matches);returnmatches;}}

注:
1、永远使用matches()方法:
BCrypt每次加密都会生成不同的盐值,导致同样的明文密码每次加密后的字符串都不一样,所以验证时必须使用matches(明文,数据库密文)。
2、强度因子(CostFactor):
默认值是10。生产环境建议设为12到14。数值每增加1,计算耗时就会翻倍。建议根据你服务器的性能,将单次加密耗时控制在50~100毫秒左右。

bcrypt 登录、修改密码时的写法
booleanmatchFlag=this.validatePassword(loginDTO.getPassword(),sysUser.getPassword());if(matchFlag){thrownewIllegalArgumentException("旧密码输入错误");}
bcrypt 新增用户时的写法
Stringpassword=passwordEncoder.encode("123456");sysUser.setPassword(password);
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 13:29:16

半人马机器人“小橙”技术拆解:轮腿融合与运动控制

各位开发者和机器人爱好者&#xff0c;大家好。 最近航天领域公开的“半人马机器人‘小橙’”成了一个热门话题。很多人第一眼看到它&#xff0c;会觉得外观很科幻&#xff1a;四条腿加上轮子&#xff0c;既能像足式机器人一样跨越障碍&#xff0c;又能像轮式平台一样高速移动…

作者头像 李华
网站建设 2026/8/26 13:29:06

MiniMax-H3 ComfyUI整合包:云端一键部署,权重免下载快速跑通

云端一键部署 MiniMax-H3 多套加速工作流整合包&#xff1a;ComfyUI 中文整合版&#xff0c;权重免下载 这次我们看一个解决实际部署痛点比较大的项目&#xff1a;MiniMax-H3 多套加速工作流整合包&#xff0c;配合 ComfyUI 中文整合版&#xff0c;主打云端一键部署和权重免下载…

作者头像 李华
网站建设 2026/8/26 13:28:39

MathorCup数学建模竞赛:从高效备赛到论文撰写的全流程实战指南

1. 项目概述&#xff1a;从零到一&#xff0c;如何高效备战MathorCup数学建模竞赛 又到了一年一度的MathorCup数学建模竞赛季&#xff0c;身边不少学弟学妹已经开始焦虑地四处寻找“思路”和“助攻”。作为一个从本科到研究生&#xff0c;带队参加过多次国赛、美赛和MathorCup&…

作者头像 李华
网站建设 2026/8/26 13:23:22

MySQL面试核心:事务隔离、性能优化与测试实战

1. MySQL面试题核心考察方向解析 2026年的软件测试岗位对MySQL技能的考察&#xff0c;已经从基础语法层面升级到更注重实战能力的验证。根据近期一线互联网企业的实际面试反馈&#xff0c;主要聚焦以下五个维度&#xff1a; 事务隔离与锁机制 &#xff1a;90%的面试会问到MVC…

作者头像 李华
网站建设 2026/8/26 13:20:32

AI Agent自主上网实战:从任务拆解到工程落地

早上打开电脑&#xff0c;我做的第一件事是看一眼行业动态&#xff1a;有没有新项目值得关注&#xff0c;有没有潜在合作机会&#xff0c;有没有突然冒出来的风险信号。这个动作我重复了三年&#xff0c;零零碎碎能花掉一两个小时。最近我把这件事交给了 AI——不是让它回答几个…

作者头像 李华
网站建设 2026/8/26 13:16:26

Task-CoEvolve实战:AI智能体评测成本优化与自适应测试选择

AI 智能体评测正在成为一项越来越奢侈的工程投入。很多团队在搭建完 Agent 应用之后&#xff0c;会发现真正的瓶颈不是模型能力&#xff0c;也不是 Prompt 调优&#xff0c;而是“怎么证明它真的变好了”。跑一版完整评测集&#xff0c;调用几千次大模型接口&#xff0c;耗时几…

作者头像 李华