2016-12-06 170 views
2

我一直在尝试将java密码验证复制到python,但是结果哈希是不同的。在Python中复制Java密码哈希代码(PBKDF2WithHmacSHA1)

密码:ABCD1234

密码令牌(JAVA):$ $ 31 $ 16 sWy1dDEx52vwQUCswXDYMQMzTJC39g1_nmrK384T4-W

生成的密码令牌(蟒):$ PBKDF2 $ $ 16 c1d5MWRERXg1MnZ3UVVDcw $ qPQvE4QbrnYJTmRXk0M7wlfhH5U

从Java代码中,Iteration是16,SALT应该是sWy1dDEx52vwQUCswXDYMQMzTJC39g1_nmrK384T4-w中的前16个字符,即sWy1dDEx52vwQU Cs和哈希应该是wXDYMQMzTJC39g1_nmrK384T4-W

然而,运用变量蟒蛇给了我不同的哈希结果是,qPQvE4QbrnYJTmRXk0M7wlfhH5U这是从Java的哈希值不同。

我错过了哪里?

的Java:

private static final String ALGORITHM = "PBKDF2WithHmacSHA1"; 
private static final int SIZE = 128; 
private static final Pattern layout = Pattern.compile("\\$31\\$(\\d\\d?)\\$(.{43})"); 

public boolean authenticate(char[] password, String token) 
    { 
     Matcher m = layout.matcher(token); 
     if (!m.matches()) 
      throw new IllegalArgumentException("Invalid token format"); 
     int iterations = iterations(Integer.parseInt(m.group(1))); 
     byte[] hash = Base64.getUrlDecoder().decode(m.group(2)); 
     byte[] salt = Arrays.copyOfRange(hash, 0, SIZE/8); 
     byte[] check = pbkdf2(password, salt, iterations); 
     int zero = 0; 
     for (int idx = 0; idx < check.length; ++idx) 
      zero |= hash[salt.length + idx]^check[idx]; 
     return zero == 0; 
    } 

的Python:

from passlib.hash import pbkdf2_sha1 

def hasher(password): 
    size = 128 

    key0 = "abcd1234" 
    iter = int(password.split("$")[2]) 
    salt0 = password.split("$")[3][0: 16] 

    hash = pbkdf2_sha1.using(rounds=iter, salt = salt0.encode()).hash(key0) 
    print(hash.split('$')[4]) 

    return hash 

原文链接Java代码:How can I hash a password in Java?

回答

2

有一堆东西之间的不同之处在于Java代码如何做的事情,以及如何passlib的pbkdf2_sha1 hasher做的事情。

  • 的Java的哈希字符串包含日志成本参数,这就需要通过1<<cost获得发/迭代次数。

  • 盐+消化需要被解码的base64,然后采取的第一个16个字节作为盐(其实际上对应于BASE64数据的前21个1/3字符)。

  • 类似地,由于消化的位在一个base64字符的中间开始,当盐+消化进行解码,并消化,然后分别进行编码,以base64字符串将是 AzNMkLf2DX-easrfzhPj7A(noticably从原来的编码的字符串不同)。

在此基础上,下面的代码位的一个java散列转换成由pbkdf1_sha1.verify使用的格式:

from passlib.utils.binary import b64s_decode, ab64_encode 

def adapt_java_hash(jhash): 
    _, ident, cost, data = jhash.split("$") 
    assert ident == "31" 
    data = b64s_decode(data.replace("_", ".").replace("-", "+")) 
    return "$pbkdf2$%d$%s$%s" % (1<<int(cost), ab64_encode(data[:16]), 
           ab64_encode(data[16:])) 

>>> adapt_java_hash("$31$16$sWy1dDEx52vwQUCswXDYMQMzTJC39g1_nmrK384T4-w") 
'$pbkdf2$65536$sWy1dDEx52vwQUCswXDYMQ$AzNMkLf2DX.easrfzhPj7A' 

结果字符串应适于穿入pbkdf2_sha1.verify("abcd1234", hash); 一个问题: java代码将sha1摘要截断为16个字节,而不是完整的20个字节;并且passlib的hasher编码方式,摘要必须是全部20个字节。

如果您更改java代码以使用SIZE = 160而不是SIZE = 128,那么通过上述adapt()函数运行散列应在passlib中工作。