密码加密及验证
目录
为什么需要加密?
密码算法分类
对称密码算法
非对称密码算法
摘要算法
DigestUtils
MD5在线解密工具原理
实现用户密码加密
代码实现
为什么需要加密?
在MySQL数据库中,我们常常需要对用户密码、身份证号、手机号码等敏感信息进行加密,以保证数据的安全性。若我们直接使用明文存储这些敏感信息,当黑客入侵数据库时,就可以轻松拿到用户的相关信息,从而造成信息泄露或财产损失
密码算法分类
密码算法主要分为三类:对称密码算法、非对称密码算法和摘要算法

对称密码算法
对称密码算法:对称密码算法使用相同的密钥来进行加密和解密。这意味着在加密和解密过程中都使用相同的密钥。对称密码算法通常比非对称密码算法更快速,因为它们不涉及复杂的数学运算。
我们可以将加密的过程看做数学中计算 y = f(x) 的过程,其中 x 为明文,y 为密文,f( )表示不同的加密算法,通过 f(x) 计算密文y
对于对称加密,由于其在加密和解密过程中使用相同的密钥,即
计算密文:y = f(x)
计算明文:x = f(y)
对称密码算法可以进一步分为两种类型:块密码和流密码
块密码:块密码将明文划分为固定大小的块,并对每个块进行加密。常见的块密码算法有:
DES(Data Encryption Standard):DES是一种早期的对称加密算法,使用56位密钥和64位分组大小。
3DES(Triple DES):3DES是对DES的改进,使用两次或三次DES加密过程来增加密钥长度和安全性。它使用的密钥长度为56位,但由于密钥被使用多次,实际的密钥长度为112位或168位。
AES(Advanced Encryption Standard):AES是目前广泛使用的对称加密算法,其密钥长度可以是128位、192位或256位。AES具有较高的安全性和性能,被广泛应用于各种加密场景。
流密码:流密码将明文与密钥流进行按位异或运算来进行加密。密钥流可以是伪随机生成的,也可以是基于密钥和其他参数的确定性生成的。常见的流密码算法有:
RC4(Rivest Cipher 4):RC4是一种流密码算法,曾经被广泛用于加密通信协议中,如WEP和SSL/TLS。
Salsa20 和 ChaCha20:Salsa20和ChaCha20是由丹尼尔·J·伯恩斯坦(Daniel J. Bernstein)设计的流密码算法,被广泛认为是高性能和安全的加密算法,被用于加密通信和随机数生成等领域。
非对称密码算法
非对称密码算法:非对称密码算法使用一对密钥,即公钥和私钥,来进行加密和解密。这意味着使用公钥对数据进行加密后,只有持有相应私钥的实体才能解密数据。非对称密码算法也被称为公钥密码算法,相对于对称密码算法来说,它提供了更好的密钥管理和安全性。
同样的,我们将加密的过程看做数学中计算 y = f(x) 的过程
而对于非对称加密,其使用用户公钥进行加密,私钥进行解密,即
计算密文:y = f(x)
计算明文:x = m(y)
其中 x 为明文,y 为密文,f( )表示加密算法(也可看做公钥),通过 f(x) 计算密文y;m( )表示解密算法(也可看做私钥),通过m(y)计算明文

