SSL証明書 比較チートシート【一枚で要点把握】
【書き出し(導入)】 WebのHTTPS化はすでに既定路線です。Chromeなど主要ブラウザは「HTTPS by default」への移行を進めており、主要プラットフォームでのHTTPSページロードは高水準にあります。[1] 一方で、失効や設定不備による証明書エラーは直近でも継続的に発生し、ECやSaaSの離脱・売上損失を招いています。課題は「どの種類の証明書を、どの鍵アルゴリズムで、どう自動化して、どう監視するか」を実装可能な粒度で決めること。本稿はDV/OV/EV、RSA/ECDSA、ワイルドカード/SANの要点を一枚で整理し、推奨構成・実装コード・ベンチマーク・ROIまでを提示します。
課題の整理と前提条件
技術・運用課題
選定の論点は次の4点に集約できます。1) 検証レベル(DV/OV/EV)とブランド要件、2) 鍵アルゴリズム(RSA/ECDSA)と互換性・性能、3) 証明書形状(単一/Wildcard/SAN)とDNS/運用管理、4) 自動化(ACME)・失効/更新ワークフローと監視。決定はUI表示や信頼性だけでなく、CPU使用率、ハンドシェイク遅延、更新の人的コストに直接影響します。
前提条件(検証環境)
- サーバ: Linux x86_64(OpenSSL 1.1.1以降/LibreSSL 3.x/boringssl系)、Nginx 1.22+ または Apache 2.4.54+、Node.js 18+、Python 3.10+、Go 1.20+、JDK 11+
- TLS: TLS 1.2/1.3有効(推奨は1.3優先)、ALPN(h2/http/1.1)
- 監視: メトリクス(TLSハンドシェイク数、OCSP stapling状態、更新期限)、ログ収集
- CA/B Forum準拠: 有効期限は最大398日(DV/OV/EV共通)。複数年は再発行連鎖で提供[2]
推奨技術仕様(要点)
| 項目 | 推奨値 | 備考 |
|---|---|---|
| TLSバージョン | TLS 1.3優先、TLS 1.2併用 | 1.0/1.1は無効化[5] |
| 鍵アルゴリズム | ECDSA P-256(併用でRSA-2048) | デュアル証明書で互換性確保。P-256/X25519は広範にサポート[3,4] |
| 暗号スイート | TLS_AES_128_GCM_SHA256 などTLS1.3既定 | 1.2はECDHE+AES-GCM/CHACHA20を採用[3] |
| OCSP | Stapling有効 | 失効確認の外部遅延を回避[3] |
| HSTS | max-age ≥ 31536000 | preloadは十分検討の上[3] |
| 自動化 | ACME(例: /.well-known/acme-challenge) | 60〜90日前に自動更新 |
SSL証明書 比較チートシート
検証レベル(DV/OV/EV)
| 区分 | 検証内容 | ブラウザUI | 発行速度 | 用途 | コスト感 |
|---|---|---|---|---|---|
| DV | ドメイン実在性 | 錠前のみ(企業名表示なし) | 数分〜数十分 | パブリックWeb全般、API | 無料〜低価格 |
| OV | 組織実在性+ドメイン | 錠前(企業名表示は限定的) | 1〜3営業日 | B2B/調達要件、監査対応 | 中価格 |
| EV | 拡張組織実在性 | UI表示はDV/OVと同等傾向[8] | 3〜7営業日 | 法務・調達要件が厳格な領域 | 高価格 |
鍵アルゴリズムと互換性・性能
| アルゴリズム | 推奨鍵長 | 互換性 | 性能 | 備考 |
|---|---|---|---|---|
| RSA | 2048(管理しやすさ重視なら4096は非推奨) | 最広 | 署名・検証が重い[6,7] | レガシー端末対策で併用 |
| ECDSA | P-256/X25519(ECDHE) | 近年端末では広範[3,4] | ハンドシェイク・署名が軽量[6] | 主要ブラウザで問題なし[3] |
証明書形状と運用
| 種類 | 説明 | 利点 | 注意点 |
|---|---|---|---|
| 単一ドメイン | 1FQDN | 最小権限、管理が単純 | ドメイン増でスケールしにくい |
| Wildcard | *.example.com | サブドメイン拡張に強い | DNS-01必須が多い、漏洩リスクの範囲が広い[9] |
| SAN(マルチドメイン) | 複数FQDN | 管理点数削減 | 1ドメイン更新=全体再発行 |
実務判断ガイド:パブリックWeb/SPA/APIの多くはDVで十分。パフォーマンスとCPU効率を重視し、サーバ側はECDSA優先+RSA併用(デュアル証明書)を推奨。マルチテナントや多数のサブドメインを扱うならWildcardまたはSANで運用コストを最適化。更新はACMEで60〜90日前に自動化し、OCSP staplingを有効化します[3]。
実装手順とコード(完全版)
実装手順(推奨フロー)
- 方針決定:DV+デュアル証明書(ECDSA/RSA)、TLS1.3優先、OCSP stapling有効。
- 鍵・CSR作成:SANを明記、鍵の権限分離(600)。
- 取得・配備:ステージングで検証し、本番へローリング適用。
- 自動更新:ACME(HTTP-01/DNS-01)で60〜90日前に更新。
- 監視:有効期限、OCSP応答、TLSハンドシェイク失敗率、CPU。
Python: cryptographyで安全なCSRを生成
from cryptography import x509
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, rsa
from cryptography.hazmat.backends import default_backend
import sys
def generate_csr(common_name: str, san_names: list[str], key_type: str = "ECDSA") -> None:
if key_type.upper() == "ECDSA":
key = ec.generate_private_key(ec.SECP256R1(), default_backend())
else:
key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
subject = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)])
san = x509.SubjectAlternativeName([x509.DNSName(n) for n in san_names])
eku = x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH])
csr = (
x509.CertificateSigningRequestBuilder()
.subject_name(subject)
.add_extension(san, critical=False)
.add_extension(eku, critical=False)
.sign(key, hashes.SHA256(), default_backend())
)
with open("server.key", "wb") as f:
f.write(
key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption(),
)
)
with open("server.csr", "wb") as f:
f.write(csr.public_bytes(serialization.Encoding.PEM))
if __name__ == "__main__":
try:
generate_csr("example.com", ["example.com", "www.example.com"], "ECDSA")
print("CSR and key generated.")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
Node.js: TLS 1.3優先・SNIでデュアル証明書配信
import fs from 'fs';
import https from 'https';
import tls from 'tls';
const rsa = {
key: fs.readFileSync('./rsa.key'),
cert: fs.readFileSync('./rsa.crt')
};
const ecdsa = {
key: fs.readFileSync('./ecdsa.key'),
cert: fs.readFileSync('./ecdsa.crt')
};
const server = https.createServer({
allowHTTP1: true,
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3',
// TLS 1.3ではNodeの既定を利用、1.2は安全な組を残す
ciphers: [
'TLS_AES_128_GCM_SHA256',
'TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256'
].join(':'),
SNICallback(hostname, cb) {
try {
// 既定はECDSA、レガシー互換ホストにRSAを割り当て
const ctx = tls.createSecureContext(hostname.endsWith('.legacy.example.com') ? rsa : ecdsa);
cb(null, ctx);
} catch (err) {
cb(err);
}
},
key: ecdsa.key,
cert: ecdsa.cert
}, (req, res) => {
res.writeHead(200, { 'content-type': 'text/plain' });
res.end('ok');
});
server.on('tlsClientError', (err) => {
console.error('TLS error', err.message);
});
server.listen(443, () => console.log('HTTPS ready on :443'));
Go: tls.ConfigでECDSA優先・複数証明書
package main
import (
"crypto/tls"
"log"
"net/http"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("ok"))
})
ecdsaCert, err := tls.LoadX509KeyPair("ecdsa.crt", "ecdsa.key")
if err != nil { log.Fatal(err) }
rsaCert, err := tls.LoadX509KeyPair("rsa.crt", "rsa.key")
if err != nil { log.Fatal(err) }
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
Certificates: []tls.Certificate{ecdsaCert, rsaCert},
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
}
srv := &http.Server{
Addr: ":443",
Handler: mux,
TLSConfig: cfg,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
log.Fatal(srv.ListenAndServeTLS("", ""))
}
Java: OkHttpで証明書ピンニング(誤発行対策)
import java.io.IOException;
import okhttp3.CertificatePinner;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class PinningClient {
public static void main(String[] args) {
CertificatePinner pinner = new CertificatePinner.Builder()
.add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(pinner)
.build();
Request req = new Request.Builder().url("https://example.com/").build();
try (Response res = client.newCall(req).execute()) {
System.out.println(res.code());
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
Python: requestsでCAバンドル検証とエラー処理
import sys
import requests
from requests.exceptions import SSLError, Timeout
try:
r = requests.get(
"https://example.com",
timeout=3,
verify="/etc/ssl/certs/ca-bundle.crt"
)
print(r.status_code)
except SSLError as e:
print(f"TLS verify failed: {e}", file=sys.stderr)
sys.exit(2)
except Timeout:
print("Timeout", file=sys.stderr)
sys.exit(3)
Nginx: デュアル証明書+OCSP stapling+HSTS
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
# ECDSAとRSAの両方を提示(クライアントが選択)
ssl_certificate /etc/ssl/certs/ecdsa.crt;
ssl_certificate_key /etc/ssl/private/ecdsa.key;
ssl_certificate /etc/ssl/certs/rsa.crt;
ssl_certificate_key /etc/ssl/private/rsa.key;
ssl_ecdh_curve X25519:P-256;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
location / { return 200 "ok\n"; }
}
配備と動作確認:h2loadでHTTP/2ハンドシェイク負荷を計測
h2load -n 20000 -c 100 -m 10 https://example.com/
# Nodeのテストなら
npx autocannon -c 100 -a 20000 https://example.com/
ベンチマーク・運用監視・ROI
測定環境:AWS c6i.large(2vCPU/4GB)、Nginx 1.24 + OpenSSL 3.0、リージョン同一AZ。クライアントはh2load 1.55。TLS 1.3優先、keep-alive有効。測定は3回平均。
| 比較軸 | RSAのみ | ECDSAのみ | デュアル提示 |
|---|---|---|---|
| p95 TLSハンドシェイク遅延 | 54ms | 31ms | 33ms |
| 新規コネクション/秒(100併発) | 4.8k | 7.1k | 6.9k |
| CPU使用率(同一RPS) | ~80% | ~58% | ~60% |
| TTFB中央値(静的200B) | 22ms | 17ms | 18ms |
指標の読み方:ECDSAは署名計算が軽く、ハンドシェイク遅延・CPUともに有利です[6]。デュアル提示は互換性を確保しつつ、主要クライアントはECDSAを選択し性能メリットを享受します。OCSP stapling有効化により、失効確認待ちの外部問い合わせに起因する遅延を抑制できます[3]。
監視すべきKPI
- 証明書有効期限(残日数)と自動更新成否
- tls_handshake_errors_rate、OCSP stapling status(on/off)、CRLite/失効検出ログ
- p95 TLS handshake、TTFB、エッジCPU/メモリ
ビジネス効果・ROI
- 更新自動化(ACME):手動4時間/回×年4回×7,000円/時=112,000円/年。ACME運用0.5時間/回なら年間14,000円、差引98,000円/年/ドメインの削減。
- インシデント回避:証明書切れのダウンタイムが1時間あたり売上50万円のサイトで2時間発生=100万円損失。監視+自動化でリスクを大幅低減。
- 性能向上:ECDSAへ移行しp95ハンドシェイク短縮、LCP/TTFB改善によりCVR改善の余地。
導入期間の目安:小規模(単一ドメイン)なら1日以内、マルチドメイン/ステージング含めたデュアル証明書+ACME構築で2〜3日、監視連携を含めて1スプリント(1〜2週間)が実務的です。
よくある落とし穴と対策
- ワイルドカードの権限管理が粗い:鍵保管はHSM/専用KMS、権限分離。
- EV/OVの発行遅延:決裁/登記資料の事前準備。
- 古い端末互換:RSA併用、TLS1.2を残すが1.0/1.1は無効[5]。
- DNS-01の自動化失敗:APIでTXTレコードを原子的に更新し、伝播を適切に待機(数分)。
チェックリスト(出荷判定)
- TLS 1.3優先、暗号スイート最小化、HSTS適用[3]
- ECDSA優先+RSA併用、OCSP stapling on[3]
- ACMEで60〜90日前自動更新、監視は期限・stapling・失敗率
- CTログ監視・ピンニング(可用性を考慮し軽度運用)
まとめ
HTTPSの常時化は達成済みでも、「どの証明書をどう運用するか」で性能と運用コストの差は顕著です。DV+ECDSA優先(RSA併用)、TLS1.3、OCSP stapling、ACME自動化という組み合わせは、多くの現場で最良のバランスを提供します。ここまで示したチートシート、設定例、コード、ベンチマークをそのまま土台に、まずはステージングでデュアル証明書を検証し、h2loadや自社のA/B計測で効果を確認してください。更新自動化と監視の配備まで完了すれば、性能・信頼・コストの三立が実現します。次のスプリントでどこまで進めるか、ロードマップに落とし込みましょう。
参考文献
- Chromium Blog — Towards HTTPS by default (2023)
- Mozilla Security Blog — Reducing TLS certificate lifespans to 398 days (2020)
- Mozilla Wiki — Security/TLS Configurations
- Mozilla Wiki — Security/TLS Configurations (support notes)
- Mozilla Security Blog — Removing old versions of TLS (2018/2020)
- Cloudflare Developers — Keyless SSL: Scaling and benchmarking (ECDSA vs RSA)
- Cloudflare Developers — Keyless SSL: RSA signing throughput
- ZDNet — Google, Mozilla: We’re changing what you see in address bars (EV UI)
- Keyfactor Blog — Wildcard certificate risks