Reproducing JS PBKDF2 Hash in C#











up vote
2
down vote

favorite












I had to implement some new security features for an Existing Database. They used to hash passwords on the side of the client with a salt from the DB. They used this code to hash the passwords before sending them to the server:



var hash = CryptoJS.PBKDF2(password, USER_SALT, {
keySize: 4,
iterations: 1000
});


Now as you can already guess, this is highly insecure, cause an attacker gets the PW as if it was sent in plain text if the user gets the Salt from the server and the hasing is done client side. That's why I need to do it server side.



Now the DB has several thousand entries with hashed passwords of users, that use this DB. I tried implementing a similar method in C# with many references from the internet, but I couldn't get the same Hashes as stored in the DB.



    public static string HashPassword(string password, string salt)
{
byte saltBytes = Encoding.UTF8.GetBytes(salt);
using (var rfc2898 = new Rfc2898DeriveBytes(password, saltBytes, 1000))
{
byte hash = rfc2898.GetBytes(16);
string hashString = string.Empty;
foreach (byte x in hash)
{
hashString += String.Format("{0:x2}", x);
}
return hashString;
}
}


This was the method I used. I wanted to reproduce the keysize 4 of CryptoJS so I'd get 4x = 32 digit long password hashes. I do get them, but they're different than the ones I would get in JS with CryptoJS.



Does anyone know how to get the same result as the CryptoJS version would? BEcause all passwords in the DB have been stored that way, it is crucial they are now generated the same way again only server side. (The Passwords are now encrypted against attacks).










share|improve this question






















  • stackoverflow.com/questions/11412882/…
    – Dmitry Bychenko
    Nov 8 at 14:42















up vote
2
down vote

favorite












I had to implement some new security features for an Existing Database. They used to hash passwords on the side of the client with a salt from the DB. They used this code to hash the passwords before sending them to the server:



var hash = CryptoJS.PBKDF2(password, USER_SALT, {
keySize: 4,
iterations: 1000
});


Now as you can already guess, this is highly insecure, cause an attacker gets the PW as if it was sent in plain text if the user gets the Salt from the server and the hasing is done client side. That's why I need to do it server side.



Now the DB has several thousand entries with hashed passwords of users, that use this DB. I tried implementing a similar method in C# with many references from the internet, but I couldn't get the same Hashes as stored in the DB.



    public static string HashPassword(string password, string salt)
{
byte saltBytes = Encoding.UTF8.GetBytes(salt);
using (var rfc2898 = new Rfc2898DeriveBytes(password, saltBytes, 1000))
{
byte hash = rfc2898.GetBytes(16);
string hashString = string.Empty;
foreach (byte x in hash)
{
hashString += String.Format("{0:x2}", x);
}
return hashString;
}
}


This was the method I used. I wanted to reproduce the keysize 4 of CryptoJS so I'd get 4x = 32 digit long password hashes. I do get them, but they're different than the ones I would get in JS with CryptoJS.



Does anyone know how to get the same result as the CryptoJS version would? BEcause all passwords in the DB have been stored that way, it is crucial they are now generated the same way again only server side. (The Passwords are now encrypted against attacks).










share|improve this question






















  • stackoverflow.com/questions/11412882/…
    – Dmitry Bychenko
    Nov 8 at 14:42













up vote
2
down vote

favorite









up vote
2
down vote

favorite











I had to implement some new security features for an Existing Database. They used to hash passwords on the side of the client with a salt from the DB. They used this code to hash the passwords before sending them to the server:



var hash = CryptoJS.PBKDF2(password, USER_SALT, {
keySize: 4,
iterations: 1000
});


Now as you can already guess, this is highly insecure, cause an attacker gets the PW as if it was sent in plain text if the user gets the Salt from the server and the hasing is done client side. That's why I need to do it server side.



