大家好,又见面了,我是你们的朋友全栈君。
以下内容整理自网上:
简介
1、一些术语
明文:加密前的信息
密文:机密后的信息
算法:加密或解密的算法
密钥:算法使用的钥匙
2、一个简单的加密
将123456每位数字都加1后得到234567,
其中123456就是明文,234567就是密文,加密密钥就是1,加密算法是每位加
3、对称加密和非对称加密
123456–>234567的加密密钥就是1,加密算法是每位+
234567–>123456的解密密钥也是1,解密算法是每位-
其中加密算法(+)和解密算法(-)相对称,这种加密算法就称作对称加密,
同样,如果加密算法和解密算法不对称就称之为非对称加密。
4、常见算法
对称加密算法:DES算法,3DES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法,AES算法。
非对称加密算法:RSA、Elgamal、背包算法、Rabin、D-H、ECC。
经典的哈希算法:MD2、MD4、MD5 和 SHA-1(目的是将任意长输入通过算法变为固定长输出,且保证输入变化一点输出都不同,且不能反向解密)
5.经典的算法
AES(对称),RSA(非对称),MD5,SHA-1(哈希)
6、加密和摘要
加密和摘要,是不一样的,加密后的消息是完整的,具有解密算法,得到原始数据,摘要得到的消息是不完整的,通过摘要的数据,不能得到原始数据
7、MD5长度
有人说md5,128位,32位,16位,到底md5多长?
md5的长度,默认为128bit,也就是128个0和1的二进制串。
这样表达是很不友好的。
所以将二进制转成了16进制,每4个bit表示一个16进制,
所以128/4 = 32 换成16进制表示后,为32位了。
8、为什么网上还有md5是16位的呢?
网上有很多帖子,md5 32位 16位 加密 区别。
仔细观察admin生成的32位和16位的md5值
查询结果:
md5(admin,32) = 21232f297a57a5a743894a0e4a801fc3
md5(admin,16) = 7a57a5a743894a0e
其实16位的长度,是从32位md5值来的。是将32位md5去掉前八位,去掉后八位得到的。
9、MD5的作用
①一致性检验,最上面那个例子
②数字签名,还是最上面那个例子。只是把md5看出了一个指纹,按了个手印说明独一无二了。
③安全访问认证,这个就是平时系统设计的问题了。
在用户注册时,会将密码进行md5加密,存到数据库中。这样可以防止那些可以看到数据库数据的人,恶意操作了。
10、md5不能激活成功教程吗?
md5是不可逆的,也就是没有对应的算法,从生产的md5值逆向得到原始数据。
但是如果使用暴力激活成功教程,那就另说了。
11、md5是唯一的吗?
md5作为数据库中的主键可行吗?这就涉及到一个问题,md5值是唯一的吗?答案是,不唯一。
也就是一个原始数据,只对应一个md5值;
但是一个md5值,可能对应多个原始数据。
一、什么是MD5加密
- 压缩性:任意长度的数据,算出的MD5值长度都是固定的。
- 容易计算:从原数据计算出MD5值很容易。
- 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
- 弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
- 强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。
一、文件校验
通过接收到的文件进行MD5摘要校验对比,就能得出文件在传输过程中有没有被篡改。
二、密码加密
由于MD5在生成后无法进行反向解析,加密后会像上面图中一样,只有一串16进制的字符数组,无法获取原本的内容是什么(其实上图中的MD5字符,是使用Java自带的MD5工具对“123456”进行处理后得到的);所以通常也可以用于传输一些私密信息,像用户密码。
以 Web 应用为例,前台页面输入密码后,可以通过MD5加密,然后直接将 MD5字符串 保存到数据库中,当用户登录时只需将数据库中的 MD5字符串获取并与之输入的密码MD5字符串比对即可通过验证。
三、JAVA中实现MD5加密
在JAVA中有MD5的这个类,在java.security.MessageDigest包中,名字就是MessageDigest。此 MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。
MessageDigest 通过其getInstance系列静态函数来进行实例化和初始化。MessageDigest 对象通过使用 update 方法处理数据。任何时候都可以调用 reset 方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用 digest 方法之一完成哈希计算并返回结果。
对于给定数量的更新数据,digest 方法只能被调用一次。digest 方法被调用后,MessageDigest 对象被重新设置成其初始状态。
MessageDigest类:
- 构造函数
-
protected MessageDigest(String algorithm) 创建具有指定算法名称的消息摘要。
-
方法
-
-
Object clone() 如果实现是可克隆的,则返回一个克隆。 byte[] digest(byte[] input) 使用指定的字节数对摘要执行最后更新,然后完成摘要计算。 String getAlgorithm() 返回标识算法的字符串,与实现详细信息无关。 int getDigestLength() 返回摘要的长度(以字节为单位),如果此操作不受提供程序支持并且实现不可克隆,则返回0。 static MessageDigest getInstance(String algorithm) 返回实现指定摘要算法的MessageDigest对象。 Provider getProvider() 返回此消息摘要对象的提供程序。 static boolean isEqual(byte[] digesta, byte[] digestb) 比较两个摘要的平等性。 void reset() 重置摘要以供进一步使用。 String toString() 返回此消息摘要对象的字符串表示形式。 void update(byte[] input) 使用指定的字节数更新摘要。
MessageDigest类使用步骤:
- 初始化MessageDigest对象
- 调用update方法传入要进行加密的byte数据
- (可选)调用reset方法重置要传入进行加密的数据
- 调用digest方法对传入的数据进行加密
MessageDigest m=MessageDigest.getInstance("MD5");
分析:
(2)传入需要计算的字符串
m.update(x.getBytes("UTF8" ));
分析:
x为需要计算的字符串,update传入的参数是字节类型或字节类型数组,对于字符串,需要先使用getBytes( )方法生成字符串数组。
(3)计算消息摘要
byte s[ ]=m.digest( );
分析:
执行MessageDigest对象的digest( )方法完成计算,计算的结果通过字节类型的数组返回。
(4)处理计算结果
必要的话可以使用如下代码将计算结果s转换为字符串。
for (int i=0; i<s.length;i++){
result+=Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6);
}
System.out.println(result);
完整程序如下:
public static void main(String[] args) {
String x="pwd";
MessageDigest m=MessageDigest.getInstance("MD5");
m.update(x.getBytes("UTF8"));
byte s[ ]=m.digest( );
String result="";
for (int i=0; i<s.length;i++){
result+=Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6);
}
System.out.println(result);
}
普遍使用的三种加密方式
方式一:使用位运算符,将加密后的数据转换成16进制
方式二:使用格式化方式,将加密后的数据转换成16进制(推荐)
方式三:使用算法,将加密后的数据转换成16进制
/**
* @param source 需要加密的字符串
* @param hashType 加密类型 (MD5 和 SHA)
* @return
*/
public static String getHash(String source, String hashType) {
// 用来将字节转换成 16 进制表示的字符
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest md = MessageDigest.getInstance(hashType);
md.update(source.getBytes()); // 通过使用 update 方法处理数据,使指定的 byte数组更新摘要
byte[] encryptStr = md.digest(); // 获得密文完成哈希计算,产生128 位的长整数
char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符
int k = 0; // 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) { // 从第一个字节开始,对每一个字节,转换成 16 进制字符的转换
byte byte0 = encryptStr[i]; // 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换, >>> 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
return new String(str); // 换后的结果转换为字符串
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**@param source 需要加密的字符串
* @param hashType 加密类型 (MD5 和 SHA)
* @return
*/
public static String getHash2(String source, String hashType) {
StringBuilder sb = new StringBuilder();
MessageDigest md5;
try {
md5 = MessageDigest.getInstance(hashType);
md5.update(source.getBytes());
for (byte b : md5.digest()) {
sb.append(String.format("%02X", b)); // 10进制转16进制,X 表示以十六进制形式输出,02 表示不足两位前面补0输出
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**@param source 需要加密的字符串
* @param hashType 加密类型 (MD5 和 SHA)
* @return
*/
public static String getHash3(String source, String hashType) {
// 用来将字节转换成 16 进制表示的字符
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
StringBuilder sb = new StringBuilder();
MessageDigest md5;
try {
md5 = MessageDigest.getInstance(hashType);
md5.update(source.getBytes());
byte[] encryptStr = md5.digest();
for (int i = 0; i < encryptStr.length; i++) {
int iRet = encryptStr[i];
if (iRet < 0) {
iRet += 256;
}
int iD1 = iRet / 16;
int iD2 = iRet % 16;
sb.append(hexDigits[iD1] + "" + hexDigits[iD2]);
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
四、MD5与Base64结合使用
Base64: 把任意序列的8为字节描述为一种不易为人识别的形式, 通常用于邮件、http加密. 登陆的用户名和密码字段通过它加密, 可以进行加密和解密.
package com.fendo.MD5;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import sun.misc.BASE64Encoder;
/**
* @Title: Temps.java
* @Package com.fendo.MD5
* @Description: TODO
* @author fendo
* @date 2017年9月11日 上午10:27:12
* @version V1.0
*/
public class Temps {
/**
* 使用md5的算法进行加密
* @param plainText 加密明文
* @return 加密密文
*/
public static String getDigest(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("error happens", e);
}
return new BigInteger(1, secretBytes).toString(16);
}
/**
* 使用Base64进行编码
* @param encodeContent 需要编码的内容
* @return 编码后的内容
*/
public static String encode(String encodeContent) {
if (encodeContent == null) {
return null;
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(encodeContent.getBytes());
}
/**
* 先使用MD5算法加密, 再使用base64算法进行编码
* @param args
*/
public static void main(String[] args) {
String plainText = "pwd";
String encodedPassword = getDigest(encode(plainText));
System.out.println(encodedPassword);
}
}
为什么使用MD5加密后还要使用Base64编码呢?
用Base64算法编码后得到的是32位长度的字符串, 这样有利于在数据库中进行存储.
五、MD5Utils帮助类
package com.fendo.MD5;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @Title: MD5Utils.java
* @Package com.fendo.MD5
* @Description: TODO
* @author fendo
* @date 2017年9月11日 上午11:56:20
* @version V1.0
*/
public class MD5Utils {
/** 16进制的字符串数组 */
private final static String[] hexDigitsStrings = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
"e", "f" };
/** 16进制的字符集 */
private final static char [] hexDigitsChar = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* MD5加密字符串
*
* @param source 源字符串
*
* @return 加密后的字符串
*
*/
public static String getMD5(String source) {
String mdString = null;
if (source != null) {
try {
mdString = getMD5(source.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return mdString;
}
/**
* MD5加密以byte数组表示的字符串
*
* @param source 源字节数组
*
* @return 加密后的字符串
*/
public static String getMD5(byte[] source) {
String s = null;
final int temp = 0xf;
final int arraySize = 32;
final int strLen = 16;
final int offset = 4;
try {
java.security.MessageDigest md = java.security.MessageDigest
.getInstance("MD5");
md.update(source);
byte [] tmp = md.digest();
char [] str = new char[arraySize];
int k = 0;
for (int i = 0; i < strLen; i++) {
byte byte0 = tmp[i];
str[k++] = hexDigitsChar[byte0 >>> offset & temp];
str[k++] = hexDigitsChar[byte0 & temp];
}
s = new String(str);
} catch (Exception e) {
e.printStackTrace();
}
return s;
}
/**
* * 获取文件的MD5值
*
* @param file
* 目标文件
*
* @return MD5字符串
* @throws Exception
*/
public static String getFileMD5String(File file) throws Exception {
String ret = "";
FileInputStream in = null;
FileChannel ch = null;
try {
in = new FileInputStream(file);
ch = in.getChannel();
ByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,
file.length());
MessageDigest messageDigest=MessageDigest.getInstance("MD5");
messageDigest.update(byteBuffer);
ret = byteArrayToHexString(messageDigest.digest());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ch != null) {
try {
ch.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return ret;
}
/**
* * 获取文件的MD5值
*
* @param fileName
* 目标文件的完整名称
*
* @return MD5字符串
* @throws Exception
*/
public static String getFileMD5String(String fileName) throws Exception {
return getFileMD5String(new File(fileName));
}
/**
* 加密
*
* @param source
* 需要加密的原字符串
* @param encoding
* 指定编码类型
* @param uppercase
* 是否转为大写字符串
* @return
*/
public static String MD5Encode(String source, String encoding, boolean uppercase) {
String result = null;
try {
result = source;
// 获得MD5摘要对象
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
// 使用指定的字节数组更新摘要信息
messageDigest.update(result.getBytes(encoding));
// messageDigest.digest()获得16位长度
result = byteArrayToHexString(messageDigest.digest());
} catch (Exception e) {
e.printStackTrace();
}
return uppercase ? result.toUpperCase() : result;
}
/**
* 转换字节数组为16进制字符串
*
* @param bytes
* 字节数组
* @return
*/
private static String byteArrayToHexString(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder();
for (byte tem : bytes) {
stringBuilder.append(byteToHexString(tem));
}
return stringBuilder.toString();
}
/**
* * 将字节数组中指定区间的子数组转换成16进制字符串
*
* @param bytes
* 目标字节数组
*
* @param start
* 起始位置(包括该位置)
*
* @param end
* 结束位置(不包括该位置)
*
* @return 转换结果
*/
public static String bytesToHex(byte bytes[], int start, int end) {
StringBuilder sb = new StringBuilder();
for (int i = start; i < start + end; i++) {
sb.append(byteToHexString(bytes[i]));
}
return sb.toString();
}
/**
* 转换byte到16进制
*
* @param b
* 要转换的byte
* @return 16进制对应的字符
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigitsStrings[d1] + hexDigitsStrings[d2];
}
/**
* * 校验密码与其MD5是否一致
*
* @param pwd
* 密码字符串
*
* @param md5
* 基准MD5值
*
* @return 检验结果
*/
public static boolean checkPassword(String pwd, String md5) {
return getMD5(pwd).equalsIgnoreCase(md5);
}
/**
* * 校验密码与其MD5是否一致
*
* @param pwd
* 以字符数组表示的密码
*
* @param md5
* 基准MD5值
*
* @return 检验结果
*/
public static boolean checkPassword(char[] pwd, String md5) {
return checkPassword(new String(pwd), md5);
}
/**
* * 检验文件的MD5值
*
* @param file
* 目标文件
*
* @param md5
* 基准MD5值
*
* @return 检验结果
* @throws Exception
*/
public static boolean checkFileMD5(File file, String md5) throws Exception {
return getFileMD5String(file).equalsIgnoreCase(md5);
}
/**
* * 检验文件的MD5值
*
* @param fileName
* 目标文件的完整名称
*
* @param md5
* 基准MD5值
*
* @return 检验结果
* @throws Exception
*/
public static boolean checkFileMD5(String fileName, String md5) throws Exception {
return checkFileMD5(new File(fileName), md5);
}
}
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/156778.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...