JavaのSpringSecurityでAESの暗号化/復号化処理を実装する

サクっと作るなら以下が参考になります。

http://terasolunaorg.github.io/guideline/current/ja/Security/Encryption.html

暗号化

    public static String encryptTextByAesWithGcm(String secret, String salt, String plainText) {
        TextEncryptor aesTextEncryptor = Encryptors.delux(secret, salt);

        return aesTextEncryptor.encrypt(plainText);
    }

復号化

    public static String decryptTextByAesWithGcm(String secret, String salt, String cipherText) {
        TextEncryptor aesTextEncryptor = Encryptors.delux(secret, salt);

        return aesTextEncryptor.decrypt(cipherText);
    }

補足

ここからが個人的には重要

1.secretはプレーンテキストでいいのですが、saltはプレーンテキストではダメなので以下みたいになります。

String salt = new String(Hex.encodeHexString("salt".getBytes()));

2.参考サイト

暗号化の結果について
encryptメソッドの返り値 (暗号化の結果) は実行毎に異なる値を返すが、鍵とソルトが同一であれば復号処理の結果は同一になる (正しく復号できる) 。

の記載、初期化ベクトル (iv)の役割よるものです。
ivは、同じ鍵を使用して同じ平文データを複数回暗号化するときに異なる暗号文を生成します。
ivはランダムな値で、セッションごとに生成される。

セッションごと?確かにソースをSpringSecurityのソースを追っていくと、以下になっている。

    /**
     * Creates a standard password-based bytes encryptor using 256 bit AES encryption.
     * Derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation
     * Function #2). Salts the password to prevent dictionary attacks against the key. The
     * provided salt is expected to be hex-encoded; it should be random and at least 8
     * bytes in length. Also applies a random 16-byte initialization vector to ensure each
     * encrypted message will be unique. Requires Java 6. NOTE: This mode is not
     * <a href="https://en.wikipedia.org/wiki/Authenticated_encryption">authenticated</a>
     * and does not provide any guarantees about the authenticity of the data. For a more
     * secure alternative, users should prefer
     * {@link #stronger(CharSequence, CharSequence)}.
     * @param password the password used to generate the encryptor's secret key; should
     * not be shared
     * @param salt a hex-encoded, random, site-global salt value to use to generate the
     * key
     *
     * @see Encryptors#stronger(CharSequence, CharSequence)
     */
    public static BytesEncryptor standard(CharSequence password, CharSequence salt) {
        return new AesBytesEncryptor(password.toString(), salt, KeyGenerators.secureRandom(16));
    }

以下のJavaDocによると、、、

https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/crypto/encrypt/AesBytesEncryptor.html

new AesBytesEncryptorの第三引数の KeyGenerators.secureRandom(16) がiv

確かに乱数

では、復号化にも同じ値のivがいるのかなと思ったらソース見たら以下のように暗号化後のテキストから取得しているようなので大丈夫そうでした。

    @Override
    public byte[] decrypt(byte[] encryptedBytes) {
        synchronized (this.decryptor) {
            byte[] iv = iv(encryptedBytes);
            CipherUtils.initCipher(this.decryptor, Cipher.DECRYPT_MODE, this.secretKey, this.alg.getParameterSpec(iv));
            return CipherUtils.doFinal(this.decryptor,
                    (this.ivGenerator != NULL_IV_GENERATOR) ? encrypted(encryptedBytes, iv.length) : encryptedBytes);
        }
    }

    private byte[] iv(byte[] encrypted) {
        return (this.ivGenerator != NULL_IV_GENERATOR)
                ? EncodingUtils.subArray(encrypted, 0, this.ivGenerator.getKeyLength())
                : NULL_IV_GENERATOR.generateKey();
    }

まとめ

ということで改めて復習です。

Secret:
Secretは一般的にユーザーが選択する文字列で、秘密情報を保護するために使用されます。
Secretベースのセキュリティでは、ユーザーのSecretから導かれた鍵を生成します。このとき、パスワードを安全に保管し、強力な鍵導出関数(PBKDF2、bcrypt、Scryptなど)を使用することが重要です。

Salt:
SaltはSecretベースのセキュリティで使用され、Secretに追加されるランダムなデータです。
Saltはユーザーごとに異なり、同じSecretを持つユーザーのハッシュ値を異なるものにします。これにより、レインボーテーブル攻撃から保護されます。

初期化ベクトル (iv):
ivは、同じ鍵を使用して同じ平文データを複数回暗号化するときに異なる暗号文を生成します。
ivはランダムな値で、セッションごとに生成される。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です