私が愛した openssl (PKI 編 その 1)

id:blooper:20120904 の続きです。前回は cryptographic primitives 関連のコマンドの使い方でした。

"PKI" は "Public Key Infrastructure" の頭文字で WikiPedia だとwikipedia:公開鍵基盤となっていますが、何のことだか名前だけでは分からないですよね〜。知りたい! 場合には前回引用した #mailerstudy 02 暗号入門 (2012-02-22更新) を見てもいいですしググってもいいです。適当に本並べとくんで適当にご購入下さい。1 冊目は前回も出ました。2 冊目は暗号技術を使うとどんなことができるのか・どんなことが考えられてるかがざっくり分かる素敵な本です。3 冊目は読んでないけどいいらしい。いいらしいけど読んでない。あと画像が引っ張れなかったんだけど「企業システムのためのPKI―公開鍵インフラストラクチャの構築・導入・運用」ってのもあります。

と思ったけど一応用語だけ並べときます。

秘密鍵
秘密にする鍵。良い乱数を使うことにより同じものが二つとないことが確保できていそうなもの。とある秘密鍵を持っている人はこの世に一人だけ、という前提。
公開鍵
公開する鍵。主に暗号化に使われるが RSA の場合秘密鍵で暗号化したものの復号ができてしまうし、もちろん公開鍵で暗号化したのは秘密鍵で復号できる。
ハッシュ関数
ハッシュドビーフのハッシュ。任意のバイト列を引数にとり、固定長のバイト列を返す関数。とてもランダムな値を返し、その値を返すような元の引数はそう簡単には作れない、という前提。
MD4,MD5,SHA1,SHA2
ハッシュ関数で流通しているものたち。上で言う固定長がそれぞれ 128bit,128bit,160bit と SHA2 は 224,256,384,512 が用意されてる。
(電子)署名
RSA を用いた電子署名は、署名したいバイト列のハッシュ値を取り、秘密にしてる方の鍵で暗号化したバイト列。文章と署名を受けとった人は、「文章のハッシュ値」と「署名を公開されてる鍵で復号した値」を比較して一致したら OK。ここでは公開鍵と秘密鍵の暗号と復号の立場が入れ換わっている。一般的には、秘密鍵で署名という操作を、公開鍵で検証という操作をする。
公開鍵証明書
公開鍵を公開しても誰のものだか保証がない。なので「身元情報」と「公開鍵」を連結したものに「署名」をして足して「公開鍵の持ち主を証明」する証明書。でも、その署名を確認するにはやっぱり誰かの公開鍵が必要なので、最初に誰かの公開鍵を信じなくてはいけない。Windows やら OS X やら Firefox には最初から証明書ベンダーの証明書がインストールされていて、それらソフトを使うことはそれらベンダーを信じることになっている。最初からまとめて信じるってわけじゃない信頼構築の仕組みもある。
認証局
PKI だと信頼の階層が木構造になっているのかな? で、署名権限を下部組織に移譲したりします。証明書発行者のことなんだけど、そもそも発行者を信じるかどうかも貴方次第で、認証局に対して別の認証局が署名して証明書発行したりします。残念ながら私は心の中で「発行者 (Issuer)」くらいの簡単な意味に読み替えています。
PKCS
Public-Key Cryptography Standards。wikipedia:PKCS を見るんだ! RSA 社が作った PKI 関係の規格の名前。PKCS#12 みたいに名前がついている。数字で分かりづらい。openssl genrsa でできるのは PKCS#1 の RSA 鍵だ。
OID
Object IDentifier。SNMP とかで見たことある方も多いかと。持ち主情報の「国」とか「名前」とか「メールアドレス」という項目に OID が振られています。OID Repository - Home で探せる.
鍵交換
インターネットなど盗聴される可能性があるところで秘密の byte 列を共有すること。RSA を使えば簡単。暗号化して送ればいい。他に Diffie-Hellman などもある。

ぐおぉぉぉ、だめだ、長くなる。

req

request の頭。CSR (Certificate Signing Request) を作ったり見たりするコマンド。PKCS#10 形式で作る。EXAMPLE という節があるのでちゃんと man req した方が幸せになれるよ。

