2015-02-24 12 views
7

MODIFICA: Per discussione nei commenti, vorrei chiarire che ciò accadrà lato server, dietro SSL. Non ho intenzione di esporre la password hash o lo schema di hashing al client.JavaScript: Come generare Rfc2898DeriveBytes come C#?

Supponiamo di disporre di un database di identità asp.net esistente con le tabelle predefinite (aspnet_Users, aspnet_Roles, ecc.). In base alla mia comprensione, l'algoritmo di hashing della password utilizza sha256 e memorizza il salt + (hashed password) come una stringa codificata in base64. MODIFICA: questa ipotesi non è corretta, vedere la risposta di seguito.

vorrei replicare la funzione della classe Microsoft.AspNet.Identity.Crypto VerifyHashedPassword funzione con una versione di JavaScript.

Diciamo che una password è welcome1 e la sua asp.net hash password è ADOEtXqGCnWCuuc5UOAVIvMVJWjANOA/LoVy0E4XCyUHIfJ7dfSY0Id + uJ20DTtG + A ==

Finora sono stato in grado di riprodurre le parti del metodo che ottenere il sale e la chiave secondaria memorizzata.

Qualora l'attuazione C# fa più o meno questo:

var salt = new byte[SaltSize]; 
Buffer.BlockCopy(hashedPasswordBytes, 1, salt, 0, SaltSize); 
var storedSubkey = new byte[PBKDF2SubkeyLength]; 
Buffer.BlockCopy(hashedPasswordBytes, 1 + SaltSize, storedSubkey, 0, PBKDF2SubkeyLength); 

Ho il seguente in JavaScript (non elegante, con uno sforzo):

var hashedPwd = "ADOEtXqGCnWCuuc5UOAVIvMVJWjANOA/LoVy0E4XCyUHIfJ7dfSY0Id+uJ20DTtG+A=="; 
var hashedPasswordBytes = new Buffer(hashedPwd, 'base64'); 
var saltbytes = []; 
var storedSubKeyBytes = []; 

for(var i=1;i<hashedPasswordBytes.length;i++) 
{ 
    if(i > 0 && i <= 16) 
    { 
    saltbytes.push(hashedPasswordBytes[i]); 
    } 
    if(i > 0 && i >16) { 
    storedSubKeyBytes.push(hashedPasswordBytes[i]); 
    } 
} 

Anche in questo caso, non è abbastanza, ma dopo aver eseguito questo snippet, saltbytes e storedSubKeyBytes corrispondono a byte per byte ciò che vedo nel debugger C# per salt e storedSubkey.

Infine, in C#, un'istanza di Rfc2898DeriveBytes viene utilizzato per generare una nuova sottochiave basato sul sale e la password fornita, in questo modo:

byte[] generatedSubkey; 
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, PBKDF2IterCount)) 
{ 
    generatedSubkey = deriveBytes.GetBytes(PBKDF2SubkeyLength); 
} 

Questo è dove mi sono bloccato. Ho provato soluzioni di altri come this one, ho usato rispettivamente CryptoJS di Google e Node e le librerie di crittografia, e il mio output non genera mai nulla che assomigli alla versione di C#.

(Esempio:

var output = crypto.pbkdf2Sync(new Buffer('welcome1', 'utf16le'), 
    new Buffer(parsedSaltString), 1000, 32, 'sha256'); 
console.log(output.toString('base64')) 

genera "LSJvaDM9u7pXRfIS7QDFnmBPvsaN2z7FMXURGHIuqdY =")

Molti degli indicatori che ho trovato on-line indicano problemi che coinvolgono l'inadeguatezza codificanti (NodeJS/UTF-8 vs .NET/UTF -16LE), quindi ho provato a codificare utilizzando il formato di codifica .NET predefinito, ma senza alcun risultato.

Oppure potrei sbagliarmi completamente su cosa presumo queste librerie stiano facendo. Ma ogni suggerimento nella giusta direzione sarebbe molto apprezzato.

+0

Si sta tentando di generare un hash della password sul client e passare l'hash al server per la convalida? – trailmax