常见的非对称密码算法有:
RSA(Rivest-Shamir-Adleman):RSA是一种基于大整数的非对称加密算法,广泛应用于安全通信、数字签名等领域。RSA算法的安全性基于大整数分解的困难性,即在已知公钥的情况下,无法有效地分解出私钥。
DSA(Digital Signature Algorithm):DSA是一种用于数字签名的非对称加密算法,主要用于确保数字信息的完整性和真实性。DSA算法与SHA-1或SHA-2哈希函数结合使用,以生成数字签名。
ECC(Elliptic Curve Cryptography):ECC是一种基于椭圆曲线离散对数问题的加密算法,它提供了与RSA相当的安全性,但使用更短的密钥长度,从而降低了计算和存储成本。
摘要算法
摘要算法:摘要算法也称为哈希函数,是一种将任意长度的输入消息转换为固定长度的输出值(哈希值)的算法。
同样的,我们将加密的过程看做数学中计算 y = f(x) 的过程,其中 x 为明文,y 为密文,f( )表示加密算法
对于摘要算法,可以通过y = f(x)计算密文,而无法通过密文计算明文
摘要算法具有以下特性:
固定长度输出:摘要算法生成的哈希值长度是固定的,不受输入消息长度的影响。
唯一性:对于不同的输入消息,摘要算法应该生成不同的哈希值。理想情况下,不同的输入应该产生唯一的哈希值,但由于输出空间有限,可能存在碰撞(多个不同的输入生成相同的哈希值)。
不可逆性:从哈希值推导原始输入消息应该是困难的,即使在已知哈希值的情况下,也应该难以确定原始输入消息。
抗碰撞性:摘要算法应该具有良好的抗碰撞性,即在计算上难以找到两个不同的输入消息产生相同的哈希值。
常见的摘要算法有:
MD5(Message Digest Algorithm 5):是一种广泛使用的哈希函数,用于产生128位(16字节)的哈希值。MD5算法主要用于对消息进行一致性校验、数据完整性验证等非加密目的。
SHA-1(Secure Hash Algorithm 1):SHA-1是一种被广泛使用的摘要算法,但由于其存在碰撞攻击,逐渐被淘汰。
SHA-256、SHA-384、SHA-512:这些是SHA-2家族的一部分,它们分别生成256位、384位和512位长度的哈希值。SHA-2算法提供了更高的安全性,被广泛应用于各种加密应用中。
SHA-3(Secure Hash Algorithm 3):SHA-3是NIST选出的新一代哈希算法标准,其设计目标是提供与SHA-2不同的算法选择,以增加算法多样性。
DigestUtils
DigestUtils 是Spring为我们提供的一个MD5加密工具类,用于生成MD5哈希值,进行消息摘要的计算,我们可以直接利用该工具类中的方法来对数据进行加密
其中,常用的方法有:
byte[] md5Digest(byte[] bytes):计算给定字节数组的MD5哈希值
byte[] md5Digest(InputStream inputStream):计算给定输入流的MD5哈希值
String md5DigestAsHex(byte[] bytes):计算给定字节数组的MD5哈希值,并以十六进制字符串形式返回结果
String md5DigestAsHex(InputStream inputStream):计算给定输入流的MD5哈希值,并以十六进制字符串形式返回结果
StringBuilder appendMd5DigestAsHex(byte[] bytes, StringBuilder builder):计算给定字节数组的MD5哈希值,并将其以十六进制字符串形式添加到给定的StringBuilder
StringBuilder appendMd5DigestAsHex(InputStream inputStream, StringBuilder builder):计算给定输入流的MD5哈希值,并将其以十六进制字符串形式添加到给定的StringBuilder
我们以对用户密码进行加密为例,来进一步学习DigestUtils:
import org.springframework.util.DigestUtils;public class MD5UtilsTest {public static void main(String[] args) {String password1 = DigestUtils.md5DigestAsHex("123456".getBytes());System.out.println("123456: " + password1);String password2 = DigestUtils.md5DigestAsHex("123456".getBytes());System.out.println("123456: " + password2);String password3 = DigestUtils.md5DigestAsHex("123457".getBytes());System.out.println("123457: " + password3);String password4 = DigestUtils.md5DigestAsHex("12345".getBytes());System.out.println("12345: " + password4);}
}
运行结果:

我们可以看到:对于相同的输入生成相同的哈希值,而不同的输入生成不同的哈希值,且 “123456” 和 “123457” 只有一个字符不相同,但生成的哈希值差别却很大,生成的哈希值长度是固定的,不受输入消息长度的影响
MD5在线解密工具原理
MD5是一种摘要算法,摘要算法具有不可逆性,那么网上的MD5在线解密工具是如何解密的呢?
这是因为其会将常用的字符串的MD5哈希值保存到数据库中,当用户输入一个MD5哈希值时,就会尝试找到对应的明文,若找到对应的哈希值与明文的映射,则返回该明文作为解密结果;若没有找到对应映射,则可能会放弃解密(这是因为实时计算MD5哈希值对应的明文需要消耗大量的计算资源,尤其是对应复杂的密码或长文本)
我们随便找一个在线解密工具网站:
输入之前 “123456”的哈希值进行解密:

