82

I set up a site with Node.js+passport for user authentication.

Now I need to migrate to Golang, and need to do authentication with the user passwords saved in db.

The Node.js encryption code is:

    var bcrypt = require('bcrypt');

    bcrypt.genSalt(10, function(err, salt) {
        if(err) return next(err);

        bcrypt.hash(user.password, salt, function(err, hash) {
            if(err) return next(err);
            user.password = hash;
            next();
        });
    });

How to make the same hashed string as Node.js bcrypt with Golang?

Cid Huang
  • 935
  • 1
  • 7
  • 6

4 Answers4

155

Using the golang.org/x/crypto/bcrypt package, I believe the equivalent would be:

hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)

Working example:

package main

import (
    "golang.org/x/crypto/bcrypt"
    "fmt"
)

func main() {
    password := []byte("MyDarkSecret")

    // Hashing the password with the default cost of 10
    hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(hashedPassword))

    // Comparing the password with the hash
    err = bcrypt.CompareHashAndPassword(hashedPassword, password)
    fmt.Println(err) // nil means it is a match
}
Graham Miln
  • 2,724
  • 3
  • 33
  • 33
ANisus
  • 74,460
  • 29
  • 162
  • 158
  • 2
    Thank you. I use bcrypt.CompareHashAndPassword to compare the password and the string written by node.js in db, and return nil. Originally I thought the generated strings are always the same. Now I understand. Really appreciate. – Cid Huang Apr 24 '14 at 05:22
  • @user2036213: I have never used bcrypt before either and was a bit surprised that each run resulted in a new hash for the same password. But it still works fine. We learn new things every day :). Happy Go coding! – ANisus Apr 24 '14 at 05:41
  • 7
    Erm, that's what the random salt is good for - to make sure that two users with the same password will not have the same (salted) hash. [Here](https://crackstation.net/hashing-security.htm)'s a lengthy but good background article. – rob74 Apr 24 '14 at 12:54
  • 1
    @rob74: I see. I missed the fact that the hash produced by bcrypt includes the bcrypt version, cost, salt and cipher, not only the cipher (http://stackoverflow.com/a/6833165/694331). Thanks for the article! – ANisus Apr 25 '14 at 00:33
  • This package seems to be slow and very cpu costly. Any alternative? – majidarif Feb 21 '15 at 09:00
  • @majidarif The bcrypt algorithm is meant to be cpu costly to make it harder to brute force. You can even choose cost (iterations). I have not benchmarked Go's bcrypt package against other bcrypt implementations though, but I doubt there is any good Go alternative. – ANisus Feb 21 '15 at 20:49
  • @ANisus, yeah, I guess that should be it. But I am not hashing, I am just comparing, and 1 "compare" is a sudden spike of `24%` cpu usage. Anyways, thanks. – majidarif Feb 21 '15 at 22:17
  • 2
    @majidarif When comparing, the string is hashed as well; it is by design. To create a hash and to compare a hash has the same cost. I'd say it is working as intended :) – ANisus Feb 23 '15 at 07:13
8

Take a look at the bcrypt package from go.crypto (docs are here).

To install it, use

go get golang.org/x/crypto/bcrypt

A blog entry describing the usage of the bcrypt package can be found here. It's from the guy who wrote the package, so it should work ;)

One difference to the node.js library you are using is that the go package doesn't have an (exported) genSalt function, but it will generate the salt automatically when you call bcrypt.GenerateFromPassword.

rob74
  • 4,939
  • 29
  • 31
2

Firstly you need import the bcrypt package

go get golang.org/x/crypto/bcrypt

Then use GenerateFromPassword

bs, err := bcrypt.GenerateFromPassword([]byte(p), bcrypt.MinCost)
    if err != nil {
        http.Error(w, "Internal server error", http.StatusInternalServerError)
        return
    }
Rohan
  • 171
  • 6
0

Another way

dataEncrypt, _ := bcrypt.GenerateFromPassword([]byte(yourData), bcrypt.DefaultCost)

the second parameter 'bcrypt' can take multiple values and for compare you can use

error := bcrypt.CompareHashAndPassword([]byte(yourDataEncript), []byte(dataText))

very useful for password use

ocjara
  • 1