Now the DB has several thousand entries with hashed passwords of users, that use this DB. I tried implementing a similar method in C# with many references from the internet, but I couldn't get the same Hashes as stored in the DB.



    public static string HashPassword(string password, string salt)
{
byte saltBytes = Encoding.UTF8.GetBytes(salt);
using (var rfc2898 = new Rfc2898DeriveBytes(password, saltBytes, 1000))
{
byte hash = rfc2898.GetBytes(16);
string hashString = string.Empty;
foreach (byte x in hash)
{
hashString += String.Format("{0:x2}", x);
}
return hashString;
}
}


This was the method I used. I wanted to reproduce the keysize 4 of CryptoJS so I'd get 4x = 32 digit long password hashes. I do get them, but they're different than the ones I would get in JS with CryptoJS.



Does anyone know how to get the same result as the CryptoJS version would? BEcause all passwords in the DB have been stored that way, it is crucial they are now generated the same way again only server side. (The Passwords are now encrypted against attacks).










share|improve this question













I had to implement some new security features for an Existing Database. They used to hash passwords on the side of the client with a salt from the DB. They used this code to hash the passwords before sending them to the server:



var hash = CryptoJS.PBKDF2(password, USER_SALT, {
keySize: 4,
iterations: 1000
});


Now as you can already guess, this is highly insecure, cause an attacker gets the PW as if it was sent in plain text if the user gets the Salt from the server and the hasing is done client side. That's why I need to do it server side.



Now the DB has several thousand entries with hashed passwords of users, that use this DB. I tried implementing a similar method in C# with many references from the internet, but I couldn't get the same Hashes as stored in the DB.



    public static string HashPassword(string password, string salt)
{
byte saltBytes = Encoding.UTF8.GetBytes(salt);
using (var rfc2898 = new Rfc2898DeriveBytes(password, saltBytes, 1000))
{
byte hash = rfc2898.GetBytes(16);
string hashString = string.Empty;
foreach (byte x in hash)
{
hashString += String.Format("{0:x2}", x);
}
return hashString;
}
}


This was the method I used. I wanted to reproduce the keysize 4 of CryptoJS so I'd get 4x = 32 digit long password hashes. I do get them, but they're different than the ones I would get in JS with CryptoJS.



Does anyone know how to get the same result as the CryptoJS version would? BEcause all passwords in the DB have been stored that way, it is crucial they are now generated the same way again only server side. (The Passwords are now encrypted against attacks).







javascript c# hash cryptography pbkdf2






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 8 at 14:10









VanDeath

1891217




1891217












  • stackoverflow.com/questions/11412882/…
    – Dmitry Bychenko
    Nov 8 at 14:42


















  • stackoverflow.com/questions/11412882/…
    – Dmitry Bychenko
    Nov 8 at 14:42
















stackoverflow.com/questions/11412882/…
– Dmitry Bychenko
Nov 8 at 14:42




stackoverflow.com/questions/11412882/…
– Dmitry Bychenko
Nov 8 at 14:42












1 Answer
1






active

oldest

votes

















up vote
1
down vote



accepted










Using a JSFiddle for CryptoJS/PBKDF2 (though I had to correct the CDN links), whose relevant code is



var password = $("[name='password']").val();

var iterations = 1000;
// sizes must be a multiple of 32
var keySize = 128;
var ivSize = 128;
var salt = CryptoJS.lib.WordArray.random(128/8);

$("#salt").html(salt.toString(CryptoJS.enc.Base64));
$("#iter").html(iterations);
$("#keysize").html(keySize);
$("#ivsize").html(ivSize);

var output = CryptoJS.PBKDF2(password, salt, {
keySize: (keySize+ivSize)/32,
iterations: iterations
});

// the underlying words arrays might have more content than was asked: remove insignificant words
output.clamp();

var key = CryptoJS.lib.WordArray.create(output.words.slice(0, keySize/32));
var iv = CryptoJS.lib.WordArray.create(output.words.slice(keySize/32));

$("#hasher").html("SHA1");

$("#key").html(key.toString(CryptoJS.enc.Base64));
$("#iv").html(iv.toString(CryptoJS.enc.Base64));


