Codec.Encryption.RSAの使い方(しかし多分Codec.Crypto.RSAを使うほうが良い)

Haskell勉強中

「すごいHaskell楽しく学ぼう」でHaskellをひと通り勉強した。
この本読みやすくて面白いですよ。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

Codec.Encryption.RSA

色々Haskellで面白そうなことを見つけては遊んでいるのだが、"Codec.Encryption.RSA"の用法について日本語の情報がなかったので、解明しつつ記事にまとめようかと思う。
多分Haskell上のRSA暗号の実装です。
RSA暗号は、公開鍵暗号方式の一つで、ネットワーク上の通信の暗号化やデジタル署名などに広く用いられています。
詳しくは→wikipedia:RSA暗号

導入

"Codec.Encryption.RSA"はパッケージ名"crypto"に入っているので、Haskell Platformが入っている環境でなら下記コマンドでインストールできる。

$ cabal install crypto

用法

hackage上の情報だとこれのみ→http://hackage.haskell.org/packages/archive/Crypto/4.2.0/doc/html/Codec-Encryption-RSA.html
「簡単に使えるよ!詳しくはここ見てね!→リンク」っていう部分がすべてリンク切れなので、どうしたものか…。

"Codec.Encryption.RSA"の関数は下記の2つ。

encrypt :: ([Octet], [Octet]) -> [Octet] -> [Octet]Source
decrypt :: ([Octet], [Octet]) -> [Octet] -> [Octet]Source

OctetはWord8の別名なので、ByteStringをunpackして[Octet]が作れる。

RSA129

RSA129というRSAの解読の懸賞問題です。参考→http://www.mew.org/~kazu/doc/rsa.html
なお今回は文字列に変換する部分は省略。暗号化、復号化した数値が一致する事を確認するのみにする。
ライブラリの使用法の確認に目的を絞る!

この問題の公開鍵と、後に解読された秘密鍵が下記の通り。

n = 114381625757888867669235779976146612010218296721242362562561842935706935245733897830597123563958705058989075147599290026879543541
e = 9007
d = 106698614368578024442868771328920154780709906633937862801226224496631063125911774470873340168597462306553968544513277109053606095

公開鍵は(n, e)で、秘密鍵は(n, d)。
また、問題に対する回答が存在することを証明する為に掲載された署名がこちら。

署名
sign = 16717861150380844246015271389168398245436901032358311217835038446929062655448792237114490509578608655662496577974840004057020373
復号された署名
sign' = 06091819200019151222051800230914190015140500082114041805040004151212011819

署名は"sign'"を出題者のみが持っている秘密鍵で暗号化したもので、公開鍵で復号化できます。
[平文署名(sign')] - [秘密鍵] -> [暗号化署名(sign)]
[暗号化署名(sign)] - [公開鍵] -> [平文署名(sign')]
秘密鍵を持っている人のみが暗号化した署名(sign)を作れるので、署名として活用されます。

また、問題として出題された暗号文と復号化した解がこちら。

暗号文
message = 96869613754622061477140922254355882905759991124574319874695120930816298225145708356931476622883989628013391990551829945157815154
復号化された暗号文
message' = 200805001301070903002315180419000118050019172105011309190800151919090618010705

[平文(message')] - [公開鍵] -> [暗号文(message)]
[暗号文(message)] - [秘密鍵] -> [平文(message')]
公開鍵で誰でも暗号文(message)を作れますが、秘密鍵を持った人しか平文を得る事ができません。

Haskellでこれらの数値はIntegerとして扱えますが、今回使用するライブラリでは、[Octet]に変換しておかないといけません。
各数値を8bitで区切り[Octet]にして各々の名前に束縛します。

import Codec.Utils
n = i2osp 8 114381625757888867669235779976146612010218296721242362562561842935706935245733897830597123563958705058989075147599290026879543541
e = i2osp 8 9007
d = i2osp 8 106698614368578024442868771328920154780709906633937862801226224496631063125911774470873340168597462306553968544513277109053606095

-- 署名
sign = i2osp 8 16717861150380844246015271389168398245436901032358311217835038446929062655448792237114490509578608655662496577974840004057020373
-- 復号された署名
sign' = i2osp 8 06091819200019151222051800230914190015140500082114041805040004151212011819

-- 暗号文
message = i2osp 8 96869613754622061477140922254355882905759991124574319874695120930816298225145708356931476622883989628013391990551829945157815154
-- 復号化された暗号文
message' = i2osp 8 200805001301070903002315180419000118050019172105011309190800151919090618010705

上記コードをファイルに保存してghciでロードして確認を行います。

署名の復号化
*Main> import qualified Codec.Encryption.RSA as RSA
*Main RSA> fromTwosComp (RSA.decrypt (n,e) sign) == fromTwosComp sign'
True

[暗号化署名(sign)] - [公開鍵] -> [平文署名(sign')]
署名を公開鍵で復号した結果と予め知っている復号後の署名と一致しました。

署名の暗号化
*Main> import qualified Codec.Encryption.RSA as RSA
*Main RSA> fromTwosComp (RSA.encrypt (n,d) sign') == fromTwosComp sign
True

[平文署名(sign')] - [秘密鍵] -> [暗号化署名(sign)]
復号後の署名を暗号化すると、署名が得られます。

暗号文の復号化
*Main> import qualified Codec.Encryption.RSA as RSA
*Main RSA> fromTwosComp (RSA.decrypt (n,d) message) == fromTwosComp message'
True

[暗号文(message)] - [秘密鍵] -> [平文(message')]

平文の暗号化
*Main> import qualified Codec.Encryption.RSA as RSA
*Main RSA> fromTwosComp (RSA.encrypt (n,e) message') == fromTwosComp message
True

[平文(message')] - [公開鍵] -> [暗号文(message)]

結論

引数がすべて[Octet]で何を与えたらいいか分からない上に、
どうやって鍵を生成すればいいかも、HackageDB上の情報を読んだ感じではわかりませんでした。
RSA Packageの中の"Codec.Crypto.RSA"を使ったほうがよさげ。