golangとX.509証明書とopenssl

golangでX.509証明書をいじってみる...

まずはX.509証明書のお話を...

X.509証明書とは

X.509証明書は公開鍵証明書の標準としてITU-Tが策定したもの
基本的に証明書と言ったら公開鍵証明書を指すが、属性証明書、特定証明書というものもある。

公開鍵証明書はさらにCA(Certification Authority)証明書とEE(End Entity)証明書という2種類に分けられる。

CA証明書は認証局に対して発行する証明書。CA自身の秘密鍵で署名した自己署名証明書と他のCAで発行された証明書がある。

それに対してEE証明書はPKIユーザー向けの証明書。Webクライアント、Webサーバーで利用されるもの。

詳しいお話は日本語だとIPAの資料がまとまっている気がする
3.3 電子証明書

証明書の作成

まずはCA秘密鍵の作成
ここからはopensslコマンドで色々やっていく

$ openssl genres -out ca.key


CA公開鍵の作成

$ openssl rsa -in ca.key -pubout -out ca.pub.key


自己証明書署名要求の作成

$ openssl req -new -key ca.key -out ca.csr
...


証明書署名要求の内容確認

$ openssl req -text -noout -in ca.csr
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b5:74:92:48:77:60:d4:14:8a:50:ad:6c:01:ed:
                    4d:6f:0d:dd:99:4a:d1:8a:eb:61:78:d2:71:47:2c:
                    4f:d9:8b:41:9b:20:16:02:df:b5:0a:3f:12:35:5a:
                    1d:c1:c3:58:a9:b2:9f:60:41:32:cf:3b:b8:a0:ce:
                    4f:be:bd:28:6d:83:f5:ca:84:56:60:ed:4f:53:cc:
                    4c:37:1a:55:de:06:5b:f8:3b:0b:7c:c9:fe:bf:fc:
                    03:04:06:ef:91:dd:e4:39:b7:82:71:bd:40:2b:c1:
                    b8:f8:3d:84:6e:fe:b0:02:7d:15:f9:c7:1c:44:17:
                    08:b0:f4:4e:91:0b:0c:69:45:01:3a:30:8e:49:8a:
                    19:0c:97:56:a6:ea:e6:34:78:64:a9:96:fe:3d:72:
                    3d:1d:e2:99:62:72:14:fa:f8:29:c5:1a:c7:c0:61:
                    d0:07:70:4d:5a:fe:d2:43:67:5a:27:7f:8c:51:ca:
                    f5:6c:0b:c6:86:a0:af:6f:d4:78:10:6e:47:f0:89:
                    06:3d:d0:e8:ce:2c:50:81:13:94:7b:7d:46:36:f6:
                    d3:86:d0:cb:0e:0f:6e:cf:0b:8f:6f:bf:d1:30:bc:
                    2c:4d:f0:64:2d:c6:5f:2f:14:7b:18:a0:b2:2c:b4:
                    74:67:14:55:81:44:9e:bc:97:f1:42:c6:15:d1:f7:
                    d8:23
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         8a:be:80:c9:3e:18:3c:58:2a:c1:88:fa:82:d1:f6:ea:89:4d:
         5d:b4:d5:f7:4b:78:a8:47:d2:c5:89:d5:47:c6:86:df:9a:ee:
         27:56:9a:b5:bd:16:e5:f7:97:b0:41:c9:66:18:fc:72:b7:c2:
         97:3f:03:b6:38:ad:f8:5d:99:6c:b4:7e:4a:2d:e7:65:14:55:
         ea:89:ff:3c:32:28:dd:81:0f:de:bd:58:16:99:6d:cf:f2:45:
         25:88:d8:d6:6d:29:39:ce:1b:ce:1c:ca:56:93:b4:82:b3:6d:
         37:d1:b8:90:db:2d:8b:f5:26:16:8a:c5:de:58:52:db:44:c1:
         ca:70:48:3f:d2:c1:60:bd:93:94:c1:52:a7:f0:f9:73:0a:cd:
         be:a4:7c:6f:97:72:7c:37:60:2e:a6:b3:f5:62:9d:30:11:a0:
         0c:b4:29:98:ae:96:df:6d:e5:5e:af:ac:c6:3f:bc:9e:3b:75:
         6c:60:d9:d1:64:ed:2a:30:f6:57:ef:3b:98:20:4b:16:9b:93:
         94:4b:1a:1e:2d:14:b2:4b:ad:fd:b9:81:49:34:cf:78:97:e0:
         a6:4a:5a:24:a3:45:a3:9c:89:ee:47:a1:72:10:02:15:48:17:
         12:49:a0:c0:50:82:9b:1c:d0:0d:fd:78:f3:a4:21:44:ea:ba:
         32:d6:7d:fb