I got as one sample input/output "hello" with a (base64) salt of 0CD1HGFdkclqcWG5aV+rvw== (and the default hash algorithm, with the specified 1000 iterations and 32+32 byte outputs); that produced tRczLRRuFy/zFiPn1PBKmQ== / dhyeE+0Dd9avSJbM/4TcNw==.



The following C# discovery code was then utilized:



string password = "hello";
byte salt = Convert.FromBase64String("0CD1HGFdkclqcWG5aV+rvw==");
int iterations = 1000;

using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA1))
{
Console.WriteLine("UTF-8 / SHA-1");
Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
Console.Write(' ');
Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
}

using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
{
Console.WriteLine("UTF-8 / SHA-2-256");
Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
Console.Write(' ');
Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
}

byte utf16 = Encoding.Unicode.GetBytes(password);
using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA1))
{
Console.WriteLine("UTF-16LE / SHA-2-256");
Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
Console.Write(' ');
Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
}

using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA256))
{
Console.WriteLine("UTF-16LE / SHA-2-256");
Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
Console.Write(' ');
Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
}


with an output of



UTF-8 / SHA-1
tRczLRRuFy/zFiPn1PBKmQ== dhyeE+0Dd9avSJbM/4TcNw==
UTF-8 / SHA-2-256
lkBtILt+xDNEQrX0aWUk3Q== ouOiijCw5sjfMcJo9YZ4Ug==
UTF-16LE / SHA-2-256
1T2gJFFECc5AnpvoiFrBwg== rmHsTuOQdM5YDsmzklMEUQ==
UTF-16LE / SHA-2-256
G4/Ik5vZAd2l8kwq45BKaw== Iqy61Eaf8jmoxO2TpA+rkg==




