2021.12.22 コラム
【テックコラム】JavaScript Web Crypto API で SHA256 ハッシュ値を作る
● はじめに
こんにちは。
今回は外部ライブラリを使わず、JavaScriptの標準ライブラリのWeb Crypto APIでSHA256でハッシュ化した話をします。
● SHA256とは
SHA256はハッシュ関数のうちのひとつです
ハッシュ関数とは、渡されたデータを決まった手順で計算し、一定の長さの文字列を出力する関数のことです。
SHA256でハッシュ化することで、256bit(32バイト)の長さのハッシュ値を得ることができます。
例)
12345abcde(ハッシュ化前)
↓
3c373f4953b85cdea588e0436c134c819f570b72c1db740eb35cfc942baa2bce(ハッシュ化後)
ハッシュ化された値を元の値に戻すことは非常に困難です。
同じ入力データでハッシュ化を行うと、毎回同じハッシュ値が生成されます。
● JavaScriptでハッシュ化する方法
1.ライブラリを利用する
jsSHAなど、サードパーティ製のライブラリがあります。これらを利用して文字列をハッシュ化することもできます。
タイトルの通り今回はこの方法は使っていません。
サーバー上に JavaScriptファイル を設置することになり、設置先サーバー等の検討をしなければいけなくなるため、JavaScript のみで出来る方法を採用しました。
2.Web Crypto APIを利用する
JavaScriptに標準で備わっているAPIです。今回はこちらの形で、SHA256への変換を行っています。
外部のコードに依存することなく変換を行うことができ便利でした。
ただし、非SSL環境で使用できない、IE11に対応したい場合はコードの変更など制約があります。以下でご紹介するコードはIE11に対応していません。※後述
コードの作成にあたっては、以下のMDNの記事を参考にしました。
SubtleCrypto.digest() – Web API | MDN
● コード
var text = 'ハッシュ化したい文字'; function async_digestMessage(message) { return new Promise(function(resolve){ var msgUint8 = new TextEncoder("utf-8").encode(message); crypto.subtle.digest('SHA-256', msgUint8).then( function(hashBuffer){ var hashArray = Array.from(new Uint8Array(hashBuffer)); var hashHex = hashArray.map(function(b){return b.toString(16).padStart(2, '0')}).join(''); return resolve(hashHex); }); }) } function getHashText(text) { // ハッシュ化後の文字列を表示 console.log(text); } if(window.Promise && window.crypto){ async_digestMessage(text).then( function(shatxt){ getHashText(shatxt); } ).catch(function(e){ console.log('エラー:', e.message); }) }else{ console.log('Promiseかcryptoに非対応');
こちらのコードの、
ar text = 'ハッシュ化したい文字';
にハッシュ化したい文字を入力してコードを実行すると、ハッシュ化後のコードを得ることができます。
試しに、この【ハッシュ化したい文字】を入力したままコードを実行してみます。
3b1f06cbba487277c3ec1d35335bae10e026fd1095721f9ad1bb50cfea380d93
という文字列を得ることができました。正しくハッシュ化できているかどうかを確認するために、他のSHA256ハッシュ化を扱うプログラムで同じ文字列 【ハッシュ化したい文字】 を変換し、同じハッシュ値になることを確認してみます。
Pythonのhashlibライブラリを使って、同じ文字を変換しました。
同じ値になりました!
● コードの中身について
下記がハッシュ化のためにWeb Crypto APIを使用している関数です。どういった手順でハッシュ値を得ているのか見てみましょう。
function async_digestMessage(message) { return new Promise(function(resolve){ var msgUint8 = new TextEncoder("utf-8").encode(message); crypto.subtle.digest('SHA-256', msgUint8).then( function(hashBuffer){ var hashArray = Array.from(new Uint8Array(hashBuffer)); var hashHex = hashArray.map(function(b){return b.toString(16).padStart(2, '0')}).join(''); return resolve(hashHex); }); }) }
まず、async_digestMessage関数を下記のように呼び出します。ハッシュ化したい文字列を引数としてasync_digestMessage関数に渡します。
async_digestMessage(text).then( function(shatxt){ getHashText(shatxt); } )
結果はPromiseで返ってくるので、.then(…)内に記載した関数の引数(shatxt)としてハッシュ化後の値が渡されます。
async_digestMessage関数の中では、引数messageにハッシュ化したい文字列が渡されます。
function async_digestMessage(message) {
最初に、文字列をバイナリへ変換しします。
var msgUint8 = new TextEncoder("utf-8").encode(message);
次に、Web Crypto APIの出番です。変換した値をcrypto.subtle.digestに渡します。
crypto.subtle.digest('SHA-256', msgUint8).then(
Promiseで返ってきた結果がhashBuffer変数に渡されます。
ここではArrayBufferとしてハッシュ値のバイナリデータが得られるので、それを16進文字列に加工します。
function(hashBuffer){ var hashArray = Array.from(new Uint8Array(hashBuffer)); var hashHex = hashArray.map(function(b){return b.toString(16).padStart(2, '0')}).join(''); return resolve(hashHex); });
加工後の値をハッシュ値として返します。
● 利用上の制限について
・window.crypto.subtleはSSL非対応ページでは利用不可です。
http://~… のページで実行すると、エラーになります。
Web Crypto APIでハッシュ化を行いたいページではSSL対応を行いましょう。
・対応ブラウザ
上記のコードはPromiseが使えないIE11は非対応としています。
また、参考にしたMDNサイトに記載のコードではasync/awaitを利用していますが、async/awaitを含む記述がある場合IE11ではスクリプト自体が構文エラーになってしまう点を鑑みて、Promiseを利用した書き方を採用しました。
● さいごに
Web Crypto APIを利用することで、標準ライブラリのみでSHA256でのハッシュ化を行うことができました。
JavaScriptのAPIはブラウザの対応状況によって使うかどうか悩むところですが、使えるものは積極的に利用を検討していきたいですね。
弊社では事業会社様のデジタルマーケティング活動の支援を行っています。
また、自社に専門人材がいない、リソースが足りない等の課題をお持ちの方に、エンジニア領域の支援サービス(Data Engineer Hub)をご提供しています。
お困りごとがございましたら、是非お気軽にご相談ください。
本件に関するお問い合わせは下記にて承ります。
株式会社DataCurrent
info@datacurrent.co.jp