Go 如何實現 PHP 的密碼加密解密

JaguarJack發表於2019-04-16

最近正在遷移自己的小專案,專案之前是基於 Laravel5.5 開發的。整個使用者登陸也是基於框架的 Auth 包認證的。其中使用者密碼這塊也是用到了 PHP 內建的函式 password_hash,用它進行密碼加密。而且 PHP 預設使用的 PASSWORD_BCRYPT 演算法。在使用 Go 的遷移過程中需要認證密碼,所以就把這個過程記錄下來。使用下面的例子來說明如何使用 GO bcrypt 包來對你的密碼進行 hash 和 salt 加密

對於這個例子,我將建立一個控制檯應用程式,用於演示如何獲取使用者輸入的密碼並使用它生成 salt 雜湊值。 完成此操作後,我將透過比較密碼與其雜湊版本來驗證密碼是否正確。

獲取使用者輸入的密碼

開始我們先建立一個可以在控制檯讀取使用者輸入的的方法。

func getPwd() []byte {
    fmt.Println("Enter a password")
    var pwd string

    // 讀取使用者輸入
    _, err := fmt.Scan(&pwd)
    if err != nil {
        log.Println(err)
    }
    return []byte(pwd)

}

Hash & Salt 使用者的密碼

現在我們可以使用 Go 的 bcrypt 包提供的GenerateFromPassword(password []byte, cost int)([]byte, error)方法對使用者的密碼進行 hash 和 salt 加密了。

GenerateFromPassword 方法以給定 cost 值返回密碼的 Bcrypt 演算法的 Hash 值,如果提供的 cost 值小於 Mincost 的話,將會預設使用 DefaultCost 代替

使用 GenerateFromPassword 函式的一個優勢就是我們不需要自己來編寫函式來生成 Salt,因為它會為我們自動生成一個 Salt。

下面的函式使用 GenerateFromPassword 生成 salted 雜湊值,該雜湊值作為位元組切片返回。 然後我們將位元組切片作為字串返回,以便我們可以將 salted 雜湊儲存在資料庫中作為使用者密碼。

func hashAndSalt(pwd []byte) string {
    hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
    if err != nil {
        log.Println(err)
    }
    return string(hash)
}

目前我們做了什麼

到目前為止,我們已經建立了一個接受來自控制檯的使用者輸入並將其作為位元組切片返回的函式。 然後,
我們再建立一個可以接收使用者輸入並返回 salted 雜湊值的函式。下面就是程式碼事例。

package main

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

func main() {
   for {
        pwd := getPwd()
        hash := hashAndSalt(pwd)
        fmt.Println("Salted Hash", pwd)
     }
}

如果你執行上面的程式碼,將會得到下面的結果

> $ Enter a password
> $ foobar
> Salted Hash $2a$10$...........

這裡需要的注意的是我使用 for 迴圈呼叫函式,直到我強制停止它。對於那些不熟悉 GO 的人來講,這個就和其他語言的 while (true){}是一樣的效果。

驗證密碼

最後一件事兒就是需要驗證密碼的正確性來登陸我們的系統,我們可以使用 bcrypt 包提供的CompareHashAndPassword(hashedPassword, password []byte) error函式

CompareHashAndPassword 將 bcrypt 雜湊密碼與其純文字進行比較。 成功時返回nil,失敗時返回錯誤

我們使用CompareHashAndPassword函式來建立另一個返回 bool 值的函式讓我們知道密碼是否匹配。

func comparePasswords(hashedPwd string, plainPwd []byte) bool {

    byteHash := []byte(hashedPwd)
    err := bcrypt.CompareHashAndPassword(byteHash, plainPwd)\
    if err != nil {
        log.Println(err)
        return false
    }
    return true
}

更新 Main 函式

我們現在可以更新我們的主要功能,以便我們能夠輸入密碼,獲取其鹽漬雜湊,然後再次輸入密碼,並查明我們的第二個密碼是否與我們輸入的第一個密碼相匹配。
我們現在修改一個 main 函式,當我們輸入密碼的時候,獲取 salted 雜湊值,然後再次輸入密碼,來檢查我們的密碼是否匹配。

func main() {
    for {
        pwd := getPwd()
        hash := hashAndSalt(pwd)
        pwd2 := getPwd()
        pwdMatch := comparePasswords(hash, pwd2)
        fmt.Println("Passwords Match?", pwd)
    }
}

全部程式碼

package main

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

func main() {
    for {
       // 輸入密碼 獲取 hash 值
        pwd := getPwd()
        hash := hashAndSalt(pwd)
       // 再次輸入密碼驗證
        pwd2 := getPwd()
        pwdMatch := comparePasswords(hash, pwd2)
        fmt.Println("Passwords Match?", pwd)
    }
}

func getPwd() []byte {
    fmt.Println("Enter a password")
    var pwd string
    _, err := fmt.Scan(&pwd)
    if err != nil {
        log.Println(err)
    }
    return []byte(pwd)
}


func hashAndSalt(pwd []byte) string {
    hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
    if err != nil {
        log.Println(err)
    }
    return string(hash)
}

func comparePasswords(hashedPwd string, plainPwd []byte) bool {
    byteHash := []byte(hashedPwd)

    err := bcrypt.CompareHashAndPassword(byteHash, plainPwd)
    if err != nil {
        log.Println(err)
        return false
    }
    return true
}

以上便是 GO 轉 php 的加密函式的過程,如果有任何錯誤或者不當的地方歡迎進行改進。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章