golang中的等效salt和hash

时间:2014-04-13 05:27:49

标签: go

以下是在python中对给定密码进行腌制和散列的示例。

import scrypt
import os

# Length of salt
PW_SALT_BYTES = 32
# Length of scrypt hash of passwords
PW_HASH_BYTES = 64
# test password
password = "hello"

salt = os.urandom(PW_SALT_BYTES).encode('hex')
# hash(password, salt, N=1 << 14, r=8, p=1, buflen=64)
hashed_password = scrypt.hash(str(password), salt.decode('hex'), buflen=PW_HASH_BYTES).encode('hex')
print(hashed_password)

这会给我们一个哈希和盐渍的字符串作为回报: -

4d1da45b401961fccb10e094ecd70ec79510f05483ca293d300bbd0024e35866ca39fe09fbc15f83a359431021a1ed9644f7d2b871b357e37a186300877edb18

我如何在golang中实现这个?

4 个答案:

答案 0 :(得分:17)

Go并没有在标准库中有scrypt但是有一个&#34;官方&#34;在go.crypto repo中实现。

import (
    "crypto/rand"
    "fmt"
    "io"
    "log"

    "code.google.com/p/go.crypto/scrypt"
)

const (
    PW_SALT_BYTES = 32
    PW_HASH_BYTES = 64

    password = "hello"
)

func main() {
    salt := make([]byte, PW_SALT_BYTES)
    _, err := io.ReadFull(rand.Reader, salt)
    if err != nil {
        log.Fatal(err)
    }

    hash, err := scrypt.Key([]byte(password), salt, 1<<14, 8, 1, PW_HASH_BYTES)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%x\n", hash)
}

答案 1 :(得分:8)

在Golang中使用随机盐安全散列密码的优秀库golang.org/x/crypto/bcrypt不是使用scrypt,而是在下面的答案中提到:

Bcrypt password hashing in Golang (compatible with Node.js)?

使用bcrypt代替scrypt的一些好处:

  1. 在对密码进行散列时会自动(并随机)生成盐,这样您就不必担心生成盐。
  2. 在数据库中存储散列密码时,您不必再担心为每个密码哈希存储salt。
  3. 简化了哈希和检查密码的语法。
  4. bcrypt生成的哈希包括bcrypt版本,cost,salt和cipher,而不仅仅是密码。
  5. 以下是使用上述答案中的bcrypt的例子:

    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
    }
    

答案 2 :(得分:6)

现在看起来Go已经在官方图书馆中得到了解读。其中许多其他加密函数中的subrepository x/crypto都有scrypt

以下是如何使用它的示例:

package main

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

func main(){
    salt := []byte("asdfasdf")
    dk, err := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32)

    fmt.Println(dk)
    fmt.Println(err)
}

答案 3 :(得分:1)

这是我根据RFC 2898 / PKCS #5 v2.0编写的一个完整的哈希实用程序函数。

Hash可用于散列密码,例如Hash("hello")

Verify可以用于针对哈希的原始密码,基本上,它的作用是哈希原始字符串并将其与实际哈希进行比较。

package common

import (
    "crypto/rand"
    "crypto/sha1"
    "encoding/base64"
    "errors"
    "fmt"
    "golang.org/x/crypto/pbkdf2"
    "io"
    "strconv"
    "strings"
)

const (
    SALT_BYTE_SIZE    = 24
    HASH_BYTE_SIZE    = 24
    PBKDF2_ITERATIONS = 1000
)

func Hash(password string) (string, error) {
    salt := make([]byte, SALT_BYTE_SIZE)
    if _, err := io.ReadFull(rand.Reader, salt); err != nil {
        fmt.Print("Err generating random salt")
        return "", errors.New("Err generating random salt")
    }

    //todo: enhance: randomize itrs as well
    hbts := pbkdf2.Key([]byte(password), salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE, sha1.New)
    //hbtstr := fmt.Sprintf("%x", hbts)

    return fmt.Sprintf("%v:%v:%v",
        PBKDF2_ITERATIONS,
        base64.StdEncoding.EncodeToString(salt),
        base64.StdEncoding.EncodeToString(hbts)), nil
}

func Verify(raw, hash string) (bool, error) {
    hparts := strings.Split(hash, ":")

    itr, err := strconv.Atoi(hparts[0])
    if err != nil {
        fmt.Printf("wrong hash %v", hash)
        return false, errors.New("wrong hash, iteration is invalid")
    }
    salt, err := base64.StdEncoding.DecodeString(hparts[1])
    if err != nil {
        fmt.Print("wrong hash, salt error:", err)
        return false, errors.New("wrong hash, salt error:" + err.Error())
    }

    hsh, err := base64.StdEncoding.DecodeString(hparts[2])
    if err != nil {
        fmt.Print("wrong hash, hash error:", err)
        return false, errors.New("wrong hash, hash error:" + err.Error())
    }

    rhash := pbkdf2.Key([]byte(raw), salt, itr, len(hsh), sha1.New)
    return equal(rhash, hsh), nil
}

//bytes comparisons
func equal(h1, h2 []byte) bool {
    diff := uint32(len(h1)) ^ uint32(len(h2))
    for i := 0; i < len(h1) && i < len(h2); i++ {
        diff |= uint32(h1[i] ^ h2[i])
    }

    return diff == 0
}

这里的单元测试将帮助您弄清楚如何调用此类函数

package common

import (

    "github.com/stretchr/testify/assert"
    "testing"
)

func TestHash(t *testing.T) {
    hash, err := Hash("hello")
    assert.Nil(t, err)
    assert.NotEmpty(t, hash)

}

func TestVerify(t *testing.T) {
    hash, err := Hash("hello")
    assert.Nil(t, err)
    assert.NotEmpty(t, hash)

    ok, err := Verify("hello", hash)
    assert.Nil(t, err)
    assert.True(t, ok)
}