俺々SSLサーバー証明書の作り方
- 「ぼくがかんがえたさいきょうの」自己署名証明書の基本的な作り方について解説する。
- どれくらい「さいきょう」かというと、可能な限り実在する認証局(Certification Authority)で行ってるパラメータの再現を目指す。
- とは言え、このパラメータはEV(Extended Validatation)はおろか、OV(Organization Validation)のパラメータは含まない。
- これは証明書のパラメータというよりは、ブラウザ側の問題であるためである。
- そのあたりも含め、俺々認証局を作るのにも必要な基本的な技術を解説する。
- もちろん実運用的には、イントラネット内に閉じた環境のSSLサーバー証明書の運用など、実務でも使うことがある。
- サーバー証明書内で閉じない内容があるので、以下の点(拡張)については対応しない。
- X509v3 Certificate Policies
- X509v3 CRL Distribution Points
- Authority Information Access
- X509v3 Certificate Extension(oid=1.3.6.1.4.1.11129.2.4.2, Signed Certificate Timestamp)
- 以下の拡張についてはEV、OVに関わる部分なので調査だけはしてみた。
- X509v3 Certificate Policies
対インターネット向けなら、Let's Encryptはもちろんのこと、年額1000円~2000円程度の認証局があるので、そちらを利用されたし。
目次
ディスティングイッシュ名(Distinguished Name)を決める
- DN(Distinguished Name)は「属性=属性値」をつなげた「識別子」のことである。
- いわゆる俺々SSLサーバー証明書の場合、コモンネーム(CN)以外は本質的に必要ない。
- 実運用上は管理責任の明確化のために、組織名(O)や部門名(OU)を加えるべきと考える。
- またその場合、国名(C)、都道府県(ST)、市区町村(L)を含めるべきかと。
- また、ワイルドカード証明書を作りたい場合も、同一手順で問題無い。
- SAN(Subject Alternative Name・あるいは複数指定する場合は SANs / Name → Names)を行いたい場合は、もう少し手順が必要となる。
- ここではSANするために、自身のドメイン(下記例に対して ninth-nine.com のこと)を付与してみる:-)。
- SAN自体はDNに含まれない(「拡張」として付与)。
属性名 |
略号 |
設定例 |
備考 |
国名(Country name) |
C |
JP |
|
都道府県名(STate or province name) |
ST |
|
|
市区町村(Locality name) |
L |
|
|
組織名(Organization name) |
O |
|
|
部門名(Organization Unit name) |
OU |
|
|
コモンネーム(Common Name) |
CN |
*.ninth-nine.com |
|
メールアドレス(emailAddress) |
emailAddress |
|
|
上記をまとめたのが下記の通りとなる。省略した項目は項目名から省略する(一切残さない)。
/C=JP/CN=*.ninth-nine.com
カスタムopenssl.cnfの準備
OS標準のopenssl.cnfは微妙に微妙なので、カスタマイズしたものを用意する。-configオプションにして指定する。 このファイルは典型的な ini ファイルなので、以下の構造を持つ。
[セクション名] 項目 = 値 :
関連セクション・項目一覧
req セクション(必須)
項目名 |
値 |
意味 |
オプション |
必須 |
備考 |
distinguished_name |
セクション名 |
ディスティングイッシュ名セクションの指定 |
なし |
必須 |
|
attributes |
セクション名 |
属性セクションの指定 |
なし |
オプション |
通常不要 |
x509_extensions |
セクション名 |
署名拡張セクションの指定 |
-extensions セクション名 |
オプション |
|
req_extensions |
セクション名 |
要求拡張セクションの指定 |
-reqexts セクション名 |
オプション |
本件では不要 |
default_md |
sha1 / sha256 等 |
署名アルゴリズムの指定 |
-sha / -sha256 等 |
オプション |
指定が無い場合 -sha などに解釈 |
default_bits |
数字 |
ビット数 |
なし |
必須 |
何の?公開鍵暗号アルゴリズムによる |
default_keyfile |
パス名 |
秘密鍵のファイル名 |
-keyout パス名 |
オプション |
指定が無い場合標準出力へ |
RANDFILE |
パス名 |
乱数シードのファイル名 |
-rand パス名 |
オプション |
厳密には RANDFILE と -rand は違う |
input_password |
パスワード |
指定された秘密鍵のパスワード |
-passin パスワード |
オプション |
事前に秘密鍵は作らないので不要 |
output_password |
パスワード |
保存する秘密鍵のパスワード |
-passout パスワード |
オプション |
パスワード保護しないので指定しない |
encrypt_rsa_key |
no |
秘密鍵を3DES暗号化するかどうか |
-nodes |
オプション |
encrypt_key と同義(compat) |
encrypt_key |
no |
秘密鍵を3DES暗号化するかどうか |
-nodes |
オプション |
3DES以外の選択肢無し |
prompt |
no |
プロンプトを表示するかどうか |
なし |
オプション |
|
string_mask |
default |
特定フィールドの文字列型で使用 |
なし |
必須 |
|
utf8only |
|||||
pkix |
|||||
nombstr |
|||||
WARNING |
|||||
MASK:値 |
|||||
utf8 |
yes |
UTF8 文字列を解釈する |
-utf8 |
オプション |
未指定の場合 ASCII |
distinguished_name セクション(必須)
-subj ~オプションで必要な項目を指定するため、本セクションでの指定は無い。 ただし、セクション名自体は存在する必要がある。
x509_extensions セクション(オプション)
項目名 |
値 |
意味 |
備考 |
subjectKeyIdentifier |
hash |
|
|
authorityKeyIdentifier |
keyid |
|
|
|
keyid:always |
|
|
|
issuer |
|
|
basicConstraints |
CA:FALSE |
CA機能無し |
|
|
CA:TRUE |
CA機能有り |
|
|
pathlen:0~ |
認証チェインの深さ(0 は子供のみ、1 は孫まで、2 ...) |
|
nsCertType |
client |
クライアント証明書 |
|
|
server |
サーバー証明書 |
|
|
メール証明書(S/MIME) |
|
|
|
objsign |
オブジェクト(コード)サイニング |
|
|
sslCA |
認証局 |
|
|
emailCA |
メール認証局 |
|
|
objCA |
オブジェクトサイニング認証局 |
|
keyUsage |
digitalSignature |
デジタル署名 |
|
|
nonRepudiation |
否認不可 |
|
|
keyEncipherment |
鍵交換 |
|
|
dataEncipherment |
データ交換 |
|
|
keyAgreement |
|
|
|
keyCertSign |
|
|
|
cRLSign |
|
|
|
encipherOnly |
暗号化のみ |
|
|
decipherOnly |
複合化のみ |
|
extendedKeyUsage |
serverAuth |
SSL/TLS Web Server Authentication. |
|
|
clientAuth |
SSL/TLS Web Client Authentication. |
|
|
codeSigning |
Code signing. |
|
|
emailProtection |
E-mail Protection (S/MIME). |
|
|
timeStamping |
Trusted Timestamping |
|
|
msCodeInd |
Microsoft Individual Code Signing (authenticode) |
|
|
msCodeCom |
Microsoft Commercial Code Signing (authenticode) |
|
|
msCTLSign |
Microsoft Trust List Signing |
|
|
msSGC |
Microsoft Server Gated Crypto |
|
|
msEFS |
Microsoft Encrypted File System |
|
|
nsSGC |
Netscape Server Gated Crypto |
|
subjectAltName |
DNS.n:ドメイン名 |
|
|
「拡張」で指定する値には「,」(カンマ)や「 」(空白)などを「直接」含めることができない。 下記の例のように間接的に指定する方法があるが、SANsの指定の場合、ドメイン名しか使わないので、セクション分けまでする必要は無い(どちらも同じ意味ではあるが)。
subjectAltName = DNS.1:*.ninth-nine.com, DNS.2:ninth-nine.com
subjectAltName = @altnames [altnames] DNS.1 = *.ninth-nine.com DNS.2 = ninth-nine.com
設定ファイル(例)
[req] distinguished_name = distinguished_name x509_extensions = x509_extensions string_mask = utf8only [distinguished_name] [x509_extensions] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer basicConstraints = critical,CA:FALSE nsCertType = server keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth nsComment = "OpenSSL Generated Certificate" subjectAltName = DNS.1:*.ninth-nine.com, DNS.2:ninth-nine.com
自己署名証明書の作り方(RSA)
※opensslコマンドのオプションは長いので複数行に分割している。実際の実行は、1行(改行無し)で記述すること。
openssl req -newkey rsa:2048 -sha256 -nodes -subj "/C=JP/CN=*.ninth-nine.com" -out "/ssl/*.ninth-nine.com/*.ninth-nine.com,rsa2048-sha256,201601-201912,0.crt" -keyout "/ssl/*.ninth-nine.com/*.ninth-nine.com,rsa2048-sha256,201601-201912,0.key" -x509 -startdate "160101000000Z" -enddate "191231235959Z" -config 設定ファイル
古いopensslコマンドだと-sha256オプションでエラーになることがある。その場合は-sha1で代用すること。ただし、本ケースで全くお勧めしない。
-startdate ~ -enddate ~は全ての環境でエラーになる。#パッチを当てられない環境では-days ~オプションに置き換えること。
実行結果例(RSA)
Certificate: Data: Version: 3 (0x2) Serial Number: 13066507578601016103 (0xb5558f69d5768b27) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, CN=*.ninth-nine.com Validity Not Before: Jan 1 00:00:00 2016 GMT Not After : Dec 31 23:59:59 2019 GMT Subject: C=JP, CN=*.ninth-nine.com Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:9e:c3:6e:6c:84:97:9a:17:d7:46:05:69:d2:7d: 65:0b:87:11:6e:65:af:dd:bf:b3:92:fe:df:32:b3: 84:c6:22:30:64:d0:90:17:93:6d:7e:77:4b:0a:86: 51:1b:c4:57:55:3b:e0:1c:63:e9:b3:d8:dd:bb:d8: ae:58:3d:4b:34:98:4e:73:a5:9c:72:bb:18:51:df: 5b:f8:48:b3:3f:21:7f:73:9e:98:96:46:af:7d:29: 08:d5:8f:a8:0e:fc:18:6a:f1:09:4d:b6:36:17:76: 1f:0a:f4:9f:bb:b2:ea:50:9c:06:3c:30:58:76:b4: 27:e4:97:3c:bb:13:3b:24:d0:d1:a2:b5:08:29:ba: da:f4:fa:fc:15:1e:73:11:1e:6b:9b:07:14:25:cd: 6b:6d:28:d0:45:85:63:69:3f:78:a7:bf:73:cd:98: c1:c0:ba:79:a3:08:64:37:70:12:2e:11:b0:d7:34: 38:bd:cf:fc:d0:f5:f4:21:21:77:5b:36:d9:fd:cb: ea:67:8c:7c:83:1e:62:6d:d1:50:e9:bb:70:c3:36: 46:c6:31:c0:a5:63:5a:92:4b:fe:3c:7f:de:39:ed: 08:ca:51:3a:f7:c7:b4:e1:b3:a5:d5:7f:b1:9c:27: 25:fc:6c:93:b6:84:4e:30:d8:eb:02:3a:24:c2:cc: b9:25 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 67:E8:B1:6F:D0:19:1A:B7:39:45:4A:83:60:39:89:73:D6:D1:0D:FD X509v3 Authority Key Identifier: keyid:67:E8:B1:6F:D0:19:1A:B7:39:45:4A:83:60:39:89:73:D6:D1:0D:FD X509v3 Basic Constraints: critical CA:FALSE Netscape Cert Type: SSL Server X509v3 Key Usage: Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Alternative Name: DNS:*.ninth-nine.com, DNS:ninth-nine.com Signature Algorithm: sha256WithRSAEncryption 06:f2:8e:7d:cd:78:37:b7:34:ca:fe:b2:47:13:a7:02:98:fe: 78:8a:e3:02:d8:15:45:12:75:6b:30:7a:15:c1:0d:e1:22:55: 56:e2:42:18:ca:d2:0d:c6:29:ac:b7:e3:e0:96:ca:58:5f:8a: 06:de:bf:68:cb:d0:f2:a4:49:6f:33:9e:f3:0d:c9:4c:28:9a: 59:d7:28:78:11:44:f5:b1:6f:b2:ad:00:d9:e3:53:42:f4:d5: 5e:1a:43:43:2b:f5:f9:94:bb:d6:cd:36:a2:b8:71:b7:17:e9: 0c:83:29:6f:d8:16:43:24:45:8d:44:a8:5c:97:27:63:36:1e: 94:75:c1:3c:f0:d8:b4:9b:64:1f:cf:c8:f9:b7:e2:19:6a:ff: 1b:f9:b5:75:82:08:3c:43:bf:23:28:1e:4b:11:ba:52:bb:ff: d9:62:3b:7b:06:ef:6c:c6:63:4f:a8:41:ae:17:87:74:93:28: 68:94:0b:e7:01:79:43:43:cd:0a:ca:d4:84:94:bb:c2:89:3c: 6e:35:97:97:05:72:55:c9:9f:07:c4:79:82:8e:c7:60:4b:52: a0:97:cd:22:6e:40:61:10:d4:84:14:d4:4d:24:c1:e7:25:08: 4b:97:a9:d2:dd:50:62:c7:ac:24:5a:c3:41:ba:ee:72:e8:1d: 6a:c5:1a:dc
自己署名証明書の作り方(ECDSA)
※opensslコマンドのオプションは長いので複数行に分割している。実際の実行は、1行(改行無し)で記述すること。
openssl req -newkey ec:<(openssl ecparam -name prime256v1) -sha256 -nodes -subj "/C=JP/CN=*.ninth-nine.com" -out "/ssl/*.ninth-nine.com/*.ninth-nine.com,prime256v1-sha256,201601-201912,0.crt" -keyout "/ssl/*.ninth-nine.com/*.ninth-nine.com,prime256v1-sha256,201601-201912,0.key" -x509 -startdate "160101000000Z" -enddate "191231235959Z" -config 設定ファイル
古いopensslコマンドだと-sha256オプションでエラーになることがある。その場合は-sha1で代用すること。ただし、あまりお勧めしない。
-startdate ~ -enddate ~は全ての環境でエラーになる。#パッチを当てられない環境では-days ~オプションに置き換えること。
<(コマンド) というイディオム(実行結果をテンポラリファイル名で渡してくれる機能)は zsh/bash 拡張なので、それ以外のシェル(sh/ash/ksh/csh/tcsh 等)では指定しないこと。
実行結果例(ECDSA)
Certificate: Data: Version: 3 (0x2) Serial Number: 15898934856903028020 (0xdca45d0424c7ed34) Signature Algorithm: ecdsa-with-SHA256 Issuer: C=JP, CN=*.ninth-nine.com Validity Not Before: Jan 1 00:00:00 2016 GMT Not After : Dec 31 23:59:59 2019 GMT Subject: C=JP, CN=*.ninth-nine.com Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:03:38:d7:62:50:7b:9b:0b:2d:9f:45:67:6d:8e: af:e1:55:d6:1e:30:65:cd:97:0a:29:15:38:7a:2d: fa:da:f1:48:e8:5b:b6:16:04:c7:6c:6f:0e:18:bb: cf:e8:aa:82:66:66:89:79:bd:b8:94:e2:68:d1:43: 5b:1e:d4:49:dd ASN1 OID: prime256v1 X509v3 extensions: X509v3 Subject Key Identifier: 32:98:80:2B:82:8F:F2:6F:4D:D5:D7:08:D9:0F:E5:CB:A0:E4:DA:99 X509v3 Authority Key Identifier: keyid:32:98:80:2B:82:8F:F2:6F:4D:D5:D7:08:D9:0F:E5:CB:A0:E4:DA:99 X509v3 Basic Constraints: critical CA:FALSE Netscape Cert Type: SSL Server X509v3 Key Usage: Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Alternative Name: DNS:*.ninth-nine.com, DNS:ninth-nine.com Signature Algorithm: ecdsa-with-SHA256 30:45:02:21:00:c1:a5:7a:be:fb:cb:d7:ba:77:87:92:85:8e: bc:0a:f8:79:4e:30:f5:0c:2c:e8:54:c8:7c:4a:d5:8c:2d:38: 9a:02:20:19:f8:83:b9:71:0e:bf:ba:9d:13:08:38:b6:93:78: f2:c2:0a:8d:75:58:fe:ae:38:94:cd:04:fa:01:fe:80:a1
Appendix
X509v3 Certificate Policies に関する考察
シマンテックの Global ID/Server ID EV SSL(要は Symantec Class 3 EV SSL CA - G3 ね)証明書を分析した結果についてメモっておく。
X509v3 Certificate Policies: Policy: 2.16.840.1.113733.1.7.23.6 CPS: https://d.symcb.com/cps User Notice: Explicit Text: https://d.symcb.com/rpa
※なお、この設定を入れたところでEV SSL証明書にはならんので注意。DV SSL証明書扱いとなります。
先の設定ファイルの x509_extentions セクションに下記のように設定を追加してやると、上記と同じ結果が得られる。
[x509_extensions] certificatePolicies = @policies [policies] policyIdentifier = 2.16.840.1.113733.1.7.23.6 CPS.1 = https://d.symcb.com/cps userNotice.1 = @notice [notice] explicitText = https://d.symcb.com/rpa
- 色々試したところ certificatePolicies の行に「直接」必要なパラメータを埋め込むことはできず「@セクション名」で取り込む必要があった。
policyIdentifierの一覧についてはEV証明書の特定が詳しい。
パッチ
- FreeBSD 10.3-RELEASEの openssl コマンド(1.0.1s)に以下のパッチを当てる。
このパッチはreq(1)に-startdateと-enddateオプションを与えるパッチである。
パッチを当てられないほとんどの環境では-startdate ~ -enddate ~オプションの代わりに-days ~オプションを指定すること。
1 Index: crypto/openssl/apps/req.c
2 ===================================================================
3 --- crypto/openssl/apps/req.c (revision 298785)
4 +++ crypto/openssl/apps/req.c (working copy)
5 @@ -126,6 +126,8 @@
6 * -x509 - output a self signed X509 structure instead.
7 * -asn1-kludge - output new certificate request in a format that some CA's
8 * require. This format is wrong
9 + * -startdate - notBefore field
10 + * -enddate - notAfter field
11 */
12
13 static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, char *dn, int mutlirdn,
14 @@ -179,6 +181,7 @@
15 int nodes = 0, kludge = 0, newhdr = 0, subject = 0, pubkey = 0;
16 char *infile, *outfile, *prog, *keyfile = NULL, *template =
17 NULL, *keyout = NULL;
18 + char *startdate=NULL,*enddate=NULL;
19 #ifndef OPENSSL_NO_ENGINE
20 char *engine = NULL;
21 #endif
22 @@ -364,6 +367,14 @@
23 if (--argc < 1)
24 goto bad;
25 req_exts = *(++argv);
26 + } else if (strcmp(*argv,"-startdate") == 0) {
27 + if (--argc < 1)
28 + goto bad;
29 + startdate= *(++argv);
30 + } else if (strcmp(*argv,"-enddate") == 0) {
31 + if (--argc < 1)
32 + goto bad;
33 + enddate= *(++argv);
34 } else if ((md_alg = EVP_get_digestbyname(&((*argv)[1]))) != NULL) {
35 /* ok */
36 digest = md_alg;
37 @@ -428,6 +439,10 @@
38 BIO_printf(bio_err,
39 " -days number of days a certificate generated by -x509 is valid for.\n");
40 BIO_printf(bio_err,
41 + " -startdate certificate validity notBefore - YYMMDDHHMMSSZ.\n");
42 + BIO_printf(bio_err,
43 + " -enddate certificate validity notAfter - YYMMDDHHMMSSZ.\n");
44 + BIO_printf(bio_err,
45 " -set_serial serial number to use for a certificate generated by -x509.\n");
46 BIO_printf(bio_err,
47 " -newhdr output \"NEW\" in the header lines\n");
48 @@ -796,13 +811,26 @@
49 if (!rand_serial(NULL, X509_get_serialNumber(x509ss)))
50 goto end;
51 }
52 -
53 + if (startdate == NULL) {
54 + if (!X509_gmtime_adj(X509_get_notBefore(x509ss), 0))
55 + goto end;
56 + } else {
57 + if (!ASN1_UTCTIME_set_string(X509_get_notBefore(x509ss), startdate)) {
58 + BIO_printf(bio_err, "start date is invalid, it should be YYMMDDHHMMSSZ\n");
59 + goto end;
60 + }
61 + }
62 + if (enddate == NULL) {
63 + if (!X509_time_adj_ex(X509_get_notAfter(x509ss), days, 0, NULL))
64 + goto end;
65 + } else {
66 + if (!ASN1_UTCTIME_set_string(X509_get_notAfter(x509ss), enddate)) {
67 + BIO_printf(bio_err, "end date is invalid, it should be YYMMDDHHMMSSZ\n");
68 + goto end;
69 + }
70 + }
71 if (!X509_set_issuer_name(x509ss, X509_REQ_get_subject_name(req)))
72 goto end;
73 - if (!X509_gmtime_adj(X509_get_notBefore(x509ss), 0))
74 - goto end;
75 - if (!X509_time_adj_ex(X509_get_notAfter(x509ss), days, 0, NULL))
76 - goto end;
77 if (!X509_set_subject_name
78 (x509ss, X509_REQ_get_subject_name(req)))
79 goto end;
クライアント(ブラウザ)取り込み
理想的には更にはActiveDirectoryで配布したいところだろうが、そこまでのノウハウはないので取り上げない。