我在大学的一个安全课上,我们现在正在研究哈希算法。我一直在寻找一个很好的解释HMAC如何从密钥和消息到哈希,但我找不到一个。维基页面和我去过的每个其他页面都非常模糊。他们说如果你有这个密钥和这个消息它变成了这个哈希,但没有解释它是如何到达那里的。我一直在https://en.wikipedia.org/wiki/Hash-based_message_authentication_code尝试使用java复制SHA-1哈希。
在Java中我正在使用:
byte[] bytes;
java.security.MessageDigest d = null;
d = java.security.MessageDigest.getInstance("SHA-1");
d.reset();
d.update(str.getBytes());
bytes = d.digest();
StringBuilder s = new StringBuilder();
for(byte b : bytes){
s.append(Util.binToHex(Util.unsign(b)));
}
return s.toString();
Java处理有符号的位,所以
Util.unsign将其从有符号转换为无符号
Util.binToHex将字节转换为十六进制
我已经验证了这些,他们工作得很好
据我所知,我这样做的方式我必须给函数一个byte [],我总是从字符串类中获取,所以每当我进行sha-1哈希我将十六进制代码转换为字符串然后得到字节数组
示例:0x65 0x73将变为“es”。
Key: "key"
Message: "The quick brown fox jumps over the lazy dog"
BlockSize: 64
IPad: "0x36" repeated 64 times (3636363636...)
OPad: "0x5C" repeated 64 times (5C5C5C5C5C...)
我知道我说得对。接下来的一切都是让我的。
首先我将Key转换为hex
KeyInHex = 0x6B 0x65 0x79
然后,因为KeyInHex比BlockSize字节短,所以我不必哈希它
我必须用0x00填充KeyInHex,直到它的大小为BlockSize
KeyInHex = 0x6B 0x65 0x79 0x00 0x00 ...
我用IPad键入KeyInHex
IKeyPad = 0x5D 0x53 0x4F 0x36 0x36 ...
接下来,我将消息附加到IKeyPad
BeforeFirstHash = 0x5D 0x53 0x4F 0x36 ... 0x36 + 0x54 0x68 0x65 ...
我将我的十六进制转换为字符串,然后获取字节数组并将其哈希
BeforeFirstHash = "]SO6...6The..."
FirstHash = 0x0E 0xFE 0x15 0xF9 0x16 0x1A 0xB7 ...
接下来我与OPad一起使用KeyInHex
OKeyPad = 0x37 0x39 0x25 0x5C 0x5C ...
将FirstHash添加到OKeyPad中
BeforeSecondHash = 0x37 0x39 0x25 0x5C 0x5C ... 0x5C + 0x0E 0xFE 0x15 ...
转换为String,获取字节数组和散列
BeforeSecondHash = "79%\\...\(Shift out)~(carriage return)..."
SecondHash = 0x5D 0x8C 0xF2 0x45 0x8A ...
正确的哈希是0xDE 0x7C 0x9B 0x85 0xB8 ......这不是我得到的。
有人可以帮帮我吗?
修改
main(){
int blockSize = 64;
String key = "key";
String message = "The quick brown fox jumps over the lazy dog";
StringBuilder ipad = new StringBuilder();
StringBuilder opad = new StringBuilder();
StringBuilder prime = new StringBuilder();
for(byte b : key.getBytes()){
prime.append(Util.binToHex(Util.unsign(b)));
}
while(prime.length() < blockSize * 2){
prime.append("00");
}
for(int i = 0; i < blockSize; ++i){
opad.append("5c");
ipad.append("36");
}
String iKeyPad = Util.xOr(prime.toString(), ipad.toString());
String oKeyPad = Util.xOr(prime.toString(), opad.toString());
String first = hash(Util.hexToString(iKeyPad) + message);
String second = hash(Util.hexToString(oKeyPad + first));
if(second.equals("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9")){
System.out.println("Success");
}
else{
System.out.println(second);
}
}
private String hash(String str){
try{
byte[] bytes;
java.security.MessageDigest d = null;
d = java.security.MessageDigest.getInstance("SHA-1");
d.reset();
d.update(str.getBytes());
bytes = d.digest();
StringBuilder s = new StringBuilder();
for(byte b : bytes){
s.append(Util.binToHex(Util.unsign(b)));
}
return s.toString();
}
catch(Exception e){
e.printStackTrace();
}
return null;
}
public static String xOr(String a, String b){
a = hexToBin(a);
b = hexToBin(b);
StringBuilder str = new StringBuilder();
for(int i = 0; i < a.length(); ++i){
if(a.charAt(i) != b.charAt(i)){
str.append("1");
}
else{
str.append("0");
}
}
return binToHex(str.toString());
}
public static String hexToString(String str){
StringBuilder string = new StringBuilder();
for(int i = 0; i < str.length(); i += 2){
int number = Integer.valueOf(str.substring(i, i + 2), 16);
string.append((char)number);
}
return string.toString();
}
public static String binToHex(String str){
StringBuilder string = new StringBuilder();
for(int i = 0; i < str.length(); i += 4){
String temp = str.substring(i, i + 4);
string.append(Integer.toString(Integer.valueOf(temp, 2), 16));
}
return string.toString();
}
public static String unsign(byte b){
int x = b;
if(x < 0){
x = x * -1;
String str = Integer.toString(x, 2);
while(str.length() != 8){
str = "0" + str;
}
str = inverse(str);
str = addOne(str);
return str;
}
else{
String str = Integer.toString(x, 2);
while(str.length() != 8){
str = "0" + str;
}
return str;
}
}
更新
感谢您的帮助,当我将其保留为byte []时,它可以工作。我仍然不完全确定原因,但我得到了正确答案。谢谢你的帮助!我真的很感激。