パスワードなどの重要な情報は、平文のまま保存せず、暗号化して保存することが望ましい状況であると言えます。
最近では保存するDBやハードウェアそのものを暗号化する技術もありますが、プログラムにおいても暗号化する仕組みを知っておくことが重要です。
本記事では、Javaで文字列を暗号化する方法について解説します。
記事内に記載しているプログラムは、Java11を使って動作確認をしています。
Javaで文字列を暗号化する方法
Javaで文字列を暗号化する方法について、以下の内容を解説します。
- 暗号化する方法の種類について
- 一方向性関数によるハッシュ化
- 共通鍵暗号方式による暗号化と復号化
- 公開鍵暗号方式による暗号化と復号化
暗号化する方法の種類について
暗号化とはデータの内容を他人には分からないようにして(暗号化)、関係者のみが解読できるようにする(復号化)技術のことです。
Javaにおいても標準機能として、暗号化と復号化する機能が提供されています。
暗号化には大きく2つの方式があります。
- 共通鍵暗号方式:暗号化と復号化を共通の鍵を使って行う方式
- 公開鍵暗号方式:暗号化と復号化を別々の鍵(公開鍵と秘密鍵)を使って行う方式
また、正確には暗号化ではないのですが、一方向性関数を使ったハッシュ化もよく使われる技術ですので、合わせて紹介します。
一方向性関数とは、関数を通したデータからは、元のデータに戻すことができない仕組みで、ハッシュ化と呼ばれたりします。
一方向性関数によるハッシュ化
ハッシュ化は、データの改ざんが行われていないかどうかを確認する目的として使われます。
一度ハッシュ化すると元に戻すことができない特性から、パスワードを保存する際に用いられることが多いです。
万が一流出しても元に戻すことができず、入力されたパスワードと保存されたパスワードが一致するかどうかを確認する用途に使用されます。
Javaでハッシュ化するには、java.security.MessageDigestクラスを使って行います。
ハッシュ化するアルゴリズムは、以下のようなもの(詳細はAPIリファレンス参照)を使うことができます。
- MD5
- SHA-1
- SHA-256
- SHA3-512
MessageDigestを使ったサンプルコードは以下のようになります。
public static void cryptoHash(String text) throws NoSuchAlgorithmException { // ハッシュ化 MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] b = digest.digest(text.getBytes()); System.out.println(b); // -> [B@16b98e56 // ハッシュ値の確認 MessageDigest testDigest = MessageDigest.getInstance("SHA-256"); String testText1 = "暗号化するメッセージ"; System.out.println(Arrays.equals(b, testDigest.digest(testText1.getBytes()))); // -> true String testText2 = "間違ったメッセージ"; System.out.println(Arrays.equals(b, testDigest.digest(testText2.getBytes()))); // -> false }
上記のプログラムでは、引数としてハッシュ化する文字列を渡しています。
今回は、「暗号化するメッセージ」という文字列を渡しています。
ハッシュ化するとbyte配列が得られます。
データベースなどに保存する場合は、このbyte配列を保存しておきます。
そして、別の画面等で入力された文字列を同じアルゴリズムでハッシュ化し、保存したbyte配列と同一であるかどうかを確認します。
同じ文字列「暗号化するメッセージ」をハッシュ化した場合は比較結果が同一となり、異なる文字列「間違ったメッセージ」ではハッシュ化した結果が異なることが分かります。
共通鍵暗号方式による暗号化と復号化
暗号化する際の鍵と、復号化する際の鍵に同じ鍵を使うのが、共通鍵暗号方式です。
鍵の作成は、javax.crypto.KeyGeneratorクラスを使って行います。
共通鍵を生成するアルゴリズムには、以下のようなもの(詳細はAPIリファレンス参照)を使うことができます。
- AES
- DES
- DESede
- HmacSHA512
共通鍵暗号方式を使った暗号化と復号化のサンプルコードは以下のようになります。
public static void cryptoSharedKey(String text) throws Exception { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); SecretKey secretKey = keyGen.generateKey(); SecureRandom secureRandom = new SecureRandom(); byte[] iv = new byte[16]; secureRandom.nextBytes(iv); IvParameterSpec ivParameter = new IvParameterSpec(iv); Cipher cryptoCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cryptoCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameter); // 暗号化 byte[] encryptoText = cryptoCipher.doFinal(text.getBytes()); System.out.println(encryptoText); // -> [B@3f8f9dd6 Cipher decryptoCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); decryptoCipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameter); // 復号化 byte[] decryptoText = decryptoCipher.doFinal(encryptoText); System.out.println(new String(decryptoText)); // -> 暗号化するメッセージ }
先ほどと同様に、暗号化するメッセージとして「暗号化するメッセージ」という文字列を引数で渡しています。
最初は共通鍵の生成と初期化ベクトル(暗号化の解読を困難にするためのランダム文字列)を生成しています。
暗号化および復号化は、javax.crypto.Cipherクラスを使って行います。
Cipherは、アルゴリズム名、アルゴリズム・モード、アルゴリズム・パディングを指定して生成します。
それぞれに指定できるものは、APIリファレンスを参照ください。
上記のプログラムでは、それぞれAES、CBC、PKCS5Paddingを使っています。
そして、共通鍵と初期化ベクトルを合わせてCipherを初期化します。
ここまでが準備で、暗号化や復号化はdoFinalで行います。
initにて設定したモードがENCRYPT_MODEであれば暗号化、DECRYPT_MODEであれば復号化を行います。
暗号化したテキストを復号化すると元の「暗号化するメッセージ」になっています。
公開鍵暗号方式による暗号化と復号化
暗号化する際の鍵と、復号化する際に別々の鍵を使うのが、公開鍵暗号方式です。
鍵の生成は、java.security.KeyPairGeneratorクラスを使って行います。
鍵を生成するアルゴリズムには、以下のようなもの(詳細はAPIリファレンス参照)を使うことができます。
- DSA
- RSA
- EC
公開鍵暗号方式を使った暗号化と復号化のサンプルコードは以下のようになります。
public static void cryptoPublicKey(String text) throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); KeyPair keyPair = keyGen.generateKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); Cipher encryptoCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); encryptoCipher.init(Cipher.ENCRYPT_MODE, publicKey); // 暗号化 byte[] encryptoText = encryptoCipher.doFinal(text.getBytes()); System.out.println(encryptoText); // -> [B@182decdb Cipher decryptoCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); decryptoCipher.init(Cipher.DECRYPT_MODE, privateKey); // 復号化 byte[] decryptoText = decryptoCipher.doFinal(encryptoText); System.out.println(new String(decryptoText)); // -> 暗号化するメッセージ }
先ほどと同様に、暗号化するメッセージとして「暗号化するメッセージ」という文字列を引数で渡しています。
鍵の生成アルゴリズムと鍵長を指定して、公開鍵と秘密鍵を生成します。
暗号化と復号化は、共通鍵のときと同様にjavax.crypto.Cipherクラスを使って行います。
今回は、アルゴリズム名、アルゴリズム・モード、アルゴリズム・パディングにそれぞれRSA、ECB、PKCS1Paddingを使っています。
そして暗号化する際には公開鍵を指定し、復号化する際には秘密鍵を指定します。
後は先程と同様に、doFinalで暗号化または復号化を行います。
暗号化したテキストを復号化すると元の「暗号化するメッセージ」になっています。
まとめ
Javaで文字列を暗号化する方法についてまとめると、以下のようになります。
- 暗号化には、共通鍵暗号方式と公開鍵暗号方式がある。
- 暗号化ではないが、一方向性関数を使ったハッシュ化もデータを秘匿化する方法としてよく使われる。
- どの方式を選択するかは、用途や目的に応じて使い分ける。
データを秘匿化したいという要件は、システム開発においてよくあることです。
適切な方式を選択して、安全に運用することで、セキュアなシステムを構築することが、今後ますます重要な技術となります。
今回は、Javaで文字列をハッシュ化/共通鍵暗号化/公開鍵暗号化する方法について解説しました。
以上、参考になれば幸いです。
コメント