zkat’s diary

技術ブログ

OCSPによる証明書検証をopensslコマンド実行する

はじめに

OCSPにてサーバー証明書の妥当性を検証してみます。検証する対象は、google.comとします。

証明書の取得

まず、google.comに設定されているサーバー証明書と中間証明書を取得します。 取得した証明書をそれぞれ、server.crt と intermediate.crt として保存します。

$ openssl s_client -connect google.com:443 -showcerts -servername google.com < /dev/null
CONNECTED(00000003)
depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = US, O = Google Trust Services, CN = Google Internet Authority G3
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.google.com
verify return:1
---
Certificate chain
 0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=*.google.com
   i:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
-----BEGIN CERTIFICATE-----
MIIHxzCCBq+gAwIBAgIIPAQwjdOwN8UwDQYJKoZIhvcNAQELBQAwVDELMAkGA1UE
BhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczElMCMGA1UEAxMc
R29vZ2xlIEludGVybmV0IEF1dGhvcml0eSBHMzAeFw0xODEwMDkxMzA5MDBaFw0x
OTAxMDExMzA5MDBaMGYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
MRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKDApHb29nbGUgTExDMRUw
EwYDVQQDDAwqLmdvb2dsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARA
8KRremypK/M1uQJAJ+gYfO5byxzfs4deBuFdUP1m8S4JlNqYJFJM7L9jRw9E9ew3
t2NZESCEvRyY+yb7nQLUo4IFVDCCBVAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDgYD
VR0PAQH/BAQDAgeAMIIEGQYDVR0RBIIEEDCCBAyCDCouZ29vZ2xlLmNvbYINKi5h
bmRyb2lkLmNvbYIWKi5hcHBlbmdpbmUuZ29vZ2xlLmNvbYISKi5jbG91ZC5nb29n
bGUuY29tggYqLmcuY2+CDiouZ2NwLmd2dDIuY29tggoqLmdncGh0LmNughYqLmdv
b2dsZS1hbmFseXRpY3MuY29tggsqLmdvb2dsZS5jYYILKi5nb29nbGUuY2yCDiou
Z29vZ2xlLmNvLmlugg4qLmdvb2dsZS5jby5qcIIOKi5nb29nbGUuY28udWuCDyou
Z29vZ2xlLmNvbS5hcoIPKi5nb29nbGUuY29tLmF1gg8qLmdvb2dsZS5jb20uYnKC
DyouZ29vZ2xlLmNvbS5jb4IPKi5nb29nbGUuY29tLm14gg8qLmdvb2dsZS5jb20u
dHKCDyouZ29vZ2xlLmNvbS52boILKi5nb29nbGUuZGWCCyouZ29vZ2xlLmVzggsq
Lmdvb2dsZS5mcoILKi5nb29nbGUuaHWCCyouZ29vZ2xlLml0ggsqLmdvb2dsZS5u
bIILKi5nb29nbGUucGyCCyouZ29vZ2xlLnB0ghIqLmdvb2dsZWFkYXBpcy5jb22C
DyouZ29vZ2xlYXBpcy5jboIUKi5nb29nbGVjb21tZXJjZS5jb22CESouZ29vZ2xl
dmlkZW8uY29tggwqLmdzdGF0aWMuY26CDSouZ3N0YXRpYy5jb22CEiouZ3N0YXRp
Y2NuYXBwcy5jboIKKi5ndnQxLmNvbYIKKi5ndnQyLmNvbYIUKi5tZXRyaWMuZ3N0
YXRpYy5jb22CDCoudXJjaGluLmNvbYIQKi51cmwuZ29vZ2xlLmNvbYIWKi55b3V0
dWJlLW5vY29va2llLmNvbYINKi55b3V0dWJlLmNvbYIWKi55b3V0dWJlZWR1Y2F0
aW9uLmNvbYIRKi55b3V0dWJla2lkcy5jb22CByoueXQuYmWCCyoueXRpbWcuY29t
ghphbmRyb2lkLmNsaWVudHMuZ29vZ2xlLmNvbYILYW5kcm9pZC5jb22CG2RldmVs
b3Blci5hbmRyb2lkLmdvb2dsZS5jboIcZGV2ZWxvcGVycy5hbmRyb2lkLmdvb2ds
ZS5jboIEZy5jb4IIZ2dwaHQuY26CBmdvby5nbIIUZ29vZ2xlLWFuYWx5dGljcy5j
b22CCmdvb2dsZS5jb22CEmdvb2dsZWNvbW1lcmNlLmNvbYIYc291cmNlLmFuZHJv
aWQuZ29vZ2xlLmNuggp1cmNoaW4uY29tggp3d3cuZ29vLmdsggh5b3V0dS5iZYIL
eW91dHViZS5jb22CFHlvdXR1YmVlZHVjYXRpb24uY29tgg95b3V0dWJla2lkcy5j
b22CBXl0LmJlMGgGCCsGAQUFBwEBBFwwWjAtBggrBgEFBQcwAoYhaHR0cDovL3Br
aS5nb29nL2dzcjIvR1RTR0lBRzMuY3J0MCkGCCsGAQUFBzABhh1odHRwOi8vb2Nz
cC5wa2kuZ29vZy9HVFNHSUFHMzAdBgNVHQ4EFgQUT7otGJu0F3BuuVZ/m3BpxBDY
MdcwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBR3wrhQmmd2drEtwobQg6B+pn66
SzAhBgNVHSAEGjAYMAwGCisGAQQB1nkCBQMwCAYGZ4EMAQICMDEGA1UdHwQqMCgw
JqAkoCKGIGh0dHA6Ly9jcmwucGtpLmdvb2cvR1RTR0lBRzMuY3JsMA0GCSqGSIb3
DQEBCwUAA4IBAQCEdXxHA71+lQrStfH+FTdD0FVyraltudglZCIs0jBIvKW5YoSM
MUH1re9diW/QQI1VPn4aTXk9iijW4YfPJFJAp0d+PieUP5Jau1bNpizK1bQiQR5e
Q0VHwrLmxlTR/qPOTA/Cd/a1w+9xnC9UszrqUWdjGnQXwTKrie5snqpFhtoNhTb6
9D0sh5F3AgVQyyFQxRCopmgaEkDliM9vjA/5WRj5tNsKgRgBV2pGLLVDUc+R1F4H
f7Eve+p98mPr2epQ6U/AjEuhRgos0asP48E4iwtZN8tPXCdrbRn0xT/vRYQVuw6I
PIDyvQsMl3ZKj+LDCtW2LVZs/qj58eUbkYL8
-----END CERTIFICATE-----
 1 s:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
   i:/OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign
