私が愛した openssl (プリミティヴ編)

猿が書いたかもしれませんがやはり /index.html にはお世話になっておりまして。GnuTLS もありますが手が馴染んでいるのでついつい openssl と叩いてしまいます。以前と比べると随分と頻度は減ってしまいましたが、やはり叩くとなると openssl なのです。というわけでライブラリではなくコマンドとしての openssl に対する愛を表現してみたいと思います。

あ、ちなみに、暗号に関しては下の左の本が素敵すぎて読んでない人は読んだ方がネットワーク社会を生き抜けます。OpenSSL に関しては右の本が定番なはずなんだけど、いかんせん古いのよねぇ。でも多分基本は変わってないはずなので libssl とか libcrypto をいじらなきゃいけなくなったら読んでもいいかも。

enc

主に共通鍵暗号方式による暗号化・復号を行いますが、BASE64 や zlib も扱えるので可逆変換を担っていると言ってもいいのかな?

$ openssl enc -aes-128-cbc -in /etc/passwd -out /tmp/passwd.enc -pass pass:himitsu

で 128bits の AES を CBC モードで /etc/passwd を暗号化し /tmp/passwd.enc に保存し、鍵 は "himitsu" というパスフレーズから生成します。ソルトや初期ベクトルは適当につっこまれますがオプションを追加して明示することも可能です。戻すときは以下:

$ openssl enc -d -aes-128-cbc -in /tmp/passwd.enc -out /tmp/passwd -pass pass:himitsu

暗号化のときはデフォルトでお塩振られているんですが

$ od -c /tmp/passwd.enc| head -n 1
0000000   S   a   l   t   e   d   _   _  \b 266 023 255   E 264 006 366

ファイルの頭に「Salted__」ってなってるのは OpenSSL のオリジナルルールなのかな?? 続く 8bytes がお塩になっています。

dgst

メッセージダイジェストです。ハッシュです。

$ sha1sum /etc/passwd
505b18302ff0f29ddf2ff32aa4b5373280a8327a  /etc/passwd
$ openssl dgst -r -sha1 /etc/passwd
505b18302ff0f29ddf2ff32aa4b5373280a8327a */etc/passwd

HMAC や署名やそれら検証もできるらしいっす! すごいっす! でも使ったことないっす! 愛っつってもその程度だったのか!! 認証コード付きの URL なんかワンライナーで作れそうですね。

genrsa, rsa, rsautl

公開鍵暗号方式もいろいろありますが RSA が基本でしょう。そうでしょう。

genrsa

秘密鍵の作成には genrsa を用います:

$ openssl genrsa -aes128 2048
Generating RSA private key, 2048 bit long modulus
......................................+++
..........................+++
e is 65537 (0x10001)
Enter pass phrase:
Verifying - Enter pass phrase:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,3F352A9B1F11589144094AF3CFE86C33

56q/1/0W/TVjuXCkeik2OxYuoSN+FMIBbK7Q+3ul8CIEgCYSBYG7I4MrAD8Tpfsc
(snip)
mxr49Ace8Ke11s5ylqQWPdrkwXgruVG46eoD52xnWRmiSYTPSOl+q3gUlXva8MG8
-----END RSA PRIVATE KEY-----

2048bit の RSA 秘密鍵を生成し 128bit AES で暗号化しています。「......................................+++」の部分は素数の生成の進捗を表現しています。詳しくは man genrsa。「-aes128」を付けなければ暗号化されません。「-out」で出力先の指定もできます。

rsa

保管する場合などには暗号化しておきたい秘密鍵ですが、ウェブサーバなどで利用する際には再起動の度にパスフレーズを聞かれ若干面倒です、というのも「不意のサーバの再起動の度にターミナル作業が発生してしまうとクラウド時代なこんな世の中にゃ」だからです。一度暗号化された秘密鍵

$ openssl rsa -in private.key.enc -out private.key
Enter pass phrase for private.key.enc:
writing RSA key
$ head -n 2 private.key
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA4CkDd5+tLLqKH6AQ6w+u9cxOqOrwwHdW1KRUY8s+Ymo/00Gu

パスフレーズが外せます。

$ openssl rsa -aes128 -in private.key -out private.key.re-enc
writing RSA key
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

再度暗号化することも可能です。

秘密鍵から公開鍵を出力することも可能です。

$ openssl rsa -in private.key -pubout
writing RSA key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CkDd5+tLLqKH6AQ6w+u
9cxOqOrwwHdW1KRUY8s+Ymo/00GuSRLAKfYSX8ROHTjTyH7kQKqJEAzXPinWKJRf
1tNel8a7MfchOw311mKbif8GjggeHxIAEBaB/u6CfbttYL0T99pgqbSrN13sO+o6
QjJwTcyKZcbsLNdnPm2QjX6TNVyiVOJwRC7+yoqWyDnOfytg+3uT6ELAJCLaxgv0
VHDtyQzQm2Y48Mr539pFwuK3976Ts+f6a23h2ufISt+MIzMzbS4GY//4f49Lfq+c
DNpGScoqeA1OaO5rhrZbGuRYgE1A9dsGvtHp0FLqQPgZK5SQfVW4ktIjBL6vcsmw
kwIDAQAB
-----END PUBLIC KEY-----

