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;
}

并将其保存在某处?