アプリ内に保存するデータを暗号化する(Android)

#最初に...
初投稿です。
普段アプリ開発をする際に、IDなどをアプリ内に保存した際、いつも平文でした。
アプリ内に保存はしていますが、念のため暗号化しようと思い
AES暗号鍵を使用して値を暗号化して保存してみましたので、それについての記事となります。
まず、AES採用の理由とAndroidにおける鍵の保存領域について。

##AES(Advanced Encryption Standard)
AESにした理由は、CRYPTRECにて共通鍵暗号にて推奨されているためです。
(参考:http://www.cryptrec.go.jp/list/cryptrec-ls-0001-2016.pdf)
また、RSAにしない理由としては鍵の使用者が一人しかいないので
非対称鍵にする必要がないと思いました。


##AndroidKeyStoreSystem? :thinking: 
AndroidOSが提供している鍵をセキュアに保存できる領域です。
(APKで使うKeyStoreじゃないです。)
ちなみに指紋認証付き端末ですと、鍵を使用する際に指紋認証をすることを
条件にすることもでき更にセキュアになります。
(Androidの指紋認証付き端末のシェア率を考慮すると、微妙なので
採用は見送りました)

##実際のソース

AES鍵作成

```java
public void createAESKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,"AndroidKeyStore");
            keyGenerator.init(new KeyGenParameterSpec.Builder(ALIAS,KeyProperties.PURPOSE_ENCRYPT|KeyProperties.PURPOSE_DECRYPT)//この鍵は、暗号化・復号を目的として作成しますということを明示してます
            .setCertificateSubject(new X500Principal("CN="+ALIAS))
            .setCertificateSerialNumber(BigInteger.ONE)
            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)//CBC=「Cipher Block Chaining mode」
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
            .setRandomizedEncryptionRequired(false)//初期化ベクトルを固定値とする設定をしています。
            .build());
            keyGenerator.generateKey();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
    }
```

暗号化

```java
public String encrypt(String text) {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            if (!keyStore.containsAlias(ALIAS)) {
                createAESKey();
            }
            SecretKey secretKey = (SecretKey)keyStore.getKey(ALIAS,null);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(IVKEY.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE,secretKey,ivParameterSpec);

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream,cipher);
            cipherOutputStream.write(text.getBytes("UTF-8"));
            cipherOutputStream.close();
            return Base64.encodeToString(outputStream.toByteArray(),Base64.NO_WRAP);
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }
```

復号

```java
public String decrypt(String encryptText) {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            SecretKey secretKey = (SecretKey)keyStore.getKey(ALIAS,null);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(IVKEY.getBytes());
            cipher.init(Cipher.DECRYPT_MODE,secretKey,ivParameterSpec);

            CipherInputStream cipherInputStream = new CipherInputStream(new ByteArrayInputStream(Base64.decode(encryptText,Base64.NO_WRAP)),cipher);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int buffer;
            while ((buffer = cipherInputStream.read())!= -1) {
                byteArrayOutputStream.write(buffer);
            }
            byteArrayOutputStream.close();
            return byteArrayOutputStream.toString("UTF-8");
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }
```

呼び出し

```java
KeyService keyService = new KeyService();
keyService.createAESKey();
String text = "5000兆円欲しい";
String encryptText = keyService.encrypt(text);
String text2 = keyService.decrypt(encryptText);
Log.d("","text="+text);
Log.d("","encryptText="+encryptText);
Log.d("","text2="+text2);
```

結果

```java
text=5000兆円欲しい
encryptText=ooIvV63AJIyyRhZUpUJ2Le4B2DyWUw1ZmSG33Z2IhQM=
text2=5000兆円欲しい
```
##最後に
ご覧いただき、ありがとうございます。
次回はSwift4で同じことをする予定です・・・。