私が愛した 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 に続く