AWS Private CA で証明書を発行する方法を比較してみた

AWS Private CA で証明書を発行する方法を比較してみた

2025.10.29

はじめに

IoT デバイスとクライアント間で HTTPS 通信をする際、閉域環境でも信頼できる TLS 証明書をどう発行・運用するかという課題があります。
業務で同じテーマに直面したことがきっかけで、AWS Private CA の証明書発行方法を試しながら比較検証しました。
発行経路ごとの違いとユースケースを整理することで、状況に応じた選び方が見えてきたので共有します。

CA (認証局) について

CA (Certificate Authority: 認証局) は、デジタル証明書を発行する信頼された第三者機関です。
身分証明書を発行する役所のようなものと考えると分かりやすいでしょう。

信頼の仕組みは、ブラウザや OS にあらかじめ組み込まれている信頼できる CA (ルート CA) のリストから始まります。
Web サイトの証明書がそのリストにある CA で署名されていれば、「このサイトは信頼できる」と判定され、HTTPS 通信時に鍵マークが表示される、という流れです。

図にすると以下のようなイメージになると思います。

ca-structure.png

実際に Private CA を用意し、どの経路で証明書を発行するのかを手を動かしながら確認していきます。

Private CA の作成

まず、証明書を発行する前に Private CA を作成する必要があります。

ルート CA は以下のように用意してみました。

AWS-Private-Certificate-Authority-1.png

証明書の発行方法1: API による発行

この方法が適しているケース

この方式は、IoT デバイスのように端末自身で秘密鍵を生成・保持したいケースに適しています。
AWS の外にあるサーバへ証明書を配布したいときや、組織のセキュリティポリシー上 AWS 外に鍵を置く必要があるときにも、この手法は選べます。

秘密鍵と CSR の作成

まず、秘密鍵と CSR を生成します。

$ openssl req -out csr.pem -new -newkey rsa:2048 -nodes -keyout private-key.pem

CSR 生成時に入力した内容は次のとおりです。

Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:WA
Locality Name (eg, city) []:Seattle
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Corp
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:private.example.com

AWS CLI での証明書発行

作成した CSR を CA に渡して証明書を発行します。
以下の issue-certificate コマンドは、署名アルゴリズムに SHA256withRSA を指定し、有効期限を 10年 に設定しています。
実行後、証明書の ARN が返却されます。

$ aws acm-pca issue-certificate \
      --certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:111111111111:certificate-authority/hoge \
      --csr fileb://csr.pem \
      --signing-algorithm "SHA256WITHRSA" \
      --validity Value=10,Type="YEARS"

{
    "CertificateArn": "arn:aws:acm-pca:ap-northeast-1:111111111111:certificate-authority/hoge/certificate/piyo"
}

発行された証明書をダウンロードするには get-certificate コマンドを利用します。

$ aws acm-pca get-certificate \
      --certificate-arn arn:aws:acm-pca:ap-northeast-1:111111111111:certificate-authority/hoge/certificate/piyo \
      --certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:111111111111:certificate-authority/hoge \
      | jq -r '.Certificate, .CertificateChain'

-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----

CA 証明書は get-certificate-authority-certificate コマンドで取得します。

$ aws acm-pca get-certificate-authority-certificate \
      --certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:111111111111:certificate-authority/hoge \
      | jq -r '.Certificate'

-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----

検証してみた

簡単なスクリプトでサーバを立てて、TLS 通信できるかどうかを検証してみます。
先ほど取得したエンドエンティティ証明書は server.crt として、秘密鍵と同じディレクトリに設置しておきます。

以下のような簡易的なスクリプトを用意して、

import ssl
import http.server

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('server.crt', 'private-key.pem')

server = http.server.HTTPServer(('localhost', 8443), http.server.SimpleHTTPRequestHandler)
server.socket = context.wrap_socket(server.socket, server_side=True)

print("TLS server running on https://localhost:8443")
server.serve_forever()

サーバを起動します。

$ python3 server.py
TLS server running on https://localhost:8443

CA 証明書は ca.crt とします。
今回は CN を private.example.com としているので、よしなにホスト名を合わせます。

$ curl -Iv --cacert ca.crt --resolve private.example.com:8443:127.0.0.1 https://private.example.com:8443
* Added private.example.com:8443:127.0.0.1 to DNS cache
* Hostname private.example.com was found in DNS cache
*   Trying 127.0.0.1:8443...
* Connected to private.example.com (127.0.0.1) port 8443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: ca.crt
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: C=US; ST=WA; L=Seattle; O=Example Corp; OU=Sales; CN=private.example.com
*  start date: Oct 28 06:41:09 2025 GMT
*  expire date: Oct 26 07:41:09 2035 GMT
*  common name: private.example.com (matched)
*  issuer: C=US; O=Example Corp; OU=Sales; ST=WA; CN=www.example.com; L=Seattle
*  SSL certificate verify ok.
* using HTTP/1.x
> HEAD / HTTP/1.1
> Host: private.example.com:8443
> User-Agent: curl/8.7.1
> Accept: */*
> 
* Request completely sent off
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.7
Server: SimpleHTTP/0.6 Python/3.12.7
< Date: Wed, 29 Oct 2025 09:50:11 GMT
Date: Wed, 29 Oct 2025 09:50:11 GMT
< Content-type: text/html; charset=utf-8
Content-type: text/html; charset=utf-8
< Content-Length: 406
Content-Length: 406
< 

* Closing connection

良い感じですね。

証明書の発行方法2: ACM (AWS Certificate Manager) 連携

この方法が適しているケース

ACM 連携は、ALB や CloudFront のように AWS リソース上で証明書を扱いたいときに有用です。
パブリック証明書を ACM で発行したときと同様に、証明書更新を ACM の自動更新に任せられる点が特に助かります。

ACM での証明書リクエスト

作業自体はシンプルです。
リクエストを確定すれば、数分でプライベート証明書が ACM 側に届きます。

Certificate-Manager-1.png

ACM 連携時の補足

証明書の有効期間は 13 ヶ月 (395 日) で固定されており、11 ヶ月目に自動更新が走ります。
より長く持たせたいときは CLI などから直接発行する必要がある点は覚えておくと良さそうです。

メリットとしては、証明書の更新タイミングを ACM が肩代わりしてくれること、そしてロードバランサへのデプロイまでコンソールから完結できることが大きいと思います。

一方で 注意点もあります。
ACM 管理の証明書は AWS サービス内でしか使えず、秘密鍵のエクスポートもできません。
また、自動更新を有効にするには、証明書を必ずどこかのリソースに関連付けておく必要がある点も気をつけましょう。

まとめ

AWS Private CA を使った証明書発行方法を2パターン試してみました。

AWS CLI 方式は IoT デバイスなど秘密鍵を外部管理したいケースに向いており、スクリプト化で大量発行にも対応できそうです。
ACM 連携は ALB や CloudFront への適用が簡単で自動更新も効きますが、AWS サービス内でしか使えない制約がありました。

用途に応じて使い分けることで、Private CA を効果的に活用できます。
基本的な発行手順が確認できたら、Lambda による自動更新や CRL 配布など、運用の効率化も検討してみてください。

この記事をシェアする

FacebookHatena blogX

関連記事