今日はJava Securityをちょこっとかじって、
ハッシュ化の処理を実装してみたいと思う。
もくじ
・ハッシュ化に用いられるアルゴリズムと、メッセージダイジェスト
1.Javaに入る前に… ハッシュ化とは
本題に入る前に、まずはセキュリティ全般の基本をおさらい。
■ハッシュ化と暗号化の違い
ハッシュ化の説明にあたり、「暗号化」との違いを考えると分かりやすいので、
先に暗号化について説明する。
暗号化とは、
ある文字列を、何かしらの規則に従って、自分にはわかるが、
他の人には分からないような文字列に変換すること。
例えば、「こんにちは」という文字列を、
「ローマ字に直したときの子音だけを先に並べ、そのあと母音を並べる。子音または母音がなければ記号_を並べる」という規則に従って変換すると、
「knntho_iia」となる。
暗号化の規則を知っている人なら、もとの文字列に戻して内容を理解できるが、
そうでなければよく分からない謎の文字列となる。
このように暗号化は「規則に従って、もとに戻すことができる」ものといえる。
IT用語では、もとに戻せることを、「可逆性」といい、
また、暗号化されたものを、もとに戻すことを、「復号」と呼んでいる。
対してハッシュ化とは、ある文字列を、何かしらの規則に従って、
自分を含め誰にも「もとに戻せない」文字列に変換することをいう。
ハッシュ化は、パスワードなど、絶対に漏れてはならない情報を暗号的に扱いたい時などに使われる。
※もとには戻せないが、規則に従って文字列変換をしているため、
ログイン認証などの際に、パスワードが合致するかどうかの確認は、
①ログイン登録時などに、一定の規則に従ってハッシュ化した後の値
②いまログインしようとしている時に、①と同じ規則に従ってハッシュ化した値
の、①・②2つの値を比べて、相違ないかを確かめることで行われる。
どれだけ複雑な規則に沿った暗号化を施しても、
暗号化した文字列と暗号化方法が外部に漏れてしまえば、
すぐに復号できていしまい、もとのパスワードが瞬時に突き止められてしまう。
一方でハッシュ化であれば、ハッシュ化した本人さえも、もとに戻すことができない規則に従っている。
従って、万が一ハッシュ化した文字列とハッシュ化方法が外部に漏れても、
もとに戻す方法が無いため、暗号化時のようにすぐにもとのパスワードを突き止められることはない。
※ただし、ハッシュ化でも完全に安全とはいえない。
ハッシュ化方法が分かれば、「パスワードっぽい文字列」を何万も作り、ひたすらその方法でハッシュ化を繰り返す → ハッシュ化された値と、漏洩したハッシュ化後の文字列とを突き合わせてもとのパスワードを類推 といった攻撃(レインボー攻撃という)も想定される。もとのパスワード類推に時間がかかるとはいえ、「password」などのよく使われる文字列をパスワードにしていると、格好の餌食になるので注意。
また、たまに企業が個人情報漏洩したとして、強制パスワード変更を求めてくることがあるが、それは上記のような攻撃に備えて、パスワードを変更してしまえば、最悪の事態を防げると想定されるためである。
セキュリティ攻撃と対策は、それだけで何百と記事が書けるようなテーマなので、気になる方は別途調べてほしい。
■ハッシュ化に用いられるアルゴリズムと、メッセージダイジェスト
暗号化やハッシュ化に用いられる「規則」のことを、アルゴリズムという。
アルゴリズムは1つではなく、様々な種類が存在している。
種類によって、ハッシュ化された後の文字列の長さ(デフォルト値)は決まっている。
たとえば、「SHA-256」という名前の付いたアルゴリズムで、「password」という文字列をハッシュ化すると、
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
という、64桁の文字列になる。
(同じアルゴリズムかつ、もとが同じ文字列からハッシュ化される値は、常に同じものとなる)
なお、ハッシュ化された後の値(上の例では、64桁の文字列)のことを、
メッセージダイジェストと呼ぶ。
ここまで出てきたカタカナ用語を整理すると、以下の図のような構成となる。
2.Java Securityを用いたハッシュ化
■Java Securityとは
Java Securityは、JDKのjava.baseに含まれている。
すなわち、余計なライブラリを自分で追加しなくとも、
Java標準のセキュリティ関連APIを用いることができる。
また、Java Securityは、
これから紹介するハッシュ化だけでなく、普通の暗号化、公開鍵基盤(PKI)、認証などなど、
セキュリティ領域を幅広くカバーしている。
詳細は公式を参照いただきたい。
(https://docs.oracle.com/javase/jp/9/security/java-security-overview1.htm)
■ハッシュ化例
長々と用語説明してきたが、ハッシュ化を実現するコードはいたって簡単である。
まずはソースコード例を示す。
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HashTest {
public byte[] hash(String plainText) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); //①byte[] b = plainText.getBytes(); //②
return messageDigest.digest(b); //③
}
}
ハッシュ化には、java.security.MessageDigestクラスを用いる。
MessageDigestクラスは、任意サイズのバイト値を、指定したアルゴリズムに沿ってハッシュ化する機能をもつ。
ハッシュ化には、MessageDigestのインスタンスを用いるが、
コンストラクタがprotectedのため、直接インスタンス生成はできない。
その代わり、getInstanceメソッド(※staticメソッド)を用い、
引数でどのアルゴリズムを用いてハッシュ化するか指定してインスタンスを取得する。(①の部分)
アルゴリズム名は、文字列で表記すれば問題ない。
(例では、SHA-256とした)
生成したMessageDigestインスタンスから、digestメソッドを用いてハッシュ化を行うことができる。(③の部分)
ただしハッシュ化の際の引数は、バイト配列である必要があるため、
先にハッシュ化したい文字列をバイト配列に変換しておく必要がある。(②の部分)
なお、上記の処理を行う時は、MessageDigestがNoSuchAlgorithmException例外をスローする可能性があるため、
スロー宣言の追加またはtry-catchによる対応が必要となる。
(例では、スロー宣言を追加した)
3.おわりに
今回はハッシュ化の基礎と、Java Securityを用いたハッシュ化の実装方法基礎を紹介した。
セキュリティは奥が深いのと、現場で公開鍵・秘密鍵らへん扱ってるので、次の機械ではそのあたりを勉強したい。
■参考
MessageDigestクラス公式:
https://docs.oracle.com/javase/jp/8/docs/api/java/security/MessageDigest.html
また、アルゴリズムの分かりやすい一覧記事もあったので自分のために貼っておく。
(https://qiita.com/KEINOS/items/c92268386d265042ea16)