LoginSignup
5
11

More than 5 years have passed since last update.

java 8 での RSA 暗号化・復号化

Last updated at Posted at 2018-06-15

環境

  • Java 8
  • RSA暗号

背景

java8 で RSAの暗号化・復号化をしたい!
後ほど Wicket で暗号化されたリクエストの復号化をする想定です。

2048bit 鍵準備

  • 秘密鍵生成
console
openssl genrsa -out rsa_2048_priv.pem 2048
  • 公開鍵生成
console
openssl rsa -pubout -in rsa_2048_priv.pem -out rsa_2048_pub.pem

生成したファイルはリソースフォルダに保存します

暗号化・復号化用クラス

RSAUtil.java
public final class RSAUtil {

    public static final Logger logger = LoggerFactory.getLogger(RSAUtil.class);
    public static ConcurrentHashMap<String, PublicKey> pubCache = new ConcurrentHashMap<String, PublicKey>();
    public static ConcurrentHashMap<Object, PrivateKey> priCache = new ConcurrentHashMap<Object, PrivateKey>();

    /**
     * 公開鍵によるRSA暗号化
     * @param plain
     * @param key
     * @return
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     */
    public static String encryptWithPublicKey(String plain, PublicKey key) throws IllegalBlockSizeException,
            BadPaddingException, UnsupportedEncodingException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchPaddingException {

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, key);

        byte[] plainBytes = plain.getBytes("UTF-8");
        String result = Base64.encodeBase64String(cipher.doFinal(plainBytes));
        return result;
    }

    /**
     * 秘密鍵によるRSA復号化
     * @param encrypted
     * @param key
     * @return
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     */
    public static String decryptWithPrivateKey(String encrypted, PrivateKey key) throws IllegalBlockSizeException,
            BadPaddingException, UnsupportedEncodingException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchPaddingException {

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, key);

        byte[] encryptedBytes = Base64.decodeBase64(encrypted);
        String result = new String(cipher.doFinal(encryptedBytes), "UTF-8");
        return result;
    }

    /**
     * 公開鍵を pem ファイルから読み込む
     * @param filename
     * @return
     */
    public static PublicKey readPublicKeyFromPem(String filename) {
        PublicKey key = null;
        InputStream is = null;

        // check cache
        if (pubCache.containsKey(filename)) {
            key = (PublicKey) pubCache.get(filename);
            if (key != null) {
                return key;
            }
        }

        try {
            is = RSAUtil.class.getClassLoader().getResourceAsStream(filename);
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            boolean inKey = false;

            for (String line = br.readLine(); line != null; line = br.readLine()) {
                if (!inKey) {
                    if (line.startsWith("-----BEGIN ") && line.endsWith(" PUBLIC KEY-----")) {
                        inKey = true;
                    }
                    continue;
                }
                else {
                    if (line.startsWith("-----BEGIN ") && line.endsWith(" PUBLIC KEY-----")) {
                        inKey = false;
                        break;
                    }
                    else {
                        sb.append(line);
                    }
                }
            }
            byte[] decoded = DatatypeConverter.parseBase64Binary(sb.toString());
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            key = kf.generatePublic(keySpec);

            pubCache.put(filename, key);
        }
        catch (Exception e) {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            logger.error("ERROR:\n"+sw.toString());
        }
        finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {}
            }
        }
        return key;
    }

    /**
     * 秘密鍵を pem ファイルから読み込む
     * @param filename
     * @return
     */
    public static PrivateKey readPrvateKeyFromPem(String filename) {
        PrivateKey key = null;
        InputStream is = null;
        boolean isRSAKey = false;

        if (priCache.containsKey(filename)) {
            key = (PrivateKey) priCache.get(filename);
            if (key != null) {
                return key;
            }
        }

        try {
            is = RSAUtil.class.getClassLoader().getResourceAsStream(filename);
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            boolean inKey = false;

            for (String line = br.readLine(); line != null; line = br.readLine()) {
                if (!inKey) {
                    if (line.startsWith("-----BEGIN ") && line.endsWith(" PRIVATE KEY-----")) {
                        inKey = true;
                        isRSAKey = line.contains("RSA");
                    }
                    continue;
                }
                else {
                    if (line.startsWith("-----BEGIN ") && line.endsWith(" PRIVATE KEY-----")) {
                        inKey = false;
                        isRSAKey = line.contains("RSA");
                        break;
                    }
                    else {
                        sb.append(line);
                    }
                }
            }
            byte[] decoded = DatatypeConverter.parseBase64Binary(sb.toString());
            KeySpec keySpec = null;
            if (isRSAKey) {
                DerInputStream derReader = new DerInputStream(decoded);
                DerValue[] seq = derReader.getSequence(0);
                if (seq.length < 9) {
                    throw new GeneralSecurityException("Could not parse a PKCS1 private key.");
                }
                // skip version seq[0]
                BigInteger modulus = seq[1].getBigInteger();
                BigInteger publicExp = seq[2].getBigInteger();
                BigInteger privateExp = seq[3].getBigInteger();
                BigInteger prime1 = seq[4].getBigInteger();
                BigInteger prime2 = seq[5].getBigInteger();
                BigInteger exponent1 = seq[6].getBigInteger();
                BigInteger exponent2 = seq[7].getBigInteger();
                BigInteger crtCoef = seq[8].getBigInteger();

                keySpec = new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp, prime1, prime2,
                        exponent1, exponent2, crtCoef);
            } else {
                keySpec = new PKCS8EncodedKeySpec(decoded);
            }
            KeyFactory kf = KeyFactory.getInstance("RSA");
            key = kf.generatePrivate(keySpec);

            priCache.put(filename, key);
        }
        catch (Exception e) {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            logger.error("ERROR:\n"+sw.toString());
        }
        finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {}
            }
        }
        return key;
    }
}

使ってみる

TestRSAEncryptDecrypt.java
public class TestRSAEncryptDecrypt {

    public static void main(String[] args) {

        final String prvPemfile = "rsa_2048_priv.pem";
        final String pubPemFile = "rsa_2048_pub.pem";

        final String plain = "THIS is a test.";

        PublicKey pubKey = RSAUtil.readPublicKeyFromPem(pubPemFile);
        PrivateKey prvKey = RSAUtil.readPrvateKeyFromPem(prvPemfile);

        String encrypted = "";
        try {
            encrypted = RSAUtil.encryptWithPublicKey(plain, pubKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Encrypted:" + encrypted);

        String decrypted = "";
        try {
            decrypted = RSAUtil.decryptWithPrivateKey(encrypted, prvKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Decrypted:" + decrypted);
    }

}

結果

console
Encrypted:P3X8B1A+N+MEt7+oSLZ+FsbpS1ddkXIz1zpiDAuTf6/EDEPK9ozPZ+VGeaKvDucj3tkVX5YLvXU4psApuyt/NGHlzbAQryaArUGxK7Ol8Nfpp+l5yUwwi2EG6J85QZWtigH8ITmjvIf+lO8676kyg7snF2MXsIx2fNoen4A8bO8gDJ5/Dh4RkPFT8AUZFz4chVQ4whl1vdegHNO2qWj8x3+2UegqGO7QIE6P57BHGbsyF/3Dpept7Gr1NFUpN43DYA/h+IgRu0OeLCx6sss0MWokK1C+1OOam59pEpNhefIV8wzCaag7N+SfSBXGKViGN25vBfOUhC7dZfYheX7BUQ==
Decrypted:THIS is a test.
5
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
11