📘

証明書を理解するため、自作証明書パーサーを作ろう(Part1 証明書とDER、ASN.1編)

2022/11/04に公開

なぜ書いたか

TLSについて良く知らないままで、特に証明書を調べているときにx509とかASN1という謎の用語が出てきたので理解のために書いてみました。

レポジトリ

https://github.com/stone-like/toyTLS

x509,ASN1,DERって?

下記が詳しいです。
https://qiita.com/TakahikoKawasaki/items/4c35ac38c52978805c69

X509
X509証明書、証明書のこと
ASN.1
X509証明書を記述するための抽象的なデータ構造、pseudocodeのようなもの
DER
X509証明書をバイトで表現するための具体的仕様、CやGoなどの言語のようなもの

さらに、DERでバイトにした後圧縮をBase64で行い、
圧縮したデータの前に、
-----BEGIN CERTIFICATE-----
後ろに、
-----END CERTIFICATE-----
を付けたのがPEMです。

全体の目標

PEMからASN.1に復元するのが目標です。
ASN.1に復元することによってPEMから公開鍵の情報だったりを抽出することができます。

今回の目標

ASN.1,DERの記法を理解する。

証明書の構造とASN.1、DER

まず証明書を作成します。

 openssl genrsa 2048 > private.key
 openssl req -new -x509 -days 3650 -key private.key -sha512 -out server.crt

証明書に詰まっている情報を下記コマンドで確認してみます。

openssl x509 -in server.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            1c:38:e2:3e:1c:c2:cd:9f:09:f9:d2:56:ce:97:fe:bc:ca:92:24:34
        Signature Algorithm: sha512WithRSAEncryption
        Issuer: C = JP, ST = Tokyo, L = Shibuya, O = TestCorp, OU = TestUnit, CN = Cube
        Validity
            Not Before: Oct 13 04:44:47 2022 GMT
            Not After : Oct 10 04:44:47 2032 GMT
        Subject: C = JP, ST = Tokyo, L = Shibuya, O = TestCorp, OU = TestUnit, CN = Cube
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:da:9b:7d:3f:67:af:16:b1:fe:24:b1:12:a5:b1:
                    cd:ef:25:04:91:e4:2c:a4:a8:65:f7:5c:b7:e6:94:
                    d1:17:43:1a:1a:6e:cc:3e:30:ff:02:b0:ec:18:d8:
                    f2:55:72:b0:c0:42:7b:b7:8b:18:a7:6f:9c:f2:af:
                    ce:43:71:73:6c:eb:56:7f:d4:74:7e:21:fe:37:b1:
                    83:d5:b4:83:3e:73:a4:c3:9a:56:d9:c3:16:a1:29:
                    de:8e:27:c3:82:db:f1:95:a4:e8:60:81:80:11:4d:
                    a8:4f:b3:c1:ac:dd:e3:75:2c:5f:88:8d:ef:f4:83:
                    3f:90:ed:12:83:88:eb:79:fe:3f:8e:d5:b1:18:20:
                    be:6c:a1:4d:99:76:ce:c6:87:2d:28:74:e1:ff:1b:
                    70:3c:f3:f0:7f:87:e7:48:4b:d3:a1:50:a3:d8:7d:
                    ac:10:ed:de:8b:97:23:9e:73:d9:8c:2d:8a:4c:b6:
                    8a:b1:d6:e2:11:03:92:a3:7f:bb:9d:82:d5:d0:cb:
                    94:f5:f7:6b:30:32:d2:e1:8c:21:70:0e:8b:92:c0:
                    e1:54:c1:88:47:05:76:8a:e4:24:b0:8e:7b:4a:b3:
                    2c:f6:74:08:f1:9d:cb:ac:0b:94:79:f8:61:1c:8b:
                    90:37:71:33:83:5c:4d:58:e0:ce:2f:28:57:1e:1a:
                    6a:21
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                B4:7D:B7:8D:C3:78:94:3B:C9:24:11:6A:A9:A4:26:B7:91:85:BA:FF
            X509v3 Authority Key Identifier: 
                keyid:B4:7D:B7:8D:C3:78:94:3B:C9:24:11:6A:A9:A4:26:B7:91:85:BA:FF

            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha512WithRSAEncryption
         cd:63:a1:6c:50:2b:fd:ef:34:9d:37:55:d7:48:d4:38:95:87:
         5a:2e:ec:66:10:cb:69:4b:cd:65:6d:46:6c:da:f6:9a:dc:34:
         09:09:32:e1:33:4c:00:de:49:c8:81:78:40:f2:94:60:85:9b:
         c4:41:cd:c3:89:b2:c2:0e:f7:31:21:7e:f1:f6:85:fd:d0:a9:
         1f:f9:4f:76:11:31:6a:cd:c8:53:38:7b:a1:68:d5:d0:18:08:
         b6:54:0f:d2:91:f9:3a:8a:1c:9f:e0:0b:79:e5:0a:cc:53:c4:
         ad:f9:bd:a6:e5:5a:19:8d:f5:3a:d2:a5:84:07:bf:6c:cc:ce:
         18:c8:20:ad:c5:46:fa:22:a5:90:22:28:a0:4c:4b:ba:be:9f:
         7d:8b:84:d5:82:df:df:e4:1b:56:ac:57:69:cf:d6:ca:b2:dd:
         e9:4a:1a:f0:82:46:13:34:78:de:6a:e0:60:df:3e:d7:d3:f6:
         37:f8:97:cf:88:a8:e9:e3:9e:2d:51:7f:5e:6f:12:3d:a6:aa:
         60:8d:cf:92:94:08:e2:9a:5c:89:8b:bc:78:00:8e:a2:ba:42:
         32:97:e3:c3:81:3d:1d:23:46:8d:85:f9:2b:1f:bd:fa:b0:45:
         d7:0a:81:88:17:8d:f7:f2:12:a5:84:d8:f5:0c:31:50:8b:43:
         f3:45:65:f7