証明書は公開鍵の持ち主を証明するのだったので、公開鍵と持ち主情報が必要です。で、CSR を作ったのが確かに秘密鍵の持ち主であることなど確認できるよう CSR にも証明依頼内容に対する署名がくっつきます。

とりあえず CSR を作ると、

$ openssl req -new -key private.key -out foo.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Chiyoda-ku
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Company
Organizational Unit Name (eg, section) []:Information Technology
Common Name (eg, YOUR name) []:www.mymymy.co.jp
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$ head -n 2 foo.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICyTCCAbECAQAwgYMxCzAJBgNVBAYTAkpQMQ4wDAYDVQQIDAVUb2t5bzETMBEG

持ち主情報が問われるので入力すると、それら情報と秘密鍵を用いて CSR が作成されます。CSR = "持ち主情報" + "公開鍵" + "持ち主情報 + 公開鍵 の秘密鍵による署名" です。(あと、署名方法も書いてあります、SH1 と RSA を使うとかね)。UTF-8 使いたい! って場合には -utf8 ってオプションもあったりします。

この持ち主情報で聞かれる内容などは openssl の設定ファイルに書かれています。Ubuntu だと /etc/ssl/openssl.cnf にありますが

(snip)
[ req ]
default_bits            = 1024
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert

(snip)

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = AU
countryName_min                 = 2
countryName_max                 = 2

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = Some-State

localityName                    = Locality Name (eg, city)

0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = Internet Widgits Pty Ltd

# we can do this but it is not needed normally :-)
#1.organizationName             = Second Organization Name (eg, company)
#1.organizationName_default     = World Wide Web Pty Ltd

organizationalUnitName          = Organizational Unit Name (eg, section)
#organizationalUnitName_default =

commonName                      = Common Name (eg, YOUR name)
commonName_max                  = 64

emailAddress                    = Email Address
emailAddress_max                = 64
(snip)

何聞くか、何文字か、などの情報が入っとるわけです。ちなみに、countryName などは OpenSSL の中でハードコードされてて OID とか書いてあるんですが、自前で追加することも可能です。oid_section という変数で指定されてる section で定義できます。

CSR 作成はバッチ処理でもできて、foo_csr.cnf として

[ req ]
distinguished_name     = req_distinguished_name
prompt                 = no
utf8                   = yes

[ req_distinguished_name ]
C                      = JP
ST                     = Okinawa
L                      = Ishigaki
O                      = Genkotsu LLC.
OU                     = Very Hungry Div.
CN                     = genkotsu

というファイルを作っておいて

openssl req -new -config foo_csr.cnf -key private.key
-----BEGIN CERTIFICATE REQUEST-----
MIICvTCCAaUCAQAweDELMAkGA1UEBhMCSlAxEDAOBgNVBAgTB09raW5hd2ExETAP
(snip)

でも CSR を作ることができます。これだとまだ UTF-8 使おうって気になりますね。

CSR の中身を見るには

$ openssl req -in foo.csr -noout -text
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=JP, ST=Tokyo, L=Chiyoda-ku, O=My Company, OU=Information Technology, CN=www.mymymy.co.jp
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:e0:29:03:77:9f:ad:2c:ba:8a:1f:a0:10:eb:0f:
(snip)

さっき入力した情報とか公開鍵の情報とか入っています。Subject が持ち主っつーか依頼主ね。

$ openssl req -in foo.csr -noout -modulus
Modulus=E02903779FAD2CBA8A1FA010EB0FAEF5CC4EA8EAF0C07756D4A45463CB3E626A3FD341AE4912C029F6125FC44E1D38D3C87EE440AA89100CD73E29D628945FD6D35E97C6BB31F7213B0DF5D6629B89FF068E081E1F1200101681FEEE827DBB6D60BD13F7DA60A9B4AB375DEC3BEA3A4232704DCC8A65C6EC2CD7673E6D908D7E93355CA254E270442EFECA8A96C839CE7F2B60FB7B93E842C02422DAC60BF45470EDC90CD09B6638F0CAF9DFDA45C2E2B7F7BE93B3E7FA6B6DE1DAE7C84ADF8C2333336D2E0663FFF87F8F4B7EAF9C0CDA4649CA2A780D4E68EE6B86B65B1AE458804D40F5DB06BED1E9D052EA40F8192B94907D55B892D22304BEAF72C9B093

