Java SHA256 生成与 Python 中不同的哈希值
Java SHA256 generates different hash as in Python
我正在尝试生成 OTP,但在我尝试将代码从 python 重写为 java 后,我得到了不同的输出。我不明白为什么,因为一些输出字符是相同的(当我更改 uname 或 ctr 时)。
PYTHON 代码:
from Crypto.Hash import SHA256
def get_otp(uname, ctr):
inp = uname+str(ctr)
binp = inp.encode('ascii')
hash=SHA256.new()
hash.update(binp)
dgst=bytearray(hash.digest())
out = ''
for x in range(9):
out += chr(ord('a')+int(dgst[x])%26)
if x % 3 == 2 and x != 8:
out += '-'
return out
print(get_otp('78951', 501585052583))
JAVA 代码:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Main
{
public static void main(String[] args) throws NoSuchAlgorithmException
{
System.out.println(get_otp("78951", "501585052583"));
}
public static String get_otp(String uname, String otp) throws NoSuchAlgorithmException
{
String input = uname + otp;
byte[] binInput = input.getBytes(StandardCharsets.US_ASCII);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(binInput);
String retVal = "";
for(int i = 0; i < 9; ++i)
{
retVal += ((char)(((int)'a') + Math.floorMod((int)hash[i], 26)));
if(i % 3 == 2 && i != 8)
retVal += '-';
}
return retVal;
}
}
感谢您的帮助。
Java 字节是有符号的,但是 Python 字节是无符号的,所以首先转换为有符号的二进制补码值应该可以解决问题:
b = dgst[x]
b = (b & 127) - (b & 128) # sign-extend
out += chr(ord('a')+(b%26))
in crypto bytes 通常是无符号的;因此,我建议 "fixing" 这种在 Java 土地上的不一致,将你的循环换成类似的东西:
for(int i = 0; i < 9; ++i) {
if(i > 0 && i % 3 == 0)
retVal += '-';
// bitwise AND with 0xff is to undo sign extension Java
// does by default
retVal += (char)('a' + (hash[i] & 0xff) % 26);
}
很多原来的括号和演员表都是多余的,所以我删除了它们。如果您的实现只会是 Java 和 Python,那么 "fix" 这个
在哪里并不重要
又一个加密点;如果你真的想要文本一次性密码,为什么不做类似的事情:
public static String get_otp2()
{
final SecureRandom rng = new SecureRandom();
String out = "";
for (int i = 0; i < 9; i++) {
if(i > 0 && i % 3 == 0)
out += '-';
out += (char)('a' + rng.nextInt(26));
}
return out;
}
并将其保存在某处?
我正在尝试生成 OTP,但在我尝试将代码从 python 重写为 java 后,我得到了不同的输出。我不明白为什么,因为一些输出字符是相同的(当我更改 uname 或 ctr 时)。
PYTHON 代码:
from Crypto.Hash import SHA256
def get_otp(uname, ctr):
inp = uname+str(ctr)
binp = inp.encode('ascii')
hash=SHA256.new()
hash.update(binp)
dgst=bytearray(hash.digest())
out = ''
for x in range(9):
out += chr(ord('a')+int(dgst[x])%26)
if x % 3 == 2 and x != 8:
out += '-'
return out
print(get_otp('78951', 501585052583))
JAVA 代码:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Main
{
public static void main(String[] args) throws NoSuchAlgorithmException
{
System.out.println(get_otp("78951", "501585052583"));
}
public static String get_otp(String uname, String otp) throws NoSuchAlgorithmException
{
String input = uname + otp;
byte[] binInput = input.getBytes(StandardCharsets.US_ASCII);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(binInput);
String retVal = "";
for(int i = 0; i < 9; ++i)
{
retVal += ((char)(((int)'a') + Math.floorMod((int)hash[i], 26)));
if(i % 3 == 2 && i != 8)
retVal += '-';
}
return retVal;
}
}
感谢您的帮助。
Java 字节是有符号的,但是 Python 字节是无符号的,所以首先转换为有符号的二进制补码值应该可以解决问题:
b = dgst[x]
b = (b & 127) - (b & 128) # sign-extend
out += chr(ord('a')+(b%26))
in crypto bytes 通常是无符号的;因此,我建议 "fixing" 这种在 Java 土地上的不一致,将你的循环换成类似的东西:
for(int i = 0; i < 9; ++i) {
if(i > 0 && i % 3 == 0)
retVal += '-';
// bitwise AND with 0xff is to undo sign extension Java
// does by default
retVal += (char)('a' + (hash[i] & 0xff) % 26);
}
很多原来的括号和演员表都是多余的,所以我删除了它们。如果您的实现只会是 Java 和 Python,那么 "fix" 这个
在哪里并不重要又一个加密点;如果你真的想要文本一次性密码,为什么不做类似的事情:
public static String get_otp2()
{
final SecureRandom rng = new SecureRandom();
String out = "";
for (int i = 0; i < 9; i++) {
if(i > 0 && i % 3 == 0)
out += '-';
out += (char)('a' + rng.nextInt(26));
}
return out;
}
并将其保存在某处?