【Java】Java で暗号化/複合化する ~Java1.8 標準を使用した場合~

■ はじめに

https://blogs.yahoo.co.jp/dk521123/32780473.html
で、暗号化/複合化を実装したが、
Java1.8 標準で、Base64エンコード・デコードが導入されているそうなので使ってみる

■ javax.crypto.Cipher.getInstance() について

https://docs.google.com/presentation/d/1xxAtkuP3s_OysDWxLaMDOZQLn-0dfTqXiPnTpM3Gvsg/edit#slide=id.g386037e24_0118
より抜粋
いろんなシステムで間違ったモードや方法で暗号化している場合が多々見受けられるのでちゃんと理解して実装しましょう

 Cipher.getInstance(“AES”) → 間違い
 Cipher.getInstance(“AES/CTR/NoPadding/”) → 正解
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/SunProviders.html
より抜粋
暗号の変換

javax.crypto.Cipher.getInstance(String transformation)ファクトリ・メソッドは、
algorithm/mode/padding形式の変換を使用してCipherを生成します。
モード/パディングを省略すると、SunJCEプロバイダとSunPKCS11プロバイダは、
多くの対称暗号でECBをデフォルト・モードとして、PKCS5Paddingをデフォルト・パディングとして使用します。

デフォルトを使用するのではなく、アルゴリズム、モードおよびパディングを
完全に指定した変換を使用することをお薦めします。

注意: ECBは、単一のデータ・ブロックに対しては正常に機能し、パラレル化可能ですが、
通常は複数のデータ・ブロックには使用しないでください。

■ Java1.8 標準を使用した場合

サンプル

Main.java
public class Main {
  public static void main(String[] args) {
    try {
      // String key = "123456789012345678901234"; // 暗号化方式「トリプル DES 暗号化 (DES-EDE)」の場合、キーは24文字で
      // String key = "123452222"; // 暗号化方式「Blowfish」の場合、キーの文字長は任意(限度はありそうだが)
      String key = "1234567890123456"; // 暗号化方式「AES」の場合、キーは16文字で
      String original = "This is a source of string!!"; // 元の文字列
      // String algorithm = "DESede"; // 暗号化方式「トリプル DES 暗号化 (DES-EDE)」
      // String algorithm = "Blowfish"; // 暗号化方式「Blowfish」
      String algorithm = "AES"; // 暗号化方式「AES(Advanced Encryption Standard)」
      

      String encrypedResult = CipherHelper.encrypt(original, key, algorithm);
      System.out.println("暗号化文字列:" + encrypedResult);

      String decryptedResult = CipherHelper.decrypt(encrypedResult, key, algorithm);
      System.out.println("復号化文字列:" + decryptedResult);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
CipherHelper.java
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class CipherHelper {
  /**
   * 文字列を16文字の秘密鍵でAES暗号化してBase64した文字列で返す
   */
  public static String encrypt(String originalSource, String secretKey, String algorithm)
      throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
      BadPaddingException {

    byte[] originalBytes = originalSource.getBytes();
    byte[] encryptBytes = cipher(true, originalBytes, secretKey, algorithm);
    byte[] encryptBytesBase64 = Base64.getEncoder().encode(encryptBytes);
    return new String(encryptBytesBase64);
  }

  /**
   * Base64されたAES暗号化文字列を元の文字列に復元する
   */
  public static String decrypt(String encryptBytesBase64String, String secretKey, String algorithm)
      throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
      BadPaddingException {

    byte[] encryptBytes = Base64.getDecoder().decode(encryptBytesBase64String);
    byte[] originalBytes = cipher(false, encryptBytes, secretKey, algorithm);
    return new String(originalBytes);
  }

  /**
   * 暗号化/複合化の共通部分
   */
  private static byte[] cipher(boolean isEncrypt, byte[] source, String secretKey, String algorithm) throws InvalidKeyException,
      NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
    byte[] secretKeyBytes = secretKey.getBytes();

    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, algorithm);
    Cipher cipher = Cipher.getInstance(algorithm);
    if (isEncrypt) {
      cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
    } else {
      cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    }
    
    return cipher.doFinal(source);
  }
}
出力結果
暗号化文字列:B/UZj3IF6F6tObxiTAqdDsDLxUKGCBuAYjVTQP1GSnI=
復号化文字列:This is a source of string!!


■ おまけ:Tomcat のJarを使った場合

 * 結局、暗号化/複合化については、Base64のデコード・エンコードできることがキーになるのかなーっと。
   で、TomcatAxis2PostgreSQLなどで、Jarを追加した場合にでも、Base64のクラスがあるので利用できる。

サンプル

 * 呼び出しMain.javaと出力結果は同じなので省略
CipherHelper.java
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.apache.axiom.util.base64.Base64Utils;

public class CipherHelper {
   /**
    * 文字列を16文字の秘密鍵でAES暗号化してBase64した文字列で返す
    */
   public static String encrypt(String originalSource, String secretKey, String algorithm)
         throws NoSuchAlgorithmException, NoSuchPaddingException,
         InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

      byte[] originalBytes = originalSource.getBytes();
      byte[] encryptBytes = cipher(
            Cipher.ENCRYPT_MODE, originalBytes, secretKey, algorithm);
      String encryptBytesBase64 = Base64Utils.encode(encryptBytes);
      return new String(encryptBytesBase64);
   }
  /**
    * Base64されたAES暗号化文字列を元の文字列に復元する
    */
   public static String decrypt(String encryptBytesBase64String,
         String secretKey, String algorithm) throws NoSuchAlgorithmException,
         NoSuchPaddingException, InvalidKeyException,
         IllegalBlockSizeException, BadPaddingException {

      byte[] encryptBytes = Base64Utils.decode(encryptBytesBase64String);
      byte[] originalBytes = cipher(
            Cipher.DECRYPT_MODE, encryptBytes, secretKey, algorithm);
      return new String(originalBytes);
   }

   /**
    * 暗号化/複合化の共通部分
    */
   private static byte[] cipher(
         int mode, byte[] source, String secretKey, String algorithm)
               throws InvalidKeyException, NoSuchAlgorithmException,
               NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
      byte[] secretKeyBytes = secretKey.getBytes();

      SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, algorithm);
      Cipher cipher = Cipher.getInstance(algorithm);
      cipher.init(mode, secretKeySpec);
      return cipher.doFinal(source);
   }
}


参考文献

* Java1.8 標準のBase64について
http://blog.k11i.biz/2014/09/java-8-base64.html

関連記事

Java

暗号化/複合化する ~Apache Commonsを使用した場合~
https://blogs.yahoo.co.jp/dk521123/32780473.html
Java で暗号化/複合化する ~Java1.8 標準を使用した場合~
https://blogs.yahoo.co.jp/dk521123/34330480.html
Java で暗号化/複合化する ~Java1.8 標準を使用した場合 / IV使用編~
https://blogs.yahoo.co.jp/dk521123/36419973.html
【トラブル】Java で初回の暗号化/複合化処理に時間が掛かる
https://blogs.yahoo.co.jp/dk521123/36783396.html
BouncyCastleライブラリ ~Java暗号ライブラリ~
https://blogs.yahoo.co.jp/dk521123/33256866.html
Java】 セキュアなランダム文字列生成を考える
https://blogs.yahoo.co.jp/dk521123/36415526.html

C#

共有キー暗号方式 ~暗号化編~
https://blogs.yahoo.co.jp/dk521123/30818470.html

その他

暗号化アルゴリズム
https://blogs.yahoo.co.jp/dk521123/36417953.html
暗号に関わる用語
https://blogs.yahoo.co.jp/dk521123/37269757.html