对应较为简单的 123456,则很快得出结果
而当我们输入较为复杂的明文进行加密:

此时再尝试进行解密:

则会解密失败
实现用户密码加密
我们通过MD5算法对用户密码进行加密,但由于其不可逆性,我们该如何进行判断用户输入的密码是否正确呢?
虽然经过MD5加密后的密文无法解密,但由于相同的密码经过MD5哈希后得到的密文是相同的,我们可以利用这个特性对密码进行验证

计算用户输入的密码的哈希值,并将其与数据库中存储的哈希值相比较,若相同,则用户输入的密码正确;若不同,则用户输入的密码错误
即,采用 判断哈希值是否一致 的方法来判断密码是否正确
但是,正是由于相同的密码经过MD5哈希后的密文是相同的,当存储用户密码的数据库泄露后,攻击者很容易找到相同密码的用户,从而降低了破解密码的难度,且用户输入的密码可能为弱密码(如 123456、666666等),此时破解密码的难度也较低。
因此,我们在对用户密码进行加密时,需要考虑对密码进行包装,即使是相同的密码,也保存为不同的密文,即用户即使是输入的弱密码,也对其进行增强,从而增加密码被攻破的难度
那么应该如何实现呢?
我们可以为密码拼接一个复杂字符后进行加密,为了进一步提供安全性,我们可以拼接一个随机复杂字符串,这个随机复杂字符串我们称之为 “盐”

这样,当黑客通过一定手段拿到这个加密串时,拿到明文并不是我们加密前的字符串,而是加密前的字符串和盐组合的字符串,这样相对来说又增加了字符串的安全性
因此,用户密码加密实现为:

如何生成随机盐值呢?
我们可以使用 UUID 生成随机盐值
UUID(Universally Unique Identifier)是一种标准化的格式,用于表示全局唯一的标识符。UUID通常以32位的十六进制数字表示,由五段组成,以连字符分隔,例如:550e8400-e29b-41d4-a716-446655440000。

我们可以通过randomUUID()方法来生成唯一识别码:
import java.util.UUID;public class MD5UtilsTest {public static void main(String[] args) {System.out.println(UUID.randomUUID());System.out.println(UUID.randomUUID());System.out.println(UUID.randomUUID());}
}
运行结果:

我们可以将其结果转换为字符串,并去掉其中的 “-”
import java.util.UUID;public class MD5UtilsTest {public static void main(String[] args) {System.out.println(UUID.randomUUID().toString().replace("-", ""));}
}
此时,我们就可以得到 32位 随机盐值:

此时,我们将密码和随机盐值一起进行MD5哈希:
import org.springframework.util.DigestUtils;
import java.util.UUID;public class MD5UtilsTest {public static void main(String[] args) {String password1 = DigestUtils.md5DigestAsHex(("123456" + UUID.randomUUID().toString().replace("-", "")).getBytes());System.out.println("123456: " + password1);String password2 = DigestUtils.md5DigestAsHex(("123456" + UUID.randomUUID().toString().replace("-", "")).getBytes());System.out.println("123456: " + password2);}
}
运行结果:

