Perl と JavaScript 間で RSA 暗号を使いたい
サンプルが見つからないってことは、普通はやらないことをやろうとしているんだと思う。
ということで、以下のようなことをしてみたかったので、試してみました。
- Client (JavaScript) 側で RSA 鍵 (秘密鍵・公開鍵) を生成
- 公開鍵を Server (Perl) に POST
- Server (Perl) 側は受け取った公開鍵を用いて暗号化し送信
- Client (JavaScript) は受け取った暗号文を 1 で生成した秘密鍵で復号
とりあえずサンプルで作ったものは
- RSA 鍵を生成 (Client)
- 公開鍵を POST (Client)
- UUID を生成 (Server)
- POST された公開鍵を使い暗号化 (Server)
- 受信した暗号文を秘密鍵で復号 (Client)
という動作をします。
できたもの、ソースコードはこちら。
- JP RSA
- http://jprsa.magicalhat.jp/
- hatyuki/p5-jprsa
- https://github.com/hatyuki/p5-jprsa
以下雑文
JavaScript 側も、Perl 側も RSA を扱うライブラリはいくつかありますが、以下の2つを選びました。
- JSEncrypt (JavaScript)
- Crypt::OpenSSL::RSA (Perl)
選ぶにあたり検討した結果は以下の通り。
JavaScript
Cryptico
ググるとまず最初にヒット。
パスワードから RSA 鍵を生成する機能も標準でついている。
ただ、実際の暗号文は AES 共通鍵を RSA 公開鍵で暗号化し、本文を AES 共通鍵で暗号化する必要がある。
暗号文は "?" で2分割されており、前半が RSA 公開鍵で暗号化された AES 共通鍵、後半が前半部分についている AES 共通鍵で暗号化された暗号文。
Perl 側で取り扱うのがめんどくさそうだったので却下。
+-------------------------------------+---+--------------------------------+ | RSA で暗号化された AES 共通鍵 (Base64) | ? | AES で暗号化された暗号文 (Base64) | +-------------------------------------+---+--------------------------------+
JSEncrypt
Password から鍵を生成する機能はない。けれど、今回は不要なのでこれで OK。
生成される鍵も Crypt::OpenSSL::RSA でそのままロードできる形なので、扱いも簡単だった。
Perl
Crypt::RSA
ドキュメントを見る限りだと、外部 "ファイル" からしか鍵生成ができないように読めた。
Crypt::RSA::Key::Public のソースコードを読んでみたところ、引数で鍵を生成することもできる感じはした。動かしてないけど多分こんな感じ。
use strict; use warnings; use feature qw/ say /; use Crypt::RSA::Key::Public; my $exponent = 'HEX_VALUE'; # <- 必要に応じて decode_base64( ) して 16 進数の値に my $modulus = 'HEX_VALUE'; # <- 必要に応じて decode_base64( ) して 16 進数の値に my $public_key = Crypt::RSA::Key::Public->new; $public_key->n($modulus); $public_key->e($exponent); say $public_key->check; # => die しないといいな
Crypt::OpenSSL::RSA
JSEncrypt の吐き出す public key を直接 import できたのでお手軽。
use_pkcs1_padding を忘れると JSEncrypt で復号できない。
暗号化されたデータはそのままではバイナリデータとなっており、これまた JSEncrypt では復号できないため Base64 でエンコードするのをお忘れなく。
use strict; use warnings; use feature qw/ say /; use Crypt::OpenSSL::RSA; use MIME::Base64 qw/ encode_base64 /; my $rsa = Crypt::OpenSSL::RSA->new_public_key($request->param('publickey')); $rsa->use_pkcs1_padding; say encode_base64( $rsa->encrypt('hogehoge') );
とりあえずこんな感じで、目的は達成できました。
わーい。