証明書署名要求を使用して署名された公開鍵証明書を作成

$ openssl x509 -req -in ca.csr -signkey ca.key -days 10000 -out ca.crt


証明書の内容確認

$ openssl x509 -text -noout -in ca.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 11311120728934412255 (0x9cf92c5fc3b16fdf)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Validity
            Not Before: Jun  8 16:45:52 2020 GMT
            Not After : Oct 25 16:45:52 2047 GMT
        Subject: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b5:74:92:48:77:60:d4:14:8a:50:ad:6c:01:ed:
                    4d:6f:0d:dd:99:4a:d1:8a:eb:61:78:d2:71:47:2c:
                    4f:d9:8b:41:9b:20:16:02:df:b5:0a:3f:12:35:5a:
                    1d:c1:c3:58:a9:b2:9f:60:41:32:cf:3b:b8:a0:ce:
                    4f:be:bd:28:6d:83:f5:ca:84:56:60:ed:4f:53:cc:
                    4c:37:1a:55:de:06:5b:f8:3b:0b:7c:c9:fe:bf:fc:
                    03:04:06:ef:91:dd:e4:39:b7:82:71:bd:40:2b:c1:
                    b8:f8:3d:84:6e:fe:b0:02:7d:15:f9:c7:1c:44:17:
                    08:b0:f4:4e:91:0b:0c:69:45:01:3a:30:8e:49:8a:
                    19:0c:97:56:a6:ea:e6:34:78:64:a9:96:fe:3d:72:
                    3d:1d:e2:99:62:72:14:fa:f8:29:c5:1a:c7:c0:61:
                    d0:07:70:4d:5a:fe:d2:43:67:5a:27:7f:8c:51:ca:
                    f5:6c:0b:c6:86:a0:af:6f:d4:78:10:6e:47:f0:89:
                    06:3d:d0:e8:ce:2c:50:81:13:94:7b:7d:46:36:f6:
                    d3:86:d0:cb:0e:0f:6e:cf:0b:8f:6f:bf:d1:30:bc:
                    2c:4d:f0:64:2d:c6:5f:2f:14:7b:18:a0:b2:2c:b4:
                    74:67:14:55:81:44:9e:bc:97:f1:42:c6:15:d1:f7:
                    d8:23
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha1WithRSAEncryption
         a1:84:89:20:5b:c5:b1:c7:ea:90:4c:af:2f:aa:98:29:70:a5:
         cf:14:b0:a3:a5:82:66:71:2e:54:93:ba:02:4f:80:bb:c2:fa:
         6f:1b:61:a9:1d:34:03:0c:7f:ae:ee:0a:e4:98:9d:d3:f5:d0:
         79:37:0f:c9:87:44:e7:25:ca:bc:45:9c:a9:09:26:a5:82:22:
         e4:8d:46:32:b4:27:fe:1f:96:a3:d9:66:f7:22:49:9c:f2:5e:
         63:73:c3:dd:a0:a7:38:f3:13:d2:ec:26:67:a8:9d:f8:b2:33:
         62:79:2d:89:3c:ed:1a:0d:94:44:54:57:ad:3d:5d:74:11:74:
         b9:ee:f6:ff:7e:90:0d:a5:76:80:af:a9:8f:75:cb:28:9e:66:
         ca:b2:07:b5:b2:c4:20:9f:55:f5:93:36:30:b6:78:93:c9:d6:
         97:a5:3f:4f:55:4f:25:9f:0f:6d:40:0e:3d:72:ca:63:87:8b:
         8f:12:31:28:ce:6d:e4:a8:c3:eb:09:a7:12:9a:28:f0:7d:4d:
         ea:6f:d7:2a:af:44:53:92:47:20:62:c6:db:0c:eb:ba:70:8d:
         c9:37:4b:8a:df:13:4c:44:c2:4b:27:62:d4:20:5d:78:29:ad:
         fa:7c:4c:1b:6e:6f:35:59:4a:5a:84:04:45:56:26:73:3e:02:
         e2:16:15:b3