上記のような情報を取得するのがゴールです。
具体的な構造を見た後で、抽象的にはどう定義されているか確認しましょう。

証明書はASN.1では下記のように表現されます。

Certificate  ::=  SEQUENCE  {
     tbsCertificate       TBSCertificate,
     signatureAlgorithm   AlgorithmIdentifier,
     signature            BIT STRING  }

このASN.1がDERによって具体的なバイトで表されるのですが、
DERでは、

|型 | 長さ | 値 |

とエンコードします。

--型について
型は通常1byteで、下記のようになっています。
より詳しく型を記述すると下記のようになります。

|型    (1byte)                           |
|class(2bit),isStructured(1bit),tag(5bit)|

Classは0,1,2,3の4種類あり、それぞれ
UNIVERSAL=0
APPLICATION=1
CONTEXT_SPECIFIC=2
PRIVATE=3
となります。
これらの使いどころは後述します。

isStructuredはprimitiveであれば0,structuredであれば1です。
structuredはsequenceとかsetとか構造体のようなものですね。
Sequenceの中にまた別のASN.1がいくつか入っているみたいな感じです。

Tagは下記のような種類があります。
Tagの種類

10進数  16進数
2	  02	INTEGER
3	  03	BIT STRING
4	  04	OCTET STRING
5	  05	NULL
6	  06	OBJECT IDENTIFIER
12	  0C	UTF8String
16	  10 (と 30)*	SEQUENCE と SEQUENCE OF
17	  11 (と 31)*	SET と SET OF
19	  13	PrintableString
22	  16	IA5String
23	  17	UTCTime
24	  18	GeneralizedTime

型部分は可変長になることもあるのですが、x509では使わないので今回は省略します。

--長さについて
長さは通常1byteですが、可変長の場合があります。
先頭1bit目が立っていなければ1byteの値がそのまま後続何byteが値の長さかを表します。

10000001(0x81)のように先頭1bit目が立っていれば可変長で、先頭1bitを除いた残りのbyte数で長さに使うbyte数を表します。0x81だったら後続1byteで長さに使うbyte数を表し、0x82だったら2byteです。

ぴったり0x80だったら可変長でもなく不定長となりますが、これは今回の証明書パーサーでは使わないので飛ばします。

例題
いくつか例題を解いて理解してみましょう。
下記サイトから例題をお借りしました。
https://blog.engelke.com/2014/10/17/parsing-ber-and-der-encoded-asn-1-objects/

04 05 .. .. .. .. ..

まず最初の1byteをみると
00000100なので、
class=0
isStructured=primitive
tag=OCTET STRING
となります。
次の1byteが長さで0x80未満なのでそのまま後続5byteが値の長さです。

30 82 02 10 ......

まず最初の1byteをみると
00110000なので、
class=0
isStructured=structured
tag=SEQUENCE
となります。
長さは
0x82=10000010なので後続2byte分が実際の長さを表すbyteとなり、
0x02 0x10で2*256 + 16 = 528byteが値の長さとなります。

このエンコードされたByteをASN.1に戻すのが目標です。
次回はDERをASN.1に戻すパーサーを作っていきましょう。

おわりに

今回はDERとASN.1、証明書の関係について学びました。
次回からは本格的にパーサーを書いていきます。

次回

https://zenn.dev/cube/articles/1939d81fd98152

参考

・DERパーサーについて
https://blog.engelke.com/2014/10/17/parsing-ber-and-der-encoded-asn-1-objects/
今回作ったパーサーの元になった記事です。わかりやすく助かりました。

・証明書パーサーについて
https://blog.engelke.com/2014/10/21/web-crypto-and-x-509-certificates/

・証明書の作り方と証明書の中身
https://qiita.com/sanyamarseille/items/46fc6ff5a0aca12e1946

・DERとASN.1の記法について
https://bearmini.hatenablog.com/entry/2014/02/05/143510
https://letsencrypt.org/ja/docs/a-warm-welcome-to-asn1-and-der/
https://web.archive.org/web/20210111101517/https://www.cnblogs.com/nliao/archive/2012/02/15/2352831.html

・RFC,ここからASN.1の定義が見れます。
https://tex2e.github.io/rfc-translater/html/rfc5280.html

Discussion