+0

No, sto provando a generare il lato server hash in node.js. Essenzialmente, mantenendo lo stesso database, ma sostituendo il livello IIS/asp.net per il nodo. Non sono un esperto di sicurezza, ma sarei cauto nel tentare di fare qualsiasi operazione client con le password. – GojiraDeMonstah

+0

Ah, che chiarisce le mie preoccupazioni. Lo menzionerei nella tua domanda. Spiacenti, non posso davvero aiutare con il lato JS delle cose qui ( – trailmax

risposta

9

Ok, penso che questo problema sia stato un po 'più semplice di quello che stavo facendo (non lo sono sempre). Dopo aver eseguito un'operazione RTFM su pbkdf2 spec, ho eseguito alcuni test side-by-side con Crypto dei nodi e crittografia .NET e ho compiuto progressi abbastanza buoni su una soluzione.

Il seguente codice JavaScript analizza correttamente il sale e la sottochiave memorizzati, quindi verifica la password specificata eseguendo l'hash con il sale memorizzato. Ci sono senza dubbio miglioramenti migliori/più puliti/più sicuri, quindi i commenti sono benvenuti.

// NodeJS implementation of crypto, I'm sure google's 
// cryptoJS would work equally well. 
var crypto = require('crypto'); 

// The value stored in [dbo].[AspNetUsers].[PasswordHash] 
var hashedPwd = "ADOEtXqGCnWCuuc5UOAVIvMVJWjANOA/LoVy0E4XCyUHIfJ7dfSY0Id+uJ20DTtG+A=="; 
var hashedPasswordBytes = new Buffer(hashedPwd, 'base64'); 

var hexChar = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]; 

var saltString = ""; 
var storedSubKeyString = ""; 

// build strings of octets for the salt and the stored key 
for (var i = 1; i < hashedPasswordBytes.length; i++) { 
    if (i > 0 && i <= 16) { 
     saltString += hexChar[(hashedPasswordBytes[i] >> 4) & 0x0f] + hexChar[hashedPasswordBytes[i] & 0x0f] 
    } 
    if (i > 0 && i > 16) { 
     storedSubKeyString += hexChar[(hashedPasswordBytes[i] >> 4) & 0x0f] + hexChar[hashedPasswordBytes[i] & 0x0f]; 
    } 
} 

// password provided by the user 
var password = 'welcome1'; 

// TODO remove debug - logging passwords in prod is considered 
// tasteless for some odd reason 
console.log('cleartext: ' + password); 
console.log('saltString: ' + saltString); 
console.log('storedSubKeyString: ' + storedSubKeyString); 

// This is where the magic happens. 
// If you are doing your own hashing, you can (and maybe should) 
// perform more iterations of applying the salt and perhaps 
// use a stronger hash than sha1, but if you want it to work 
// with the [as of 2015] Microsoft Identity framework, keep 
// these settings. 
var nodeCrypto = crypto.pbkdf2Sync(new Buffer(password), new Buffer(saltString, 'hex'), 1000, 256, 'sha1'); 

// get a hex string of the derived bytes 
var derivedKeyOctets = nodeCrypto.toString('hex').toUpperCase(); 

console.log("hex of derived key octets: " + derivedKeyOctets); 

// The first 64 bytes of the derived key should 
// match the stored sub key 
if (derivedKeyOctets.indexOf(storedSubKeyString) === 0) { 
    console.info("passwords match!"); 
} else { 
    console.warn("passwords DO NOT match!"); 
} 
+2

Lei mi ha appena salvato la vita Grazie mille Sto migrando da ASP.NET a node.js e ora non devo dire ai miei utenti che la loro password è scaduta! D – javorosas

+0

Se me lo permetti, pubblicherò alcune parole chiave in modo che qualcuno come me possa trovarlo più facile in futuro: SimpleMembershipAlgoritmo hash del preliminare ASP.NET MVC hashing della password confrontare – javorosas

+0

Certo cosa ... cosa devo fare per aiutare a postare le parole chiave? – GojiraDeMonstah

Problemi correlati