Java实现各种加密验证算法(MD5、SHA256、base64、pdkdf2、pdkdf2_sha256)
Java方式通实现用户密码加密
目录
- 一、概念
- 1.消息摘要算法
- 2.常见的加密方式有
- 二、加密算法实现
- 1.Java实现MD5加密(单向加密)
- 1.1 MD5加密方式1:借助apache工具类DigesUtils实现(推荐使用)
- 1.2 MD5加密方式2:使用JDK自带的java.security.MessageDigest下的MessageDigest类
- 1.3 MD5加密方式3: Spring核心包(Spring Boot 自带MD5加密)
- 1.4 测试
- 2.Java实现SHA256 加密(单向加密)
- 2.1 SHA256 加密方式1:借助apache工具类DigesUtils实现(推荐使用)
- 2.2 SHA256 加密方式2:使用JDK自带的java.security.MessageDigest下的MessageDigest类
- 2.3 测试
- 2.4 SHA-256在线解密
- 3.Java实现base64加解密
- 3.1 示例1:简单文本的 Base64 编码、解码
- 3.2 示例2:图片的 Base64 编码、解码
- 4 Java实现pbkdf2加密验证算法
- 4.1 概念信息
- 4.2 代码实现
- 5 Java实现pdkdf2_sha256加密验证算法
- 三、源码
一、概念
1.消息摘要算法
消息摘要算法的特征是加密过程不需要秘钥
,并且加密的数据无法被解密
。任何消息经过散列函数
处理后,都会获得唯一的散列值,这一过程称为“消息摘要”。
消息摘要算法最著名的是MD5算法和SHA-1算法及其变体
。
MD5算法长度为128位(16字节),SHA-1算法长度为160位(20字节),SHA-256算法长度为256位(64字节)。
消息摘要函数是单向函数,只能正向求得密文,无法反向求明文信息
。
单向加密,即加密之后不能解密
,一般用于数据验证。
2.常见的加密方式有
- MD5加密
- SHA加密(SHA-1、SHA-256)
- base64加密
- pbkdf2&sha256加密
二、加密算法实现
idea新建maven项目:
1.Java实现MD5加密(单向加密)
java没有实现MD5解密操作
,但是有些网站可以完成解密
MD5加密的三种方式:
- 借助apache工具类DigesUtils实现(推荐使用)
- 使用JDK自带的java.security.MessageDigest下的MessageDigest类
- Spring核心包(Spring Boot 自带MD5加密)
说明:都是返回长度为32位的16进制字符串(小写)。
MD5 是哈希散列算法(也称摘要算法),对于 MD5 而言,有两个特性是很重要的,
明文数据经过散列以后的值是定长的;
是任意一段明文数据,经过散列以后,其结果必须永远是不变的。
1.1 MD5加密方式1:借助apache工具类DigesUtils实现(推荐使用)
此方式需要 安装 commons-codec 依赖
<!--commons-codec 依赖-->
<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.15</version>
</dependency>
import org.apache.commons.codec.digest.DigestUtils;/*** MD5加密方式一:借助apache工具类DigesUtils实现(推荐使用)** 此方式需要 安装 commons-codec 依赖* @param str 待加密字符串* @return 16进制加密字符串(32位MD5码)*/public static String encryptToMD5(String str){//MD5 加密,返回 32 位return DigestUtils.md5Hex(str);}
org.apache.commons.codec.digest.DigestUtils 类:
1.2 MD5加密方式2:使用JDK自带的java.security.MessageDigest下的MessageDigest类
/*** MD5加密方式二:使用JDK自带的java.security.MessageDigest下的MessageDigest类* @param str 待加密字符串* @return 16进制加密字符串(32位MD5码)*/public static String encrypt2ToMD5(String str) throws NoSuchAlgorithmException {//MD5 加密(使用MD5算法)MessageDigest md = MessageDigest.getInstance("MD5");byte[] md5Bytes = md.digest(str.getBytes(StandardCharsets.UTF_8));//为了使用方便,将输出值转换为十六进制保存return bytesToHexString(md5Bytes);}/*** 将输出值转换为十六进制保存* @param bytes* @return*/public static String bytesToHexString(byte[] bytes) {StringBuilder stringBuilder = new StringBuilder();if (bytes == null || bytes.length <= 0) {return null;}for (byte aByte : bytes) {int v = aByte & 0xFF;String hv = Integer.toHexString(v);if (hv.length() < 2) {stringBuilder.append(0);}stringBuilder.append(hv);}return stringBuilder.toString();}
1.3 MD5加密方式3: Spring核心包(Spring Boot 自带MD5加密)
安装spring 核心包 依赖
<!--spring 核心包 依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.2</version>
</dependency>
org.springframework.util.DigestUtils.md5DigestAsHex
/*** MD5加密方式三:Spring核心包(Spring Boot 自带MD5加密)* @param str 待加密字符串* @return 16进制加密字符串(32位MD5码 小写)*/public static String encrypt3ToMD5(String str){return org.springframework.util.DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8));}
1.4 测试
MD5工具类 完整代码:
package utils;import org.apache.commons.codec.digest.DigestUtils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;/*** ND5加密* @author qzz*/
public class MD5Util {/*** MD5加密方式一:借助apache工具类DigesUtils实现(推荐使用)** 此方式需要 安装 commons-codec 依赖* @param str 待加密字符串* @return 16进制加密字符串(32位MD5码)*/public static String encryptToMD5(String str){//MD5 加密,返回 32 位return DigestUtils.md5Hex(str);}/*** MD5加密方式二:使用JDK自带的java.security.MessageDigest下的MessageDigest类* @param str 待加密字符串* @return 16进制加密字符串(32位MD5码)*/public static String encrypt2ToMD5(String str) throws NoSuchAlgorithmException {//MD5 加密(使用MD5算法)MessageDigest md = MessageDigest.getInstance("MD5");byte[] md5Bytes = md.digest(str.getBytes(StandardCharsets.UTF_8));//为了使用方便,将输出值转换为十六进制保存return bytesToHexString(md5Bytes);}/*** MD5加密方式三:Spring核心包(Spring Boot 自带MD5加密)* @param str 待加密字符串* @return 16进制加密字符串(32位MD5码 小写)*/public static String encrypt3ToMD5(String str){return org.springframework.util.DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8));}/*** 将输出值转换为十六进制保存* @param bytes* @return*/public static String bytesToHexString(byte[] bytes) {StringBuilder stringBuilder = new StringBuilder();if (bytes == null || bytes.length <= 0) {return null;}for (byte aByte : bytes) {int v = aByte & 0xFF;String hv = Integer.toHexString(v);if (hv.length() < 2) {stringBuilder.append(0);}stringBuilder.append(hv);}return stringBuilder.toString();}public static void main(String[] args) throws NoSuchAlgorithmException {String str="123456";System.out.println("MD5待加密字符串:"+str);String md5Str=MD5Util.encryptToMD5(str);System.out.println("方式一:MD5加密结果:"+md5Str);String md5Str2=MD5Util.encrypt2ToMD5(str);System.out.println("方式二:MD5加密结果:"+md5Str2);String md5Str3=MD5Util.encrypt3ToMD5(str);System.out.println("方式三:MD5加密结果:"+md5Str3);}
}
执行结果:
"D:\Program Files\Java\jdk1.8.0_40\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=62529:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_40\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\rt.jar;F:\study-project\password-validate\target\classes;D:\mvnrepository\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-web\2.7.2\spring-boot-starter-web-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter\2.7.2\spring-boot-starter-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot\2.7.2\spring-boot-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-autoconfigure\2.7.2\spring-boot-autoconfigure-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-logging\2.7.2\spring-boot-starter-logging-2.7.2.jar;D:\mvnrepository\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;D:\mvnrepository\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;D:\mvnrepository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\mvnrepository\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;D:\mvnrepository\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;D:\mvnrepository\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;D:\mvnrepository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\mvnrepository\org\springframework\spring-core\5.3.22\spring-core-5.3.22.jar;D:\mvnrepository\org\springframework\spring-jcl\5.3.22\spring-jcl-5.3.22.jar;D:\mvnrepository\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-json\2.7.2\spring-boot-starter-json-2.7.2.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-databind\2.13.3\jackson-databind-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-annotations\2.13.3\jackson-annotations-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-core\2.13.3\jackson-core-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.3\jackson-datatype-jdk8-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.3\jackson-datatype-jsr310-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.3\jackson-module-parameter-names-2.13.3.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-tomcat\2.7.2\spring-boot-starter-tomcat-2.7.2.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-core\9.0.65\tomcat-embed-core-9.0.65.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-el\9.0.65\tomcat-embed-el-9.0.65.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.65\tomcat-embed-websocket-9.0.65.jar;D:\mvnrepository\org\springframework\spring-web\5.3.22\spring-web-5.3.22.jar;D:\mvnrepository\org\springframework\spring-beans\5.3.22\spring-beans-5.3.22.jar;D:\mvnrepository\org\springframework\spring-webmvc\5.3.22\spring-webmvc-5.3.22.jar;D:\mvnrepository\org\springframework\spring-aop\5.3.22\spring-aop-5.3.22.jar;D:\mvnrepository\org\springframework\spring-context\5.3.22\spring-context-5.3.22.jar;D:\mvnrepository\org\springframework\spring-expression\5.3.22\spring-expression-5.3.22.jar" utils.MD5Util
MD5待加密字符串:123456
方式一:MD5加密结果:e10adc3949ba59abbe56e057f20f883e
方式二:MD5加密结果:e10adc3949ba59abbe56e057f20f883e
方式三:MD5加密结果:e10adc3949ba59abbe56e057f20f883eProcess finished with exit code 0
MD5 曾一度被认为是非常安全的。但是 MD5 也不会完全不重复,从概率来说 16 的 32 次方遍历后至少出现两个相同的 MD5 值。
以 Google 公司为例,Google 公司明确指出不建议再使用 MD5 算法,而使用 SHA256 算法替代
。
2.Java实现SHA256 加密(单向加密)
SHA-256 算法单向 Hash
函数是密码学和信息安全领域中的一个非常重要的基本算法,它是把任意长的消息转化为较短的、固定长度的消息摘要的算法
。SHA-256 算法是 SHA
算法族中的一员,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布;是美国的政府标准。它的前辈还有 SHA-1。
随着密码学<small>(破解)</small>的发展,美国政府计划从 2010 年起不再使用 SHA-1,全面推广使用 SHA-256 和 SHA-512 等加密算法
。
对于任意长度的消息,SHA256 都会产生一个 256bit 长的哈希值,称作消息摘要
。这个摘要相当于是个长度为 32 个字节的数组,通常用一个长度为 64 的十六进制字符串来表示。
SHA256 加密实现方式:
- 加密方式一:借助apache工具类DigesUtils实现(推荐使用)
- 加密方式二:使用JDK自带的java.security.MessageDigest下的MessageDigest类
2.1 SHA256 加密方式1:借助apache工具类DigesUtils实现(推荐使用)
import org.apache.commons.codec.digest.DigestUtils;/*** 加密方式一:借助apache工具类DigesUtils实现(推荐使用)** 此方式需要 安装 commons-codec 依赖* @param str 待加密字符串* @return 16进制加密字符串(64位)*/public static String encryptToSHA256(String str){//SHA-256 加密,返回 64 位return DigestUtils.sha256Hex(str.getBytes(StandardCharsets.UTF_8));}
2.2 SHA256 加密方式2:使用JDK自带的java.security.MessageDigest下的MessageDigest类
/*** 加密方式二:使用JDK自带的java.security.MessageDigest下的MessageDigest类** 此方式需要 安装 commons-codec 依赖* @param str 待加密字符串* @return 16进制加密字符串(64位)*/public static String encrypt2ToSHA256(String str) throws NoSuchAlgorithmException {//SHA-256 加密,返回 64 位MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] md5Bytes = md.digest(str.getBytes(StandardCharsets.UTF_8));//为了使用方便,将输出值转换为十六进制保存return bytesToHexString(md5Bytes);}/*** 将输出值转换为十六进制保存* @param bytes* @return*/public static String bytesToHexString(byte[] bytes) {StringBuilder stringBuilder = new StringBuilder();if (bytes == null || bytes.length <= 0) {return null;}for (byte aByte : bytes) {int v = aByte & 0xFF;String hv = Integer.toHexString(v);if (hv.length() < 2) {stringBuilder.append(0);}stringBuilder.append(hv);}return stringBuilder.toString();}
2.3 测试
SHA256加密工具类完整代码:
package utils;import org.apache.commons.codec.digest.DigestUtils;import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;/*** SHA256加密* @author qzz*/
public class SHAUtil {/*** 加密方式一:借助apache工具类DigesUtils实现(推荐使用)** 此方式需要 安装 commons-codec 依赖* @param str 待加密字符串* @return 16进制加密字符串(64位)*/public static String encryptToSHA256(String str){//SHA-256 加密,返回 64 位return DigestUtils.sha256Hex(str.getBytes(StandardCharsets.UTF_8));}/*** 加密方式二:使用JDK自带的java.security.MessageDigest下的MessageDigest类** 此方式需要 安装 commons-codec 依赖* @param str 待加密字符串* @return 16进制加密字符串(64位)*/public static String encrypt2ToSHA256(String str) throws NoSuchAlgorithmException {//SHA-256 加密,返回 64 位MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] md5Bytes = md.digest(str.getBytes(StandardCharsets.UTF_8));//为了使用方便,将输出值转换为十六进制保存return bytesToHexString(md5Bytes);}/*** 将输出值转换为十六进制保存* @param bytes* @return*/public static String bytesToHexString(byte[] bytes) {StringBuilder stringBuilder = new StringBuilder();if (bytes == null || bytes.length <= 0) {return null;}for (byte aByte : bytes) {int v = aByte & 0xFF;String hv = Integer.toHexString(v);if (hv.length() < 2) {stringBuilder.append(0);}stringBuilder.append(hv);}return stringBuilder.toString();}public static void main(String[] args) throws NoSuchAlgorithmException {String str="123456";System.out.println("SHA-256待加密字符串:"+str);String sha256Str=SHAUtil.encryptToSHA256(str);System.out.println("方式一:SHA-256加密结果:"+sha256Str);String sha256Str2=SHAUtil.encrypt2ToSHA256(str);System.out.println("方式二:SHA-256加密结果:"+sha256Str2);}
}
执行结果:
2.4 SHA-256在线解密
java没有实现SHA-256解密操作
,但是有些网站可以完成解密
在线网址:
http://www.ttmd5.com/hash.php?type=9
注意:算法缺陷
使用MD5、SHA1等单向HASH算法保护密码,使用这些算法后,无法通过计算还原出原始密码,而且实现比较简单,因此很多互联网公司都采用这种方式保存用户密码,曾经这种方式也是比较安全的方式,但随着彩虹表
技术的兴起,可以建立彩虹表进行查表破解
,目前MD5算法和SHA-256算法已经很不安全了。
彩虹表是一个用于散列函数逆运算的预先计算好的表,该表有明文和一一对应的密文,所以能破解上面的算法。
3.Java实现base64加解密
3.1 示例1:简单文本的 Base64 编码、解码
package utils;import java.nio.charset.StandardCharsets;
import java.util.Base64;/*** base64加解码** Java8 之后,JDK 工具包中提供了 Base64 特性,可以直接使用来完成编码和解码操作。* @author qzz*/
public class Base64Utils {/*** base64 编码* @param text* @return*/public static String encode(String text){return new String(Base64.getEncoder().encode(text.getBytes(StandardCharsets.UTF_8)));}/*** base64 解码* @param encodedText* @return*/public static String decode(String encodedText){return new String(Base64.getDecoder().decode(encodedText.getBytes(StandardCharsets.UTF_8)));}public static void main(String[] args) {String str="123456";//编码String encodedText=Base64Utils.encode(str);System.out.println("编码后的字符串为:"+encodedText);//解码String text=Base64Utils.decode(encodedText);System.out.println("解码后的字符串为:"+text);}
}
执行结果:
"D:\Program Files\Java\jdk1.8.0_40\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=59517:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_40\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\rt.jar;F:\study-project\password-validate\target\classes" utils.Base64Utils
编码后的字符串为:MTIzNDU2
解码后的字符串为:123456Process finished with exit code 0
注意:
使用 UTF-8 指定编码和解码格式,保证操作过程中不会出现中文乱码问题,如果不指定编码格式 Base64 会使用环境默认编码格式。
windows 下默认为 GBK 编码,而 Linux 下默认为 utf-8 编码,因此在编码和解码时指定统一的编码格式是良好的编程习惯。
3.2 示例2:图片的 Base64 编码、解码
package utils;import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;/*** 图片的Base64加解码* @author qzz*/
public class Base64ImageUtil {/*** 远程服务器图片转Base64字符串* @param imageUrl* @return*/public static String getImageBase64Str(String imageUrl){//将图片文件转化为字节数组字符串,并进行Base64编码处理byte[] data=null;InputStream in=null;ByteArrayOutputStream out=null;//1.读取图片字节数组try {URL url = new URL(imageUrl);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(5000);in = connection.getInputStream();out = new ByteArrayOutputStream();data=new byte[1024];int len = 0;while((len = in.read(data))!=-1){out.write(data,0,len);}} catch (Exception e) {e.printStackTrace();}finally {try {if(out!=null){out.close();}if(in!=null){in.close();}} catch (IOException e) {e.printStackTrace();}}//2.对字节数组进行Base64编码String base64Image = new String(Base64.getEncoder().encode(out.toByteArray()));//如果需要将得到的 base64 字符串展示在 html 的 img 标签中,需要在字符串中添加 data:image/jpg;base64, 前缀return base64Image;}/*** Base64字符串转图片* @param base64Image Base64字符串* @param imagePath 转换完之后的图片存储地址* @return*/public static void getImageUrlFormBase64(String base64Image,String imagePath){if(base64Image==null){return;}OutputStream out=null;try {byte[] data=Base64.getDecoder().decode(base64Image);for(int i=0;i<data.length;i++){if(data[i]<0){data[i]+=256;}}out=new FileOutputStream(imagePath);out.write(data);out.flush();} catch (Exception e) {e.printStackTrace();}finally {try {if(out!=null){out.close();}} catch (IOException e) {e.printStackTrace();}}}public static void main(String[] args) {//图片编码String base64Image=Base64ImageUtil.getImageBase64Str("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");System.out.println("图片url转Base64字符串:"+base64Image);//图片解码Base64ImageUtil.getImageUrlFormBase64(base64Image,"F:\\image\\b1.png");}
}
执行结果:
"D:\Program Files\Java\jdk1.8.0_40\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=61033:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_40\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\rt.jar;F:\study-project\password-validate\target\classes" utils.Base64ImageUtil
图片url转Base64字符串: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Ukk3/Mu1H72ejW7sb7GI9dOPhi+14IT/or+LPd6ixZ9f3xj77DM9ZgyQoeGWBjrYIObNu2HTO0vn0srbcygWx8l0bCLVjaqILRWBvINEkf3+lApKJtPph6r9VrvppOik9ead2l+6UXbSj3mHh3vXjwqHm51yoaT3qHjbgkF/JvmkKqt86NAY/+QTU+9dSKU7E+kzZ7aM6FLUu+nyehU5dwnCdGXCEq0MWrfEWYh+kevpR7mBS0ILDrdTYny3JKQOxSt3sLjYI9PIkMKVISoTQ2UmDaF4LsNI7nBSMIEcvAnkIB9GEwSz/zDD2T+ZR/rBhhXVKZ9k7SDc7JfP4hTjzA5dTtEhFAoJLvF24ri55sZbN5h91XqLT75AlZmu33f1Tbcv2l/OlpKSiqgSPkEvD+16ylW+y8c9H48Mhz/LxLWzfdiwYd2RPRNVRYyiM9ir11bu9TRWd0Q+0SufC3J553DkQqOhDrlHYOrMliNicfU6wcWldPMuTX8NuUrPZE10fV5B3cTveFT1PV7m/2DNK6MsRz6smy984rPgeHJfptDlfxY5JrPImTqK6pR2Z5VuMNrqgr9yLt/f1FC5LP0cUQIIgIBeAnA49JKCHAhkgMCcOW2l7T09l5EjMJ+Km21LTwH1mlGvw3pyZJqoB4OcDGk1K/WtzOTkSc0JYZ+2TKPeEG1p5CSyRYvJMJHqWU5/LW9Uv/epg+Y5r0968P3l1aaG0SwbAQUgAAJJCcDhSIoHiSCQPQKzZu0Y0hHvOpFuykdTb8FEuqFW0VDbcOqZ8NOJK1Gk6Dg5ED3UjdxNVu7gQuygCcVtNN+nhZ70m5kimj1e6dM1K6pasleL5CUfNXdzeaQ7Po4JtVYwqZZztZrqWkFDMyOoniOoh6SE/lJ0SqF1VWvDj9qbXnZT/Vtp90PB5QaJ+15tWlmBSK7JUSMVBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAgXwj8P1QjL/BchIZ8AAAAAElFTkSuQmCCProcess finished with exit code 0
4 Java实现pbkdf2加密验证算法
4.1 概念信息
(1)PBKDF2算法
PBKDF2算法通过多次hash来对密码进行加密
。
原理是通过password和
salt进行hash,然后将结果作为salt在与password进行hash,多次重复此过程,生成最终的密文。
此过程可能达到上千次,逆向破解的难度太大,破解一个密码的时间可能需要几百年,所以PBKDF2算法是安全的.
使用PBKDF2
算法时,HASH算法一般选用sha1或者sha256
,随机盐的长度一般不能少于8字节,HASH次数至少也要1000次
,这样安全性才足够高。一次密码验证过程进行1000次HASH运算,对服务器来说可能只需要1ms,但对于破解者来说计算成本增加了1000倍,而至少8字节随机盐,更是把建表难度提升了N个数量级,使得大批量的破解密码几乎不可行,该算法也是美国国家标准与技术研究院推荐使用的算法。
(2)如何验证密码正确?
用相同的盐值对用户输入的密码进行加密,如果与密文比对,相同则密码正确。
(3)加盐处理
在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”(比如可以在密码中混入一段“随机”的字符串再进行哈希加密,这个被字符串被称作盐值
)。
(4)为什么要加盐
涉及身份验证的系统都需要存储用户的认证信息,常用的用户认证方式主要为用户名和密码的方式,为了安全起见,用户输入的密码需要保存为密文形式,可采用已公开的
不可逆的hash加密算法
,比如SHA256, SHA512,SHA3等
,对于同一密码,同一加密算法会产生相同的hash值,这样,当用户进行身份验证时,也可对用户输入的明文密码应用相同的hash加密算法,得出一个hash值,然后使用该hash值和之前存储好的密文值进行对照,如果两个值相同,则密码认证成功,否则密码认证失败.
由于密码是由用户设定的,在实际应用中,用户设置的密码复杂度可能不够高,同时不同的用户极有可能会使用相同的密码,那么这些用户对应的密文也会相同,这样,当存储用户密码的数据库泄露后,攻击者会很容易便能找到相同密码的用户,从而也降低了破解密码的难度,因此,在对用户密码进行加密时,需要考虑对密码进行掩饰,即使是相同的密码,也应该要保存为不同的密文,即使用户输入的是弱密码,也需要考虑进行增强,从而增加密码被攻破的难度,而使用带盐的加密hash值便能满足该需求。
4.2 代码实现
PBKDF2加密工具:
package utils;import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;/*** PBKDF2加密工具* @author qzz*/
public class PBKDF2Util {/*** 算法*/public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";/*** 盐的长度*/public static final int SALT_BYTE_SIZE = 16;/*** 生成密文的长度*/public static final int HASH_BIT_SIZE = 128 * 4;/*** 迭代次数*/public static final int PBKDF2_ITERATIONS = 1000;/*** @describe: 对输入的密码进行验证* @param: [attemptedPassword(待验证密码), encryptedPassword(密文), salt(盐值)]* @return: boolean*/private static boolean authenticate(String attemptedPassword, String encryptedPassword, String salt)throws NoSuchAlgorithmException, InvalidKeySpecException {// 用相同的盐值对用户输入的密码进行加密String encryptedAttemptedPassword = getEncryptedPassword(attemptedPassword, salt);// 把加密后的密文和原密文进行比较,相同则验证成功,否则失败return encryptedAttemptedPassword.equals(encryptedPassword);}/*** @describe: 生成密文* @param: [password(明文密码), salt(盐值)]* @return: java.lang.String*/private static String getEncryptedPassword(String password, String salt) throws NoSuchAlgorithmException,InvalidKeySpecException {//参数 :明文密码 ,盐值,和迭代次数和长度生成密文KeySpec spec = new PBEKeySpec(password.toCharArray(), fromHex(salt), PBKDF2_ITERATIONS, HASH_BIT_SIZE);//返回转换指定算法的秘密密钥的 SecretKeyFactory 对象//此方法从首选 Provider 开始遍历已注册安全提供者列表。返回一个封装 SecretKeyFactorySpi 实现的新 SecretKeyFactory 对象,该实现取自支持指定算法的第一个 Provider。SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);//根据提供的密钥规范(密钥材料)生成 SecretKey 对象。 然后以主要编码格式返回键,最后转换为16进制字符串return toHex(factory.generateSecret(spec).getEncoded());}/*** @describe: 通过加密的强随机数生成盐(最后转换为16进制)* @return: java.lang.String*/private static String generateSalt() throws NoSuchAlgorithmException {//返回一个实现指定的 SecureRandom 对象//随机数生成器 (RNG) 算法。SecureRandom random = SecureRandom.getInstance("SHA1PRNG");//生成盐byte[] salt = new byte[SALT_BYTE_SIZE];random.nextBytes(salt);//返回16进制的字符串return toHex(salt);}/*** @describe: 十六进制字符串转二进制字符串* @param: [hex]* @return: byte[]*/private static byte[] fromHex(String hex) {byte[] binary = new byte[hex.length() / 2];for (int i = 0; i < binary.length; i++) {binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);}return binary;}/*** @describe: 二进制字符串转十六进制字符串* @param: [array]* @return: java.lang.String*/private static String toHex(byte[] array) {BigInteger bigInteger = new BigInteger(1, array);String hex = bigInteger.toString(16);int paddingLength = (array.length * 2) - hex.length();if (paddingLength > 0) {return String.format("%0" + paddingLength + "d", 0) + hex;} else {return hex;}}/*** 生成可以保存的数据* @param password 明文* @throws NoSuchAlgorithmException* @throws InvalidKeySpecException*/public static String generateStorngPasswordHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {//先获取盐值String salt = PBKDF2Util.generateSalt();//生成密文String hash =PBKDF2Util.getEncryptedPassword(password,salt);return salt + ":" + hash;}/*** 明文密码和数据库保存的值比较* @param originalPassword 明文密码* @param storedPassword 数据库保存的值* @throws NoSuchAlgorithmException* @throws InvalidKeySpecException*/public static boolean validatePassword(String originalPassword, String storedPassword) throws NoSuchAlgorithmException, InvalidKeySpecException {String[] parts = storedPassword.split(":");String salt = parts[0];String hash = parts[1];return PBKDF2Util.authenticate(originalPassword,hash,salt);}
}
测试使用:
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {//---------------------------加密-------------------------------//原始密码String password="123456";System.out.println("原始密码:"+password);//生成的可以保存的数据String storedPassword=PBKDF2Util.generateStorngPasswordHash(password);System.out.println("生成的可以保存的数据:"+storedPassword);String[] parts = storedPassword.split(":");String salt = parts[0];String hash = parts[1];System.out.println("盐值:"+salt);System.out.println("加密后的密码:"+hash);//---------------------------加密-------------------------------//---------------------------校验-------------------------------//明文密码String originalPassword="1234156";//数据库保存的值String storedPassword2=storedPassword;boolean isSuccess=PBKDF2Util.validatePassword(originalPassword,storedPassword2);System.out.println("验证结果:"+isSuccess);}
执行结果:
"D:\Program Files\Java\jdk1.8.0_40\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=55260:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_40\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\rt.jar;F:\study-project\password-validate\target\classes;D:\mvnrepository\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-web\2.7.2\spring-boot-starter-web-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter\2.7.2\spring-boot-starter-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot\2.7.2\spring-boot-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-autoconfigure\2.7.2\spring-boot-autoconfigure-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-logging\2.7.2\spring-boot-starter-logging-2.7.2.jar;D:\mvnrepository\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;D:\mvnrepository\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;D:\mvnrepository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\mvnrepository\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;D:\mvnrepository\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;D:\mvnrepository\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;D:\mvnrepository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\mvnrepository\org\springframework\spring-core\5.3.22\spring-core-5.3.22.jar;D:\mvnrepository\org\springframework\spring-jcl\5.3.22\spring-jcl-5.3.22.jar;D:\mvnrepository\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-json\2.7.2\spring-boot-starter-json-2.7.2.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-databind\2.13.3\jackson-databind-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-annotations\2.13.3\jackson-annotations-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-core\2.13.3\jackson-core-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.3\jackson-datatype-jdk8-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.3\jackson-datatype-jsr310-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.3\jackson-module-parameter-names-2.13.3.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-tomcat\2.7.2\spring-boot-starter-tomcat-2.7.2.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-core\9.0.65\tomcat-embed-core-9.0.65.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-el\9.0.65\tomcat-embed-el-9.0.65.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.65\tomcat-embed-websocket-9.0.65.jar;D:\mvnrepository\org\springframework\spring-web\5.3.22\spring-web-5.3.22.jar;D:\mvnrepository\org\springframework\spring-beans\5.3.22\spring-beans-5.3.22.jar;D:\mvnrepository\org\springframework\spring-webmvc\5.3.22\spring-webmvc-5.3.22.jar;D:\mvnrepository\org\springframework\spring-aop\5.3.22\spring-aop-5.3.22.jar;D:\mvnrepository\org\springframework\spring-context\5.3.22\spring-context-5.3.22.jar;D:\mvnrepository\org\springframework\spring-expression\5.3.22\spring-expression-5.3.22.jar" utils.PBKDF2Util
原始密码:123456
生成的可以保存的数据:43cde27428c726f7bb6f4aa1621be967:5197e1bdfadace22f3afc35dfe53880130c01eb6d34dedc1700f592025e8922a6429a4866aa1c2c4245e5a43041ba7c7ff8cf5ddbc39920f88666051d1a64319
盐值:43cde27428c726f7bb6f4aa1621be967
加密后的密码:5197e1bdfadace22f3afc35dfe53880130c01eb6d34dedc1700f592025e8922a6429a4866aa1c2c4245e5a43041ba7c7ff8cf5ddbc39920f88666051d1a64319
验证结果:falseProcess finished with exit code 0
5 Java实现pdkdf2_sha256加密验证算法
pdkdf2_sha256加密验证算法工具类:
package utils;import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.Random;/*** pdkdf2_sha256 加密验证算法* @author qzz*/
public class Pbkdf2Sha256 {/*** 默认迭代次数*/private static final Integer DEFAULT_ITERATIONS=30000;/*** 算法名称*/private static final String algorithm="pdkdf2_sha256";/*** 获取密文* @param password 密码明文* @param salt 加盐* @param iterations 迭代次数* @return*/public static String getEncodedHash(String password,String salt,int iterations){SecretKeyFactory keyFactory=null;try {keyFactory=SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");} catch (NoSuchAlgorithmException e) {//无法检索 PBKDF2WithHmacSHA256 算法System.err.println("Could not retrieve PBKDF2WithHmacSHA256 algorithm");System.exit(1);}KeySpec keySpec = new PBEKeySpec(password.toCharArray(),salt.getBytes(StandardCharsets.UTF_8),iterations,256);SecretKey secretKey = null;try {secretKey = keyFactory.generateSecret(keySpec);} catch (InvalidKeySpecException e) {//无法生成密钥System.out.println("Could not generate secret key");e.printStackTrace();}byte[] rawHash=secretKey.getEncoded();byte[] hashBase64 = Base64.getEncoder().encode(rawHash);return new String(hashBase64);}/*** 密文加盐* @return*/private static String getSalt(){int length = 12;Random rand = new Random();char[] rs = new char[length];for(int i = 0; i < length; i++){int t = rand.nextInt(3);if (t == 0) {rs[i] = (char)(rand.nextInt(10)+48);} else if (t == 1) {rs[i] = (char)(rand.nextInt(26)+65);} else {rs[i] = (char)(rand.nextInt(26)+97);}}return new String(rs);}/*** rand salt* iterations is default 30000* @param password* @return*/public static String encode(String password){return encode(password,getSalt());}/*** iterations is default 30000* @param password* @param salt* @return*/public static String encode(String password,String salt){return encode(password,salt,DEFAULT_ITERATIONS);}/**** @param password 密码明文* @param salt 加盐* @param iterations 迭代次数* @return*/public static String encode(String password,String salt,int iterations){//获取密文String hash=getEncodedHash(password,salt,iterations);//返回字符串组装后的结果集:算法$迭代次数$盐值$密文$return String.format("%s$%d$%s$%s",algorithm,iterations,salt,hash);}/*** rand salt* @param password* @param iterations* @return*/public static String encode(String password,int iterations){return encode(password,getSalt(),iterations);}/*** 校验密码是否合法* @param password 明文* @param hashedPassword 密文* @return*/public static boolean verification(String password,String hashedPassword){//将密文截取 parts[0]:算法 parts[1]:迭代次数 parts[2]:盐值 parts[3]:密文String[] parts = hashedPassword.split("\\$");if(parts.length!=4){return false;}//迭代次数Integer iterations = Integer.parseInt(parts[1]);//盐值String salt=parts[2];//待校验的明文转密文String hash=encode(password,salt,iterations);//密文比对return hash.equals(hashedPassword);}
}
测试方法:
public static void main(String[] args) {String password="111111";System.out.println("明文密码:"+password);//迭代次数Integer iterations =1000;//明文转密文(密文由四部分组成)String hashedPassword=Pbkdf2Sha256.encode(password,iterations);System.out.println("生成的可以保存的数据:"+hashedPassword);//获取盐值String[] parts = hashedPassword.split("\\$");String salt=parts[2];System.out.println("盐值:"+salt);//待验证的明文String password1="111111";//密文校验String hash=Pbkdf2Sha256.encode(password1,salt,iterations);boolean isSuccess = hash.equals(hashedPassword);System.out.println("验证结果:"+isSuccess);
}
执行结果:
"D:\Program Files\Java\jdk1.8.0_40\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=60197:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_40\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_40\jre\lib\rt.jar;F:\study-project\password-validate\target\classes;D:\mvnrepository\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-web\2.7.2\spring-boot-starter-web-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter\2.7.2\spring-boot-starter-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot\2.7.2\spring-boot-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-autoconfigure\2.7.2\spring-boot-autoconfigure-2.7.2.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-logging\2.7.2\spring-boot-starter-logging-2.7.2.jar;D:\mvnrepository\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;D:\mvnrepository\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;D:\mvnrepository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\mvnrepository\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;D:\mvnrepository\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;D:\mvnrepository\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;D:\mvnrepository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\mvnrepository\org\springframework\spring-core\5.3.22\spring-core-5.3.22.jar;D:\mvnrepository\org\springframework\spring-jcl\5.3.22\spring-jcl-5.3.22.jar;D:\mvnrepository\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-json\2.7.2\spring-boot-starter-json-2.7.2.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-databind\2.13.3\jackson-databind-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-annotations\2.13.3\jackson-annotations-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\core\jackson-core\2.13.3\jackson-core-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.3\jackson-datatype-jdk8-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.3\jackson-datatype-jsr310-2.13.3.jar;D:\mvnrepository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.3\jackson-module-parameter-names-2.13.3.jar;D:\mvnrepository\org\springframework\boot\spring-boot-starter-tomcat\2.7.2\spring-boot-starter-tomcat-2.7.2.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-core\9.0.65\tomcat-embed-core-9.0.65.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-el\9.0.65\tomcat-embed-el-9.0.65.jar;D:\mvnrepository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.65\tomcat-embed-websocket-9.0.65.jar;D:\mvnrepository\org\springframework\spring-web\5.3.22\spring-web-5.3.22.jar;D:\mvnrepository\org\springframework\spring-beans\5.3.22\spring-beans-5.3.22.jar;D:\mvnrepository\org\springframework\spring-webmvc\5.3.22\spring-webmvc-5.3.22.jar;D:\mvnrepository\org\springframework\spring-aop\5.3.22\spring-aop-5.3.22.jar;D:\mvnrepository\org\springframework\spring-context\5.3.22\spring-context-5.3.22.jar;D:\mvnrepository\org\springframework\spring-expression\5.3.22\spring-expression-5.3.22.jar" utils.Pbkdf2Sha256
明文密码:111111
生成的可以保存的数据:pdkdf2_sha256$1000$zC38kS8N4fqM$ZZhIo303sDRjPFycIlz2NlOiuxSzJkfGAfYx5Bj+iv8=
盐值:zC38kS8N4fqM
验证结果:trueProcess finished with exit code 0
三、源码
点击此处下载
Java实现各种加密验证算法(MD5、SHA256、base64、pdkdf2、pdkdf2_sha256)相关推荐
- java编码解码加密解密--md5、SHA-256、SHA-512、MAC、DES、AES、RSA
md5 md5是一种散列算法,不可逆,是一种消息摘要算法,生成的字节数组的长度是128位,也就是等于16个字节,那么有的字节转化为字符之后,这些字符不一定是存在于ascii码之中,通常为了便于输出,我 ...
- java 实现 pbkdf2_sha256加密验证算法
2019独角兽企业重金招聘Python工程师标准>>> import java.nio.charset.Charset; import java.security.NoSuchAlg ...
- iOS经常使用加密方式(MD5,AES,BASE64)与网络数据安全
演示样例项目下载地址 https://github.com/cerastes/Encryption 1MD5 创建MD5类 #import <Foundation/Foundation.h&g ...
- js的常见的三种密码加密方式-MD5、Base64、sha1加密详解总结
写前端的时候,很多的时候是避免不了注册这一关的,但是一般的注册是没有任何的难度的,无非就是一些简单的获取用户输入的数据,然后进行简单的校验以后调用接口,将数据发送到后端,完成一个简单的注册的流程,那么 ...
- Java和go加密,解密,Base64失败
在客户端和go后台对接口的时候,加解密出现了问题记录. 问题主要出现在base64上,刚开始Android使用 import android.util.Base64;public class AesU ...
- C#加密解密(DES,AES,Base64,md5,SHA256,RSA,RC4)
一:异或^简单加解密(数字类型) 1:原理: 异或用于比较两个二进制数的相应位,在执行按位"异或"运算时,如果两个二进制数的相应位都为1或者都为0,则返回0;如果两个二进制数的相应 ...
- Java 开发中常用的 4 种加密方法。MD5加密工具类测试 base64加密工具类测试 SHA加密工具类测试 BCrypt加密工具类测试
一.工具类 1, md5加密工具类 2, base64加密工具类 3, Bcrypt工具类 二.加密测试 MD5加密测试 base64加密测试 SHA加密测试 BCrypt加密测试 一.工具类 1, ...
- java实现MD5,SHA256,AES
密码学支持 在日常工作中经常需要涉及到对数据的加密.解密以及数据验证等操作,Java提供了一些类来完成加密解密功能. 哈希函数 哈希函数通常用于验证数据指纹,同一个文件或数据,使用同一种哈希函数得到的 ...
- python学习-通过md5/sha1/sha256/base64进行加解密
hashlib模块为加密算法 MD5算法运行1000次的平均时间为:226ms SHA1算法运行1000次的平均时间为:308ms SHA256算法运行1000次的平均时间为:473ms 常见的为后面 ...
- 常用的加密算法(md5,sha1,base64加密解密)使用
import org.apache.commons.codec.digest.DigestUtils; import java.util.Base64; import java.util.Scanne ...
最新文章
- Attention跟一维卷积有什么区别?
- 7.1 pdo 宝塔面板php_宝塔面板配置阿里云服务器步骤和教程
- Linux Shell -- 无网不利
- java幂等性原理_Java接口幂等性设计原理解析
- 判刑后再上诉会改判吗_近视眼手术后还会再近视反弹吗?有哪些后遗症,温州眼科专家告诉你...
- 【编程珠玑】第十章 节省空间
- Django完整的开发一个博客系统
- python unittest接口测试_python+request+unittest 接口测试练习
- 【数学建模】评价类算法
- 电商后台权限设置有哪些规范你知道吗?
- svn图标消失解决办法
- vue中使用微信聊天表情
- 女人假正经的十三种表现
- 语音信号的梅尔频率倒谱系数(MFCC)的原理讲解及python实现
- 封神台——手工注入基础(猫舍)
- ZYNQ PS GPIO MIO 基础知识
- jQuery 第二篇
- Spring初见杀——classNotFound异常
- NFC·(近距离无线通讯技术)
- ListView控件的理解——自洽理论