2016-12-06 2 views
2

Ich habe versucht, das Java-Passwort authentifizieren zu python replizieren, aber der resultierende Hash ist anders.Java-Password-Hashing-Code in Python replizieren (PBKDF2WithHmacSHA1)

Passwort: abcd1234

Passwort-Token (Java): $ 31 $ 16 $ sWy1dDEx52vwQUCswXDYMQMzTJC39g1_nmrK384T4-w

generiertes Passwort-Token (Python): PBKDF2 16 $ $ $ c1d5MWRERXg1MnZ3UVVDcw $ qPQvE4QbrnYJTmRXk0M7wlfhH5U

Aus dem Java-Code ist die Iteration 16, SALT sollte die erste 16 char in sWy1dDEx52vwQUCswXDYMQMzTJC39g1_nmrK384T4-w, die sWy1dDEx52vwQU ist Cs und die Hash sollten

jedoch wXDYMQMzTJC39g1_nmrK384T4-w sein, um die Variablen zu Python gab mir ein anderes Hash-Ergebnis der Anwendung, die, qPQvE4QbrnYJTmRXk0M7wlfhH5U, die von Java-Hash unterscheidet.

Wo habe ich verpasst?

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 

Original-Link für Java-Code: How can I hash a password in Java?

Antwort

2

Es gibt ein paar Dinge anders zwischen dem, wie der Java-Code Dinge tut, und wie passlib der pbkdf2_sha1 hasher macht dinge.

  • Der Java-Hash-String enthält ein Protokoll Kostenparameter, die durch 1<<cost muss das Bestehen die Anzahl der Runden/Iterationen zu erhalten.

  • Das Salz + Bedürfnisse verdauen Base64 decodiert, um als dann nehmen die ersten 16 Bytes als das Salz (die ersten 21 Zeichen der Base64 1/3 Daten entspricht tatsächlich).

  • ähnlicher Weise, da die Digest-Bits in der Mitte eines Base64-Zeichen beginnen, wenn das Salz + verdauen decodiert wird, und verdauen wird dann separat codiert, würde die base64 String AzNMkLf2DX-easrfzhPj7A (noticably unterscheidet sich von der ursprünglichen codierten Zeichenfolge).

Darauf basierend, wandelt der folgende Code einen Java-Hash in das Format von pbkdf1_sha1.verify verwendet:

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' 

Die sich ergebende Zeichenfolge sollte für die Weitergabe in pbkdf2_sha1.verify("abcd1234", hash) geeignet sein; außer für ein Problem: Der Java-Code schneidet den sha1-Digest auf 16 Byte statt auf die vollen 20 Byte ab; und wie die Hash-Nummer des Passlibs codiert ist, muss der Digest die vollen 20 Bytes sein.

Wenn Sie den Java-Code ändern, um SIZE = 160 statt SIZE = 128 zu verwenden, sollte der Hash durch die obige Funktion adapt() in PassLib funktionieren.