2012年3月24日土曜日

JavaでAES暗号

KeyStoreで鍵を管理して、その鍵をつかって暗号化と復号化をするコード。


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;

public class Encryptor {

    /** エリアス名 */
    private static final String ALIAS_NAME = "aesKey";

    /** 暗号化キーを保存するファイル */
    private File keyFile;

    /** 認証用パスワード */
    private static final char[] KEY_PASSWORD = {'p', 'a', 's', 's', 'w', 'o',
            'r', 'd'};

    /** IV */
    private static final IvParameterSpec IV = new IvParameterSpec("1234567812345678".getBytes());

    /** keyStore */
    private KeyStore jceks;

    public static void main(
            String[] args) throws IOException {
        Encryptor encryptor = new Encryptor(new File(".keyFile"));
        
        /* 初期化(keyStoreをロード */
        encryptor.initialize();
        /* 鍵を保存 */
        encryptor.saveKey();
        /* 暗号化 */
        byte[] encrypt = encryptor.encrypt("これを暗号化するよ");
        /* 復号化 */
        byte[] decrypt = encryptor.decrypt(encrypt);
        System.out.println("new String(decrypt) = " + new String(decrypt));
    }

    public Encryptor(File keyFile) {
        this.keyFile = keyFile;
    }

    private void initialize() throws IOException {
        FileInputStream inputStream = null;
        if (keyFile.exists()) {
            inputStream = new FileInputStream(keyFile);
        }
        KeyStore jceks;
        try {
            jceks = KeyStore.getInstance("JCEKS");
            jceks.load(inputStream, KEY_PASSWORD);
        } catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException e) {
            throw new IllegalStateException("failed to load key store.", e);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
        this.jceks = jceks;
    }

    private byte[] encrypt(String text) {
        try {
            Key key = loadKey(ALIAS_NAME);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key, IV);
            return cipher.doFinal(text.getBytes());
        } catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalStateException("failed to encrypt.", e);
        }
    }

    private byte[] decrypt(byte[] encryptData) {
        try {
            Key key = loadKey(ALIAS_NAME);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key, IV);
            return cipher.doFinal(encryptData);
        } catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalStateException("failed to encrypt.", e);
        }
    }

    private Key loadKey(String key) throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
        return jceks.getKey(ALIAS_NAME, KEY_PASSWORD);
    }

    private void saveKey() throws IOException {
        try {
            if (!jceks.isKeyEntry(ALIAS_NAME)) {
                // 任意のキーを生成してkeyStoreにセット
                KeyGenerator aes = KeyGenerator.getInstance("AES");
                jceks.setKeyEntry(ALIAS_NAME, aes.generateKey(), KEY_PASSWORD, null);
            }

            // keyStoreファイルを保存
            try (FileOutputStream outputStream = new FileOutputStream(keyFile)) {
                jceks.store(outputStream, KEY_PASSWORD);
            }
        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
            throw new IllegalStateException("failed to store key.", e);
        }
    }
}