証明書を理解するため、自作証明書パーサーを作ろう(Part1 証明書とDER、ASN.1編)
なぜ書いたか
TLSについて良く知らないままで、特に証明書を調べているときにx509とかASN1という謎の用語が出てきたので理解のために書いてみました。
レポジトリ
x509,ASN1,DERって?
下記が詳しいです。
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だったら可変長でもなく不定長となりますが、これは今回の証明書パーサーでは使わないので飛ばします。
例題
いくつか例題を解いて理解してみましょう。
下記サイトから例題をお借りしました。
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、証明書の関係について学びました。
次回からは本格的にパーサーを書いていきます。
次回
参考
・DERパーサーについて
今回作ったパーサーの元になった記事です。わかりやすく助かりました。・証明書パーサーについて
・証明書の作り方と証明書の中身
・DERとASN.1の記法について
・RFC,ここからASN.1の定義が見れます。
Discussion