rsa と同様公開鍵の modulus が取り出せて秘密鍵とのペアリングの確認もできます。

-x509 というオプションもあって、これは証明書の形式 X.509 のことなのですが、これを足すと CSR を作るんじゃなくって自己署名証明書 (署名の秘密鍵が内容の公開鍵とペアの証明書) を作ります。とりあえず証明書さえあれば! という場合には簡単に作れるってことですね!!

-nameopt というオプションもあって、CSR を -text で読める形にするときの parse の方法や表示形式をいろいろいじれるんですが、詳細は man x509 しれ (と man に書いてあります)。

x509

X.509 は証明書の形式の名前で、いまはバージョン 3 で X.509v3 と書いたりします。そもそも ITU-T の X シリーズってのがあってその 500 番台が DAP (LDAPDAP, Lightweight じゃないのね) の定義? で、X.509 で証明書が定義されています。最新は RFC 5280 を見ましょう。

man x509 の EXAMPLES もとても豊富なので是非一度見てみるといいよ!

証明書の中身を見る

証明書が欲しいのでちょっと証明書を取ってきましょう。

$ openssl s_client -connect www.google.com:443 < /dev/null > google.crt
depth=1 C = ZA, O = Thawte Consulting (Pty) Ltd., CN = Thawte SGC CA
verify error:num=20:unable to get local issuer certificate
verify return:0
DONE
$ openssl x509 -in google.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            4f:9d:96:d9:66:b0:99:2b:54:c2:95:7c:b4:15:7d:4d
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=ZA, O=Thawte Consulting (Pty) Ltd., CN=Thawte SGC CA
        Validity
            Not Before: Oct 26 00:00:00 2011 GMT
            Not After : Sep 30 23:59:59 2013 GMT
        Subject: C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (1024 bit)
                Modulus:
                    00:de:b7:26:43:a6:99:85:cd:38:a7:15:09:b9:cf:
                    0f:c9:c3:55:8c:88:ee:8c:8d:28:27:24:4b:2a:5e:
                    a0:d8:16:fa:61:18:4b:cf:6d:60:80:d3:35:40:32:
                    72:c0:8f:12:d8:e5:4e:8f:b9:b2:f6:d9:15:5e:5a:
                    86:31:a3:ba:86:aa:6b:c8:d9:71:8c:cc:cd:27:13:
                    1e:9d:42:5d:38:f6:a7:ac:ef:fa:62:f3:18:81:d4:
                    24:46:7f:01:77:7c:c6:2a:89:14:99:bb:98:39:1d:
                    a8:19:fb:39:00:44:7d:1b:94:6a:78:2d:69:ad:c0:
                    7a:2c:fa:d0:da:20:12:98:d3
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 CRL Distribution Points: 

                Full Name:
                  URI:http://crl.thawte.com/ThawteSGCCA.crl

            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication, Netscape Server Gated Crypto
            Authority Information Access: 
                OCSP - URI:http://ocsp.thawte.com
                CA Issuers - URI:http://www.thawte.com/repository/Thawte_SGC_CA.crt

    Signature Algorithm: sha1WithRSAEncryption
        21:ac:d5:ae:ca:34:89:5a:c2:ab:52:d2:b2:34:66:9d:7a:ab:
        ee:e6:7c:d5:7e:c2:5c:28:bb:74:00:c9:10:1f:42:13:fc:69:
        8a:1e:24:a0:02:00:e9:ba:5b:ca:19:04:b2:d3:af:01:b2:7e:
        5f:14:db:a6:db:52:b9:9a:f3:12:7f:7c:a2:9c:3b:6f:99:7d:
        ea:50:0d:76:23:12:ff:f7:66:73:29:b7:95:0a:ad:d8:8b:b2:
        de:20:e9:0a:70:64:11:08:c8:5a:f1:7d:9e:ec:69:a5:a5:d5:
        82:d7:27:1e:9e:56:cd:d2:76:d5:79:2b:f7:25:43:1c:69:f0:
        b8:f9

上から順に

  • 署名対象
    • version
    • 証明書発行者に対して一意なシリアルナンバー
    • 署名アルゴリズム
    • 証明書発行者
    • 有効期間
    • 証明書発行対象
    • 公開鍵情報 (1024bit だと!?)
    • 証明書拡張
  • 署名方式と署名

