アプリ開発ときどきアウトドア

主にJavaを使ったアプリ開発やトラブルシューティング等のノウハウ、キャンプや登山の紹介や体験談など。

1. システムエンジニアリング SSL/TLS/PKI 基盤技術

ルートCA、サーバ・クライアント証明書の作成方法

投稿日:

概要

  • 検証・開発環境での使用を前提とした、ルートCA証明書の作成、その証明書で署名したサーバ証明書とクライアント証明書を作成する手順を説明します。
  • 可能な限りシンプルで簡単な条件・手順になっています。結果として、証明書の作成条件やコマンド操作ではセキュリティを考慮していないことに注意してください。
    (証明書の有効期限やその他条件、パスワードの取り扱い等)
  • 単純に自己署名のサーバ証明書やクライアント証明書を作成したい場合、ルートCA証明書を作成せずにサーバ証明書、クライアント証明書を作成するだけで済みます。このような手順は、別サイトの情報を参考にしてください。
  • Windows 10(64ビット)環境のPowerShellで検証しています。
    PowerShellで行えない一部操作は、Windows向けのopenssl(slproweb.com)のWin64 OpenSSL v1.1.1h Lightを使用しています。
    その他のバイナリはopensselのwikiを参照のこと。
  • 手順の前提
    • 証明書関連の用語(和名)は、可能な限り、証明書管理ツール(certmgr.msc)等のWindowsのGUIの表示名に合わせています。
    • 証明書の作成場所(証明書ストア)として、「現在のユーザー」の「個人」(“cert:\CurrentUser\My”)を使用します。
      証明書ストアの基礎知識と証明書ストアの論理名・物理名の対応はこちらをご覧ください。
      なお、証明書の作成先として「信頼されたルート証明機関」を指定するとエラーになってしまいます。
    • 検証用なので証明書の有効期限は10年にしています。
      (一般的な証明書の有効期限としては長すぎると思います。)
  • 手順実施のための参考
    • X.509のバージョン3から、後述の「拡張キー使用方法」「サブジェクト代替名」等のような拡張属性が追加されました。-DnsName等の特定用途のオプションはこのような拡張属性に対する値を設定します。より一般的な拡張属性に対する値の設定は、-TextExtensionオプションで行います。
      拡張属性の定義はRFC2549を参考にしてください。
    • 証明書に設定する項目や値はOIDと呼ばれる番号体系が使用されます。証明書関連のOIDの定義はRFC5280で定義されています。(参考
    • 証明書の作成時に、その証明書の使用方法「キー使用方法」と、その詳細「拡張キー使用方法」を指定できます。「拡張キー使用方法」(OID: 2.5.29.37)の英語名は、インターネット標準だと”Extended Key Usage: EKU”が正しいのですが、ここではマイクロソフトのリファレンスに合わせて”Enhanced Key Usage”と記載しています。(この辺の表記の違いはマイクロソフトの都合のようです。)
    • ファイルの拡張子の違いについては、こちらをご参考にしてください。
    • PowerShellの証明書関連のコマンド体系はこちらをご覧ください。

ルート証明書の作成

  1. サーバ証明書、クライアント証明書に署名するためのルートCA証明書を作成します。
    $rootca = New-SelfSignedCertificate `
      -Subject myrootca -NotAfter $(Get-Date).AddDays(3650) `
      -CertStoreLocation cert:\CurrentUser\My `
      -KeyUsage CertSign,CRLSign
    
    • New-SelfSignedCertificateを使用して、次の条件の証明書のルートCA証明書を作成します。
      • 名称(発行先)は”myrootca”
      • 有効期限は10年
      • 格納先の証明書ストアは「現在のユーザー」の「個人」
    • この証明書でサーバ証明書やクライアント証明書に署名できるよう「キー使用方法」(Key Usage)で署名関連のキーを指定する必要があります。これは、-keyusageオプションで指定しています。
      なお、このオプションを指定しなくても署名できるのですが、この証明書で署名したサーバ証明書はブラウザで信頼されません。(参考
    • キー使用方法の詳細を示す「拡張キー使用方法」(2.5.29.37)は、既定の「サーバー認証 (1.3.6.1.5.5.7.3.1)、クライアント認証 (1.3.6.1.5.5.7.3.2)」になります。
  2. 信頼するルート証明書として登録
    後述のサーバ証明書で警告がでないよう、作成したルートCA証明書を「信頼されたルート証明機関」にインストールします。
    Export-Certificate -Cert $rootca -FilePath myrootca.cer
    Import-Certificate -Cert cert:\CurrentUser\Root -FilePath myrootca.cer
    
    • 証明書ストアにあるルートCA証明書(証明書のみ、鍵は含まない)をファイルにエクスポートし、そのファイルを「信頼されたルート証明機関」にインポートしています。
    • 証明書管理ツール(certmgr.msc)のGUIで証明書をエクスポート・インポートする方法や、作成したcerファイルをダブルクリックしてインストールする方法でも可能です。

サーバ証明書の作成

前述の手順で作成したルート証明書を使用して、サーバ証明書を作成します。
ローカルでの動作確認を想定しているため、サーバ名は”localhost”にしています。

$rootca = Get-ChildItem `
  -Path cert:\CurrentUser\My | where { $_.Subject -eq "CN=myrootca" }

$server = New-SelfSignedCertificate `
  -DnsName localhost -NotAfter $(Get-Date).AddDays(3650) `
  -CertStoreLocation cert:\CurrentUser\My `
  -Signer $rootca `
  -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1")
  • 前述の手順で作成済みのルートCA証明書を$rootcaに取得します。
  • New-SelfSignedCertificateを使用して、次の条件のサーバ証明書を作成します。
    • サーバ名は”localhost”
    • 有効期限は10年
    • 格納先の証明書ストアは「現在のユーザー」の「個人」
    • ルートCA証明書($rootca)で署名
  • サーバ名を指定するために-DnsNameオプションを指定します。
    • 以前は、サブジェクト(CN値)に指定されたサーバ名を使ってサーバ証明書の検証が行われていました。
    • この方法は曖昧さがあるため、近年のブラウザではサブジェクト代替名(Subject Alternative Name: SAN)に指定されたサーバ名で検証するように変わっています。
    • 仕様の根拠はRFC2818の”3.1. Server Identity“にあります。
    • この対応はChrome58から始まっています。
    • -DnsNameの最初のホスト名がサブジェクト(CN値)に設定されます。
  • -TextExtensionで次を設定しています。
    • キーの使用方法である「拡張キー使用法」(Enhanced Key Usage: 2.5.29.37)として「サーバー認証 (1.3.6.1.5.5.7.3.1)」を指定しています。

    拡張キー使用法で指定できるOIDは、New-SelfSignedCertificateのリファレンスの”-TextExtension”オプションの説明をご覧ください。

クライアント証明書の作成

前述の手順で作成したルート証明書を使用して、クライアント証明書を作成します。

$rootca = Get-ChildItem `
  -Path cert:\CurrentUser\My | where { $_.Subject -eq "CN=myrootca" }

$client= New-SelfSignedCertificate `
  -Subject "CN=Ichiro Sato, O=Kaisha, OU=Busho, L=Shinjuku, S=Tokyo, C=Japan, E=test@example.com" `
  -NotAfter $(Get-Date).AddDays(3650) `
  -CertStoreLocation cert:\CurrentUser\My `
  -Signer $rootca `
  -TextExtension @( `
    "2.5.29.37={text}1.3.6.1.5.5.7.3.2", `
    "2.5.29.17={text}upn=test@example.com" `
   )
  • 前述の手順で作成済みのルートCA証明書を$rootcaに取得します。
  • New-SelfSignedCertificateを使用して、次の条件のクライアント証明書を作成します。
    • サブジェクトはユーザを識別するための情報(ユーザ名やメールアドレス等)
    • 有効期限は10年
    • 格納先の証明書ストアは「現在のユーザー」の「個人」
    • ルートCA証明書($rootca)で署名
  • -Subjectの値は例であり、ユーザを識別する情報として名前ではなくメールアドレスのみであれば、”-Subject test@example.com”のような指定も可能です。
  • -TextExtensionで次を設定しています。
    • 「拡張キー使用法」(Enhanced Key Usage: 2.5.29.37)として「クライアント認証 (1.3.6.1.5.5.7.3.2)」を指定しています。
    • サブジェクト代替名(2.5.29.17)としてUPNを指定しています。

    拡張キー使用法やサブジェクト代替名で指定できるOIDは、New-SelfSignedCertificateのリファレンスの”-TextExtension”オプションの説明をご覧ください。

PEM形式でエクスポート

Apache(httpd)で証明書を使う想定で用意された手順です。
(Apacheのhttpdでは、各種証明書や鍵をPEM形式のファイルで指定する必要があるため。)

ルート証明書、サーバ証明書、サーバ証明書の鍵、の3つをPEM形式のファイルにエクスポートします。

$password = ConvertTo-SecureString -String "password" -Force -AsPlainText
Export-PfxCertificate -Cert $rootca -FilePath myrootca.pfx -Password $password
Export-PfxCertificate -Cert $server -FilePath localhost.pfx -Password $password

openssl pkcs12 -in myrootca.pfx -clcerts -nokeys -out myrootca.crt
openssl pkcs12 -in localhost.pfx -clcerts -nokeys -out localhost.crt
openssl pkcs12 -in localhost.pfx -nocerts -nodes -out localhost.key
  • 少々乱暴ですが、とりあえずExport-PfxCertificateを使って、証明書も鍵も一旦pfxにエクスポートしてから、opensslでPEM形式に変換しています。
  • Windowsの機能では鍵をPEM形式でエクスポートできないので、opensslを使っています。
    同様に、証明書もopensslでエクスポートしていますが、Export-Certificateで実現できます。
  • 5, 6行目: 対象の証明書のみを出力するために”-clcerts”、キーを出力を抑制するために”-nokeys”を指定しています。
    (“-clcerts”の説明を読むと”output client certificate”とありますが、この文脈ではCAに署名された証明書のことを言っているようです。このオプションを付けないと、ルート証明書まで出力される場合があります。)
  • 7行目: 証明書の出力を抑制(結果として鍵のみ出力)するために”-nocerts”、httpdからパスワードなしでキーを参照できるようにするために、ファイルの暗号化を抑制する”-nodes”を指定しています。
  • Windowsでは証明書ファイルの拡張子としてcerを使うことが多いのですが、apache(httpd)ではcrt, keyを使っているので、ここではそれに合わせています。

参考

証明書エラーについて

  • 「信頼されたルート証明機関のストアに存在しないためこの CA ルート証明書は信頼されていません。」
    • ルート証明書を作成しただけでは信頼されず、この警告が出ます。
    • 作成したルート証明書を「信頼されたルート証明機関」にインストールすれば、信頼された証明書として認識されるため、この警告は出なくなります。
  • 「この証明書は選択された目的には有効でないと思われます。」
    (This Certificate is not valid for the selected purpose)
    • キー使用方法(-KeyUsageオプション)が適切に指定されていないルート証明書でサーバ証明書を署名すると、この警告が出ます。
    • 警告を無視して、ルート証明書とサーバ証明書をWebサーバで公開すると、次の警告が出ます。
      Chromeでは、警告画面に”NET::ERR_CERT_AUTHORITY_INVALID”と表示されており、正しいルート証明書として認識していないようです。

      証明書を開くと次のように警告が出ます。
      「The original certificate provided by the web server is untrusted.」
      「この証明書の発行者を検出できませんでした。」
    • キー使用方法(Key Usage)として「電子証明書の検証(CertSign)」「CRLの署名検証(CRLSign)」を指定したルート証明書を作成し、サーバ証明書を再作成する必要があります。

RFC5280でのOIDの定義

  • X.509証明書はRFC5280で定義されており、証明書を作成する際に使用するOIDもこの中で定義されています。
  • 例えば、拡張キー使用法(EKU)の記載を探すために、RFC5280を”2.5.29.37″で検索しても見つかりません…OIDの定義は後述のようにASN.1という形式で定義されており、単純な番号の羅列では定義されていないためです。
  • 拡張キー使用法(EKU: “2.5.29.37”)の定義
    • 証明書拡張(id-ce)としてOID: “2.5.29”が定義されています。
      4.2.1.  Standard Extensions
      ...
         id-ce   OBJECT IDENTIFIER ::=  { joint-iso-ccitt(2) ds(5) 29 }
      
    • 拡張キー使用法(id-ce-extKeyUsage)は”{ id-ce 37 }”と定義されています。
      前項のid-ceの値を展開すると”2.5.29.37″になります。
      4.2.1.12.  Extended Key Usage
      ...
         id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
      
  • 拡張キー使用法(EKU)で指定可能なキーの定義
    EKUの値として、サーバ認証やクライアント認証等の目的を表現するキーを指定します。
    目的キーの値は、例えばサーバ認証であれば”1.3.6.1.5.5.7.3.1″、クライアント認証であれば”1.3.6.1.5.5.7.3.2″を指定します。これらの目的キーは次のように定義されています。
    • X.509証明書の標準(id-pkix)として、”1.3.6.1.5.5.7″が定義されています。
      (PKIXは”the Public-Key Infrastructure using X.509″の意味のようです。)
      A.1.  Explicitly Tagged Module, 1988 Syntax
      ...
      id-pkix  OBJECT IDENTIFIER  ::=
               { iso(1) identified-organization(3) dod(6) internet(1)
                          security(5) mechanisms(5) pkix(7) }
      
    • 目的キー共通(id-kp)として”{ id-pkix 3 }”が定義されています。前項のid-pkixを展開すると”1.3.6.1.5.5.7.3″となります。
      目的キーの一つであるサーバ認証(id-kp-serverAuth)は”{ id-kp 1 }”と定義されています。前述のid-kpを展開すると”1.3.6.1.5.5.7.3.1″となります。
      同様に、クライアント認証は”{ id-kp 2 }”と定義されているので、id-kpを展開して”1.3.6.1.5.5.7.3.2″となります。
      4.2.1.12.  Extended Key Usage
      ...
         id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
      
         id-kp-serverAuth             OBJECT IDENTIFIER ::= { id-kp 1 }
         -- TLS WWW server authentication
         -- Key usage bits that may be consistent: digitalSignature,
         -- keyEncipherment or keyAgreement
      
         id-kp-clientAuth             OBJECT IDENTIFIER ::= { id-kp 2 }
         -- TLS WWW client authentication
         -- Key usage bits that may be consistent: digitalSignature
         -- and/or keyAgreement
      ...
      


(adsbygoogle = window.adsbygoogle || []).push({});


(adsbygoogle = window.adsbygoogle || []).push({});

-1. システムエンジニアリング, SSL/TLS/PKI, 基盤技術

執筆者:

関連記事

デジタル証明書のエンコードと拡張子の違い

デジタル証明書の種類と拡張子の理解が曖昧だったので整理してみました。 証明書や鍵そのものは基本的にはバイナリデータです。 これらのデータは、X.509やPKCSで仕様化されています。 X.509: 公 …

Apache FTPClientでのコマンドのトレース

Apache FTPClientを使って、JavaアプリケーションからFTP操作が可能となります。 この際、FTPクライアントとFTPサーバ間との通信内容をログに出力する方法を説明します。 概要 Ap …

保守運用

運用 と 保守 の 違い

若い頃は 運用 と 保守 の違いを調べても良くわからなかった… この辺を使い分けられる人をほとんど見たことない… ある事項が運用なのか保守なのかの話をすると認識が合わない&#8 …

Javaによるzipファイルの安全な解凍方法

以前、業務アプリ(Java)でzipファイルの操作が必要となったため、Javaにおけるzip圧縮解凍について調査しました。また、zip4jを使った圧縮・解凍についても説明しました。 ここでは、もう少し …

Windows10のインストール場所を選べない

DELLのノートPCであるVostro 5370を購入しました。 既定ではWindows 10 Homeがインストールされていましたが、会社用のボリュームライセンスのWindows10 Enterpr …