如何在Golang中使用Argon2id对密码进行散列和验证

258 阅读1分钟

在写这篇文章的时候,Argon2是首选的密码散列方式,用于存储目的。你可以使用例子来散列和验证密码。

package main

import (
    "crypto/rand"
    "crypto/subtle"
    "encoding/base64"
    "fmt"
    "log"
    "strings"

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

type Argon2ID struct {
    format  string
    version int
    time    uint32
    memory  uint32
    keyLen  uint32
    saltLen uint32
    threads uint8
}

func main()  {
    argon2ID := NewArgon2ID()

    plain := "inanzzz"

    hash1, err := argon2ID.Hash(plain)
    if err != nil {
        log.Fatal(err)
    }
    ok1, err := argon2ID.Verify(plain, hash1)
    if err != nil {
        log.Fatal(err)
    }

    hash2, err := argon2ID.Hash(plain)
    if err != nil {
        log.Fatal(err)
    }
    ok2, err := argon2ID.Verify(plain, hash2)
    if err != nil {
        log.Fatal(err)
    }

    hash3, err := argon2ID.Hash(plain)
    if err != nil {
        log.Fatal(err)
    }
    ok3, err := argon2ID.Verify(plain, hash3+"uuu")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("PLAIN:", plain)
    fmt.Println("HASH 1:", hash1)
    fmt.Println("VALID 1:", ok1)
    fmt.Println("HASH 2:", hash2)
    fmt.Println("VALID 2:", ok2)
    fmt.Println("HASH 3:", hash3)
    fmt.Println("VALID 3:", ok3)
}

func NewArgon2ID() Argon2ID {
    return Argon2ID{
        format:  "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
        version: argon2.Version,
        time:    1,
        memory:  64 * 1024,
        keyLen:  32,
        saltLen: 16,
        threads: 4,
    }
}

func (a Argon2ID) Hash(plain string) (string, error) {
    salt := make([]byte, a.saltLen)
    if _, err := rand.Read(salt); err != nil {
        return "", err
    }

    hash := argon2.IDKey([]byte(plain), salt, a.time, a.memory, a.threads, a.keyLen)

    return fmt.Sprintf(
            a.format,
            a.version,
            a.memory,
            a.time,
            a.threads,
            base64.RawStdEncoding.EncodeToString(salt),
            base64.RawStdEncoding.EncodeToString(hash),
        ),
        nil
}

func (a Argon2ID) Verify(plain, hash string) (bool, error) {
    hashParts := strings.Split(hash, "$")

    _, err := fmt.Sscanf(hashParts[3], "m=%d,t=%d,p=%d", &a.memory, &a.time, &a.threads)
    if err != nil {
        return false, err
    }

    salt, err := base64.RawStdEncoding.DecodeString(hashParts[4])
    if err != nil {
        return false, err
    }

    decodedHash, err := base64.RawStdEncoding.DecodeString(hashParts[5])
    if err != nil {
        return false, err
    }

    hashToCompare := argon2.IDKey([]byte(plain), salt, a.time, a.memory, a.threads, uint32(len(decodedHash)))

    return subtle.ConstantTimeCompare(decodedHash, hashToCompare) == 1, nil
}
PLAIN: inanzzz
HASH 1: $argon2id$v=19$m=65536,t=1,p=4$3Ymrg20KqXd9mMawXP/YzA$nf7ubeO0tB1NDk4nBscgsHvIcDECMjIuEeEjgBRMe3s
VALID 1: true

HASH 2: $argon2id$v=19$m=65536,t=1,p=4$DneZEKdFvOarVcS5A6WsZA$dmpO4kOSbJr1ZoDWRIG+Bx8wuvpiVp9tQyyNjSwPWYk
VALID 2: true

HASH 3: $argon2id$v=19$m=65536,t=1,p=4$L/6i/DD9Ie5dKo7L6PpvVg$8OLc5G2E715nbsSgA4ZKcGngVLtAeCnB4CD76XbShic
VALID 3: false