此时,即使是相同的密码,加密后生成的哈希值也是不同的
由于我们通过 判断哈希值是否一致 的方法来判断密码是否正确,因此在用户登录输入密码时,我们需要拿到 用户注册时生成的随机盐值,因此我们需要在数据库中存储 盐值 和 密文,(即 盐值 + MD5(明文 + 盐值))
在存储盐值和密文时,我们可以存储:
盐值 + 密文
密文 + 盐值
4位盐值 + 4位密文 + 4位盐值 + 4位密文 + ...
4位密文+ 4位盐值 + 4位密文 + 4位盐值 + ...
......
有多种存储方式,同样,我们在拼接明文和盐值时也有多种拼接方式
在学习了如何实现用户密码加密后,我们来通过代码实现 加密 和 验证
代码实现
我们首先来实现加密:
1. 生成随机盐值
2. 对 明文 + 随机盐值进行MD5加密
/*** 对用户注册密码进行加密* @param password 用户注册密码* @return 数据库中存储信息(盐值 + 密文)*/public static String encipher(String password) {// 生成盐值String salt = UUID.randomUUID().toString().replace("-", "");//将盐值 + 明文进行加密String secretPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());return salt + secretPassword;}
接下来,我们实现验证:
1. 校验输入的密码是否有效
2. 校验数据库中存储的密码是否有效
3. 获取盐值
4. 根据用户登录输入的密码和盐值进行加密,生成哈希值
5. 比较生成的哈希值和数据库中存储的密文是否相同
/*** 验证密码是否正确* @param inputPassword 用户登录时输入的密码* @param sqlPassword 数据库中存储的密码(盐值 + 密文)* @return 密码是否正确*/public static Boolean verify(String inputPassword, String sqlPassword) {//校验if(!StringUtils.hasLength(inputPassword)) {return false;}if(!StringUtils.hasLength(sqlPassword) || sqlPassword.length() != 64) {return false;}// 解析盐值String salt = sqlPassword.substring(0, 32);// 生成哈希值String secretPassword = DigestUtils.md5DigestAsHex((salt + inputPassword).getBytes());//判断是否相同return sqlPassword.equals(salt + secretPassword);}
除了使用 MD5进行加密,我们也可以使用 AES、RSA等加密算法或自己实现加密算法进行加密
相关文章:
密码加密及验证
目录 为什么需要加密? 密码算法分类 对称密码算法 非对称密码算法 摘要算法 DigestUtils MD5在线解密工具原理 实现用户密码加密 代码实现 为什么需要加密? 在MySQL数据库中,我们常常需要对用户密码、身份证号、手机号码等敏感信息进…...
找出字符串中出现最多次数的字符以及出现的次数
str.charAt(i) 是JavaScript中获取字符串中特定位置字符的方法,表示获取当前的字符。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…...
如何看待央行买卖长期国债?
央行远比大家想象中的要渴求货币宽松。 引子 今年以来,有不少关于“央行买卖长期国债”的讨论,前些时候关注点在“买”,最近关注点在“卖”。 然而,市场上的讨论采用了十分粗糙和松散的“自然语言”,所以࿰…...
MATLAB算法实战应用案例精讲-【数模应用】Turf组合模型(附MATLAB、python和R语言代码实现)
目录 几个高频面试题目 如何以最小的成本覆盖到最大的消费群体? 应用场景 TURF举例...
android源码下载编译模拟器运行
安卓aosp源码下载,编译,模拟器运行 virtualbox7 安装ubuntu20.04,ubuntu22.04 编译android aosp 源码可以,但是模拟器跑不了,哪个版本都是要么黑屏,要么整个vbox虚拟机闪退。解决方案使用vmware17 ##拯救…...
Golang:Sirupsen/logrus是一个日志库
Sirupsen/logrus是一个日志库 文档 https://github.com/Sirupsen/logrus 安装 go get github.com/sirupsen/logrus代码示例 package mainimport ("github.com/sirupsen/logrus" )func main() {var log logrus.New()log.Trace("Something very low level.&…...
Android Studio插件开发 - Dora SDK的IDE插件
IDE插件开发简介 Android Studio是一种常用的集成开发环境(IDE),用于开发Android应用程序。它提供了许多功能和工具,可以帮助开发人员更轻松地构建和调试Android应用程序。 如果你想开发Android Studio插件,以下是一…...
【mybatis】缓存
一级缓存和二级缓存 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从一级缓存中直接获取,不会从数据库重新查询。一级缓存默认是开启 使一级缓存失效的四种情况: 11.1…...
自定义类型:结构体类型
在学习完指针相关的知识后将进入到c语言中又一大重点——自定义类型,在之前学习操作符以及指针时我们对自定义类型中的结构体类型有了初步的了解,学习了结构体类型的创建以及如何创建结构体变量,还有结构体成员操作符的使用,现在我…...
C++对象移动
在某些情况下,对象拷贝后就立即被销毁了,这时利用新标准(C11)提供的对象移动而非拷贝将大幅提升性能. 1.右值引用 为了支持移动操作,c11新增了一种引用 - 右值引用(rvalue reference)。这种引用必须指向右值,使用&&声明。 右值引用只能引用临时变量或常量值. 右值引用…...
“华为杯”第十三届中国研究生 数学建模竞赛-E题:粮食最低收购价政策问题研究(续)
目录 4.3 问题三:粮食价格的特殊规律性模型 4.3.1 分析和建模 4.3.2 求解和结果...
(一)django目录介绍
1、生成django项目,得到的目录如下 manage.py:命令行工具,内置多种方式与项目进行交互。在命令提示符窗口下,将路径切换到项目并输入python manage.py help,可以查看该工具的指令信息。 默认的数据库工具,sqlite 在…...
leetcode5 最长回文子串
给你一个字符串 s,找到 s 中最长的 回文 子串。 示例 1: 输入:s "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。示例 2: 输入:s "cbbd" 输…...
《论文阅读》通过顺序不敏感的表示正则化实现稳健的个性化对话生成 ACL 2023
《论文阅读》通过顺序不敏感的表示正则化实现稳健的个性化对话生成 ACL 2023 前言 相关个性化生成论文推荐简介问题定义方法损失函数实验结果 前言 亲身阅读感受分享,细节画图解释,再也不用担心看不懂论文啦~ 无抄袭,无复制,纯手…...
python采集汽车价格数据
python采集汽车价格数据 一、项目简介二、完整代码一、项目简介 本次数据采集的目标是车主之家汽车价格数据,采集的流程包括寻找数据接口、发送请求获取响应、解析数据和持久化存储,先来看一下数据情况,完整代码附后: 二、完整代码 #输入请求页面url #返回html文档 imp…...
德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第四周) - 语言建模
语言建模 1. 统计语言模型2. N-gram语言建模 2.1. N-gram语言模型中的平滑处理 3. 语言模型评估4. 神经语言模型5. 循环神经网络 5.1. Vanilla RNN5.2. LSTM 1. 统计语言模型 统计语言模型旨在量化自然语言文本中序列的概率分布,即计算一个词序列(如一…...
Jitsi meet 退出房间后,用户还在房间内
前言 Jitsi Meet 如果客户端非正常退出会议,会产生用户还在房间内,实际用户已经退出的情况,需要一段时间内,才会在UI离开房间,虽然影响不大,但是也容易导致体验不好。 保活 Jitsi Meet 会和前端做一个保…...
Java 18 新特性
Java 作为一门广泛应用于企业级开发和系统编程的编程语言,一直以来都在不断进化和改进。2022 年发布的 Java 18 版本为开发者带来了一些新的特性和改进,这些特性不仅提升了开发效率,还进一步增强了 Java 语言的功能和灵活性。本文将深入探讨 …...
c++基础创建对象
在C中,test a; 和 test a new test(); 是两种不同的初始化或创建对象的方式,而且它们之间存在根本的区别。 test a; 这是对象a的栈上分配。在声明test a;时,编译器会在栈上为a分配内存,并调用test类的默认构造函数(…...
WHAT - 容器化系列(二)- docker
目录 一、前言二、Docker镜像:可运行软件包三、Docker容器:可执行环境四、容器和镜像的关系五、创建镜像的过程5.1 编写Dockerfile5.2 构建Docker镜像5.3 查看构建的镜像5.4 运行Docker容器5.5 验证容器运行状态5.6 推送镜像到镜像仓库(可选&…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