見ての通りIssuerとSubjectが同じである。
自己署名証明書ってやつですね。
コレでCAの証明書生成は終わりです。


次はユーザー用の証明書を作成する

まずユーザー用の秘密鍵の生成

$ openssl genres -out private.key


証明書署名要求の作成

$ openssl req -new -key private.key -out x509.csr


証明書署名要求の内容確認

$ openssl req -text -noout -in x509.csr
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=JP, ST=Tokyo, O=gucchisk, CN=gucchi.info
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c1:88:ef:3c:a4:c7:59:3f:00:88:39:20:37:19:
                    8d:18:0e:ee:3e:2e:1d:ec:78:90:c5:31:a0:f6:6d:
                    4d:8c:22:ba:94:05:f5:da:9a:94:65:86:66:16:9a:
                    2c:cb:0f:3f:ac:66:fd:a5:3b:0f:06:fe:5c:02:a5:
                    6c:13:5d:50:ad:65:05:b4:43:26:04:8a:34:69:68:
                    a7:29:c2:f6:a0:05:4d:a0:23:bf:05:70:70:5f:27:
                    d1:d3:b9:b0:ab:51:1d:bd:62:a3:27:9a:a2:0c:ab:
                    c5:23:79:d7:c0:69:1d:77:23:34:f4:30:e2:17:28:
                    21:ed:84:6b:55:40:99:f6:40:aa:79:14:86:71:b6:
                    40:45:bf:3b:89:c8:b0:d0:20:b6:c1:0a:8f:39:9f:
                    a6:4f:06:11:22:db:0d:cc:8b:8b:44:46:74:61:88:
                    7b:c8:8c:11:bb:f5:f4:ab:ad:98:90:8e:0c:0d:21:
                    10:5f:62:97:83:df:94:ca:19:ee:1b:25:5a:cc:33:
                    ca:b7:f0:63:35:96:6e:9e:6d:56:a6:4d:ca:6d:9c:
                    ca:f0:8a:81:33:13:04:44:bb:38:fd:d5:fa:76:c1:
                    75:57:6b:8a:a6:c6:6d:04:c1:ba:5e:6c:c3:ea:db:
                    8e:2a:84:9b:5e:d8:2e:a8:81:44:af:60:67:7d:83:
                    07:09
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         44:fb:d4:1b:30:f6:54:bd:5f:b2:3c:83:b4:e1:0a:37:72:ad:
         60:8e:0c:c3:3c:8c:d0:d6:69:3a:1b:4b:19:c8:7f:5f:62:52:
         ad:3f:a9:50:61:7c:3e:31:20:99:b5:bd:8b:22:00:37:45:71:
         3b:67:1f:48:66:3d:c5:c9:0a:11:d1:02:11:37:28:bc:90:12:
         1d:b0:7b:9d:1f:8e:09:d3:1a:78:51:56:c7:bc:4e:24:2b:0e:
         33:d5:2a:59:d8:1b:11:52:87:3e:0f:22:91:ca:a3:e9:03:1b:
         3b:c4:96:33:0a:25:e5:ee:63:df:e8:27:93:b7:de:9c:0e:31:
         41:d1:80:8e:d0:ff:e8:8e:e7:d3:b5:d5:c4:f8:50:e7:99:86:
         dc:73:3b:c6:6c:4f:76:02:61:e6:44:49:b1:21:c3:ca:f2:7f:
         79:7b:ba:94:47:05:77:1a:48:5c:63:04:b5:07:0c:23:3b:17:
         29:75:81:9c:28:1d:0e:df:f0:50:f3:9f:fe:5a:70:9b:99:f8:
         6c:9f:7a:b9:29:03:97:6c:50:e2:ec:27:6f:4d:3d:d4:d1:42:
         e3:50:72:29:4c:1f:89:9d:12:f2:00:26:35:7c:f4:e1:97:05:
         e7:25:e9:51:41:b6:cc:e5:73:6f:79:5f:a2:c5:6c:15:5c:1b:
         f4:85:b1:cb


