普段の帳簿周りを自動化したいから、
自分ら用にちょっとしたアプリを作ることにした。
せっかくの機会なので、
Goで一方向ハッシュ関数によるパスワードの暗号化を書いてみたので、勉強がてらメモを残しておく。
一方向ハッシュ関数とは、
アプリでアカウント登録の際にパスワードの設定をしてもらうでしょ。
それをそのままサービス側のデータベースに保存すると危ないので、
入力したパスワードを暗号化してからデータベースに登録する処理のこと。
暗号化したパスワードはどうやって使うの?
という話は割愛します。
書いたコードは、
import ( "crypto/rand" "encoding/hex" "encoding/base64" "fmt" "io" "golang.org/x/crypto/scrypt" ) func createHashFromPassword(password string) string { b := make([]byte, 14) _, err := io.ReadFull(rand.Reader, b) if err != nil { fmt.Println("error:", err) } //ソルト原文 salt := base64.StdEncoding.EncodeToString(b) converted, _ := scrypt.Key([]byte(password), []byte(salt), 16384, 8, 1, 16) return hex.EncodeToString(converted[:]) }
こんな感じ。
scrypt - GoDoc - The Go Programming Languageのライブラリを利用した。
直接暗号化に関わる箇所は自作したら危ないからね。
createHashFromPassword関数を実行する時にパスワードを渡すと、
「071f9e078c569096582f6566c8df204c」 ← こんな感じのランダムのような文字列を返す様にした。
※実際にはハッシュ値はランダムではない
関数内の処理を見ていくと、
最初のb := make([]byte, 14)で値が14個で、各値がすべて0のバイト配列を作成した。
b := make([]byte, 14) fmt.Println(b) //[0,0,0,0,0,0,0,0,0,0,0,0,0,0]と表示される
この配列bをio.ReadFull(rand.Reader, b)することで、
配列内の各値にランダムの数字を挿入した。
この処理の後のbを実行してみると、
[143 225 142 201 118 65 192 4 41 74 186 38 201 89] ← 実行の度に値が変わる。
これを、
salt := base64.StdEncoding.EncodeToString(b)することで、
ランダムの文字列に変換する。
saltを実行してみると、
「j+GOyXZBwAQpSromyVk=」が出力された。
ソルト(salt)は暗号化の際に復元されない様にする工夫みたいなもので、
今回出力された値は最後に出力される値と一緒に保持しておく。
最後に
converted, _ := scrypt.Key([]byte(password), []byte(salt), 16384, 8, 1, 16) return hex.EncodeToString(converted[:])
入力したパスワードを暗号化するKey関数を実行して、
出力された値を16進法表記に変換したら終了。
「fc8c5d8204142cf9d27fbddc83a4700d」
今回の実装では上記の値が出力された。
Keyの引数にある各値は推奨されている数字をそのまま使用する。
ソルトの長さとKeyの最後の値(現在は16)の数字を変えると暗号化が強化されるはず。
今回の関数はcreateHashFromPassword(登録したいパスワード)で使える。
追記
scrypt.goのコードを読む限り、アルゴリズムはSHA-2(SHA-256)で、
暗号化の際に指定した回数分、何度も計算をしているのでしばらくは大丈夫でしょ…