Conclusion: CryptoJS.PBKDF2 is using SHA-1 and UTF-8, which means that the most likely problem is that you're loading in the wrong salt if you're not getting the same answer. If it's not a UTF-8 string it's probably either hexadecimal data or base64 (but that depends completely on your usage, there's no "the right answer is" here, as the salt is "just bytes").






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53209486%2freproducing-js-pbkdf2-hash-in-c-sharp%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote



    accepted










    Using a JSFiddle for CryptoJS/PBKDF2 (though I had to correct the CDN links), whose relevant code is



    var password = $("[name='password']").val();

    var iterations = 1000;
    // sizes must be a multiple of 32
    var keySize = 128;
    var ivSize = 128;
    var salt = CryptoJS.lib.WordArray.random(128/8);

    $("#salt").html(salt.toString(CryptoJS.enc.Base64));
    $("#iter").html(iterations);
    $("#keysize").html(keySize);
    $("#ivsize").html(ivSize);

    var output = CryptoJS.PBKDF2(password, salt, {
    keySize: (keySize+ivSize)/32,
    iterations: iterations
    });

    // the underlying words arrays might have more content than was asked: remove insignificant words
    output.clamp();

    var key = CryptoJS.lib.WordArray.create(output.words.slice(0, keySize/32));
    var iv = CryptoJS.lib.WordArray.create(output.words.slice(keySize/32));

    $("#hasher").html("SHA1");

    $("#key").html(key.toString(CryptoJS.enc.Base64));
    $("#iv").html(iv.toString(CryptoJS.enc.Base64));


    I got as one sample input/output "hello" with a (base64) salt of 0CD1HGFdkclqcWG5aV+rvw== (and the default hash algorithm, with the specified 1000 iterations and 32+32 byte outputs); that produced tRczLRRuFy/zFiPn1PBKmQ== / dhyeE+0Dd9avSJbM/4TcNw==.



    The following C# discovery code was then utilized:



    string password = "hello";
    byte salt = Convert.FromBase64String("0CD1HGFdkclqcWG5aV+rvw==");
    int iterations = 1000;

    using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA1))
    {
    Console.WriteLine("UTF-8 / SHA-1");
    Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    Console.Write(' ');
    Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    }

    using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
    {
    Console.WriteLine("UTF-8 / SHA-2-256");
    Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    Console.Write(' ');
    Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    }

    byte utf16 = Encoding.Unicode.GetBytes(password);
    using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA1))
    {
    Console.WriteLine("UTF-16LE / SHA-2-256");
    Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    Console.Write(' ');
    Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    }

    using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA256))
    {
    Console.WriteLine("UTF-16LE / SHA-2-256");
    Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    Console.Write(' ');
    Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    }


    with an output of



    UTF-8 / SHA-1
    tRczLRRuFy/zFiPn1PBKmQ== dhyeE+0Dd9avSJbM/4TcNw==
    UTF-8 / SHA-2-256
    lkBtILt+xDNEQrX0aWUk3Q== ouOiijCw5sjfMcJo9YZ4Ug==
    UTF-16LE / SHA-2-256
    1T2gJFFECc5AnpvoiFrBwg== rmHsTuOQdM5YDsmzklMEUQ==
    UTF-16LE / SHA-2-256
    G4/Ik5vZAd2l8kwq45BKaw== Iqy61Eaf8jmoxO2TpA+rkg==




    Conclusion: CryptoJS.PBKDF2 is using SHA-1 and UTF-8, which means that the most likely problem is that you're loading in the wrong salt if you're not getting the same answer. If it's not a UTF-8 string it's probably either hexadecimal data or base64 (but that depends completely on your usage, there's no "the right answer is" here, as the salt is "just bytes").






    share|improve this answer

























      up vote
      1
      down vote



      accepted










      Using a JSFiddle for CryptoJS/PBKDF2 (though I had to correct the CDN links), whose relevant code is



      var password = $("[name='password']").val();

      var iterations = 1000;
      // sizes must be a multiple of 32
      var keySize = 128;
      var ivSize = 128;
      var salt = CryptoJS.lib.WordArray.random(128/8);

      $("#salt").html(salt.toString(CryptoJS.enc.Base64));
      $("#iter").html(iterations);
      $("#keysize").html(keySize);
      $("#ivsize").html(ivSize);

      var output = CryptoJS.PBKDF2(password, salt, {
      keySize: (keySize+ivSize)/32,
      iterations: iterations
      });

      // the underlying words arrays might have more content than was asked: remove insignificant words
      output.clamp();

      var key = CryptoJS.lib.WordArray.create(output.words.slice(0, keySize/32));
      var iv = CryptoJS.lib.WordArray.create(output.words.slice(keySize/32));

      $("#hasher").html("SHA1");

      $("#key").html(key.toString(CryptoJS.enc.Base64));
      $("#iv").html(iv.toString(CryptoJS.enc.Base64));


      I got as one sample input/output "hello" with a (base64) salt of 0CD1HGFdkclqcWG5aV+rvw== (and the default hash algorithm, with the specified 1000 iterations and 32+32 byte outputs); that produced tRczLRRuFy/zFiPn1PBKmQ== / dhyeE+0Dd9avSJbM/4TcNw==.



      The following C# discovery code was then utilized:



      string password = "hello";
      byte salt = Convert.FromBase64String("0CD1HGFdkclqcWG5aV+rvw==");
      int iterations = 1000;

      using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA1))
      {
      Console.WriteLine("UTF-8 / SHA-1");
      Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
      Console.Write(' ');
      Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
      }

      using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
      {
      Console.WriteLine("UTF-8 / SHA-2-256");
      Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
      Console.Write(' ');
      Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
      }

      byte utf16 = Encoding.Unicode.GetBytes(password);
      using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA1))
      {
      Console.WriteLine("UTF-16LE / SHA-2-256");
      Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
      Console.Write(' ');
      Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
      }

      using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA256))
      {
      Console.WriteLine("UTF-16LE / SHA-2-256");
      Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
      Console.Write(' ');
      Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
      }


      with an output of



      UTF-8 / SHA-1
      tRczLRRuFy/zFiPn1PBKmQ== dhyeE+0Dd9avSJbM/4TcNw==
      UTF-8 / SHA-2-256
      lkBtILt+xDNEQrX0aWUk3Q== ouOiijCw5sjfMcJo9YZ4Ug==
      UTF-16LE / SHA-2-256
      1T2gJFFECc5AnpvoiFrBwg== rmHsTuOQdM5YDsmzklMEUQ==
      UTF-16LE / SHA-2-256
      G4/Ik5vZAd2l8kwq45BKaw== Iqy61Eaf8jmoxO2TpA+rkg==




      Conclusion: CryptoJS.PBKDF2 is using SHA-1 and UTF-8, which means that the most likely problem is that you're loading in the wrong salt if you're not getting the same answer. If it's not a UTF-8 string it's probably either hexadecimal data or base64 (but that depends completely on your usage, there's no "the right answer is" here, as the salt is "just bytes").






      share|improve this answer























        up vote
        1
        down vote



        accepted







        up vote
        1
        down vote



        accepted






        Using a JSFiddle for CryptoJS/PBKDF2 (though I had to correct the CDN links), whose relevant code is



        var password = $("[name='password']").val();

        var iterations = 1000;
        // sizes must be a multiple of 32
        var keySize = 128;
        var ivSize = 128;
        var salt = CryptoJS.lib.WordArray.random(128/8);

        $("#salt").html(salt.toString(CryptoJS.enc.Base64));
        $("#iter").html(iterations);
        $("#keysize").html(keySize);
        $("#ivsize").html(ivSize);

        var output = CryptoJS.PBKDF2(password, salt, {
        keySize: (keySize+ivSize)/32,
        iterations: iterations
        });

        // the underlying words arrays might have more content than was asked: remove insignificant words
        output.clamp();

        var key = CryptoJS.lib.WordArray.create(output.words.slice(0, keySize/32));
        var iv = CryptoJS.lib.WordArray.create(output.words.slice(keySize/32));

        $("#hasher").html("SHA1");

        $("#key").html(key.toString(CryptoJS.enc.Base64));
        $("#iv").html(iv.toString(CryptoJS.enc.Base64));


        I got as one sample input/output "hello" with a (base64) salt of 0CD1HGFdkclqcWG5aV+rvw== (and the default hash algorithm, with the specified 1000 iterations and 32+32 byte outputs); that produced tRczLRRuFy/zFiPn1PBKmQ== / dhyeE+0Dd9avSJbM/4TcNw==.



        The following C# discovery code was then utilized:



        string password = "hello";
        byte salt = Convert.FromBase64String("0CD1HGFdkclqcWG5aV+rvw==");
        int iterations = 1000;

        using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA1))
        {
        Console.WriteLine("UTF-8 / SHA-1");
        Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        Console.Write(' ');
        Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        }

        using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
        {
        Console.WriteLine("UTF-8 / SHA-2-256");
        Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        Console.Write(' ');
        Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        }

        byte utf16 = Encoding.Unicode.GetBytes(password);
        using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA1))
        {
        Console.WriteLine("UTF-16LE / SHA-2-256");
        Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        Console.Write(' ');
        Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        }

        using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA256))
        {
        Console.WriteLine("UTF-16LE / SHA-2-256");
        Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        Console.Write(' ');
        Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        }


        with an output of



        UTF-8 / SHA-1
        tRczLRRuFy/zFiPn1PBKmQ== dhyeE+0Dd9avSJbM/4TcNw==
        UTF-8 / SHA-2-256
        lkBtILt+xDNEQrX0aWUk3Q== ouOiijCw5sjfMcJo9YZ4Ug==
        UTF-16LE / SHA-2-256
        1T2gJFFECc5AnpvoiFrBwg== rmHsTuOQdM5YDsmzklMEUQ==
        UTF-16LE / SHA-2-256
        G4/Ik5vZAd2l8kwq45BKaw== Iqy61Eaf8jmoxO2TpA+rkg==




        Conclusion: CryptoJS.PBKDF2 is using SHA-1 and UTF-8, which means that the most likely problem is that you're loading in the wrong salt if you're not getting the same answer. If it's not a UTF-8 string it's probably either hexadecimal data or base64 (but that depends completely on your usage, there's no "the right answer is" here, as the salt is "just bytes").






        share|improve this answer












        Using a JSFiddle for CryptoJS/PBKDF2 (though I had to correct the CDN links), whose relevant code is



        var password = $("[name='password']").val();

        var iterations = 1000;
        // sizes must be a multiple of 32
        var keySize = 128;
        var ivSize = 128;
        var salt = CryptoJS.lib.WordArray.random(128/8);

        $("#salt").html(salt.toString(CryptoJS.enc.Base64));
        $("#iter").html(iterations);
        $("#keysize").html(keySize);
        $("#ivsize").html(ivSize);

        var output = CryptoJS.PBKDF2(password, salt, {
        keySize: (keySize+ivSize)/32,
        iterations: iterations
        });

        // the underlying words arrays might have more content than was asked: remove insignificant words
        output.clamp();

        var key = CryptoJS.lib.WordArray.create(output.words.slice(0, keySize/32));
        var iv = CryptoJS.lib.WordArray.create(output.words.slice(keySize/32));

        $("#hasher").html("SHA1");

        $("#key").html(key.toString(CryptoJS.enc.Base64));
        $("#iv").html(iv.toString(CryptoJS.enc.Base64));


        I got as one sample input/output "hello" with a (base64) salt of 0CD1HGFdkclqcWG5aV+rvw== (and the default hash algorithm, with the specified 1000 iterations and 32+32 byte outputs); that produced tRczLRRuFy/zFiPn1PBKmQ== / dhyeE+0Dd9avSJbM/4TcNw==.



        The following C# discovery code was then utilized:



        string password = "hello";
        byte salt = Convert.FromBase64String("0CD1HGFdkclqcWG5aV+rvw==");
        int iterations = 1000;

        using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA1))
        {
        Console.WriteLine("UTF-8 / SHA-1");
        Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        Console.Write(' ');
        Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        }

        using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
        {
        Console.WriteLine("UTF-8 / SHA-2-256");
        Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        Console.Write(' ');
        Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        }

        byte utf16 = Encoding.Unicode.GetBytes(password);
        using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA1))
        {
        Console.WriteLine("UTF-16LE / SHA-2-256");
        Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        Console.Write(' ');
        Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        }

        using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA256))
        {
        Console.WriteLine("UTF-16LE / SHA-2-256");
        Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        Console.Write(' ');
        Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
        }


        with an output of



        UTF-8 / SHA-1
        tRczLRRuFy/zFiPn1PBKmQ== dhyeE+0Dd9avSJbM/4TcNw==
        UTF-8 / SHA-2-256
        lkBtILt+xDNEQrX0aWUk3Q== ouOiijCw5sjfMcJo9YZ4Ug==
        UTF-16LE / SHA-2-256
        1T2gJFFECc5AnpvoiFrBwg== rmHsTuOQdM5YDsmzklMEUQ==
        UTF-16LE / SHA-2-256
        G4/Ik5vZAd2l8kwq45BKaw== Iqy61Eaf8jmoxO2TpA+rkg==




        Conclusion: CryptoJS.PBKDF2 is using SHA-1 and UTF-8, which means that the most likely problem is that you're loading in the wrong salt if you're not getting the same answer. If it's not a UTF-8 string it's probably either hexadecimal data or base64 (but that depends completely on your usage, there's no "the right answer is" here, as the salt is "just bytes").







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 9 at 17:42









        bartonjs

        12.7k12052




        12.7k12052






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53209486%2freproducing-js-pbkdf2-hash-in-c-sharp%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            這個網誌中的熱門文章

            Hercules Kyvelos

            Tangent Lines Diagram Along Smooth Curve

            Yusuf al-Mu'taman ibn Hud