## page was renamed from certificate/俺々SSLサーバー証明書の作り方 ## page was renamed from SSL証明書/俺々SSLサーバー証明書の作り方 = 俺々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 * 対インターネット向けなら、[[https://letsencrypt.jp|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に含まれない(「拡張」として付与)。 ||<#FFFF00> 属性名 ||<#FFFF00> 略号 ||<#FFFF00> 設定例 ||<#FFFF00> 備考 || || 国名(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 セクション(必須) === ||<#FFFF00> 項目名 ||<#FFFF00> 値 ||<#FFFF00> 意味 ||<#FFFF00> オプション ||<#FFFF00> 必須 ||<#FFFF00> 備考 || || 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 || プロンプトを表示するかどうか || なし || オプション || || ||<|6> string_mask || default ||<|6> 特定フィールドの文字列型で使用 ||<|6> なし ||<|6> 必須 ||<|6> || || utf8only || || pkix || || nombstr || || WARNING || || MASK:値 || || utf8 || yes || UTF8 文字列を解釈する || -utf8 || オプション || 未指定の場合 ASCII || === distinguished_name セクション(必須) === {{{-subj ~}}}オプションで必要な項目を指定するため、本セクションでの指定は無い。 ただし、セクション名自体は存在する必要がある。 === x509_extensions セクション(オプション) === ||<#FFFF00> 項目名 ||<#FFFF00> 値 ||<#FFFF00> 意味 ||<#FFFF00> 備考 || || subjectKeyIdentifier || hash || || || || authorityKeyIdentifier || keyid || || || || || keyid:always || || || || || issuer || || || || basicConstraints || CA:FALSE || CA機能無し || || || || CA:TRUE || CA機能有り || || || || pathlen:0~ || 認証チェインの深さ(0 は子供のみ、1 は孫まで、2 ...) || || || nsCertType || client || クライアント証明書 || || || || server || サーバー証明書 || || || || email || メール証明書(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 }}} {{{#!wiki comment/dotted = クライアント(ブラウザ)取り込み = * イントラネットユースで多数のクライアントに、先に作った俺々SSLサーバー証明書を事前に組み込みたいときがある。 * 初回アクセス時に警告を無視させる手順もあるが(少人数ならアリだろうが)、不特定多数となると面倒だと思われる。 * 理想的には更にはActiveDirectoryで配布したいところだろうが、そこまでのノウハウはないので取り上げない。 * とりあえず手元にダウンロードしてインポートするまでとする。ダウンロード(配布)方法については取り上げない。 * なお手順としては.csr(PKCS#1)をそのまま取り込むのもアリだが、ここではPKCS#12に変換するものとする。 * PKCS#7については取り扱わない。 * Windowsでは、Firefoxとそれ以外のブラウザ(IE/Chrome等)では取り込み方が違うので二度作業すること。 * これはFirefoxはFirefoxで取り込むのとは別に、Windowsで取り込み作業を行えば、IE/Chromeについては自動的に反映されることを意味する。 }}} = 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の一覧については[[https://ja.wikipedia.org/wiki/Extended_Validation_証明書#EV.E8.A8.BC.E6.98.8E.E6.9B.B8.E3.81.AE.E7.89.B9.E5.AE.9A|EV証明書の特定]]が詳しい。 == パッチ == * FreeBSD 10.3-RELEASEの openssl コマンド(1.0.1s)に以下のパッチを当てる。 * このパッチはreq(1)に{{{-startdate}}}と{{{-enddate}}}オプションを与えるパッチである。 * パッチを当てられないほとんどの環境では{{{-startdate ~ -enddate ~}}}オプションの代わりに{{{-days ~}}}オプションを指定すること。 {{{#!highlight diff Index: crypto/openssl/apps/req.c =================================================================== --- crypto/openssl/apps/req.c (revision 298785) +++ crypto/openssl/apps/req.c (working copy) @@ -126,6 +126,8 @@ * -x509 - output a self signed X509 structure instead. * -asn1-kludge - output new certificate request in a format that some CA's * require. This format is wrong + * -startdate - notBefore field + * -enddate - notAfter field */ static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, char *dn, int mutlirdn, @@ -179,6 +181,7 @@ int nodes = 0, kludge = 0, newhdr = 0, subject = 0, pubkey = 0; char *infile, *outfile, *prog, *keyfile = NULL, *template = NULL, *keyout = NULL; + char *startdate=NULL,*enddate=NULL; #ifndef OPENSSL_NO_ENGINE char *engine = NULL; #endif @@ -364,6 +367,14 @@ if (--argc < 1) goto bad; req_exts = *(++argv); + } else if (strcmp(*argv,"-startdate") == 0) { + if (--argc < 1) + goto bad; + startdate= *(++argv); + } else if (strcmp(*argv,"-enddate") == 0) { + if (--argc < 1) + goto bad; + enddate= *(++argv); } else if ((md_alg = EVP_get_digestbyname(&((*argv)[1]))) != NULL) { /* ok */ digest = md_alg; @@ -428,6 +439,10 @@ BIO_printf(bio_err, " -days number of days a certificate generated by -x509 is valid for.\n"); BIO_printf(bio_err, + " -startdate certificate validity notBefore - YYMMDDHHMMSSZ.\n"); + BIO_printf(bio_err, + " -enddate certificate validity notAfter - YYMMDDHHMMSSZ.\n"); + BIO_printf(bio_err, " -set_serial serial number to use for a certificate generated by -x509.\n"); BIO_printf(bio_err, " -newhdr output \"NEW\" in the header lines\n"); @@ -796,13 +811,26 @@ if (!rand_serial(NULL, X509_get_serialNumber(x509ss))) goto end; } - + if (startdate == NULL) { + if (!X509_gmtime_adj(X509_get_notBefore(x509ss), 0)) + goto end; + } else { + if (!ASN1_UTCTIME_set_string(X509_get_notBefore(x509ss), startdate)) { + BIO_printf(bio_err, "start date is invalid, it should be YYMMDDHHMMSSZ\n"); + goto end; + } + } + if (enddate == NULL) { + if (!X509_time_adj_ex(X509_get_notAfter(x509ss), days, 0, NULL)) + goto end; + } else { + if (!ASN1_UTCTIME_set_string(X509_get_notAfter(x509ss), enddate)) { + BIO_printf(bio_err, "end date is invalid, it should be YYMMDDHHMMSSZ\n"); + goto end; + } + } if (!X509_set_issuer_name(x509ss, X509_REQ_get_subject_name(req))) goto end; - if (!X509_gmtime_adj(X509_get_notBefore(x509ss), 0)) - goto end; - if (!X509_time_adj_ex(X509_get_notAfter(x509ss), days, 0, NULL)) - goto end; if (!X509_set_subject_name (x509ss, X509_REQ_get_subject_name(req))) goto end; }}} = 参考文献 = * [[https://www.openssl.org/docs/manmaster/apps/x509v3_config.html|x509v3_config(1)]] * [[https://ja.wikipedia.org/wiki/Extended_Validation_証明書|EV証明書]]