Java SHA256生成与Python中不同的哈希

时间:2018-09-27 20:20:52

标签: java python hash cryptography sha256

我试图生成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;
    }
}

谢谢您的帮助。

2 个答案:

答案 0 :(得分:2)

Java字节是带符号的,而Python字节是无符号的,因此首先转换为带符号的二进制补码应该可以解决问题:

        b = dgst[x]
        b = (b & 127) - (b & 128) # sign-extend                                 
        out += chr(ord('a')+(b%26))  

答案 1 :(得分:2)

密码字节中的

通常是无符号的;因此,我建议“修复” 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,则在哪里“修复”此问题都没关系

另一个加密点;如果您真的想使用一次文本密码,为什么不这样做:

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

并将其保存在某处?