CA秘密鍵で署名した証明書を生成

$ openssl x509 -req -in x509.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -out x509.crt


証明書の内容確認

$ openssl x509 -text -noout -in x509.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 12170714301618296499 (0xa8e7102c92b996b3)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Validity
            Not Before: Jun  9 15:38:57 2020 GMT
            Not After : Jun  9 15:38:57 2021 GMT
        Subject: C=JP, ST=Tokyo, O=gucchisk, CN=gucchi.info
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c1:88:ef:3c:a4:c7:59:3f:00:88:39:20:37:19:
                    8d:18:0e:ee:3e:2e:1d:ec:78:90:c5:31:a0:f6:6d:
                    4d:8c:22:ba:94:05:f5:da:9a:94:65:86:66:16:9a:
                    2c:cb:0f:3f:ac:66:fd:a5:3b:0f:06:fe:5c:02:a5:
                    6c:13:5d:50:ad:65:05:b4:43:26:04:8a:34:69:68:
                    a7:29:c2:f6:a0:05:4d:a0:23:bf:05:70:70:5f:27:
                    d1:d3:b9:b0:ab:51:1d:bd:62:a3:27:9a:a2:0c:ab:
                    c5:23:79:d7:c0:69:1d:77:23:34:f4:30:e2:17:28:
                    21:ed:84:6b:55:40:99:f6:40:aa:79:14:86:71:b6:
                    40:45:bf:3b:89:c8:b0:d0:20:b6:c1:0a:8f:39:9f:
                    a6:4f:06:11:22:db:0d:cc:8b:8b:44:46:74:61:88:
                    7b:c8:8c:11:bb:f5:f4:ab:ad:98:90:8e:0c:0d:21:
                    10:5f:62:97:83:df:94:ca:19:ee:1b:25:5a:cc:33:
                    ca:b7:f0:63:35:96:6e:9e:6d:56:a6:4d:ca:6d:9c:
                    ca:f0:8a:81:33:13:04:44:bb:38:fd:d5:fa:76:c1:
                    75:57:6b:8a:a6:c6:6d:04:c1:ba:5e:6c:c3:ea:db:
                    8e:2a:84:9b:5e:d8:2e:a8:81:44:af:60:67:7d:83:
                    07:09
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha1WithRSAEncryption
         a7:a7:14:04:53:d7:68:cb:fd:32:cb:3a:8d:6d:6c:5a:1f:8f:
         58:04:0d:9d:ad:c0:2d:d6:79:4e:90:3f:97:29:6a:80:51:d1:
         04:d6:27:67:62:d9:21:0e:0d:66:d6:0b:89:e3:f5:98:c8:7e:
         0a:57:be:40:6c:bc:df:65:ac:32:59:ea:46:30:7b:96:ee:bc:
         bd:24:d3:df:6c:4c:56:3e:29:7c:db:5d:ed:93:19:3a:9d:12:
         6a:4e:81:53:2d:f0:24:25:3b:f3:bc:1b:b4:0d:31:ed:a8:48:
         b5:45:d1:59:3b:50:23:ef:a0:25:90:40:ee:4e:af:43:54:40:
         dc:1b:33:05:aa:e1:5f:17:8c:ba:e2:22:54:d4:7c:07:2f:c9:
         15:24:a8:3f:9d:6b:7e:a0:05:bd:0f:00:0e:82:d3:83:2b:9d:
         b6:a2:2b:10:b2:42:65:ee:6b:36:81:a9:3f:f5:4b:2d:3a:ee:
         46:03:b3:d1:c7:e6:7f:e4:7c:97:aa:cc:7f:50:ff:c8:95:bc:
         6d:84:a7:ff:17:ff:2b:60:2e:3d:e2:93:8b:df:5d:c3:24:a8:
         f4:e8:16:01:8a:43:95:e6:7c:87:f0:ba:4d:9a:b3:81:b9:50:
         46:33:d0:f8:1e:98:6e:01:9b:7d:2a:91:1c:e7:82:4a:12:74:
         ef:65:ef:3a

