RSAを使った暗号化と復号化のやり方を今日も忘れたので備忘録として残しておく。
マイクロサービスアーキテクチャにおいて、ユーザーの認証情報を引き渡していくための方法として、いろいろなやり方があるが、オーバーヘッドが小さく確実にサービス側、呼び出し元の相互に認証と認可が可能なので公開鍵を使った認証済みのユーザー情報の暗号化がいいと思う。
なお、RSAの場合、鍵長に1024ビットを指定すると128バイトとなり、パディングが11バイトなので暗号化可能な文字列の長さは117バイトまでとなる。
認証済みのユーザーのIDなどを送信するには十分であるが、このあたりは注意が必要だ。
RSA鍵ペアの作成
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024); // RSA鍵の長さを指定する
KeyPair keyPair = keyGen.generateKeyPair();
KeyPair
には、秘密鍵と公開鍵の両方が含まれている。
秘密鍵のPEM形式での書き出し
"-----BEGIN RSA PRIVATE KEY-----\r\n" +
encodeBase64(keyPair.getPrivate().getEncoded()) + "\r\n" +
"-----END RSA PRIVATE KEY-----\r\n";
encodeBase64(byte[])
は64文字ずつ改行を入れたBase64エンコードを行う関数。
公開鍵のPEM形式での書き出し
"-----BEGIN PUBLIC KEY-----\r\n" +
encodeBase64(keyPair.getPublic().getEncoded()) + "\r\n" +
"-----END PUBLIC KEY-----\r\n";
公開鍵での暗号化
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
String encrypted = Base64.getEncoder()
.encodeToString(cipher.doFinal(plainText.getBytes("ISO-8859-1")));
暗号化モードとして ECB
を利用しているので、平文が同じ場合は常に同じ暗号文になる。これが許容できない場合はデータの頭にでも乱数でIVの代わりとなるダミーデータを入れておくとよいだろう。
秘密鍵での複合化
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPublic());
String plainText = new String(
cipher.doFinal(Base64.getDecoder().decode(encrypted)), "ISO-8859-1");
まとめ
JWTほど面倒ではなく、あらゆる言語環境で共通して利用可能なRSAによる暗号化はマイクロサービス間の認証と認可の現実解だ。