实验三
HMAC消息认证(基于MD5)
(1)在了解HMAC、MD5的基础上编写HMAC-MD5源码
(2)构造长度任意的字符串以及任意长度的密钥字符串,以HMAC算法对文件计算Hash值
三、算法描述
使用 MD5 哈希函数计算基于哈希值的消息验证代码 (HMAC)。MD5(消息摘要算法)是 RSA Laboratories 开发的加密哈希算法。HMACMD5 接受任何大小的密钥,并生成长度为 128 位的哈希序列。
图1:HMAC-MD5流程图
(1) 在密钥key后面添加0来创建一个长为B(64字节)的字符串(str)。
(2) 将上一步生成的字符串(str)与ipad(0x36)做异或运算,形成结果字符串(istr)。
(3) 将数据流data附加到第二步的结果字符串(istr)的末尾。
(4) 做md5运算于第三步生成的数据流(istr)。
(5) 将第一步生成的字符串(str)与opad(0x5c)做异或运算,形成结果字符串(ostr)。
(6) 再将第四步的结果(istr)附加到第五步的结果字符串(ostr)的末尾。
(7) 做md5运算于第六步生成的数据流(data),输出最终结果(out)。
注:如果第一步中,key的长度klen大于64字节,则先进行md5运算,使其长度klen=16字节。
四、算法设计
整体采用byte[]数组存储数据
首先对密钥进行操作:
如果密钥长度,大于64字节,就使用哈希算法,计算其摘要,作为真正的密钥赋值给actualKey, 此时actualKey存放的就是所需密钥
if (key.length > length) actualKey = md5(key);
如果密钥长度不足64字节,就使用0x00左补齐到64字节。
if (actualKey.length < length) {
for (int i = actualKey.length; i < keyArr.length; i++)
keyArr[i] = 0x00;
}
之后就是对密钥之类的进行的图示中各种操作:
使用密钥和ipad[]进行异或运算;
将待加密数据追加到K XOR ipad[]计算结果后面;
使用哈希算法计算H(K XOR ipad, text);
使用密钥和opad进行异或运算;
S0 + H(Si||M) = secondAppendResult;
H(secondAppendResult)计算SecondAppendResult的哈希值;
界面部分: 采用FlowLayout布局,用标签label1接受输入的需要加密的字符串message,用标签label2接受输入的密钥secret字符串,用button设置相应的ActionListener,关联HMAC操作函数getHmacMd5Bytes(),按下按钮则执行。HMAC完成后的字符串out送到标签outtext显示
五、运行结果
与网站在线HMAC结果对比得知结果正确
附录:关键代码(可选)
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Hmacmd5 {
//md5
private static byte[] md5(byte[] str) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str);
return md.digest();
}
//数据data 密钥key
public static byte[] getHmacMd5Bytes(byte[] key, byte[] data) throws NoSuchAlgorithmException {
int length = 64;
byte[] ipad = new byte[length];
byte[] opad = new byte[length];
for (int i = 0; i < 64; i++) {
ipad[i] = 0x36;
opad[i] = 0x5C;
}
byte[] actualKey = key;
byte[] keyArr = new byte[length]; // 64 bytes
//如果密钥长度,大于64字节,就使用哈希算法,计算其摘要,作为真正的密钥赋值给actualKey
if (key.length > length) {
actualKey = md5(key);
}//此时actualKey存放的就是所需密钥
for (int i = 0; i < actualKey.length; i++) {
keyArr[i] = actualKey[i];
}
// 如果密钥长度不足64字节,就使用0x00左补齐到64字节。
if (actualKey.length < length) {
for (int i = actualKey.length; i < keyArr.length; i++)
keyArr[i] = 0x00;
}
// 使用密钥和ipad[]进行异或运算。
byte[] kIpadXorResult = new byte[length];
for (int i = 0; i < length; i++) {
kIpadXorResult[i] = (byte) (keyArr[i] ^ ipad[i]);
}
//将待加密数据追加到K XOR ipad[]计算结果后面。
byte[] firstAppendResult = new byte[kIpadXorResult.length + data.length];
for (int i = 0; i < kIpadXorResult.length; i++) {
firstAppendResult[i] = kIpadXorResult[i];
}
for (int i = 0; i < data.length; i++) {
firstAppendResult[i + keyArr.length] = data[i];
}
//使用哈希算法计算H(K XOR ipad, text)
byte[] firstHashResult = md5(firstAppendResult);
//使用密钥和opad进行异或运算。
byte[] kOpadXorResult = new byte[length];
for (int i = 0; i < length; i++) {
kOpadXorResult[i] = (byte) (keyArr[i] ^ opad[i]);
}
//S0 + H(Si||M) = secondAppendResult
byte[] secondAppendResult = new byte[kOpadXorResult.length + firstHashResult.length];
for (int i = 0; i < kOpadXorResult.length; i++) {
secondAppendResult[i] = kOpadXorResult[i];
}
for (int i = 0; i < firstHashResult.length; i++) {
secondAppendResult[i + keyArr.length] = firstHashResult[i];
}
//H(secondAppendResult)
byte[] hmacMd5Bytes = md5(secondAppendResult);
return hmacMd5Bytes;
}
//byte[] -> String
public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 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 Exception {
JFrame f = new JFrame("HMAC-Md5");
f.setSize(400, 180);
f.setLocation(200, 200);
f.setLayout(new FlowLayout());
JLabel label1 = new JLabel("message:");
// 输入框
JTextField text1 = new JTextField("");
text1.setText("");
text1.setPreferredSize(new Dimension(100, 30));
JLabel label2 = new JLabel("secret:");
// 输入框
JTextField text2 = new JTextField("");
text2.setText("");
text2.setPreferredSize(new Dimension(100, 30));
JLabel outlabel = new JLabel("after:");
// 输入框
JTextField outtext = new JTextField("");
outtext.setText("");
outtext.setPreferredSize(new Dimension(250, 30));
JButton button = new JButton("加密");
//相应的函数
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//点击按钮后执行的代码
String src = text1.getText();
String key = text2.getText();
try {
byte[] macmd5 = Hmacmd5.getHmacMd5Bytes(key.getBytes(), src.getBytes());
String out = Hmacmd5.bytesToHexString(macmd5);
outtext.setText(out);
} catch (NoSuchAlgorithmException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
f.add(label1); f.add(text1);
f.add(label2); f.add(text2);
f.add(button);
f.add(outlabel); f.add(outtext);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
text1.grabFocus();
}
}