简体   繁体   English

如何在Golang pbkdf2中模拟pbkdf2-scala的密码hash

[英]How to simulate password hash of pbkdf2-scala in Golang pbkdf2

Our app uses the library, SecureHash object, in order to create one-way password: https://github.com/nremond/pbkdf2-scala/blob/master/src/main/scala/io/github/nremond/SecureHash.scala我们的应用程序使用库 SecureHash object 来创建单向密码: https://github.com/nremond/pbkdf2-scala/blob/master/src/main/scala/io/github/nremond/SecureHash。 scala

Now my problem is that my code in Go returns -1 for password check.现在我的问题是我在 Go 中的代码返回-1进行密码检查。

package main

import (
    "bytes"
    "crypto/rand"
    "crypto/sha512"
    "fmt"
    "golang.org/x/crypto/pbkdf2"
    "math/big"
    "strings"
)

func main() {
    iteration := 25000

    // Hash User input
    password := []byte("123")
    salt := "yCyQMMMBt1TuPa1F9FeKfT0yrNIF8tLB"
    key := pbkdf2.Key(password, []byte(salt), iteration, sha512.Size, sha512.New)

    // COMPARE PASSWORD fetched from DB
        // 123 hash in scala
    tokenS := strings.Split("$pbkdf2-sha512$25000$yCyQMMMBt1TuPa1F9FeKfT0yrNIF8tLB$TtQt5BZLs4qlA0YAkcGukZwu7pkxOLcxwuoQB3qNtxM", "$")
    passwordHashInDatabase := tokenS[4]
    out := bytes.Compare(key, []byte(passwordHashInDatabase))
    fmt.Println("out: ", out)
}

Verification fails because:验证失败,因为:

  • the hash to be verified, tokenS[4] , has a length of 32 bytes, while the calculated hash, key , has a length of 64 bytes,待验证的hash, tokenS[4] ,长度为32字节,而计算出的hash, key ,长度为64字节,
  • the salt is not Base64 decoded before the hash is computed,在计算 hash 之前,salt 不是 Base64 解码的,
  • when comparing, the hash to be verified is Base64 encoded while the calculated hash is raw.比较时,要验证的 hash 是 Base64 编码的,而计算出的 hash 是原始的。

A possible fix is:一个可能的修复是:

iteration := 25000

// Hash User input
password := []byte("123")
salt, _ := base64.RawStdEncoding.DecodeString("yCyQMMMBt1TuPa1F9FeKfT0yrNIF8tLB") // Fix 1: Base64 decode salt (Base64: without padding and with . instead of +)
key := pbkdf2.Key(password, []byte(salt), iteration, sha256.Size, sha512.New)     // Fix 2: Apply an output size of 32 bytes

// COMPARE PASSWORD fetched from DB
// 123 hash in scala
tokenS := strings.Split("$pbkdf2-sha512$25000$yCyQMMMBt1TuPa1F9FeKfT0yrNIF8tLB$TtQt5BZLs4qlA0YAkcGukZwu7pkxOLcxwuoQB3qNtxM", "$")
passwordHashInDatabase, _ := base64.RawStdEncoding.DecodeString(tokenS[4]) // Fix 3: Base64 decode the hash to be verified (Base64: without padding and with . instead of +)
out := bytes.Compare(key, passwordHashInDatabase) // better apply an constant-time comparison
fmt.Println("out: ", out) // 0

Although this code works for this particular token, in general a modified Base64 is applied with .虽然此代码适用于此特定令牌,但通常修改后的 Base64 与. instead of + (thus, if . are present, they must be replaced by + before Base64 decoding) and without padding (the latter is the reason for using base64.RawStdEncoding in above code snippet).而不是+ (因此,如果.存在,则必须在 Base64 解码之前将它们替换为+ )并且没有填充(后者是在上面的代码片段中使用base64.RawStdEncoding的原因)。

Note that there is a passlib implementation for Go ( passlib package ), but it seems to use essentially default values (eg an output size of 64 bytes for pbkdf2-sha512) and so cannot be applied directly here.请注意,Go ( passlib package ) 有一个passlib实现,但它似乎基本上使用默认值(例如,pbkdf2-sha512 的 output 大小为 64 字节),因此不能直接在此处应用。
Nevertheless, this implementation can be used as a blueprint for your own implementation (eg regarding Base64 encoding, s. Base64Decode() , constant-time comparison , s. SecureCompare() , preventive against side channel attacks etc.).尽管如此,此实现可以用作您自己实现的蓝图(例如,关于 Base64 编码、s.Base64Decode Base64Decode()恒定时间比较、s.SecureCompare SecureCompare() 、预防侧信道攻击等)。

You have a couple problems:你有几个问题:

  • You're not base64 decoding the salt before passing it to pbkdf2.Key() and you're not base64 decoding the key fetched from the database before comparing it to the result from pbkdf2.Key() .在将盐传递给pbkdf2.Key()之前,您不是 base64 解码盐,您也不是 base64 解码从数据库中获取的密钥,然后再将其与pbkdf2.Key()的结果进行比较。 Also note that the Scala implementation does some character replacement before/after base64 decoding/encoding.另请注意,Scala 实现在 base64 解码/编码之前/之后进行了一些字符替换。 This too needs to be replicated in the Go implementation.这也需要在 Go 实现中复制。

  • The createHash() method in the Scala implementation has a dkLength parameter which defaults to 32 . Scala 实现中的createHash()方法有一个默认为32dkLength参数。 In the Go implementation, you're instead providing the result of sha512.Size , which is 64 and does not match.在 Go 实现中,您改为提供sha512.Size的结果,该结果为64且不匹配。 I know that the default value was used because the value from the database is 32 bytes long.我知道使用了默认值,因为数据库中的值是 32 字节长。

Here's a hastily fixed implemnentation:这是一个匆忙修复的实现:

package main

import (
    "bytes"
    "crypto/sha512"
    "encoding/base64"
    "fmt"
    "log"
    "strings"

    "golang.org/x/crypto/pbkdf2"
)

func b64Decode(s string) ([]byte, error) {
    s = strings.ReplaceAll(s, ".", "+")
    return base64.RawStdEncoding.DecodeString(s)
}

func main() {
    iteration := 25000

    // Hash User input
    password := []byte("123")
    salt, err := b64Decode("yCyQMMMBt1TuPa1F9FeKfT0yrNIF8tLB")
    if err != nil {
        log.Fatal("Failed to base64 decode the salt: %s", err)
    }
    key := pbkdf2.Key(password, salt, iteration, 32, sha512.New)

    // COMPARE PASSWORD fetched from DB
    // 123 hash in scala
    tokens := strings.Split("$pbkdf2-sha512$25000$yCyQMMMBt1TuPa1F9FeKfT0yrNIF8tLB$TtQt5BZLs4qlA0YAkcGukZwu7pkxOLcxwuoQB3qNtxM", "$")
    passwordHashInDatabase, err := b64Decode(tokens[4])
    if err != nil {
        log.Fatal("Failed to base64 decode password hash from the database: %s", err)
    }
    fmt.Printf("%x\n%x\n", key, passwordHashInDatabase)
    fmt.Printf("%d\n%d\n", len(key), len(passwordHashInDatabase))
    out := bytes.Compare(key, passwordHashInDatabase)
    fmt.Println("out: ", out)
}

Output: Output:

4ed42de4164bb38aa503460091c1ae919c2eee993138b731c2ea10077a8db713
4ed42de4164bb38aa503460091c1ae919c2eee993138b731c2ea10077a8db713
32
32
out:  0

Go Playground Go 游乐场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM