Share
Embed
PHPでローカル環境用のSSL/TLS証明書を発行することはできるのか? #phpconkagawa
PHPカンファレンス香川2026で発表したスライドです。
akase244
May 08, 2026
Transcript
155 slides
-
HTTP / HTTPS で動作が異なる機能の開発には注意が必要 • Mixed Content • Cookie の
Secure 属性 • SameSite Cookie • HSTS • CORS • Service Worker • WebSocket • getUserMedia • navigator.geolocation • navigator.clipboard • などなど -
“localhost” は HTTP 接続でも Secure Context 扱いなので要注意 ※Secure Context ≒
HTTPS 接続 localhost の注意点 -
ローカル環境におけるSSL/TLS証明書の発行手順の例 • ルート証明書 ◦ ルートCAの秘密鍵を作成 ◦ ルートCAの秘密鍵で署名してルート証明書を作成 • 中間CA証明書 ◦
中間CAの秘密鍵を作成 ◦ 中間CA証明書用のCSRを作成 ◦ ルートCAの秘密鍵で署名して中間証明書を作成 • サーバー証明書 ◦ サーバー証明書用の秘密鍵を作成 ◦ サーバー証明書用のCSRを作成 ◦ 中間CAの秘密鍵で署名してサーバー証明書を作成 -
ローカル環境におけるSSL/TLS証明書の発行手順の例 • ルート証明書 ◦ ルートCAの秘密鍵を作成 ◦ ルートCAの秘密鍵で署名してルート証明書を作成 • 中間CA証明書 ◦
中間CAの秘密鍵を作成 ◦ 中間CA証明書用のCSRを作成 ◦ ルートCAの秘密鍵で署名して中間証明書を作成 • サーバー証明書 ◦ サーバー証明書用の秘密鍵を作成 ◦ サーバー証明書用のCSRを作成 ◦ 中間CAの秘密鍵で署名してサーバー証明書を作成 -
ローカル環境におけるSSL/TLS証明書の発行手順の例 • ルート証明書 ◦ ルートCAの秘密鍵を作成 ◦ ルートCAの秘密鍵で署名してルート証明書を作成 • 中間CA証明書 ◦
中間CAの秘密鍵を作成 ◦ 中間CA証明書用のCSRを作成 ◦ ルートCAの秘密鍵で署名して中間証明書を作成 • サーバー証明書 ◦ サーバー証明書用の秘密鍵を作成 ◦ サーバー証明書用のCSRを作成 ◦ 中間CAの秘密鍵で署名してサーバー証明書を作成 -
ローカル環境におけるSSL/TLS証明書の発行手順の例 • ルート証明書 ◦ ルートCAの秘密鍵を作成 ◦ ルートCAの秘密鍵で署名してルート証明書を作成 • 中間CA証明書 ◦
中間CAの秘密鍵を作成 ◦ 中間CA証明書用のCSRを作成 ◦ ルートCAの秘密鍵で署名して中間証明書を作成 • サーバー証明書 ◦ サーバー証明書用の秘密鍵を作成 ◦ サーバー証明書用のCSRを作成 ◦ 中間CAの秘密鍵で署名してサーバー証明書を作成 -
ローカル環境におけるSSL/TLS証明書の発行手順の例 • ルート証明書 ◦ ルートCAの秘密鍵を作成 ◦ ルートCAの秘密鍵で署名してルート証明書を作成 • 中間CA証明書 ◦
中間CAの秘密鍵を作成 ◦ 中間CA証明書用のCSRを作成 ◦ ルートCAの秘密鍵で署名して中間証明書を作成 • サーバー証明書 ◦ サーバー証明書用の秘密鍵を作成 ◦ サーバー証明書用のCSRを作成 ◦ 中間CAルートCAの秘密鍵で署名してサーバー証明書を作成 中間CAの秘密鍵がなくても、ルートCAの秘密鍵で 署名を行えばサーバー証明書は作成可能 -
ローカル環境におけるSSL/TLS証明書の発行手順の例 • ルート証明書 ◦ ルートCAの秘密鍵を作成 ◦ ルートCAの秘密鍵で署名してルート証明書を作成 • 中間CA証明書 ◦
中間CAの秘密鍵を作成 ◦ 中間CA証明書用のCSRを作成 ◦ ルートCAの秘密鍵で署名して中間証明書を作成 • サーバー証明書 ◦ サーバー証明書用の秘密鍵を作成 ◦ サーバー証明書用のCSRを作成 ◦ 中間CAの秘密鍵で署名してサーバー証明書を作成 -
ローカル環境におけるSSL/TLS証明書の発行手順の例 • ルート証明書 ◦ ルートCAの秘密鍵を作成 ◦ ルートCAの秘密鍵で署名してルート証明書を作成 • 中間CA証明書 ◦
中間CAの秘密鍵を作成 ◦ 中間CA証明書用のCSRを作成 ◦ ルートCAの秘密鍵で署名して中間証明書を作成 • サーバー証明書 ◦ サーバー証明書用の秘密鍵を作成 ◦ サーバー証明書用のCSRを作成 ◦ 中間CAの秘密鍵で署名してサーバー証明書を作成 -
ローカル環境におけるSSL/TLS証明書の発行手順の例 • ルート証明書 ◦ ルートCAの秘密鍵を作成 ◦ ルートCAの秘密鍵で署名してルート証明書を作成 • 中間CA証明書 ◦
中間CAの秘密鍵を作成 ◦ 中間CA証明書用のCSRを作成 ◦ ルートCAの秘密鍵で署名して中間証明書を作成 • サーバー証明書 ◦ サーバー証明書用の秘密鍵を作成 ◦ サーバー証明書用のCSRを作成 ◦ 中間CAの秘密鍵で署名してサーバー証明書を作成 -
ルートCAは誰がどのように信頼しているのか? • 誰が ◦ OSベンダ ◦ ブラウザベンダ • どのように ◦
Root Storeプログラムへの申請と審査 ◦ CA/Browser Forum が策定している Baseline Requirements への準拠 ◦ WebTrust 、 ETSI による定期的な第三者監査 -
Linux(Ubuntu)の場合 (/etc/ssl/certs) $ ls -1 /etc/ssl/certs/ |sort ・ ・ Amazon_Root_CA_4.pem
Atos_TrustedRoot_2011.pem Atos_TrustedRoot_Root_CA_ECC_TLS_2021.pem Atos_TrustedRoot_Root_CA_RSA_TLS_2021.pem Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem BJCA_Global_Root_CA1.pem ・ ・ -
証明書に関連するインシデント例 • Comodo(2011年) ◦ 証明書発行申請者の情報を確認し、証明書を発行しても問題がないか 審査を行う機関である登録局がハッキング被害により、不正な証明書 の発行依頼が行われた ◦ 中間CAを介さずルートCAから直接発行していたという問題も発覚 •
DigiNotar(2011年) ◦ オランダの認証局がハッキング被害によりGoogleなどの著名なドメイ ンの偽証明書が500枚以上も発行された • Symantec(2015〜2017年) ◦ Google、Operaなどの証明書をドメイン所有者に知らせず誤発行した -
不正発行への対応 • CRL (Certificate Revocation List) ◦ ローカルに証明書失効リストをダウンロードして照合する。 ◦ 対象件数が多いのでダウンロードサイズが大きくなってしまう。
◦ リアルタイム性がない。 • OCSP (Online Certificate Status Protocol) ◦ リクエスト時にOCSPサーバに有効な証明書か照合する。 ◦ OCSPサーバーがダウンした場合に照合できない。 ◦ レイテンシ、プライバシーの問題。 • CT (Certificate Transparency) ログ ◦ すべての証明書の発行を記録しておいて、事後にチェックする。 ◦ 不正な証明書の発行は防げない。 • CAA レコード ◦ CAが発行可能なドメインをDNSで照合する。 ◦ DNSレコードに誤りや改ざんが発生すると破綻する。 -
TLS 証明書の有効期間は 47 日へ短縮されることが決定 • 2026年3月14日までに発行された証明書:最長398日 • 2026年3月15日から2027年3月14日:最長200日 • 2027年3月15日から2029年3月14日:最長100日
• 2029年3月15日以降:最長47日 https://www.digicert.com/jp/blog/tls-certificate-lifetimes-will-officially-reduce-to-47-days -
ルートCAの秘密鍵で署名してルート証明書を作成 openssl req -x509 \ ・・・証明書を作成(=自己署名) -key /etc/nginx/certs/snakeoil_root_ca.key \ ・・・秘密鍵のファイル名
-sha256 \ ・・・ハッシュアルゴリズム -days 3650 \ ・・・有効期限 -out /etc/nginx/certs/snakeoil_root_ca.crt \ ・・・証明書のファイル名 -config /etc/nginx/certs/snakeoil_root_ca.cnf ・・・設定ファイルのファイル名 -
ルートCAの秘密鍵で署名してルート証明書を作成 openssl req -x509 \ ・・・証明書を作成(=自己署名) -key /etc/nginx/certs/snakeoil_root_ca.key \ ・・・秘密鍵のファイル名
-sha256 \ ・・・ハッシュアルゴリズム -days 3650 \ ・・・有効期限 -out /etc/nginx/certs/snakeoil_root_ca.crt \ ・・・証明書のファイル名 -config /etc/nginx/certs/snakeoil_root_ca.cnf ・・・設定ファイルのファイル名 -
ルートCAの秘密鍵で署名してルート証明書を作成 openssl req -x509 \ ・・・証明書を作成(=自己署名) -key /etc/nginx/certs/snakeoil_root_ca.key \ ・・・秘密鍵のファイル名
-sha256 \ ・・・ハッシュアルゴリズム -days 3650 \ ・・・有効期限 -out /etc/nginx/certs/snakeoil_root_ca.crt \ ・・・証明書のファイル名 -config /etc/nginx/certs/snakeoil_root_ca.cnf ・・・設定ファイルのファイル名 -
snakeoil_root_ca.cnf の内容(抜粋) cat /etc/nginx/certs/snakeoil_root_ca.cnf ・ ・ [v3_root_ca] basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical,keyCertSign,cRLSign subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer -
snakeoil_root_ca.cnf の内容(抜粋) cat /etc/nginx/certs/snakeoil_root_ca.cnf ・ ・ [v3_root_ca] basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical,keyCertSign,cRLSign subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer -
ルート証明書の内容を確認 openssl x509 \ ・・・証明書の内容を表示 -in certs/snakeoil_root_ca.crt \ ・・・証明書のファイル名 -noout
\ ・・・指定オプション以外の表示を抑制 -issuer \ ・・・証明書の署名者(発行者) -subject \ ・・・証明書の主体者 -serial \ ・・・シリアル番号 -ext basicConstraints \ ・・・基本制約 -dates ・・・有効期限 -
ルート証明書の内容を確認 openssl x509 -in certs/snakeoil_root_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA serial=249019A1E67522361F1265075B70EB6ABCE400F1 X509v3 Basic Constraints: critical CA:TRUE, pathlen:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 24 14:42:11 2036 GMT -
ルート証明書の内容を確認 openssl x509 -in certs/snakeoil_root_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA serial=249019A1E67522361F1265075B70EB6ABCE400F1 X509v3 Basic Constraints: critical CA:TRUE, pathlen:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 24 14:42:11 2036 GMT -
ルート証明書の内容を確認 openssl x509 -in certs/snakeoil_root_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA serial=249019A1E67522361F1265075B70EB6ABCE400F1 X509v3 Basic Constraints: critical CA:TRUE, pathlen:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 24 14:42:11 2036 GMT -
ルート証明書の内容を確認 openssl x509 -in certs/snakeoil_root_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA serial=249019A1E67522361F1265075B70EB6ABCE400F1 X509v3 Basic Constraints: critical CA:TRUE, pathlen:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 24 14:42:11 2036 GMT -
ルート証明書の内容を確認 openssl x509 -in certs/snakeoil_root_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA serial=249019A1E67522361F1265075B70EB6ABCE400F1 X509v3 Basic Constraints: critical CA:TRUE, pathlen:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 24 14:42:11 2036 GMT -
中間CA証明書用のCSRを作成 openssl req -new \ ・・・CSRを作成 -key /etc/nginx/certs/snakeoil_intermediate_ca.key \ ・・・秘密鍵のファイル名
-out /etc/nginx/certs/snakeoil_intermediate_ca.csr \ ・・・CSRのファイル名 -config /etc/nginx/certs/snakeoil_intermediate_ca.cnf ・・・設定ファイルのファイル名 -
中間CA証明書用のCSRを作成 openssl req -new \ ・・・CSRを作成 -key /etc/nginx/certs/snakeoil_intermediate_ca.key \ ・・・秘密鍵のファイル名
-out /etc/nginx/certs/snakeoil_intermediate_ca.csr \ ・・・CSRのファイル名 -config /etc/nginx/certs/snakeoil_intermediate_ca.cnf ・・・設定ファイルのファイル名 -
ルートCAの秘密鍵で署名して中間証明書を作成 openssl x509 -req \ ・・・証明書を作成(≠自己署名) -in /etc/nginx/certs/snakeoil_intermediate_ca.csr \ ・・・CSRのファイル名
-CA /etc/nginx/certs/snakeoil_root_ca.crt \ ・・・署名を行うCAの証明書 -CAkey /etc/nginx/certs/snakeoil_root_ca.key \ ・・・署名を行うCAの秘密鍵 -CAcreateserial \ ・・・シリアル番号ファイルを作成 -sha256 \ ・・・ハッシュアルゴリズム -days 1825 \ ・・・有効期限 -out /etc/nginx/certs/snakeoil_intermediate_ca.crt \ ・・・証明書のファイル名 -extfile /etc/nginx/certs/snakeoil_intermediate_ca.cnf \ ・・・設定ファイルのファイル名 -extensions v3_intermediate_ca ・・・設定ファイルのセクション名 -
ルートCAの秘密鍵で署名して中間証明書を作成 openssl x509 -req \ ・・・証明書を作成(≠自己署名) -in /etc/nginx/certs/snakeoil_intermediate_ca.csr \ ・・・CSRのファイル名
-CA /etc/nginx/certs/snakeoil_root_ca.crt \ ・・・署名を行うCAの証明書 -CAkey /etc/nginx/certs/snakeoil_root_ca.key \ ・・・署名を行うCAの秘密鍵 -CAcreateserial \ ・・・シリアル番号ファイルを作成 -sha256 \ ・・・ハッシュアルゴリズム -days 1825 \ ・・・有効期限 -out /etc/nginx/certs/snakeoil_intermediate_ca.crt \ ・・・証明書のファイル名 -extfile /etc/nginx/certs/snakeoil_intermediate_ca.cnf \ ・・・設定ファイルのファイル名 -extensions v3_intermediate_ca ・・・設定ファイルのセクション名 -
snakeoil_intermediate_ca.cnf の内容(抜粋) cat /etc/nginx/certs/snakeoil_intermediate_ca.cnf ・ ・ [v3_intermediate_ca] basicConstraints = critical,CA:TRUE,pathlen:0
keyUsage = critical,keyCertSign,cRLSign subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer -
snakeoil_intermediate_ca.cnf の内容(抜粋) cat /etc/nginx/certs/snakeoil_intermediate_ca.cnf ・ ・ [v3_intermediate_ca] basicConstraints = critical,CA:TRUE,pathlen:0
keyUsage = critical,keyCertSign,cRLSign subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer -
中間CA証明書の内容を確認 openssl x509 \ ・・・証明書の内容を表示 -in certs/snakeoil_intermediate_ca.crt \ ・・・証明書のファイル名 -noout
\ ・・・指定オプション以外の表示を抑制 -issuer \ ・・・証明書の署名者(発行者) -subject \ ・・・証明書の主体者 -serial \ ・・・シリアル番号 -ext basicConstraints \ ・・・基本制約 -dates ・・・有効期限 -
中間CA証明書の内容を確認 openssl x509 -in certs/snakeoil_intermediate_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA serial=169B4D55B1A55713AA53CD5D29AE37B8ECD005FE X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 26 14:42:11 2031 GMT -
中間CA証明書の内容を確認 openssl x509 -in certs/snakeoil_intermediate_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA serial=169B4D55B1A55713AA53CD5D29AE37B8ECD005FE X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 26 14:42:11 2031 GMT -
中間CA証明書の内容を確認 openssl x509 -in certs/snakeoil_intermediate_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA serial=169B4D55B1A55713AA53CD5D29AE37B8ECD005FE X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 26 14:42:11 2031 GMT -
中間CA証明書の内容を確認 openssl x509 -in certs/snakeoil_intermediate_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA serial=169B4D55B1A55713AA53CD5D29AE37B8ECD005FE X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 26 14:42:11 2031 GMT -
中間CA証明書の内容を確認 openssl x509 -in certs/snakeoil_intermediate_ca.crt -noout -issuer -subject -serial -ext
basicConstraints -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Root CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA serial=169B4D55B1A55713AA53CD5D29AE37B8ECD005FE X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 26 14:42:11 2031 GMT -
サーバー証明書用のCSRを作成 openssl req -new \ ・・・CSRを作成 -key /etc/nginx/certs/snakeoil.key \ ・・・秘密鍵のファイル名
-out /etc/nginx/certs/snakeoil.csr \ ・・・CSRのファイル名 -config /etc/nginx/certs/snakeoil.cnf ・・・設定ファイルのファイル名 -
中間CAの秘密鍵で署名してサーバー証明書を作成 openssl x509 -req \ ・・・証明書を作成(≠自己署名) -in /etc/nginx/certs/snakeoil.csr \ ・・・CSRのファイル名
-CA /etc/nginx/certs/snakeoil_intermediate_ca.crt \ ・・・署名を行うCAの証明書 -CAkey /etc/nginx/certs/snakeoil_intermediate_ca.key \ ・・・署名を行うCAの秘密鍵 -CAcreateserial \ ・・・シリアル番号ファイルを作成 -sha256 \ ・・・ハッシュアルゴリズム -days 365 \ ・・・有効期限 -out /etc/nginx/certs/snakeoil.crt \ ・・・証明書のファイル名 -extfile /etc/nginx/certs/snakeoil.cnf \ ・・・設定ファイルのファイル名 -extensions v3_req ・・・設定ファイルのセクション名 -
snakeoil.cnf の内容(抜粋) cat /etc/nginx/certs/snakeoil.cnf ・ ・ [v3_req] basicConstraints = CA:FALSE
keyUsage = digitalSignature,keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = localhost IP.1 = 127.0.0.1 IP.2 = ::1 -
snakeoil.cnf の内容(抜粋) cat /etc/nginx/certs/snakeoil.cnf ・ ・ [v3_req] basicConstraints = CA:FALSE
keyUsage = digitalSignature,keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = localhost IP.1 = 127.0.0.1 IP.2 = ::1 -
snakeoil.cnf の内容(抜粋) cat /etc/nginx/certs/snakeoil.cnf ・ ・ [v3_req] basicConstraints = CA:FALSE
keyUsage = digitalSignature,keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = localhost IP.1 = 127.0.0.1 IP.2 = ::1 -
サーバー証明書の内容を確認 openssl x509 \ ・・・証明書の内容を表示 -in certs/snakeoil.crt \ ・・・証明書のファイル名 -noout
\ ・・・指定オプション以外の表示を抑制 -issuer \ ・・・証明書の署名者(発行者) -subject \ ・・・証明書の主体者 -serial \ ・・・シリアル番号 -ext basicConstraints,subjectAltName \ ・・・基本制約、SAN -dates ・・・有効期限 -
サーバー証明書の内容を確認 openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial -ext
basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=7B2204794683F8B181DA49D87DD8E630FF8AE0D7 X509v3 Basic Constraints: CA:FALSE X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 27 14:42:11 2027 GMT -
サーバー証明書の内容を確認 openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial -ext
basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=7B2204794683F8B181DA49D87DD8E630FF8AE0D7 X509v3 Basic Constraints: CA:FALSE X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 27 14:42:11 2027 GMT -
サーバー証明書の内容を確認 openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial -ext
basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=7B2204794683F8B181DA49D87DD8E630FF8AE0D7 X509v3 Basic Constraints: CA:FALSE X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 27 14:42:11 2027 GMT -
サーバー証明書の内容を確認 openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial -ext
basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=7B2204794683F8B181DA49D87DD8E630FF8AE0D7 X509v3 Basic Constraints: CA:FALSE X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 27 14:42:11 2027 GMT -
サーバー証明書の内容を確認 openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial -ext
basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=7B2204794683F8B181DA49D87DD8E630FF8AE0D7 X509v3 Basic Constraints: CA:FALSE X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 27 14:42:11 2027 GMT -
サーバー証明書の内容を確認 openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial -ext
basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=7B2204794683F8B181DA49D87DD8E630FF8AE0D7 X509v3 Basic Constraints: CA:FALSE X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 14:42:11 2026 GMT notAfter=Apr 27 14:42:11 2027 GMT -
秘密鍵を作成 $key = openssl_pkey_new([ 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA,
]); openssl_pkey_export_to_file($key, $path); -
秘密鍵を作成 $key = openssl_pkey_new([ 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA,
]); openssl_pkey_export_to_file($key, $path); -
秘密鍵を作成 $key = openssl_pkey_new([ 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA,
]); openssl_pkey_export_to_file($key, $path); -
自己署名で証明書を作成 $csr = openssl_csr_new($dn, $rootKey, [ 'config' => $configPath, 'digest_alg'
=> 'sha256', ]); $cert = openssl_csr_sign($csr, null, $rootKey, 3650, [ 'config' => $configPath, 'x509_extensions' => 'v3_root_ca', 'digest_alg' => 'sha256', ], $serial); openssl_x509_export_to_file($cert, $certPath); -
自己署名で証明書を作成 $csr = openssl_csr_new($dn, $rootKey, [ 'config' => $configPath, 'digest_alg'
=> 'sha256', ]); $cert = openssl_csr_sign($csr, null, $rootKey, 3650, [ 'config' => $configPath, 'x509_extensions' => 'v3_root_ca', 'digest_alg' => 'sha256', ], $serial); openssl_x509_export_to_file($cert, $certPath); -
自己署名で証明書を作成 $csr = openssl_csr_new($dn, $rootKey, [ 'config' => $configPath, 'digest_alg'
=> 'sha256', ]); $cert = openssl_csr_sign($csr, null, $rootKey, 3650, [ 'config' => $configPath, 'x509_extensions' => 'v3_root_ca', 'digest_alg' => 'sha256', ], $serial); openssl_x509_export_to_file($cert, $certPath); -
自己署名で証明書を作成 $csr = openssl_csr_new($dn, $rootKey, [ 'config' => $configPath, 'digest_alg'
=> 'sha256', ]); $cert = openssl_csr_sign($csr, null, $rootKey, 3650, [ 'config' => $configPath, 'x509_extensions' => 'v3_root_ca', 'digest_alg' => 'sha256', ], $serial); openssl_x509_export_to_file($cert, $certPath); -
CAによる署名で証明書を作成 $csr = openssl_csr_new($dn, $key, [ 'config' => $configPath, 'digest_alg'
=> 'sha256', ]); $cert = openssl_csr_sign($csr, $rootCert, $rootKey, 1825, [ 'config' => $configPath, 'x509_extensions' => 'v3_intermediate_ca', 'digest_alg' => 'sha256', ], $serial); openssl_x509_export_to_file($cert, $certPath); -
CAによる署名で証明書を作成 $csr = openssl_csr_new($dn, $key, [ 'config' => $configPath, 'digest_alg'
=> 'sha256', ]); $cert = openssl_csr_sign($csr, $rootCert, $rootKey, 1825, [ 'config' => $configPath, 'x509_extensions' => 'v3_intermediate_ca', 'digest_alg' => 'sha256', ], $serial); openssl_x509_export_to_file($cert, $certPath); -
CAによる署名で証明書を作成 $csr = openssl_csr_new($dn, $key, [ 'config' => $configPath, 'digest_alg'
=> 'sha256', ]); $cert = openssl_csr_sign($csr, $rootCert, $rootKey, 1825, [ 'config' => $configPath, 'x509_extensions' => 'v3_intermediate_ca', 'digest_alg' => 'sha256', ], $serial); openssl_x509_export_to_file($cert, $certPath); -
PHP 8.0.0 以降でOpenSSL関係のクラスが謎に充実している • OpenSSLAsymmetricKey ◦ https://www.php.net/manual/ja/class.opensslasymmetrickey.php • OpenSSLCertificate ◦
https://www.php.net/manual/ja/class.opensslcertificate.php • OpenSSLCertificateSigningRequest ◦ https://www.php.net/manual/ja/class.opensslcertificatesigningrequest.php -
証明書は一意なシリアル番号で管理されている openssl x509 -req \ ・ ・ -CAcreateserial ・ ・
$ cat certs/snakeoil_root_ca.srl 169B4D55B1A55713AA53CD5D29AE37B8ECD005FE -
openssl_csr_sign にはシリアル番号の採番機能がない $cert = openssl_csr_sign($csr, $rootCert, $rootKey, 1825, [ 'config'
=> $configPath, 'x509_extensions' => 'v3_intermediate_ca', 'digest_alg' => 'sha256', ], $serial); -
今回は動けばいいのでひとまず決め打ちで。。。 final class CertificateGenerator { ・ ・ private const SERIAL_ROOT_CA
= 1; private const SERIAL_INTER_CA = 2; private const SERIAL_SERVER = 3; -
PHP で openssl コマンド / OpenSSL 関数を利用した実装 • spomky-labs/pki-framework ◦
https://github.com/spomky-labs/pki-framework • adaopedro/php-openssl-proxy ◦ https://github.com/AdaoPedro/php-openssl-proxy • marguskaidja/php-openssl-wrapper ◦ https://github.com/marguskaidja/php-openssl-wrapper • fyrkat/php-openssl ◦ https://github.com/fyrkat/php-openssl • hsdn/phpopenssl ◦ https://github.com/hsdn/phpopenssl • madeny/lhttps ◦ https://github.com/madeny/lhttps • ryoluo/sail-ssl ◦ https://github.com/ryoluo/sail-ssl -
世の中には OpenSSL に頼らない実装がたくさんある • square/certstrap (Golang) • cloudflare/cfssl (Golang) •
caddyserver/caddy (Golang) • jsha/minica (Golang) • smallstep/cli (Golang) • traefik/traefik (Golang) • rustls/rcgen (Rust) • atgreen/pure-tls (Common Lisp) • The Bouncy Castle Crypto package (Java) • phpseclib/phpseclib (PHP) -
AIに以下のキーワードを元に証明書発行を実装してもらった • X.509 (RFC 5280) ◦ 公開鍵証明書のフォーマットと検証ルールを定義する標準規格 • X.680 (ASN.1)
◦ データ構造を言語・プラットフォーム非依存に記述するための表記法 ◦ X.509 証明書の構造は ASN.1 で記述されている • X.690 (BER / CER / DER) ◦ ASN.1で定義されたデータ構造を実際のバイト列に符号化(エンコー ド)するルールを定めた規格 ◦ 実際のファイル(.crt 等)は DER エンコードされたバイト列 -
なんかわからんけど ヨシッ! とコミットしたコードたち(抜粋) $n = gmp_mul($p, $q); $p1 = gmp_sub($p,
1); $q1 = gmp_sub($q, 1); // λ(n) = lcm(p-1, q-1) $lambda = gmp_div(gmp_mul($p1, $q1), gmp_gcd($p1, $q1)); // gcd(e, λ(n)) = 1 を確認 if (gmp_cmp(gmp_gcd($e, $lambda), 1) !== 0) { continue; } -
なんかわからんけど ヨシッ! とコミットしたコードたち(抜粋) final class X509Builder { // 署名アルゴリズム OID
private const OID_SHA256_WITH_RSA = '1.2.840.113549.1.1.11'; // RDN 属性 OID private const OID_COUNTRY = '2.5.4.6'; private const OID_STATE = '2.5.4.8'; private const OID_LOCALITY = '2.5.4.7'; private const OID_ORGANIZATION = '2.5.4.10'; private const OID_COMMON_NAME = '2.5.4.3'; -
なんかわからんけど ヨシッ! とコミットしたコードたち(抜粋) private static function oidComponent(int $val): string {
if ($val < 0x80) { return chr($val); } // Base-128 エンコーディング (最下位グループ以外は MSB を立てる) $bytes = chr($val & 0x7F); $val >>= 7; while ($val > 0) { $bytes = chr(0x80 | ($val & 0x7F)) . $bytes; $val >>= 7; } return $bytes; -
AI実装によって発行したサーバー証明書の内容を確認 $ openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial
-ext basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=03 X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: critical DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 16:07:49 2026 GMT notAfter=Apr 27 16:07:49 2027 GMT -
AI実装によって発行したサーバー証明書の内容を確認 $ openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial
-ext basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=03 X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: critical DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 16:07:49 2026 GMT notAfter=Apr 27 16:07:49 2027 GMT -
AI実装によって発行したサーバー証明書の内容を確認 $ openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial
-ext basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=03 X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: critical DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 16:07:49 2026 GMT notAfter=Apr 27 16:07:49 2027 GMT -
AI実装によって発行したサーバー証明書の内容を確認 $ openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial
-ext basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=03 X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: critical DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 16:07:49 2026 GMT notAfter=Apr 27 16:07:49 2027 GMT -
AI実装によって発行したサーバー証明書の内容を確認 $ openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial
-ext basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=03 X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: critical DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 16:07:49 2026 GMT notAfter=Apr 27 16:07:49 2027 GMT -
AI実装によって発行したサーバー証明書の内容を確認 $ openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial
-ext basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=03 X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: critical DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 16:07:49 2026 GMT notAfter=Apr 27 16:07:49 2027 GMT -
AI実装によって発行したサーバー証明書の内容を確認 $ openssl x509 -in certs/snakeoil.crt -noout -issuer -subject -serial
-ext basicConstraints,subjectAltName -dates issuer=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = Local Development Intermediate CA subject=C = JP, ST = Tokyo, L = Chiyoda, O = Local Development, CN = localhost serial=03 X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: critical DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 notBefore=Apr 27 16:07:49 2026 GMT notAfter=Apr 27 16:07:49 2027 GMT -
まとめ・感想 • SSL/TLS 証明書周りの技術は少々とっつきにくい。 • セキュリティ侵害を経て改善してきた歴史が興味深い。 • SSL/TLS 証明書の有効期限の短縮については早めの検討と対策をやっ ていきましょう。
• PHPによる実装は個人の趣味の範囲による活動です。実際の開発環境で は「なんかわからんけど ヨシッ!」 とコミットしてはいけません。 • ローカル環境で SSL/TLS 証明書を発行するのであれば openssl よりも mkcert がオススメです。