-----BEGIN CERTIFICATE-----
MIIEXDCCA0SgAwIBAgINAeOpMBz8cgY4P5pTHTANBgkqhkiG9w0BAQsFADBMMSAw
HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs
U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xNzA2MTUwMDAwNDJaFw0yMTEy
MTUwMDAwNDJaMFQxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVHb29nbGUgVHJ1c3Qg
U2VydmljZXMxJTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzMw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKUkvqHv/OJGuo2nIYaNVW
XQ5IWi01CXZaz6TIHLGp/lOJ+600/4hbn7vn6AAB3DVzdQOts7G5pH0rJnnOFUAK
71G4nzKMfHCGUksW/mona+Y2emJQ2N+aicwJKetPKRSIgAuPOB6Aahh8Hb2XO3h9
RUk2T0HNouB2VzxoMXlkyW7XUR5mw6JkLHnA52XDVoRTWkNty5oCINLvGmnRsJ1z
ouAqYGVQMc/7sy+/EYhALrVJEA8KbtyX+r8snwU5C1hUrwaW6MWOARa8qBpNQcWT
kaIeoYvy/sGIJEmjR0vFEwHdp1cSaWIr6/4g72n7OqXwfinu7ZYW97EfoOSQJeAz
AgMBAAGjggEzMIIBLzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFHfCuFCa
Z3Z2sS3ChtCDoH6mfrpLMB8GA1UdIwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYu
MDUGCCsGAQUFBwEBBCkwJzAlBggrBgEFBQcwAYYZaHR0cDovL29jc3AucGtpLmdv
b2cvZ3NyMjAyBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLnBraS5nb29nL2dz
cjIvZ3NyMi5jcmwwPwYDVR0gBDgwNjA0BgZngQwBAgIwKjAoBggrBgEFBQcCARYc
aHR0cHM6Ly9wa2kuZ29vZy9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEA
HLeJluRT7bvs26gyAZ8so81trUISd7O45skDUmAge1cnxhG1P2cNmSxbWsoiCt2e
ux9LSD+PAj2LIYRFHW31/6xoic1k4tbWXkDCjir37xTTNqRAMPUyFRWSdvt+nlPq
wnb8Oa2I/maSJukcxDjNSfpDh/Bd1lZNgdd/8cLdsE3+wypufJ9uXO1iQpnh9zbu
FIwsIONGl1p3A8CgxkqI/UAih3JaGOqcpcdaCIzkBaR9uYQ1X4k2Vg5APRLouzVy
7a8IVk6wuy6pm+T7HT4LY8ibS5FEZlfAFLSW8NwsVz9SBK2Vqn1N0PIMn5xA6NZV
c7o835DLAFshEWfC7TIe3g==
-----END CERTIFICATE-----
Certificate chain
 0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=*.google.com
   i:/C=US/O=Google Trust Services/CN=Google Internet Authority G3

Certificate chainの0番目以降に記載されている1つめのPEMデータが、サーバー証明書となります。

OCSP問い合わせ先の取得と問い合わせ

つづいて、server.crt からOCSPのアクセス先を取得します。

$ cat server.crt | openssl x509 -noout -text | grep -i ocsp
                OCSP - URI:http://ocsp.pki.goog/GTSGIAG3