SANSを設定した証明書を作る場合はこちら

$ openssl x509 -req -in x509.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days  -out x509_sans.crt -extfile sans.txt

sans.txtの中身はこんな感じ

subjectAltName = DNS:gucchi.info, DNS:*.gucchi.info


証明書の内容確認

$ openssl x509 -text -noout -in x509_sans.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 12170714301618296498 (0xa8e7102c92b996b2)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Validity
            Not Before: Jun  9 15:37:20 2020 GMT
            Not After : Jun  9 15:37:20 2021 GMT
        Subject: C=JP, ST=Tokyo, O=gucchisk, CN=gucchi.info
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c1:88:ef:3c:a4:c7:59:3f:00:88:39:20:37:19:
                    8d:18:0e:ee:3e:2e:1d:ec:78:90:c5:31:a0:f6:6d:
                    4d:8c:22:ba:94:05:f5:da:9a:94:65:86:66:16:9a:
                    2c:cb:0f:3f:ac:66:fd:a5:3b:0f:06:fe:5c:02:a5:
                    6c:13:5d:50:ad:65:05:b4:43:26:04:8a:34:69:68:
                    a7:29:c2:f6:a0:05:4d:a0:23:bf:05:70:70:5f:27:
                    d1:d3:b9:b0:ab:51:1d:bd:62:a3:27:9a:a2:0c:ab:
                    c5:23:79:d7:c0:69:1d:77:23:34:f4:30:e2:17:28:
                    21:ed:84:6b:55:40:99:f6:40:aa:79:14:86:71:b6:
                    40:45:bf:3b:89:c8:b0:d0:20:b6:c1:0a:8f:39:9f:
                    a6:4f:06:11:22:db:0d:cc:8b:8b:44:46:74:61:88:
                    7b:c8:8c:11:bb:f5:f4:ab:ad:98:90:8e:0c:0d:21:
                    10:5f:62:97:83:df:94:ca:19:ee:1b:25:5a:cc:33:
                    ca:b7:f0:63:35:96:6e:9e:6d:56:a6:4d:ca:6d:9c:
                    ca:f0:8a:81:33:13:04:44:bb:38:fd:d5:fa:76:c1:
                    75:57:6b:8a:a6:c6:6d:04:c1:ba:5e:6c:c3:ea:db:
                    8e:2a:84:9b:5e:d8:2e:a8:81:44:af:60:67:7d:83:
                    07:09
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                DNS:gucchi.info, DNS:*.gucchi.info
    Signature Algorithm: sha1WithRSAEncryption
         90:a6:d9:91:29:87:fb:46:32:b4:8a:77:a0:9c:c1:a7:38:ce:
         ea:4a:9d:aa:0a:5a:21:7f:59:a9:ce:89:7a:e3:03:2e:18:07:
         67:d2:f8:19:10:4b:7f:9e:2e:ba:d9:ba:ae:14:a0:9f:fc:e3:
         08:8b:87:37:f3:6b:80:de:75:a8:43:cd:3d:b1:50:27:be:05:
         0a:20:3c:2e:cf:2c:d0:91:43:47:3b:61:c8:d6:46:8c:a0:6f:
         40:06:4e:f5:27:f4:6d:00:26:a0:dd:65:a8:30:17:d1:11:87:
         24:2e:76:c9:9a:7e:fa:67:2f:52:79:a4:39:6b:53:0e:a1:af:
         10:2a:ef:22:75:14:ce:e8:b7:ee:ac:bb:a9:18:5a:e1:44:48:
         02:b5:63:e5:3b:bf:3e:92:37:3a:c2:e4:42:e9:76:24:79:ac:
         c0:f1:95:da:10:44:69:0c:32:d4:ec:e6:86:40:1d:84:2c:53:
         4e:6f:29:62:16:19:dd:38:0c:1d:08:44:8c:8a:d2:8c:d0:3e:
         26:cc:67:6f:61:1b:8e:6a:53:b2:20:fc:2e:d1:36:66:7c:39:
         0b:cf:cb:9d:e0:e6:a7:ac:bc:7f:f8:de:34:92:97:32:09:86:
         30:d5:93:49:20:22:b1:3e:f6:f8:47:9c:27:f8:f9:dd:b0:78:
         d8:74:72:39

