Assuming you use the PasswordHasher
. NET side pattern, as per that SOEN post the whole magic of the algorithm happens in the class Rfc2898DeriveBytes
(algorithm implementation PBKDF2
using Hmac
based on SHA1
).
The result of the hash stored in the database is in Base 64, divided as follows:
- 16 bytes: Random-generated Salt
- 32 Bytes: The password hash itself.
That article gives an idea of how to derive the key from Java:
SecretKeyFactory factory =
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passwordChars,
saltBytes, iterationCount, 256 + 128);
SecretKey secretKey = factory.generateSecret(spec);
byte[] data = secretKey.getEncoded();
byte[] keyBytes = new byte[256 / 8]; // hash de 32 bytes
byte[] ivBytes = new byte[128 / 8]; // salt de 16 bytes
System.arraycopy(data, 0, keyBytes, 0, 256 / 8);
System.arraycopy(data, 256 / 8, ivBytes, 0, 128 / 8);
SecretKey secretKey = factory.generateSecret(spec);
byte[] key = secretKey.getEncoded();
Alternatively you can search for a class implementation Rfc2898DeriveBytes
(found this one).
Finally once you have an implementation of the above algorithm, simply port the password verification solution from response from the SOEN (class Crypto of Assembly Identity) which consists in breaking the hash persisted in the bank at salt + key, compute password key using the salt persisted and check if the computed key is equal to the persisted key. Follow the steps (I could not test because I am without a compiler).
public boolean verifyPassword(String hashedPassword, String plainPassword) {
// Se o hash é vazio não autentica
if (hashedPassword == null) {
return false;
}
if (plainPassword== null) {
throw new IllegalArgumentException("Password should not be null");
}
// Extrai os bytes do hash persistido no banco
byte[] src = Base64.getDecoder().decode(hashedPassword);
// Array de 49 bytes, o primeiro byte é vazio
if (src.length != 0x31 || array[0] != 0) {
return false;
}
// Extrai o salt
byte[] salt = new byte[0x10];
System.arraycopy(src, 1, salt, 0, 0x10);
// Extrai a chave
byte[] persistedKey = new byte[0x20];
System.arraycopy(src, 0x11, persistedKey , 0, 0x20);
// Computa 1000 iteracoes em cima da senha limpa com o salt do banco
Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(
plainPassword, salt, 0x3e8);
byte[] computedKey = rfc2898DeriveBytes.getBytes(0x20);
// Se a chave computada for igual a persistida o usuário entrou a senha correta
return Arrays.equals(persistedKey, computedKey);
}
What code is recording this in C# (share it with us)? What kind of hash function is this (MD5, SHA, bcrypt, etc)? What are the characteristics of this algorithm (size, salt, etc)?
– Anthony Accioly
The code that generates this hash is inside the . netFramework 4.5.1 in the Usermanager and Passwordhasher class of the Microsoft.AspNet.Identity namespace the encryption algorithm is encapsulated in these classes
– Leonardo Rivera