RSA の場合、2 つ素数 p,q を作ってその積を modulus とし、また (p-1)*(q-1) と互いに素な数をとってくるのですがこれは一般的に 65537 = 2**16 + 1 で決め打ちになっています。0x1000.....0001 の形の数字が RSA には計算効率が良いのだそうです。詳しくは知りません。この modulus を表現するのに必要な bit 長が鍵の長さになります。

$ openssl genrsa 32| openssl rsa -noout -text
Generating RSA private key, 32 bit long modulus
.+++++++++++++++++++++++++++
.+++++++++++++++++++++++++++
e is 65537 (0x10001)
Private-Key: (32 bit)
modulus: 4140734281 (0xf6ce9749)
publicExponent: 65537 (0x10001)
privateExponent: 3946959905 (0xeb41d421)
prime1: 65267 (0xfef3)
prime2: 63443 (0xf7d3)
exponent1: 63821 (0xf94d)
exponent2: 42759 (0xa707)
coefficient: 29592 (0x7398)

試しに 32bit で作成した秘密鍵を標準出力に流してパイプで繋いで内容を人間が読める形に変換してみました。カモン Python!

>>> import math
>>> math.log(65267*63443, 2)
31.94723947910808
>>> 65267*63443
4140734281

というわけで 32bit になっています。他の値は計算が速く済むように事前に計算されている値です。

$ openssl genrsa 32| openssl rsa -pubout| openssl rsa -pubin -noout -text
Generating RSA private key, 32 bit long modulus
.+++++++++++++++++++++++++++
.+++++++++++++++++++++++++++
e is 65537 (0x10001)
writing RSA key
Public-Key: (32 bit)
Modulus: 3678991741 (0xdb48f57d)
Exponent: 65537 (0x10001)

公開鍵の場合は modulus と exponent しか入っていません。この Modulus を素因数分解しないといけないけど大きいと大変だ、というのが RSA の固さになっているのでした。exponent は 65537 で決め打ちでしたから、公開鍵というのは実質 modulus のみとなります。

modulus は大切なのでこれだけで出せるオプションがあります。

$ openssl rsa -in private.key -noout -modulus
Modulus=E02903779FAD2CBA8A1FA010EB0FAEF5CC4EA8EAF0C07756D4A45463CB3E626A3FD341AE4912C029F6125FC44E1D38D3C87EE440AA89100CD73E29D628945FD6D35E97C6BB31F7213B0DF5D6629B89FF068E081E1F1200101681FEEE827DBB6D60BD13F7DA60A9B4AB375DEC3BEA3A4232704DCC8A65C6EC2CD7673E6D908D7E93355CA254E270442EFECA8A96C839CE7F2B60FB7B93E842C02422DAC60BF45470EDC90CD09B6638F0CAF9DFDA45C2E2B7F7BE93B3E7FA6B6DE1DAE7C84ADF8C2333336D2E0663FFF87F8F4B7EAF9C0CDA4649CA2A780D4E68EE6B86B65B1AE458804D40F5DB06BED1E9D052EA40F8192B94907D55B892D22304BEAF72C9B093
$ openssl rsa -pubin -in public.key -noout -modulus
Modulus=E02903779FAD2CBA8A1FA010EB0FAEF5CC4EA8EAF0C07756D4A45463CB3E626A3FD341AE4912C029F6125FC44E1D38D3C87EE440AA89100CD73E29D628945FD6D35E97C6BB31F7213B0DF5D6629B89FF068E081E1F1200101681FEEE827DBB6D60BD13F7DA60A9B4AB375DEC3BEA3A4232704DCC8A65C6EC2CD7673E6D908D7E93355CA254E270442EFECA8A96C839CE7F2B60FB7B93E842C02422DAC60BF45470EDC90CD09B6638F0CAF9DFDA45C2E2B7F7BE93B3E7FA6B6DE1DAE7C84ADF8C2333336D2E0663FFF87F8F4B7EAF9C0CDA4649CA2A780D4E68EE6B86B65B1AE458804D40F5DB06BED1E9D052EA40F8192B94907D55B892D22304BEAF72C9B093

結果が同じなので private.key と public.key は対になっていることが分かります。

rsautl

で、鍵を使って暗号化・復号・署名・検証できるのが rsautl なんです、が、使ったことないや。愛っつっても(ry。手で RSA 使うことなんてないですよねぇ〜。始めて man を眺めましたが何だかワクワクしてきました。

rand

「pseudo-random bytes」って何て訳すんだ? まぁ疑似乱数生成ですね。きっと暗号学的に違いありません。

$ openssl rand 16 | od -c
0000000   #   S   ~   L 026 232  \a 026   Y 211 020 204   u   j 005   V
0000020

ちょーバイナリ。

$ openssl rand -hex 16
2b16bbd22c21f0e26351ee170fe931c0

hex で逃げることもできますし、「-base64」もあります。

id:blooper:20120907 に続く