となっています。
x509 にこそ -nameopt が使えて出力形式も色々いじれます。

個別にも出せます。

$ openssl x509 -in google.crt -noout -serial -issuer -startdate -enddate -subject -modulus
serial=4F9D96D966B0992B54C2957CB4157D4D
issuer= /C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
notBefore=Oct 26 00:00:00 2011 GMT
notAfter=Sep 30 23:59:59 2013 GMT
subject= /C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
Modulus=DEB72643A69985CD38A71509B9CF0FC9C3558C88EE8C8D2827244B2A5EA0D816FA61184BCF6D6080D335403272C08F12D8E54E8FB9B2F6D9155E5A8631A3BA86AA6BC8D9718CCCCD27131E9D425D38F6A7ACEFFA62F31881D424467F01777CC62A891499BB98391DA819FB3900447D1B946A782D69ADC07A2CFAD0DA201298D3

結局オプションを沢山書いて一辺に指定しましたが、1 項目だけでも出せます。あと -dates とか他にもオプション満載です。

RFC 5280 の中には拡張として BasicConstraint/KeyUsage/ExtendedKeyUsage など定義されているのですが、これらを用いて認証局となれる (他の CSR への署名が許された) 証明書か、クライアント認証に用いて良いか、などが記述できます。

$ openssl x509 -in google.crt -noout -purpose
Certificate purposes:
SSL client : Yes
SSL client CA : No
SSL server : Yes
SSL server CA : No
Netscape SSL server : Yes
Netscape SSL server CA : No
S/MIME signing : No
S/MIME signing CA : No
S/MIME encryption : No
S/MIME encryption CA : No
CRL signing : Yes
CRL signing CA : No
Any Purpose : Yes
Any Purpose CA : Yes
OCSP helper : Yes
OCSP helper CA : No
Time Stamp signing : No
Time Stamp signing CA : No

と -purpose を用いると見ることができます。この内容も含めて署名されるので、つまり署名した Issuer がこれらの用途を認めてるっつーことになります。

証明書を作る (CSR に署名する)

CSR への署名も可能です。-req を付けると「入力が CSR = 署名して出力」、ということになります。面倒になってきたから man x509 から引用

$ openssl x509 -req -in req.pem -extfile openssl.cnf -extensions v3_usr -CA cacert.pem -CAkey key.pem -CAcreateserial
  • CSR は req.pem
  • Issuer の証明書は cacert.pem でこれの Subject が新証明書の Issuer になる
  • Issuer の秘密鍵は key.pem でこれで署名する
  • 設定ファイル openssl.cnf の v3_usr という項目にある証明書拡張を付ける
  • シリアルナンバーは cacert.srl に保存される

といった塩梅で署名が可能です。でもまぁ、これだけだと管理が大変ですね、ということで openssl ca や CA.sh など色々なコマンド、スクリプトが別途用意されています。拡張を決める設定ファイルの項目の書き方は man x509v3_config を参照。

asn1parse

ASN.1 っつーのは X.509 などで用いられているバイナリフォーマットの形式です。wikipedia:Abstract_Syntax_Notation_One を見てちょ。で、ここで定義されてるうち DER っつーのを使っています。CSR も X.509 証明書も次に出てくる CRL も DER でエンコードされています。で、今までそれは BASE64 でテキストにされていました。このテキストにされたフォーマットを PEM と呼んでいる、っつっていいのかな?
Privacy-enhanced Mail の略なんだけど。http://en.wikipedia.org/wiki/Privacy-enhanced_Electronic_Mail むむーん。

でまぁ、ともかく、パースしてくれます。