こんな感じでX509v3 extensions以下にSANsが設定されている


最後に証明書の署名を検証する

$ openssl verify -CAfile ca.crt x509.crt
x509.crt: OK

OK!!(当たり前)

golangでX.509証明書をゴニョゴニョ

やっとここからgolangで作成した証明書を見ていく
もちろん検証も行う


まずはCAの公開鍵証明書の登録
ファイルを読み込んで証明書のpoolに加える(登録)

f, err := os.Open("ca.crt")
b, err := ioutil.ReadAll(f)
pool := x509.NewCertPool()
ok := pool.AppendCertsFromPEM(b)


次に検証したい証明書を読み込む

f, err = os.Open(certFile)
b, err = ioutil.ReadAll(f)
block, _ := pem.Decode(b)
cert, err := x509.ParseCertificate(block.Bytes)

証明書プールへの追加はPEM用のインターフェイスがあったが、直接PEMフォーマットからx509.Certificate型にするインターフェイスはないのでpemモジュールを使って一度pem.Blockにしてからx509.Certificateにする。


そして検証

hostname := "gucchi.info"
opts := x509.VerifyOptions{
	Roots: pool,
	DNSName: hostname,
}
if _, err := cert.Verify(opts); err == nil {
	fmt.Println("Verify: OK")
} else {
	fmt.Printf("Verify: NG, %s\n", err.Error())
}

x509.VerifyOptionsに必須のRootsプロパティに先程の証明書プールをセットし、ホスト名もチェックしたければホスト名をDNSNameに設定する。
そしてcert.Verify(opts)
成功ならばnilが返ってきて、失敗ならば理由が入ったerrorが返ってくる。

ちなみに期限切れの証明書だった場合はこんな感じに表示される。

Verify: NG, x509: certificate has expired or is not yet valid: current time 2020-06-12T23:14:49+09:00 is after 2020-06-10T15:39:58Z


VerifyHostname()はホスト名のチェックのみで署名の検証を行わない。

SANsが設定されている場合はCommonNameに適したホスト名かチェックする。
SANsが設定されていない場合はCommonNameは無視してSANsの設定に適したホスト名かチェックする。

X.509証明書の扱いはこんな感じ。
結構簡単にできますね。

今回のソースコードはこちらに置いておきます

https://github.com/gucchisk/go-samples/tree/master/x509cert

コメントする