怠惰系エンジニアのメモ帳

勉強した内容をメモしていきます。解説ブログではないので悪しからず。

【Java】AESを利用した暗号化・復号を標準APIで行う

結論

  • javax.crypto.Cipher を使用して暗号化・復号を行う
  • 鍵は javax.crypto.spec.SecretKeySpec を使用
  • 暗号利用モードが「初期ベクトル」を必要とする方式の場合、javax.crypto.spec.IvParameterSpec を使用。
  • 最終的には、SecretKeySpecIvParameterSpec を元に Cipher を初期化する。

AESについて

  • 共通鍵暗号方式アルゴリズム
  • ブロック暗号である
    • ある一定のデータ長で区切って、それを「ブロック」という単位で処理する。
    • なお、「ブロック暗号」とは別に「ストリーム暗号」もある。
  • 鍵の長さは128bit、192bit、256bitの3種
    • Javaの場合は128bitがデフォルト

コード

final String secretKey = "iS5KfTPfn4xLjYYy";     // ①
final String initVector = "ncfeKKfPYedi9hJs";    // ②
final String algorithm = "AES/CBC/PKCS5Padding"; // ③

final SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES"); // ④
final IvParameterSpec ivSpec = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8));    // ⑤
final Cipher cipher = Cipher.getInstance(algorithm); // ⑥
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);   // ⑦

// 暗号化対象となるデータ
final byte[] source = "あいうえお".getBytes(StandardCharsets.UTF_8);

byte[] encryptBytes = cipher.doFinal(source); // ⑧
    • 暗号化・複合で使用する鍵。(④で使用する)
    • デフォルトの場合、128bit(=16byte)である必要がある。
    • 16byteでない場合 java.security.InvalidKeyException がスローされる。
    • 初期ベクトル。(⑤で使用する)
    • 今回は暗号利用モードを「CBC(Cipher Block Chaining)」に指定しているため、初期ベクトルが必要となる。
    • 鍵の長さと同一でなければならない(はず)。
    • 長さが異なる場合 java.security.InvalidAlgorithmParameterException がスローされる。
    • 実際に暗号化・複合を行うアルゴリズム。(⑥で使用する)
    • アルゴリズム」「暗号利用モード」「パディング方式」の順で、/区切りで定義する。
    • パディング方式は、暗号化対象となるデータのブロックがブロック長に満たない場合にパディングするか否かを指定する。
    • SecretKeySpec の生成。
    • 第一引数に ① で用意した鍵のバイト配列、第二引数にアルゴリズムを文字列で指定する。
    • 第二引数のアルゴリズムは単に AES と指定する。
      • ①の文字列を渡すと java.security.InvalidKeyException がスローされる。
    • IvParameterSpec の生成。
    • 第一引数には ② の文字列をバイト配列に変換して渡す。
    • Cipherを使用するために、初期化を行う。
    • 第一引数には暗号化を行うか、複合を行うかを int で指定する。
      • 今回は暗号化を想定して Cipher.ENCRYPT_MODE を指定しているが、複合したい場合は Cipher.DECRYPT_MODE を指定する。
    • 暗号化

おまけ

⑥の Cipher の生成で、"AES" しか指定しなかった場合は "AES/ECB/PKCS5Padding" と同義っぽい

@Test
public void test() throws Exception {
    final SecretKeySpec key = new SecretKeySpec("xhnCSSG6LRzgfgrp".getBytes(), "AES");

    final Cipher cipher1 = Cipher.getInstance("AES");
    final Cipher cipher2 = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher1.init(Cipher.ENCRYPT_MODE, key);
    cipher2.init(Cipher.ENCRYPT_MODE, key);

    final byte[] source = "abcde".getBytes();
    final byte[] encrypt1 = cipher1.doFinal(source);
    final byte[] encrypt2 = cipher2.doFinal(source);

    assertThat(encrypt1).containsSequence(encrypt2);
}