$ openssl x509 -in google.crt | openssl asn1parse
    0:d=0  hl=4 l= 801 cons: SEQUENCE          
    4:d=1  hl=4 l= 650 cons: SEQUENCE          
    8:d=2  hl=2 l=   3 cons: cont [ 0 ]        
   10:d=3  hl=2 l=   1 prim: INTEGER           :02
   13:d=2  hl=2 l=  16 prim: INTEGER           :4F9D96D966B0992B54C2957CB4157D4D
   31:d=2  hl=2 l=  13 cons: SEQUENCE          
   33:d=3  hl=2 l=   9 prim: OBJECT            :sha1WithRSAEncryption
   44:d=3  hl=2 l=   0 prim: NULL              
   46:d=2  hl=2 l=  76 cons: SEQUENCE          
   48:d=3  hl=2 l=  11 cons: SET               
   50:d=4  hl=2 l=   9 cons: SEQUENCE          
   52:d=5  hl=2 l=   3 prim: OBJECT            :countryName
   57:d=5  hl=2 l=   2 prim: PRINTABLESTRING   :ZA
   61:d=3  hl=2 l=  37 cons: SET               
   63:d=4  hl=2 l=  35 cons: SEQUENCE          
   65:d=5  hl=2 l=   3 prim: OBJECT            :organizationName
   70:d=5  hl=2 l=  28 prim: PRINTABLESTRING   :Thawte Consulting (Pty) Ltd.
  100:d=3  hl=2 l=  22 cons: SET               
  102:d=4  hl=2 l=  20 cons: SEQUENCE          
  104:d=5  hl=2 l=   3 prim: OBJECT            :commonName
  109:d=5  hl=2 l=  13 prim: PRINTABLESTRING   :Thawte SGC CA
  124:d=2  hl=2 l=  30 cons: SEQUENCE          
  126:d=3  hl=2 l=  13 prim: UTCTIME           :111026000000Z
(snip)

ちょっとゴミが多くて google.crt がそのまま使えなかったので openssl x509 で綺麗にしてみました。-i オプションを付けると出力にインデントをしてくれます。。何バイトのところから何が始まってるかとか、人の目で見えるようにするとどうなるかなどが書いてあります。INTEGER / SEQUENCE / OBJECT / PRINTABLESTRING / UTCTIME などは ASN.1 の中で定義されているものです。

で、DER や PEM を扱うコマンドは -inform や -outform といった入力や出力の形式を指定するオプションを持っていて、der やら pem やらを指定することができます。自動判別しろよ、って思いますよね。でもしてくれません。デフォルトは pem なので、今まで出てきませんでした。

crl

CRL は Certificate Revocation List の頭文字です。これもやっぱり X.509 だというか RFC 5280 で定義されています。証明書を失効 (無効化) したいことがあります。社員に出してた証明書を、社員の退職に伴い無効化したいというのは良くある話です。また対となる秘密鍵が漏洩してしまった証明書をいつまでも有効なままにしておくと悪用される危険性があるのでやっぱり無効化したいです。なので認証局は CRL を定期的に発行して発行した証明書全体を健全な状態に保ちます。

Google の証明書の拡張の部分に

            X509v3 CRL Distribution Points: 

                Full Name:
                  URI:http://crl.thawte.com/ThawteSGCCA.crl

ってのがありました。認証局がしっかり CRL の場所を記載しています。

$ wget http://crl.thawte.com/ThawteSGCCA.crl
$ openssl crl -in ThawteSGCCA.crl -inform der -noout -text
Certificate Revocation List (CRL):
        Version 1 (0x0)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: /C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
        Last Update: Sep  5 09:00:50 2012 GMT
        Next Update: Sep 15 09:00:50 2012 GMT
Revoked Certificates:
    Serial Number: 01478912B47F5ABE8BFC3043864D7B85
        Revocation Date: Sep 28 10:21:25 2009 GMT
    Serial Number: 01A56766526CD4591600448A1A2EEB39
        Revocation Date: May 18 23:53:47 2010 GMT
(snip)

やっぱり「誰が発行したか」「有効期限はいつか」「失効された証明書のシリアル」あと省略しましたが「CRL に対する署名」が記載されています。「失効しちゃったよ!」という偽情報をばらまかれることを防ぐために CRL にも Issuer が署名をします。この Issuer は元の証明書の Issuer ですね。

とある証明書が失効されてるかどうか調べるには

  • CRL がある場所を確認して
  • CRL 取得して
  • 有効期間と署名を確認して
  • その証明書のシリアルが含まれているかどうか調べる

という手順になります。

crl では CRL は作れないのですが、ca を用いると CRL が作成できます。

まとめ

req, x509, crl, asn1parse を見ました。まだまだ PKI 関連のコマンドあるので以下次回。