😸

Goでのハッシュ値生成

2025/02/04に公開

はじめに

現在携わっているプロジェクトで、特定の文字列をハッシュ化する機会がありました。
実際やったものも含め、どんな実装ができるのか改めて調べてみました!

SHA-1

  • Secure Hash Algorithm 1の略
  • 20バイトのハッシュ値を生成する
  • 脆弱性が発見されているため仕様の廃止が検討されている

SHA-2

  • SHA-1のアップデート版
  • SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256の全部で6種類がある
  • SHA-2の改良版であるSHA-3も存在するが、SHA-2でも安全性が確保されているという

実装

今回は、SHA-1、SHA-224、SHA-256、SHA-384、SHA-512の実装を見ていきます。

package main

import (
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
)

func main() {
	msg := "Hello, World!"

	// sha1
	hash1 := sha1.Sum([]byte(msg))
	fmt.Println("hash1:",hash1)
	// 出力結果
	// バイトスライス化し、その結果をバイト配列で返している
	// hash1: [10 10 159 42 103 114 148 37 87 171 83 85 215 106 244 66 248 246 94 1]

	str1 := hex.EncodeToString(hash1[:])
	fmt.Println("str1:",str1)
	// 出力結果
	// スライスをhex.EncodeToString関数で16進数の文字列に変換
	// str1: 0a0a9f2a6772942557ab5355d76af442f8f65e01

	sum1 := sha1.Sum([]byte(msg))
	fmt.Printf("sum1: %x\n", sum1)
	// 出力結果
	// 書式指定子%xを使うことで、hex/encodingを使わなくても文字列に変換される
	// sum1: 0a0a9f2a6772942557ab5355d76af442f8f65e01

	// sha224
	hash224 := sha256.Sum224([]byte(msg))
	fmt.Println("hash224:",hash224)
	// 出力結果
	// hash224: [114 162 61 250 65 27 166 253 224 29 191 171 243 176 10 112 156 147 235 242 115 220 41 226 216 178 97 255]

	str224 := hex.EncodeToString(hash224[:])
	fmt.Println("str224:",str224)
	// 出力結果
	// str224: 72a23dfa411ba6fde01dbfabf3b00a709c93ebf273dc29e2d8b261ff

	sum224 := sha256.Sum224([]byte(msg))
	fmt.Printf("sum224: %x\n", sum224)
	// 出力結果
	// sum224: 72a23dfa411ba6fde01dbfabf3b00a709c93ebf273dc29e2d8b261ff

	// sha256
	hash256 := sha256.Sum256([]byte(msg))
	fmt.Println("hash256:",hash256)
	// 出力結果
	// hash256: [223 253 96 33 187 43 213 176 175 103 98 144 128 158 195 165 49 145 221 129 199 247 10 75 40 104 138 54 33 130 152 111]

	str256 := hex.EncodeToString(hash256[:])
	fmt.Println("str256:",str256)
	// 出力結果
	// str256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f

	sum256 := sha256.Sum256([]byte(msg))
	fmt.Printf("sum256: %x\n", sum256)
	// 出力結果
	// sum256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f

	// sha384
	hash384 := sha512.Sum384([]byte(msg))
	fmt.Println("hash384:",hash384)
	// 出力結果
	// hash384: [84 133 204 155 51 101 180 48 93 251 78 131 55 224 165 152 165 116 248 36 43 241 114 137 224 221 108 32 163 205 68 160 137 222 22 171 74 179 8 246 62 68 177 23 14 181 245 21]

	str384 := hex.EncodeToString(hash384[:])
	fmt.Println("str384:",str384)
	// 出力結果
	// str384: 5485cc9b3365b4305dfb4e8337e0a598a574f8242bf17289e0dd6c20a3cd44a089de16ab4ab308f63e44b1170eb5f515

	sum384 := sha512.Sum384([]byte(msg))
	fmt.Printf("sum384: %x\n", sum384)
	// 出力結果
	// sum384: 5485cc9b3365b4305dfb4e8337e0a598a574f8242bf17289e0dd6c20a3cd44a089de16ab4ab308f63e44b1170eb5f515

	// sha512
	hash512 := sha512.Sum512([]byte(msg))
	fmt.Println("hash512:",hash512)
	// 出力結果
	// hash512: [55 77 121 74 149 205 207 216 179 89 147 24 95 239 155 163 104 241 96 216 218 244 50 208 139 169 241 237 30 90 190 108 198 146 145 224 250 47 224 0 106 82 87 14 241 140 25 222 244 230 23 195 60 229 46 240 166 229 251 227 24 203 3 135]

	str512 := hex.EncodeToString(hash512[:])
	fmt.Println("str512:",str512)
	// 出力結果
	// str512: 374d794a95cdcfd8b35993185fef9ba368f160d8daf432d08ba9f1ed1e5abe6cc69291e0fa2fe0006a52570ef18c19def4e617c33ce52ef0a6e5fbe318cb0387

	sum512 := sha512.Sum512([]byte(msg))
	fmt.Printf("sum512: %x\n", sum512)
	// 出力結果
	// sum512: 374d794a95cdcfd8b35993185fef9ba368f160d8daf432d08ba9f1ed1e5abe6cc69291e0fa2fe0006a52570ef18c19def4e617c33ce52ef0a6e5fbe318cb0387
}

まとめ

SHA-1が20バイトのハッシュ値を生成するのに対し、SHA-512では64バイトのハッシュ値を生成するので、長さが圧倒的に異なることがわかりました。
また、文字列生成の方法として、

  • hex.EncodeToStringを使う
  • 書式指定子である%xを使う
    これらが存在し、どっちも同じことを示しているということもわかりました。
    今回は見ていませんが、SHA-512/224やSHA-512/256の実装も改めて見てみたいと思います!

参考

Discussion