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

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

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

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

投稿日:2020年11月14日 更新日:

概要

  • 検証・開発環境での使用を前提とした、ルート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=Ken 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" `
   )

$password = ConvertTo-SecureString -String "password" -Force -AsPlainText
Export-PfxCertificate -Cert $client -FilePath KenSato.pfx -Password $password
  • 前述の手順で作成済みのルート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”オプションの説明をご覧ください。

  • 作成したクライアント証明書をユーザに配布できるよう、Export-PfxCertificateを使用して、証明書と鍵をPFX(PKCS#12)形式でファイルにエクスポートします。

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 server-ca.crt
openssl pkcs12 -in localhost.pfx -clcerts -nokeys -out server.crt
openssl pkcs12 -in localhost.pfx -nocerts -nodes -out server.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. システムエンジニアリング, PowerShell, SSL/TLS/PKI, windows, 基盤技術

執筆者:

関連記事

.NET Core: Azure AD B2Cユーザアカウント操作のサンプル

概要 管理者がAzure AD B2Cのユーザアカウントの管理が行えるASP.NET Coreのアプリの開発を想定している。Azure AD B2Cユーザアカウントの作成や更新等の操作はMicroso …

500円でVPS:VULTRの$2.5/$3.5プラン

月額500円程度でVPSを運用できるVULTRの紹介です。 VULTR(ヴァルチャー)は米国フロリダにある会社で、日本法人はない。(LinkedIn) 欧米・アジア・日本等の17拠点のデータセンターを …

JIS X 0208, Shift_JIS, Windows-31Jの整理

文字コードの話は難しそうなイメージがあり、必要になったタイミングでその都度、最小限の知識を習得して対応してきました…が、効率が悪く踏み込んだ話になった時に困る場合もあるので、ここで真面目に …

Javaでのパスワード付きzipファイルの圧縮/解凍方法(ZipCrypto/AES)

先日、JavaでのZIP暗号化の考察という記事を書きましたが、zip4jのメンテナンスが再開されており、バージョン2系が公開されていましたので、これを使って通常のzip圧縮/解凍、パスワード付きzip …

Apache HttpClientの通信内容をダンプ

アプリやミドルウェの動作の正常性確認や問題発生時の問題切り分けのために、HTTPリクエストやレスポンスのヘッダやボディを確認したい場合がある。Java系のアプリではApacheのHttpClientが …