RSAキーペアの作成・暗号化・復号化のサンプル（JAVA）

タイトルどおり。
ストアに入れたり、ファイルで渡したり色々あると思うので
キーペアはいったんBase64エンコードをかましてます。

```xml:依存性(maven)
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk16</artifactId>
    <version>1.45</version>
</dependency>
```

```java:RSAKeyPairSample.java
/**
 * RSAKeyPairの作成、暗号化、復号化のサンプル
 * @author ryutaro_hakozaki
 */
public class RSAKeyPairSample {

    public static void main(String argv[]){
        
        RSAKeyPairSample sample = new RSAKeyPairSample();
        
        /**
         * KeyPairの作成
         */
        String[] keyPair = sample.createKeyPairAsBase64();
        System.out.println("公開鍵 == " + keyPair[0]);
        System.out.println("秘密鍵 == " + keyPair[1]);
        
        /**
         * 秘密鍵で暗号化
         *  RSAなのでどっちで暗号化してもいい
         *  セキュア通信では公開鍵で暗号化するし
         *  電子署名では文書のハッシュを秘密鍵で暗号化する
         */
        final String message = "Qiitaは、プログラマのための技術情報共有サービスです。";
        byte[] encryptBytes = sample.encryptByBase64Key(message.getBytes(), keyPair[1]);
        System.out.println("----------------------------------------");
        System.out.println("【平文】");
        System.out.println(message);
        System.out.println("【暗号化結果】");
        System.out.println(new String(encryptBytes));
        
        /**
         * 公開鍵で復号化
         *  対になる鍵であれば復号が可能
         */
        byte[] decryptBytes = sample.decryptBtBase64Key(encryptBytes, keyPair[0]);
        System.out.println("【復号化結果】");
        System.out.println(new String(decryptBytes));
        
    }
    
    /**
     * RSAキーペアを作成する
     * @return Base64エンコードされた公開鍵と暗号鍵
     */
    public String[] createKeyPairAsBase64(){
        String[] keyPair = new String[2];

        /**
         * KeyPairを作成
         */
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        Cipher cipher;
        KeyPairGenerator generator;
        try {
            cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
            generator = KeyPairGenerator.getInstance("RSA", "BC");
        } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException ex) {
            return keyPair;
        }
        SecureRandom random = new SecureRandom();
        generator.initialize(2048, random);
        KeyPair pair = generator.generateKeyPair();
        
        /**
         * KeyPairをBase64エンコードして返却
         */
        keyPair[0] = encodeObjectAsBase64(pair.getPublic());
        keyPair[1] = encodeObjectAsBase64(pair.getPrivate());
        
        return keyPair;        
    }

    /**
     * 暗号化を行う
     * @param data
     * @param base64Key
     * @return 暗号化されたデータ
     */
    public byte[] encryptByBase64Key(byte[] data, String base64Key){
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        try {
            Key myKey = (Key) decodeObjectFromBase64(base64Key);
            Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
            cipher.init(Cipher.ENCRYPT_MODE, myKey, new SecureRandom());
            return cipher.doFinal(data);
        } catch (IOException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidKeyException  ex) {
            return null;
        }
    }
    
    /**
     * 復号化を行う
     * @param data
     * @param base64Key
     * @return 復号化されたデータ
     */
    private byte[] decryptBtBase64Key(byte[] data, String base64Key){
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        try {
            Key myKey = (Key) decodeObjectFromBase64(base64Key);
            Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
            cipher.init(Cipher.DECRYPT_MODE, myKey, new SecureRandom());
            return cipher.doFinal(data);
        } catch (IOException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidKeyException  ex) {
            return null;
        }
    }
    
    /**
     * ObjectをBase64エンコード
     * @param o
     * @return エンコード結果
     */    
    private static String encodeObjectAsBase64(Object o){
        // 状況に応じてだが、今回はバイトを圧縮して少しでも小さなサイズを返す
        try(ByteArrayOutputStream byteos = new ByteArrayOutputStream();
            GZIPOutputStream gos = new GZIPOutputStream(byteos);) {
            try(ObjectOutputStream objos = new ObjectOutputStream(gos)){
                objos.writeObject(o);
            }
            byte[] retObject = byteos.toByteArray();
            return Base64.getEncoder().encodeToString(retObject);
        } catch (IOException ex) {
            Logger.getLogger(RSAKeyPairSample.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    /**
     * Base64エンコードをObjectに変換
     * @param s
     * @return
     * @throws IOException 
     */
    private static Object decodeObjectFromBase64(String s) throws IOException{
       byte[] bytes = Base64.getDecoder().decode(s);
       try(GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes))) {
           return new ObjectInputStream(gis).readObject();
       } catch (ClassNotFoundException ex) {
           return null;
       }
    }

}
```
```text:実行結果
公開鍵 == H4sIAAAAAAAAAFvzloG1uIhBN78oXS8pvzQvuTI5sbgkJ1UvKzlVr6AovywzJbVI（省略）
秘密鍵 == H4sIAAAAAAAAAJVVe1QTdxae8AiRhzwiUApUEamAJRBQtx608lBsIArIY4GAOEmGZGJeTiaQAA（省略）
----------------------------------------
【平文】
Qiitaは、プログラマのための技術情報共有サービスです。
【暗号化結果】
（省略）
【復号化結果】
Qiitaは、プログラマのための技術情報共有サービスです。
```