シェルコマンドでSHA256ハッシュをおこなう

この記事に書いてあること

シェルコマンドでSHA256ハッシュをおこなう方法をメモしておきます。

hex形式(16進数)で出力

$ echo -n "abcde12345xyz" | sha256sum | awk '{ print $1 }'
5b4538f8719e2662a2be85365d53d6b583bf06d92dd98cc1d682ecb3b69e977e

Base64形式で出力

$ echo -n "abcde12345xyz" | sha256sum | xxd -r -p | base64
W0U4+HGeJmKivoU2XVPWtYO/Btkt2YzB1oLss7ael34=

もしくはbasencコマンドを使ってBase64形式で出力

$ echo -n "abcde12345xyz" | sha256sum | xxd -r -p | basenc --base64
W0U4+HGeJmKivoU2XVPWtYO/Btkt2YzB1oLss7ael34=

Base64URL形式で出力

$ echo -n "abcde12345xyz" | sha256sum | xxd -r -p | base64 | sed s/\\+/-/g | sed s/\\//_/g | sed -E s/=+$//
W0U4-HGeJmKivoU2XVPWtYO_Btkt2YzB1oLss7ael34

もしくはbasencコマンドを使ってBase64URL形式で出力

$ echo -n "abcde12345xyz" | sha256sum | xxd -r -p | basenc --base64url | sed -E s/=+$//
W0U4-HGeJmKivoU2XVPWtYO_Btkt2YzB1oLss7ael34

コマンドの詳細

$ echo -n "abcde12345xyz" | sha256sum | awk '{ print $1 }'
5b4538f8719e2662a2be85365d53d6b583bf06d92dd98cc1d682ecb3b69e977e

上のコマンドについて見ていきます。 echoコマンドの-nオプションによって改行コードを出力しないようにしています。除外しないと改行コードまでSHA256ハッシュ化されてしまいます。 また、awkコマンドによって、sha256sumコマンドで出力される余計な文字列(-*)を除外しています。

echoコマンドの-nオプションとawkコマンドを使わないと以下のようになります。 改行コードまでハッシュ化しているので、値が変わってしまいました。

$ echo "abcde12345xyz" | sha256sum
33630eeb0ed053423d052d0f59bccb654bd1c1edc872124df9f85b66d0a1e525 *-

xxdコマンド

xxdコマンドはファイルまたは標準入力を16進数で出力するコマンドですが、-rオプションで反対に16進数からファイルに変換できます。

-p-psと同じオプションだが、ポストスクリプト形式の16進ダンプ(プレーン16進ダンプ)で出力する、らしいです。 よくわからないけど必要だったのでつけています。

$ echo -n "abcde12345xyz" | sha256sum | xxd -r -p | base64
W0U4+HGeJmKivoU2XVPWtYO/Btkt2YzB1oLss7ael34=

base64コマンド

ファイルまたは標準入力をBase64形式にエンコードBase64形式からデコードするコマンド。

また、今回は不要ですが-dオプション(--decode)によってデコードをおこなうことができます。

$ echo -n "abcde12345xyz" | base64
YWJjZGUxMjM0NXh5eg==
$ echo -n "YWJjZGUxMjM0NXh5eg==" | base64 -d
abcde12345xyz

basencコマンド

ファイルまたは標準入力をBase64形式またはBase64URL形式にエンコードBase64形式またはBase64URL形式からデコードするコマンド。 入っていないディストリビューションもあるようなので、注意(手元のUbuntu 20.04.1には入っていませんでした)。

今回はエンコードしたいので、オプションで--base64または--base64urlを指定しています。 注意点としては、basencコマンドのBase64URL形式は末尾のパディング=を削除してくれないので、sedコマンドで削除しています。

$ echo -n "abcde12345xyz" | sha256sum | xxd -r -p | basenc --base64
W0U4+HGeJmKivoU2XVPWtYO/Btkt2YzB1oLss7ael34=
$ echo -n "abcde12345xyz" | sha256sum | xxd -r -p | basenc --base64url | sed -E s/=+$//
W0U4-HGeJmKivoU2XVPWtYO_Btkt2YzB1oLss7ael34

basencコマンドが使えない場合には、sedコマンドを使ってBase64形式からBase64URL形式に変換することもできます。

$ echo -n "abcde12345xyz" | sha256sum | xxd -r -p | base64 | sed s/\\+/-/g | sed s/\\//_/g | sed -E s/=+$//
W0U4-HGeJmKivoU2XVPWtYO_Btkt2YzB1oLss7ael34

basencコマンドでBase64URL形式をデコードするにはパディングをつける必要がある

前述のとおり、basencコマンドではBase64URL形式にパディングがつけられてしまいます。

$ echo -n "abcde12345xyz" | basenc --base64url
YWJjZGUxMjM0NXh5eg==

パディングはsedコマンドで削除できます。

$ echo -n "abcde12345xyz" | basenc --base64url | sed -E s/=+$//
YWJjZGUxMjM0NXh5eg

basencコマンドは-dオプション(--decode)によってデコードをおこなうことができますが、パディングを削除したBase64URL形式の文字列をデコードしようとするとエラーになります。

正しい値のあとにエラーメッセージが続くというバグのような怪しい動作になっています。

$ echo -n "YWJjZGUxMjM0NXh5eg" | basenc -d --base64url
abcde12345xyzbasenc: invalid input

これを回避するにはパディングを補ってやる必要があります。 パディングは4の倍数になるようにつけられるので、以下のようにデコードしたい文字列の長さに合わせてパディングをつけてデコードをおこないます。

$ str="YWJjZGUxMjM0NXh5eg"; echo -n "${str}====" | fold -w 4 | sed '$ d'| basenc --base64url -d
abcde12345xyz

変数strにデコードしたい文字列を入れます。 foldコマンドによって4文字ずつ改行をおこない、sedコマンドによって最後の行を削除することでパディングを適切な長さにしています。

basencコマンドが使えないとき、Base64URL形式からデコードするには一度Base64形式にしてからbase64コマンドでデコードします。

$ str="YWJjZGUxMjM0NXh5eg"; echo -n "${str}====" | sed s/-/\\+/g | sed s/_/\\//g | fold -w 4 | sed '$ d'| base64 -d
abcde12345xyz

参考