はちゅにっき

こっちのブログはまったり更新

Perl と JavaScript 間で RSA 暗号を使いたい

サンプルが見つからないってことは、普通はやらないことをやろうとしているんだと思う。
ということで、以下のようなことをしてみたかったので、試してみました。

  1. Client (JavaScript) 側で RSA 鍵 (秘密鍵・公開鍵) を生成
  2. 公開鍵を Server (Perl) に POST
  3. Server (Perl) 側は受け取った公開鍵を用いて暗号化し送信
  4. Client (JavaScript) は受け取った暗号文を 1 で生成した秘密鍵で復号

とりあえずサンプルで作ったものは

  1. RSA 鍵を生成 (Client)
  2. 公開鍵を POST (Client)
  3. UUID を生成 (Server)
  4. POST された公開鍵を使い暗号化 (Server)
  5. 受信した暗号文を秘密鍵で復号 (Client)

という動作をします。
できたもの、ソースコードはこちら。

JP RSA
http://jprsa.magicalhat.jp/
hatyuki/p5-jprsa
https://github.com/hatyuki/p5-jprsa

以下雑文


JavaScript 側も、Perl 側も RSA を扱うライブラリはいくつかありますが、以下の2つを選びました。

選ぶにあたり検討した結果は以下の通り。

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') );

とりあえずこんな感じで、目的は達成できました。
わーい。