最後に、OCSPリクエストにより問い合わせを実施します。 問い合わせ先は、上記のURLとなります。

$ openssl ocsp -no_nonce -issuer intermediate.crt -cert server.crt -url http://ocsp.pki.goog/GTSGIAG3 -text
OCSP Request Data:
    Version: 1 (0x0)
    Requestor List:
        Certificate ID:
          Hash Algorithm: sha1
          Issuer Name Hash: F6EDB0636232819A35F68D75A09D024A11AA6CAD
          Issuer Key Hash: 77C2B8509A677676B12DC286D083A07EA67EBA4B
          Serial Number: 3C04308DD3B037C5
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: 77C2B8509A677676B12DC286D083A07EA67EBA4B
    Produced At: Oct 29 01:40:18 2018 GMT
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: F6EDB0636232819A35F68D75A09D024A11AA6CAD
      Issuer Key Hash: 77C2B8509A677676B12DC286D083A07EA67EBA4B
      Serial Number: 3C04308DD3B037C5
    Cert Status: good
    This Update: Oct 29 01:40:18 2018 GMT
    Next Update: Nov  5 01:40:18 2018 GMT

    Signature Algorithm: sha256WithRSAEncryption
         c9:24:76:a4:d4:a9:5e:62:74:3c:38:0f:df:70:ae:36:61:87:
         3b:dc:a2:64:28:a9:67:b9:41:b4:78:de:f8:a3:a0:0c:d9:28:
         b5:ed:52:1e:c2:7c:73:6d:82:17:1d:4b:9e:fe:69:bb:e5:cd:
         f2:d7:56:f9:09:9a:0a:f1:e2:5b:26:72:94:47:42:ba:66:83:
         6d:4b:f8:7f:4b:90:b8:19:01:77:76:81:83:e6:b6:6a:58:e5:
         af:39:04:8e:f0:d0:cf:2b:13:0b:1d:f6:0f:ad:ed:57:6a:e3:
         93:2e:e7:43:20:81:ce:94:e5:db:32:d4:ef:d2:8e:6b:81:30:
         f2:84:88:e5:f8:0a:b4:b8:a1:dc:16:b0:d3:f6:67:92:df:e6:
         1d:8a:9a:8d:d5:01:02:1b:70:9a:e5:2a:33:f4:93:87:17:86:
         86:a9:47:0b:9c:40:c5:ce:88:ac:e5:e2:80:5e:82:3c:ef:f9:
         45:a9:fc:d3:10:49:f6:0a:55:58:9b:a9:fa:43:9c:68:f7:6c:
         81:ef:20:93:8f:7a:3c:12:7c:56:a2:dc:91:4f:df:d4:75:ff:
         e3:28:55:29:c4:bf:c6:f5:ec:69:aa:38:68:40:0a:06:5c:65:
         1b:36:40:cc:4b:d8:31:7f:52:d8:50:89:ff:4a:32:bd:ae:3d:
         4e:12:ea:5f
Response verify OK
server.crt: good
        This Update: Oct 29 01:40:18 2018 GMT
        Next Update: Nov  5 01:40:18 2018 GMT

Cert Statusとしてgoodが返却され証明書失効していないことを確認できました。

SNIに非対応のHTTPSサーバーを立ち上げる

はじめに

SNI(Server Name Indication)に非対応のHTTPSサーバーを、検証のために立ち上げる必要がありました。
構築の方法をメモします。

うまくいった方法

Python の BaseHTTPServer で HTTPS サーバーを立ち上げる方法です。

肝になるのは、SSLContextオブジェクトにset_servername_callbackをしている箇所です。
set_servername_callbackについてはドキュメントには下記のように記載されています。

コールバック関数 server_name_callback は 3 つの引数で呼び出されます; 最初の引数は ssl.SSLSocket です。2 つ目の引数は、クライアントが相手をしようと意図しているサーバ名を表す文字列 (または TLS Client Hello がサーバ名を含まない場合は None) です。そして 3 つ目の引数はオリジナルの SSLContext です。サーバ名引数は IDNA デコードされたサーバ名です。


TLS ネゴシエーションを継続させるならば、 server_name_callback 関数は None を返さなければなりません。TLS が失敗することを必要とするなら、 constant ALERT_DESCRIPTION_* を返してください。ここにない値を返すと、致命エラー ALERT_DESCRIPTION_INTERNAL_ERROR を引き起こします。

18.2. ssl --- ソケットオブジェクトに対する TLS/SSL ラッパー — Python 3.6.7 ドキュメント

ということで、SNIに対応しないためには、ALERT_DESCRIPTION_UNRECOGNIZED_NAMEを返せばおっけいです。

うまくいかなかった方法

SNIに対応する前の Nginx をビルドしようと思いましたが、ビルドにはまってうまくいきませんでした。
次のページよれば、

SNI has been supported since 0.5.23.

Configuring HTTPS servers

とのことなので、0.5.22 を使えば、SNIに非対応のHTTPSサーバーになるはずです。