标签: 算法
前言:加密算法分对称加密与非对称加密,在常见对称加密算法中,信息摘要算法MD5是被广泛应用的一种,本文深入分析MD5算法的实现及其底层实现
1.什么是MD5算法?
MD5信息摘要算法,是将对输入的任意长度的信息进行计算,产生一个128位长度的“指纹”或“信息摘要”。MD5算法适合用在数据签名应用中,在此应用中,一个大的文件必须在类似RSA算法的公用密钥系统中用私人密钥加密前被“压缩”在一种安全模式下。
2.MD5算法的特性
* 长度固定:任意长的字符串加密后长度相同,方便平时信息的统计和管理
* 易计算:字符串和文件加密过程较简单,开发者很容易理解和做出加密工具
* 细微性:字符串里任意一个字符改变都会引起MD5值改变
* 不可逆性:提高数据的安全性
3.暴力破解方法:破解概率较低,步骤如下:
- 第一步:建立一个大型的数据库,将日常的各个语句,通过MD5加密成密文,不断地积累大量的语句,组成一个庞大的数据库
- 第二步:将截取到的密文,进行数据库匹配,撞库成功即破解
4.如何实现一个MD5加密算法?
//一种Java实现,详细介绍见注释
public static String MD5(String key) {
//十六进制转换表,0xf
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
try {
byte[] btInput = key.getBytes();
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获取摘要密文
byte[] md = mdInst.digest();
//md是字节类型的数组,而MD5是一串十六进制的字符
// 因此要把密文转换成十六进制的字符串形式,长度为32个字符
int j = md.length;
//开辟一个缓冲区,用于存转换结果
char str[] = new char[j * 2];
int k = 0;
//加噪声
for (int i = 0; i < j; i++) {
//逐个取摘要密文中的字符
byte byte0 = md[i];
//无符号右移4位,与0xf([1111])取与,获得高四位
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
//获取低四位,这也是srt[]数组长度是2*j的原因
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
logger.error("生成MD5失败", e);
return null;
}
}
5.深入MD5底层实现:
- 信息摘要类的实现:Java对其封装在java.security.MessageDigest包下,部分源码,如下:
public abstract class MessageDigest extends MessageDigestSpi {
private static final Debug pdebug =
Debug.getInstance("provider", "Provider");
private static final boolean skipDebug =
Debug.isOn("engine=") && !Debug.isOn("messagedigest");
private String algorithm;
// The state of this digest
private static final int INITIAL = 0;
private static final int IN_PROGRESS = 1;
private int state = INITIAL;
// The provider
private Provider provider;
...
//主要方法,该类对以下方法作了许多重载,仅列出主要方法名
//获取单例信息摘要对象
public static MessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException{...}
//更新摘要方法
public void update(byte input) {...}
//摘要方法
public byte[] digest() {...}
//更新摘要方法引擎
protected void engineUpdate(ByteBuffer input) {...}
}
- 获取单例信息摘要对象的方法,其源码如下:
public static MessageDigest getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException
{
if (provider == null || provider.length() == 0)
throw new IllegalArgumentException("missing provider");
Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
if (objs[0] instanceof MessageDigest) {
MessageDigest md = (MessageDigest)objs[0];
md.provider = (Provider)objs[1];
return md;
} else {
MessageDigest delegate =
new Delegate((MessageDigestSpi)objs[0], algorithm);
delegate.provider = (Provider)objs[1];
return delegate;
}
}
可以看到,该方法返回一个MessageDigest实例
- 更新摘要方法,其源码如下:
/**
* Updates the digest using the specified array of bytes.
*
* @param input the array of bytes.
*/
public void update(byte[] input) {
engineUpdate(input, 0, input.length);
state = IN_PROGRESS;
}
内部通过调用MessageDigestSpi类的engineUpdate方法实现,其源码如下:
/**
* Update the digest using the specified ByteBuffer. The digest is
* updated using the {@code input.remaining()} bytes starting
* at {@code input.position()}.
* Upon return, the buffer's position will be equal to its limit;
* its limit will not have changed.
*
* @param input the ByteBuffer
* @since 1.5
*/
protected void engineUpdate(ByteBuffer input) {
if (input.hasRemaining() == false) {
return;
}
if (input.hasArray()) {
byte[] b = input.array();
int ofs = input.arrayOffset();
int pos = input.position();
int lim = input.limit();
engineUpdate(b, ofs + pos, lim - pos);
input.position(lim);
} else {
int len = input.remaining();
int n = JCAUtil.getTempArraySize(len);
if ((tempArray == null) || (n > tempArray.length)) {
tempArray = new byte[n];
}
while (len > 0) {
int chunk = Math.min(len, tempArray.length);
input.get(tempArray, 0, chunk);
engineUpdate(tempArray, 0, chunk);
len -= chunk;
}
}
}
内部通过io操作来完成执行操作(这部分较为复杂,待深究)
- 摘要方法,放回摘要密文,源码如下:
/**
* Completes the hash computation by performing final operations
* such as padding. The digest is reset after this call is made.
*
* @return the array of bytes for the resulting hash value.
*/
public byte[] digest() {
/* Resetting is the responsibility of implementors. */
byte[] result = engineDigest();
state = INITIAL;
return result;
}
内部通过调用MessageDigestSpi类的engineDigest方法实现,跟踪engineDigest方法源码,
protected int engineDigest(byte[] buf, int offset, int len)
throws DigestException {
byte[] digest = engineDigest();
if (len < digest.length) throw new DigestException("partial digests not returned");
if (buf.length - offset < digest.length)
throw new DigestException("insufficient space in the output "
+ "buffer to store the digest");
System.arraycopy(digest, 0, buf, offset, digest.length);
return digest.length;
}
至此,MD算法主要源码展示完毕。
6.总结:
-
Java实现MD5算法过程:输入字符串并转换字节数组--->获取java.security.MessageDigest类实例mdInst--->调用MD实例mdInst.update()方法,将字符串数组更新到MD内部进行摘要--->调用MD实例mdInst.digest方法获取摘要字节数组--->将摘要字节数组转换成十六进制字符(32个)--->返回加密字符串结果
-
MessageDigest底层实现:
-
主要类:MessageDigest类与MessageDigestSpi类
-
主要方法:
-
getInstance()获取摘要对象实例
-
update()更新摘要字节数组,进行取摘要
-
digest()获取摘要字节数组
-
engineUpdate()执行摘要更新
-
engineDigest()执行摘要操作
-
MD5算法应用场景:实现安全登录、文件加密等,实际使用中,单单使用MD5加密还是不够的,一般会进行加盐值,通过UUID生成系统唯一标识符(其中一种方法),将标识符与待加密字符串进行拼接后,再执行MD5加密,从而进一步提升加密效果。
希望本文能对你有所帮助,欢迎回来~
网友评论