前回はSalesforceでの暗号化、複合化の仕組みをご紹介しました。
今回はJavaでの仕組みの回となります。
また、Base64化した暗号文字列をやり取りすることで、相互に可逆的な暗号化テキストのやり取りが行えるところまで試してみましょう。
JavaによるAES暗号化
はじめに暗号化を行います。
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import java.util.Arrays; public class MyAESEncrypt { private static final String characterEncoding = "UTF-8"; private static final String cipherTransformation = "AES/CBC/PKCS5Padding"; private static final String aesEncryptionAlgorithm = "AES"; private static final String ivInitial = "secrettoencrypts"; private static final String keyString = "1234567890abcdef"; public static byte[] encryptBase64EncodedWithManagedIV(String clearText) throws Exception { byte[] bClearText = clearText.getBytes(characterEncoding); return encrypt(bClearText); } public static byte[] encrypt(byte[] bClearText) throws Exception{ Cipher cipher = Cipher.getInstance(cipherTransformation); SecretKeySpec secretKeySpecy = new SecretKeySpec(keyString.getBytes(characterEncoding), aesEncryptionAlgorithm); byte[] initialVector = Arrays.copyOfRange(ivInitial.getBytes(characterEncoding),0,16); IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpecy, ivParameterSpec); byte[] trimmedClearText2 = cipher.doFinal(bClearText); return trimmedClearText2; } public static void main(String args[]) throws Exception{ byte[] clearText = encryptBase64EncodedWithManagedIV( "This is new Crypton"); byte[] encryptBytesBase64 = Base64.encodeBase64(clearText, false); System.out.println("encryptedValue11=" + new String(encryptBytesBase64)); } }
Cipher cipher = Cipher.getInstance(cipherTransformation);
最初にCipherクラスをインスタンス化します。引数のcipherTransformationは「AES/CBC/PKCS5Padding」ですね。最初のAES
は暗号化の方式、次のCBCはモードです。モードは簡単に言うと、処理方法みたいなものです。最後のPKCS5Paddingはパディング方式になります。詳しくはJavaのこのページを参照してください。
https://docs.oracle.com/javase/jp/8/docs/api/javax/crypto/Cipher.html
SecretKeySpec secretKeySpecy = new SecretKeySpec(keyString.getBytes(characterEncoding), aesEncryptionAlgorithm);
このコードで秘密鍵を生成します。keyStringに指定された文字列をもとに生成するので、この文字列が漏洩しないように気を付けましょう。
IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
このコードで初期化ベクトルを生成します。初期化ベクトルは、同じ鍵を使った暗号化でも、毎回違った結果を出すための引数のようなものだと考えると良いと思います。
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpecy, ivParameterSpec);
上記までに生成した秘密鍵と初期化ベクトルをもとに、Cipherクラスをinitします。これで準備ができましたので、あとは、cipher.doFinalで実際にテキストを暗号化します。
複合
では、複合の方も行ってみましょう。
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import java.util.Arrays; public class MyAESDecrypt { private static final String characterEncoding = "UTF-8"; private static final String cipherTransformation = "AES/CBC/PKCS5Padding"; private static final String aesEncryptionAlgorithm = "AES"; private static final String ivInitial = "secrettoencrypts"; private static final String keyString = "1234567890abcdef"; public static byte[] decryptBase64EncodedWithManagedIV(String encryptedText) throws Exception { byte[] cipherText = Base64.decodeBase64(encryptedText.getBytes()); return decryptWithManagedIV(cipherText); } public static byte[] decryptWithManagedIV(byte[] cipherText) throws Exception{ byte[] initialVector = Arrays.copyOfRange(ivInitial.getBytes(),0,16); byte[] trimmedCipherText = Arrays.copyOfRange(cipherText,0,cipherText.length); return decrypt(trimmedCipherText, initialVector); } public static byte[] decrypt(byte[] cipherText, byte[] initialVector) throws Exception{ Cipher cipher = Cipher.getInstance(cipherTransformation); SecretKeySpec secretKeySpecy = new SecretKeySpec(keyString.getBytes(), aesEncryptionAlgorithm); IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector); cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec); cipherText = cipher.doFinal(cipherText); return cipherText; } public static void main(String args[]) throws Exception{ byte[] clearText = decryptBase64EncodedWithManagedIV("qMtiTAjz9zINB8l7ni2My***OAhdFjoiRNWYMU2AbRQ="); System.out.println("ClearText:" + new String(clearText,characterEncoding)); } }
復号もやっていることは暗号化の逆なので、ほとんど変わらないですね。暗号化のところで出力したBase64を最後に施した文字列を受け取って復号するクラスになります。
下は、前回のSalesforceで生成したBase64の暗号化文字列を復号するコードになります。初期化ベクトルの関係でしょうか、最初の16Byteを削らないと正常に複合できませんでした。この理由についてはまた、別途調査しようと思いますが。。
package test; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import java.util.Arrays; public class MyAESDecrypt2 { private static final String characterEncoding = "UTF-8"; private static final String cipherTransformation = "AES/CBC/PKCS5Padding"; private static final String aesEncryptionAlgorithm = "AES"; public static byte[] decryptBase64EncodedWithManagedIV(String encryptedText, String key) throws Exception { byte[] cipherText = Base64.decodeBase64(encryptedText.getBytes()); byte[] keyBytes = Base64.decodeBase64(key.getBytes()); return decryptWithManagedIV(cipherText, keyBytes); } public static byte[] decryptWithManagedIV(byte[] cipherText, byte[] key) throws Exception{ byte[] initialVector = Arrays.copyOfRange(cipherText,0,16); //ここで16byte削ったものを戻す。なぜなら初期化ベクターが16byteだから。 byte[] trimmedCipherText = Arrays.copyOfRange(cipherText,16,cipherText.length); return decrypt(trimmedCipherText, key, initialVector); } public static byte[] decrypt(byte[] cipherText, byte[] key, byte[] initialVector) throws Exception{ Cipher cipher = Cipher.getInstance(cipherTransformation); SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm); IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector); cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec); cipherText = cipher.doFinal(cipherText); return cipherText; } public static void main(String args[]) throws Exception{ byte[] clearText = decryptBase64EncodedWithManagedIV("SR1cArIMJBNYEKK1uw***3yiLL2xy00i2tlBp5hGAp8=", "Y65DvnhcZ**X2Z**56vJ2g=="); System.out.println("ClearText:" + new String(clearText,characterEncoding)); } }