普段の帳簿周りを自動化したいから、

自分ら用にちょっとしたアプリを作ることにした。


せっかくの機会なので、

Goで一方向ハッシュ関数によるパスワードの暗号化を書いてみたので、勉強がてらメモを残しておく。

Go (プログラミング言語) - Wikipedia

暗号学的ハッシュ関数 - Wikipedia


一方向ハッシュ関数とは、

アプリでアカウント登録の際にパスワードの設定をしてもらうでしょ。

それをそのままサービス側のデータベースに保存すると危ないので、

入力したパスワードを暗号化してからデータベースに登録する処理のこと。


暗号化したパスワードはどうやって使うの?

という話は割愛します。




書いたコードは、


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)で、

暗号化の際に指定した回数分、何度も計算をしているのでしばらくは大丈夫でしょ…

SHA-2 Wikipedia