Certificate Transparency(CT)ログとは何か
CTログは、公開鍵証明書の不正発行を防止するために設計された公開ログシステム。Googleによって提唱され、現在ではChromeやSafariなど主要なブラウザが証明書の監視に利用している。
CA(認証局)が新しいTLSサーバ証明書を発行する際、その証明書を一つまたは複数のCTログに登録する。CTログは基本的に公開されており、誰でもログの内容を検証・監視できる。この仕組みにより、不正な証明書や誤って発行された証明書を迅速に検知することが可能になる。
仕組みの概要
- 証明書の登録(Submit)
CAは、発行予定の証明書または証明書チェーンをCTログサーバーに送信する。これにより、サーバーからSCT(Signed Certificate Timestamp)と呼ばれる署名付きタイムスタンプが返ってくる。 - SCTの埋め込み
取得したSCTは証明書本体やTLS拡張、OCSPレスポンスなどに埋め込まれる。クライアント(主にブラウザ)は証明書を検証する際、SCTの存在と有効性を確認する。 - ログの検証と監視
CTログはMerkle Treeというデータ構造で管理されており、第三者(モニタやオーディタ)がログの整合性や完全性を検証できるようになっている。これにより、ログの改ざんがあれば発覚する仕組みになっている。
なぜCTが重要なのか
過去に複数の認証局が誤って、あるいは悪意をもって不正な証明書を発行した事例がある。例えば、ある組織のドメインに対して、その組織の管理下にない者が証明書を取得したとすれば、なりすまし攻撃や中間者攻撃のリスクが生じる。
CTログは、すべての証明書を透明性のある形で公開することで、こうした不正の早期発見を可能にする。例えば、ある企業が自社ドメインのCTログを監視していれば、未知の証明書が登録されたときに即座に気付くことができる。
各ブラウザベンダーがCTログを必須化(証明書にSCTの埋め込みを要求)したタイミングは以下の通り。
Google Chrome
- 2018年4月30日以降発行の証明書に対してCTを必須化
これは Chrome CT Policy に基づくもので、具体的には 2018年4月30日以降に発行されたすべてのTLSサーバ証明書は、複数のCTログに登録し、SCTを提供しなければならない。 - 証明書にCT情報(SCT)が含まれていない場合、Chromeはその証明書をエラー扱いで拒否する。
Apple (Safari)
- 2020年9月1日以降発行の証明書に対してCTを事実上必須化
Appleは2020年9月1日以降に発行された証明書について、CTを含まない場合はSafariで信頼されない動作に変更した。 - 参考資料: Apple Developer: Requirements for trusted certificates in iOS 13 and macOS 10.15
- 実際には、1個以上のSCTがTLS拡張・OCSP・証明書本体いずれかに含まれていなければ接続エラーとなる。
Mozilla Firefox
- CTの使用を強制はしていない(任意)
FirefoxはChromeやSafariと異なり、2025年現在でもCTログの使用を必須にはしていない。ただし、Mozilla Root Storeにおける監視・透明性の観点から、CAに対してCTの活用を推奨している。
まとめ表
ブラウザ | CT必須化時期 | 備考 |
---|---|---|
Chrome | 2018年4月30日以降 | SCT必須。CTログに複数登録必要 |
Safari (Apple) | 2020年9月1日以降 | 実質必須。SCTなしはエラー |
Firefox (Mozilla) | 必須化されていない | 任意。CTの活用は推奨 |
どのログに登録すべきか
ブラウザベンダー(特にGoogle)は、信頼できるCTログのリストを定期的に公開している。Chrome Root Programでは、「Qualified Logs」として承認されたログに証明書を登録することが求められており、必要に応じて複数のログに分散して登録する必要がある。
Google Chrome: Recognized Logs
AppleのCertificate Transparencyポリシー
また、証明書の有効期間や発行時期に応じて、登録すべきログが指定されるケースもある(例:argon2025h1
や argon2026h2
など)。これはログの保持期間や信頼期間が制限されているため。
CTログ送信スクリプト例
CTログには、ルート証明書から信頼の連鎖がつながっている証明書であれば、誰でも送信できる。送信する証明書が自己署名証明書や、ルート証明書からのパスが存在しない中間CA証明書、あるいは期限切れや失効済みの証明書であっても、形式的に正しい構造を持っていればCTログは受け付ける仕様になっている。ただし、各CTログサーバーにはそれぞれポリシーがあり、例えば特定の有効期間を超える証明書や、署名アルゴリズムが古いものは拒否されることがある。基本的には、RFC 6962に従っており、ルートからのパスが存在する証明書であれば誰でもログにエントリを追加できる設計になっている。
FQDNを指定して送信
利用方法
[root@localhost CT]# ./submit_to_ct_log_fqdn.sh pki.world-tls.com
2025-05-06 17:41:09 [INFO] main: Start
2025-05-06 17:41:09 [INFO] FQDNから証明書取得: pki.world-tls.com
2025-05-06 17:41:09 [INFO] openssl s_client -connect試行 1: pki.world-tls.com
2025-05-06 17:41:09 [INFO] 証明書チェーンの取得に成功: pki.world-tls.com (試行 1)
2025-05-06 17:41:09 [INFO] EE証明書が存在: ./cert/cert-01.pem
2025-05-06 17:41:09 [INFO] CA証明書が存在: ./cert/cert-02.pem
2025-05-06 17:41:09 [INFO] EE証明書をsubmission.pemに追加: ./cert/ee/cert-01.pem
2025-05-06 17:41:09 [INFO] 証明書情報:./cert/ee/cert-01.pem: Subject: subject=CN=pki.world-tls.com, Issuer: issuer=C=US, O=Let's Encrypt, CN=R11, 発行日: Mar 8 04:11:13 2025 GMT, 有効期限: Jun 6 04:11:12 2025 GMT
2025-05-06 17:41:09 [INFO] CA証明書をsubmission.pemに追加: ./cert/ca/cert-02.pem
2025-05-06 17:41:09 [INFO] 証明書情報:./cert/ca/cert-02.pem: Subject: subject=C=US, O=Let's Encrypt, CN=R11, Issuer: issuer=C=US, O=Internet Security Research Group, CN=ISRG Root X1, 発行日: Mar 13 00:00:00 2024 GMT, 有効期限: Mar 12 23:59:59 2027 GMT
2025-05-06 17:41:09 [INFO] submission.pem OK: ./cert-ct/submission.pem
./cert-ct/submission.pem
2025-05-06 17:41:09 [INFO] submission_chain: ./cert-ct/submission.pem
2025-05-06 17:41:09 [INFO] ct_log: https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain
2025-05-06 17:41:09 [INFO] create_json_submission:pem_chain_file:./cert-ct/submission.pem
2025-05-06 17:41:10 [INFO] CTログ送信中: cert-ct/payload.json
2025-05-06 17:41:10 [INFO] CTログ送信完了(HTTP Status: 200):https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain → Response:{"sct_version":0,"id":"TnWjJ1yaEMM4W2zU3z9S6x3w4I4bjWnAsfpksWKaOd8=","timestamp":1741410583875,"extensions":"","signature":"BAMASDBGAiEAw5p9IVKHG7+KoBLOqq392L35Pf16SvFdOvRJAlpUWSkCIQDcRGjiLnXDU5U/m1aKu2ikfxW7VLmTxrkxBeVvBVr6ig=="}HTTPSTATUS:200
2025-05-06 17:41:10 [INFO] main: End
スクリプト
証明書チェーンの取得
FQDNを指定した場合、openssl s_client
でサーバーから証明書チェーンを取得。
証明書の分類
取得した証明書を、有効期間やBasic Constraintsなどの情報をもとに、エンドエンティティ証明書(EE証明書)と中間CA証明書に分類。
CA証明書の補完
authorityInfoAccess
拡張にあるcaIssuers
から中間CA証明書を取得し、不足している中間CA証明書を補完。
CTログ送信用データの作成
EE証明書と中間CA証明書を連結し、JSON形式に変換。
有効期限に応じたログサーバー選択
EE証明書の有効期限に基づいて、Googleが運用する適切なCTログ(argon2025h1 など)を自動選択。
CTログへの送信
作成したJSONをGoogleのCTログにPOSTリクエストで送信し、レスポンスをログに記録。
submit_to_ct_log_fqdn.sh
#!/bin/bash
set -euo pipefail
logfile="$(date '+%Y%m%d')-CT_LOG_FQDN.txt"
log_info() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $*" | tee -a "$logfile"
}
log_error() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $*" | tee -a "$logfile" >&2
}
###############################
# ディレクトリ初期化
###############################
init_directories() {
mkdir -p cert cert/ee cert/ca cert-ct ee_cert_path
rm -rf cert/ee/* cert/ca/* cert-ct/* cert/* ee_cert_path/*
> ee_cert_path/ee_cert_path.txt || true
}
###############################
# FQDNから証明書チェーンを取得
###############################
fetch_cert_chain_from_fqdn() {
local fqdn="$1"
local cert_dir="./cert"
log_info "FQDNから証明書取得: $fqdn"
local tmpfile="$cert_dir/chain.pem"
local attempt=1
local max_attempts=3
while (( attempt <= max_attempts )); do
log_info "openssl s_client -connect試行 $attempt: $fqdn"
echo | openssl s_client -connect "$fqdn:443" -showcerts < /dev/null 2>/dev/null \
| awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/ { print }' > "$tmpfile"
#log_info "openssl s_client -connect $fqdn:443"
if [[ -s "$tmpfile" ]]; then
log_info "証明書チェーンの取得に成功: $fqdn (試行 $attempt)"
break
else
log_error "試行 $attempt で証明書チェーンの取得に失敗: $fqdn"
(( attempt++ ))
sleep 1
fi
done
if [[ ! -s "$tmpfile" ]]; then
log_error "証明書チェーンの取得に失敗しました(最大試行回数: $max_attempts): $fqdn"
exit 1
fi
csplit -q -f "$cert_dir/cert-" -b "%02d.pem" "$tmpfile" '/-----BEGIN CERTIFICATE-----/' '{*}'
rm -f "$tmpfile"
find "$cert_dir" -name "cert-*.pem" -size 0 -delete
}
###############################
# EE証明書かどうかの判定(Basic Constraintsで判断)
# ※Basic Constraints拡張が無い場合もEE証明書とみなす
###############################
is_ee_cert() {
local certfile="$1"
if ! openssl x509 -in "$certfile" -noout -text | grep -q "X509v3 Basic Constraints"; then
return 0
fi
if openssl x509 -in "$certfile" -noout -text | grep -q "CA:TRUE"; then
return 1
else
return 0
fi
}
###############################
# 入力ディレクトリまたはFQDNから証明書ファイルを用意する
###############################
handle_input() {
local input="$1"
if [[ -f "$input" ]]; then
log_error "FQDNを指定してください"
exit 1
elif [[ -d "$input" ]]; then
for cert in "$input"/*.pem; do
cp "$cert" ./cert/
done
else
fetch_cert_chain_from_fqdn "$input"
fi
}
###############################
# 証明書の分類(EE/CA)
# ※EE証明書の場合は、パスを ee_cert_path/ee_cert_path.txt に保存
###############################
classify_and_save_certificates() {
local cert_dir="./cert"
local ee_cert_dir="$cert_dir/ee"
local ca_cert_dir="$cert_dir/ca"
mkdir -p "$ee_cert_dir" "$ca_cert_dir"
for cert in "$cert_dir"/cert-*.pem; do
[[ -s "$cert" ]] || continue
if is_ee_cert "$cert"; then
cp "$cert" "$ee_cert_dir/"
echo "$ee_cert_dir/$(basename "$cert")" >> ee_cert_path/ee_cert_path.txt
log_info "EE証明書が存在: $cert"
else
cp "$cert" "$ca_cert_dir/"
log_info "CA証明書が存在: $cert"
fi
done
if [[ ! -f ee_cert_path/ee_cert_path.txt ]]; then
log_error "EE証明書が見つかりません"
exit 1
fi
}
###############################
# CTログ送信用submission.pem作成
###############################
create_submission_chain() {
local cert_ct_dir="./cert-ct"
local ee_cert
ee_cert=$(cat ee_cert_path/ee_cert_path.txt)
local submission_file="$cert_ct_dir/submission.pem"
cp "$ee_cert" "$submission_file"
log_info "EE証明書をsubmission.pemに追加: $ee_cert"
log_cert_info "$ee_cert"
for ca_cert in "./cert/ca/"*.pem; do
if [[ -f "$ca_cert" ]]; then
if openssl x509 -in "$ca_cert" -inform PEM -noout -text &>/dev/null; then
cat "$ca_cert" >> "$submission_file"
log_info "CA証明書をsubmission.pemに追加: $ca_cert"
log_cert_info "$ca_cert"
fi
fi
done
if ! openssl crl2pkcs7 -nocrl -certfile "$submission_file" -out /dev/null 2>/dev/null; then
log_error "submission.pem NG: $submission_file"
return 1
fi
log_info "submission.pem OK: $submission_file"
echo "$submission_file"
}
###############################
# 証明書情報のログ出力
###############################
log_cert_info() {
local cert="$1"
local subject issuer not_before not_after
subject=$(openssl x509 -in "$cert" -noout -subject | sed 's/subject= //')
issuer=$(openssl x509 -in "$cert" -noout -issuer | sed 's/issuer= //')
not_before=$(openssl x509 -in "$cert" -noout -startdate | sed 's/notBefore=//')
not_after=$(openssl x509 -in "$cert" -noout -enddate | sed 's/notAfter=//')
log_info "証明書情報:$cert: Subject: $subject, Issuer: $issuer, 発行日: $not_before, 有効期限: $not_after"
}
###############################
# CTログへの送信(create_json_submissionはそのまま)
###############################
send_to_ct_log() {
local submission_chain="$1"
local ct_log="$2"
local json_payload="cert-ct/payload.json"
log_info "submission_chain: $submission_chain"
log_info "ct_log: $ct_log"
create_json_submission "$submission_chain" "$json_payload"
log_info "CTログ送信中: $json_payload"
response=$(curl --silent --show-error --fail --write-out "HTTPSTATUS:%{http_code}" \
--request POST \
--header "Content-Type: application/json" \
--data @"$json_payload" \
"$ct_log")
local http_status
http_status=$(echo "$response" | sed -n 's/.*HTTPSTATUS://p')
if [[ "$http_status" =~ ^2 ]]; then
log_info "CTログ送信完了(HTTP Status: $http_status):$ct_log → Response:$response"
else
log_error "CTログ送信失敗(HTTP Status: $http_status):$ct_log → Response:$response"
return 1
fi
}
###############################
# PEMチェーンからJSONペイロード生成
###############################
create_json_submission() {
local pem_chain_file="$1"
local json_out_file="$2"
local certs_array=()
if [[ ! -f "$pem_chain_file" ]]; then
log_error "指定されたPEMチェーンファイルが存在しません: $pem_chain_file"
exit 1
fi
local tmpdir="./cert-ct"
log_info "create_json_submission:pem_chain_file:$pem_chain_file"
csplit -s -f "$tmpdir/cert_" "$pem_chain_file" '/-----BEGIN CERTIFICATE-----/' '{*}' || {
log_error "csplit失敗: $pem_chain_file"
exit 1
}
for f in "$tmpdir"/cert_*; do
if [[ -s "$f" ]]; then
base64_cert=$(openssl x509 -in "$f" -outform DER | base64 | tr -d '\n')
certs_array+=("\"$base64_cert\"")
fi
done
local json_data
json_data=$(printf '{ "chain": [\n %s\n] }\n' "$(IFS=,; echo "${certs_array[*]}")")
echo "$json_data" > "$json_out_file"
}
###############################
# FQDNの選択
###############################
handle_input() {
local input="$1"
if [[ -f "$input" ]]; then
log_error "FQDNを指定してください"
exit 1
elif [[ -d "$input" ]]; then
for cert in "$input"/*.pem; do
cp "$cert" ./cert/
done
else
fetch_cert_chain_from_fqdn "$input"
fi
}
###############################
# CTログURL選択
###############################
select_ct_log() {
local certfile="$1"
local end_date end_epoch
end_date=$(openssl x509 -in "$certfile" -noout -enddate | cut -d= -f2)
end_epoch=$(date -d "$end_date" +%s)
declare -A logs=(
["2025-01-01T00:00:00Z|2025-07-01T00:00:00Z"]="https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain"
["2025-07-01T00:00:00Z|2026-01-01T00:00:00Z"]="https://ct.googleapis.com/logs/us1/argon2025h2/ct/v1/add-chain"
["2026-01-01T00:00:00Z|2026-07-01T00:00:00Z"]="https://ct.googleapis.com/logs/us1/argon2026h1/ct/v1/add-chain"
["2026-07-01T00:00:00Z|2027-01-01T00:00:00Z"]="https://ct.googleapis.com/logs/us1/argon2026h2/ct/v1/add-chain"
)
for range in "${!logs[@]}"; do
IFS='|' read -r start end <<< "$range"
local start_epoch end_range_epoch
start_epoch=$(date -d "$start" +%s)
end_range_epoch=$(date -d "$end" +%s)
if (( end_epoch > start_epoch && end_epoch <= end_range_epoch )); then
echo "${logs[$range]}"
return
fi
done
log_error "有効なCTログURLが見つかりません: $certfile ($end_date)"
exit 1
}
###############################
# メイン処理
###############################
main() {
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <FQDN or directory>"
exit 1
fi
log_info "main: Start"
init_directories
local input="$1"
handle_input "$input"
classify_and_save_certificates
local ee_cert
ee_cert=$(cat ee_cert_path/ee_cert_path.txt)
local ct_log
ct_log=$(select_ct_log "$ee_cert")
create_submission_chain
local cert_ct_dir="./cert-ct"
local submission_file="$cert_ct_dir/submission.pem"
send_to_ct_log "$submission_file" "$ct_log"
log_info "main: End"
}
main "$@"
フォルダーを指定して送信
利用方法
[root@localhost CT]# ./submit_to_ct_log_dir.sh input
引数として渡されたディレクトリ: input
2025-05-06 17:50:18 [INFO] main: Start
入力されたディレクトリ: input
2025-05-06 17:50:18 [INFO] ファイルをコピー: input/pki.world-tls.com.pem
2025-05-06 17:50:18 [INFO] ファイルをコピー: input/www.world-tls.com.pem
2025-05-06 17:50:18 [INFO] EE証明書が存在: ./cert/pki.world-tls.com.pem
2025-05-06 17:50:18 [INFO] EE証明書が存在: ./cert/www.world-tls.com.pem
2025-05-06 17:50:18 [INFO] EE証明書のパス: ./cert/ee/pki.world-tls.com.pem
./cert/ee/www.world-tls.com.pem
2025-05-06 17:50:18 [INFO] EE証明書をsubmissionファイルに追加: ./cert/ee/pki.world-tls.com.pem
2025-05-06 17:50:18 [INFO] 証明書情報:./cert/ee/pki.world-tls.com.pem: Subject: subject=CN=pki.world-tls.com, Issuer: issuer=C=US, O=Let's Encrypt, CN=R11, 発行日: Mar 8 04:11:13 2025 GMT, 有効期限: Jun 6 04:11:12 2025 GMT
2025-05-06 17:50:18 [INFO] CA証明書をダウンロード: http://r11.i.lencr.org/ → ./cert/ca/ca-49a31b1b5bb2933afb46d4641da4e9e1.pem
2025-05-06 17:50:18 [INFO] CA証明書がPEM形式として読み込めません。DER形式としてチェックします。
2025-05-06 17:50:18 [INFO] CA証明書をDER形式からPEM形式に変換完了: ./cert/ca/ca-49a31b1b5bb2933afb46d4641da4e9e1.pem
2025-05-06 17:50:18 [INFO] caIssuers.json 更新: http://r11.i.lencr.org/
2025-05-06 17:50:18 [INFO] CA証明書をsubmissionファイルに追加: ./cert/ca/ca-49a31b1b5bb2933afb46d4641da4e9e1.pem
2025-05-06 17:50:18 [INFO] 証明書情報:./cert/ca/ca-49a31b1b5bb2933afb46d4641da4e9e1.pem: Subject: subject=C=US, O=Let's Encrypt, CN=R11, Issuer: issuer=C=US, O=Internet Security Research Group, CN=ISRG Root X1, 発行日: Mar 13 00:00:00 2024 GMT, 有効期限: Mar 12 23:59:59 2027 GMT
2025-05-06 17:50:18 [INFO] submissionファイルの作成完了: ./cert-ct/submission-pki.world-tls.com.pem
2025-05-06 17:50:18 [INFO] 処理対象証明書: '/root/ドキュメント/CT/cert/ee/pki.world-tls.com.pem'
2025-05-06 17:50:19 [INFO] CTログURL選択: /root/ドキュメント/CT/cert/ee/pki.world-tls.com.pem → https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain
2025-05-06 17:50:19 [INFO] CTログ送信中: ./cert-ct/submission-pki.world-tls.com.pem → https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain
2025-05-06 17:50:19 [INFO] create_json_submission:pem_chain_file:./cert-ct/submission-pki.world-tls.com.pem
2025-05-06 17:50:20 [INFO] CTログ送信完了(HTTP Status: 200):https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain → Response:{"sct_version":0,"id":"TnWjJ1yaEMM4W2zU3z9S6x3w4I4bjWnAsfpksWKaOd8=","timestamp":1741410583875,"extensions":"","signature":"BAMARzBFAiAh+2URfIXmqe244DuaU/bj7CwRTRDN9j8a0tF1MDT6XQIhALI0Xn5rz3PjyzF67JrApGsimBsuoQIb707IiXr28abu"}HTTPSTATUS:200
2025-05-06 17:50:20 [INFO] EE証明書をsubmissionファイルに追加: ./cert/ee/www.world-tls.com.pem
2025-05-06 17:50:20 [INFO] 証明書情報:./cert/ee/www.world-tls.com.pem: Subject: subject=CN=www.world-tls.com, Issuer: issuer=C=US, O=Let's Encrypt, CN=R10, 発行日: Mar 9 06:06:48 2025 GMT, 有効期限: Jun 7 06:06:47 2025 GMT
2025-05-06 17:50:20 [INFO] CA証明書をダウンロード: http://r10.i.lencr.org/ → ./cert/ca/ca-27e92890843f23b4353afe8efbf7e628.pem
2025-05-06 17:50:20 [INFO] CA証明書がPEM形式として読み込めません。DER形式としてチェックします。
2025-05-06 17:50:20 [INFO] CA証明書をDER形式からPEM形式に変換完了: ./cert/ca/ca-27e92890843f23b4353afe8efbf7e628.pem
2025-05-06 17:50:20 [INFO] caIssuers.json 更新: http://r10.i.lencr.org/
2025-05-06 17:50:20 [INFO] CA証明書をsubmissionファイルに追加: ./cert/ca/ca-27e92890843f23b4353afe8efbf7e628.pem
2025-05-06 17:50:20 [INFO] 証明書情報:./cert/ca/ca-27e92890843f23b4353afe8efbf7e628.pem: Subject: subject=C=US, O=Let's Encrypt, CN=R10, Issuer: issuer=C=US, O=Internet Security Research Group, CN=ISRG Root X1, 発行日: Mar 13 00:00:00 2024 GMT, 有効期限: Mar 12 23:59:59 2027 GMT
2025-05-06 17:50:20 [INFO] submissionファイルの作成完了: ./cert-ct/submission-www.world-tls.com.pem
2025-05-06 17:50:20 [INFO] 処理対象証明書: '/root/ドキュメント/CT/cert/ee/www.world-tls.com.pem'
2025-05-06 17:50:20 [INFO] CTログURL選択: /root/ドキュメント/CT/cert/ee/www.world-tls.com.pem → https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain
2025-05-06 17:50:20 [INFO] CTログ送信中: ./cert-ct/submission-www.world-tls.com.pem → https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain
2025-05-06 17:50:20 [INFO] create_json_submission:pem_chain_file:./cert-ct/submission-www.world-tls.com.pem
2025-05-06 17:50:21 [INFO] CTログ送信完了(HTTP Status: 200):https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain → Response:{"sct_version":0,"id":"TnWjJ1yaEMM4W2zU3z9S6x3w4I4bjWnAsfpksWKaOd8=","timestamp":1741503919373,"extensions":"","signature":"BAMARzBFAiAfWQ9ryx2mqa87aTnM03qbk3YGK+AduPuER57p+z/s6QIhAMUftHFHJJ73eq0sd6XH2p+/W2gQ5UYjLut+GeTtmoBN"}HTTPSTATUS:200
2025-05-06 17:50:21 [INFO] main: End
スクリプト
初期化
作業用ディレクトリ(cert/
, cert/ee/
, cert/ca/
など)を作成し、キャッシュファイル(caIssuers.json
)の準備も行う。
入力処理
指定されたディレクトリから証明書ファイルをcert/
ディレクトリにコピー。
証明書の分類
コピーした証明書をEE証明書(End-Entity)とCA証明書に分類し、それぞれ対応するディレクトリに保存。EE証明書のパスはee_cert_path.txt
に記録。
CTログURLの選択
各EE証明書の有効期限に基づいて、適切なCTログサーバーのURLを選定。
チェーン作成とCA証明書の取得
- EE証明書に含まれるCA Issuers URLを使って中間CA証明書を取得(キャッシュ活用あり)。
- 証明書チェーン(submissionファイル)を組み立てる。
JSON形式のペイロード作成とCTログ送信
チェーンをJSON形式にエンコードし、CTログにPOST送信。送信結果はログに記録。
submit_to_ct_log_dir.sh
#!/bin/bash
set -euo pipefail
logfile="$(date '+%Y%m%d')-CT_LOG_DIR.txt"
log_info() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $*" | tee -a "$logfile"
}
log_error() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $*" | tee -a "$logfile" >&2
}
# 必要なディレクトリの初期化
init_directories() {
mkdir -p cert cert/ee cert/ca cert-ct cert/ee_cert_path
rm -rf cert/ee/* cert/ct_log_urls.txt cert-ct/* cert/*
# 必要なディレクトリを再作成
mkdir -p cert/ee_cert_path
# 空ファイルを作成(もしくは上書き)
> cert/ee_cert_path/ee_cert_path.txt || true
if [[ ! -s caIssuers.json ]]; then
echo "{}" > caIssuers.json
fi
}
# 入力ディレクトリから PEM ファイルをコピー
# 入力ディレクトリからすべてのファイルを証明書ファイルとしてコピーする
handle_input() {
local input="$1"
echo "入力されたディレクトリ: $input"
if [[ ! -d "$input" ]]; then
log_error "指定されたディレクトリが存在しません: $input"
exit 1
fi
# inputディレクトリ内のすべてのファイルをそのまま ./cert/ にコピー
for file in "$input"/*; do
if [[ -f "$file" ]]; then
cp "$file" ./cert/
log_info "ファイルをコピー: $file"
fi
done
}
# EE証明書を抽出し、cert/ee にコピー。各EEのパスをee_cert_path/ee_cert_path.txtに記録
classify_and_save_certificates() {
local cert_dir="./cert"
local ee_cert_dir="$cert_dir/ee"
local ca_cert_dir="$cert_dir/ca"
mkdir -p "$ee_cert_dir" "$ca_cert_dir" "$cert_dir/ee_cert_path"
# certディレクトリ内のすべてのファイルを対象(拡張子・内容による判断は行わない)
for cert in "$cert_dir"/*; do
if [[ -f "$cert" && -s "$cert" ]]; then
# ここで is_ee_cert() は、証明書がEEかどうかの判定。
if is_ee_cert "$cert"; then
cp "$cert" "$ee_cert_dir/"
echo "$ee_cert_dir/$(basename "$cert")" >> "$cert_dir/ee_cert_path/ee_cert_path.txt"
log_info "EE証明書が存在: $cert"
else
cp "$cert" "$ca_cert_dir/"
log_info "CA証明書として分類: $cert"
fi
fi
done
if [[ ! -s "$cert_dir/ee_cert_path/ee_cert_path.txt" ]]; then
log_error "EE証明書が見つかりません!分類処理に問題あり"
ls -l "$cert_dir/ee/"
log_error "現在の EE 証明書ディレクトリの内容: $(ls -l "$cert_dir/ee/")"
exit 1
else
log_info "EE証明書のパス: $(cat "$cert_dir/ee_cert_path/ee_cert_path.txt")"
fi
}
# EE証明書かどうかを判断
is_ee_cert() {
local certfile="$1"
# X509v3 Basic Constraints拡張が存在しない場合はEE証明書とみなす
if ! openssl x509 -in "$certfile" -noout -text | grep -q "X509v3 Basic Constraints"; then
return 0
fi
# 存在する場合、"CA:TRUE"が含まれていればCA証明書なのでfalse(返値1)を返す
if openssl x509 -in "$certfile" -noout -text | grep -q "CA:TRUE"; then
return 1
else
return 0
fi
}
# CTログURL選択時のログ出力はstderrへリダイレクトして、echoでCTログURLのみを返す
select_ct_log() {
local certfile
certfile=$(realpath "$1")
log_info "処理対象証明書: '$certfile'" >&2
if [[ ! -f "$certfile" ]]; then
log_error "ファイルが見つかりません: '$certfile'" >&2
exit 1
fi
local end_date end_epoch
end_date=$(openssl x509 -in "$certfile" -noout -enddate | cut -d= -f2)
end_epoch=$(date -d "$end_date" +%s)
declare -A logs=(
["2025-01-01T00:00:00Z|2025-07-01T00:00:00Z"]="https://ct.googleapis.com/logs/us1/argon2025h1/ct/v1/add-chain"
["2025-07-01T00:00:00Z|2026-01-01T00:00:00Z"]="https://ct.googleapis.com/logs/us1/argon2025h2/ct/v1/add-chain"
["2026-01-01T00:00:00Z|2026-07-01T00:00:00Z"]="https://ct.googleapis.com/logs/us1/argon2026h1/ct/v1/add-chain"
["2026-07-01T00:00:00Z|2027-01-01T00:00:00Z"]="https://ct.googleapis.com/logs/us1/argon2026h2/ct/v1/add-chain"
)
for range in "${!logs[@]}"; do
IFS='|' read -r start end <<< "$range"
local start_epoch end_range_epoch
start_epoch=$(date -d "$start" +%s)
end_range_epoch=$(date -d "$end" +%s)
if (( end_epoch > start_epoch && end_epoch <= end_range_epoch )); then
log_info "CTログURL選択: $certfile → ${logs[$range]}" >&2
echo "${logs[$range]}"
return
fi
done
log_error "有効なCTログURLが見つかりません: $certfile ($end_date)" >&2
exit 1
}
create_json_submission() {
local pem_chain_file="$1"
local json_out_file="$2"
local certs_array=()
if [[ ! -f "$pem_chain_file" ]]; then
log_error "指定されたPEMチェーンファイルが存在しません: $pem_chain_file"
exit 1
fi
local tmpdir="./cert-ct"
log_info "create_json_submission:pem_chain_file:$pem_chain_file"
csplit -s -f "$tmpdir/cert_" "$pem_chain_file" '/-----BEGIN CERTIFICATE-----/' '{*}' || {
log_error "csplit失敗: $pem_chain_file"
exit 1
}
for f in "$tmpdir"/cert_*; do
if [[ -s "$f" ]]; then
base64_cert=$(openssl x509 -in "$f" -outform DER | base64 | tr -d '\n')
certs_array+=("\"$base64_cert\"")
fi
done
# JSON文字列を作成
local json_data
json_data=$(printf '{ "chain": [\n %s\n] }\n' "$(IFS=,; echo "${certs_array[*]}")")
# 作成したJSONデータをログに表示
#log_info "作成したJSONデータ: $json_data"
echo "$json_data" > "$json_out_file"
}
# CTログへ送信(第3引数があればそれをpayloadファイル名として利用)
send_to_ct_log() {
local submission_chain="$1"
local ct_log="$2"
local json_payload
if [ "$#" -eq 3 ]; then
json_payload="$3"
else
json_payload="cert-ct/payload.json"
fi
log_info "CTログ送信中: $submission_chain → $ct_log"
create_json_submission "$submission_chain" "$json_payload"
#log_info "生成されたpayloadファイル ($json_payload) の内容: $(cat "$json_payload")"
response=$(curl --silent --show-error --fail --write-out "HTTPSTATUS:%{http_code}" \
--request POST \
--header "Content-Type: application/json" \
--data @"$json_payload" \
"$ct_log")
local http_status
http_status=$(echo "$response" | sed -n 's/.*HTTPSTATUS://p')
if [[ "$http_status" =~ ^2 ]]; then
log_info "CTログ送信完了(HTTP Status: $http_status):$ct_log → Response:$response"
else
log_error "CTログ送信失敗(HTTP Status: $http_status):$ct_log → Response:$response"
return 1
fi
}
###############################################################################
# 各EE証明書を1枚ずつ処理する
# ・EE証明書からCNを抽出して submissionファイル名と payload ファイル名に反映
# ・EE証明書本体およびCA証明書(キャッシュを使用またはキャッシュ更新)を submissionファイルに追記
# ・submissionファイルの形式検証後、CTログに送信
###############################################################################
create_submission_chain() {
local cert_dir="./cert"
local cert_ct_dir="./cert-ct"
mkdir -p "$cert_ct_dir"
while IFS= read -r ee_cert || [ -n "$ee_cert" ]; do
if [[ ! -f "$ee_cert" ]]; then
log_error "EE証明書が見つかりません: $ee_cert"
continue
fi
# EE証明書のSubjectからCommon Name (CN) を抽出
local cn
cn=$(openssl x509 -in "$ee_cert" -noout -subject | sed -n 's/.*CN=\([^,\/]*\).*/\1/p')
if [ -z "$cn" ]; then
cn="unknown"
fi
# submissionファイル名は "submission-<CN>.pem" とする
local submission_file="$cert_ct_dir/submission-${cn}.pem"
> "$submission_file"
# EE証明書本体を追加
cat "$ee_cert" >> "$submission_file"
log_info "EE証明書をsubmissionファイルに追加: $ee_cert"
log_cert_info "$ee_cert"
# EE証明書中の CA Issuers URL を抽出して、各URLに対して処理
local urls
urls=$(openssl x509 -in "$ee_cert" -noout -text | grep -A1 "CA Issuers" | grep "http" | sed -E 's/.*URI://')
for url in $urls; do
# OCSPやocspが含まれているURLは、問答無用でスキップ
if echo "$url" | grep -qi "ocsp"; then
log_info "OCSP URLが含まれているためスキップ: $url"
continue
fi
local ca_dir="$cert_dir/ca"
mkdir -p "$ca_dir"
# URLから一意なキャッシュファイル名を生成(MD5ハッシュ使用)
local ca_filename
ca_filename=$(echo -n "$url" | md5sum | cut -d' ' -f1)
local ca_file="$ca_dir/ca-${ca_filename}.pem"
if [[ -f "$ca_file" ]]; then
log_info "CA証明書キャッシュ済み: $url → $ca_file"
else
local cached_pem
cached_pem=$(jq -r --arg url "$url" '.[$url]' caIssuers.json)
if [[ -n "$cached_pem" && "$cached_pem" != "null" ]]; then
log_info "キャッシュファイル利用: $url → $ca_file"
echo "$cached_pem" | base64 -d > "$ca_file"
else
if curl -s "$url" -o "$ca_file"; then
log_info "CA証明書をダウンロード: $url → $ca_file"
else
log_error "CA証明書のダウンロード失敗: $url"
continue
fi
fi
# --- PEMとして読み込めるかのチェック ---
if ! openssl x509 -in "$ca_file" -inform PEM -noout &>/dev/null; then
log_info "CA証明書がPEM形式として読み込めません。DER形式としてチェックします。"
if openssl x509 -in "$ca_file" -inform DER -noout &>/dev/null; then
openssl x509 -in "$ca_file" -inform DER -out "${ca_file}.converted.pem"
mv "${ca_file}.converted.pem" "$ca_file"
log_info "CA証明書をDER形式からPEM形式に変換完了: $ca_file"
else
log_error "ダウンロードファイルはopensslで読み込めないため削除: $ca_file"
rm -f "$ca_file"
continue
fi
fi
# --- チェック完了後、caIssuers.jsonへの登録 ---
local pem_data
pem_data=$(base64 -w 0 "$ca_file")
jq --arg url "$url" --arg pem "$pem_data" '.[$url] = $pem' caIssuers.json > caIssuers.tmp && mv caIssuers.tmp caIssuers.json
log_info "caIssuers.json 更新: $url"
fi
if openssl x509 -in "$ca_file" -inform PEM -noout &>/dev/null; then
cat "$ca_file" >> "$submission_file"
log_info "CA証明書をsubmissionファイルに追加: $ca_file"
log_cert_info "$ca_file"
else
log_error "ダウンロード後のCA証明書が無効: $ca_file"
rm -f "$ca_file"
fi
done
if ! openssl crl2pkcs7 -nocrl -certfile "$submission_file" -out /dev/null 2>/dev/null; then
log_error "submissionファイルのフォーマットが不正: $submission_file"
continue
fi
log_info "submissionファイルの作成完了: $submission_file"
# payload ファイル名は "payload-<CN>.json" とする
local payload_file="$cert_ct_dir/payload-${cn}.json"
local ct_log
ct_log=$(select_ct_log "$ee_cert")
send_to_ct_log "$submission_file" "$ct_log" "$payload_file"
if [ $? -ne 0 ]; then
log_error "CTログ送信に失敗したため処理を中断します。"
exit 1
fi
done < <(cat "./cert/ee_cert_path/ee_cert_path.txt")
}
# 証明書の詳細情報をログ出力
log_cert_info() {
local cert="$1"
local subject issuer not_before not_after
subject=$(openssl x509 -in "$cert" -noout -subject | sed 's/subject= //')
issuer=$(openssl x509 -in "$cert" -noout -issuer | sed 's/issuer= //')
not_before=$(openssl x509 -in "$cert" -noout -startdate | sed 's/notBefore=//')
not_after=$(openssl x509 -in "$cert" -noout -enddate | sed 's/notAfter=//')
log_info "証明書情報:$cert: Subject: $subject, Issuer: $issuer, 発行日: $not_before, 有効期限: $not_after"
}
# メイン処理:入力ディレクトリ → 初期化 → 分類 → 各EE証明書を順次処理
main() {
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <証明書ディレクトリ>"
exit 1
fi
local input_dir="$1"
echo "引数として渡されたディレクトリ: $input_dir"
log_info "main: Start"
init_directories
handle_input "$input_dir"
classify_and_save_certificates
create_submission_chain
log_info "main: End"
}
main "$@"
get-sth(Signed Tree Head を取得)
ログの最新の状態(Merkle Treeの根:STH)を取得する。
[root@localhost CT]# curl https://ct.googleapis.com/logs/us1/argon2026h1/ct/v1/get-sth
{"tree_size":132407315,"timestamp":1746523324303,"sha256_root_hash":"FwU2n8gYz+4XhonSdODwZ3gvzY42ACk24tYKl8imO18=","tree_head_signature":"BAMARzBFAiAGINDpHohfQijc9YocFPHmHLpnJIjLlIKZtZlt2KB+NQIhAIC9HbPWltEYrf+/fhqII7JqeUtW141Wf2NbwRuaWdYW"}[root@localhost CT]#
項目名 | 説明 |
---|---|
tree_size | 現在のMerkle Treeに含まれているエントリ数(SCTの数)。この例では 132,407,315。 |
timestamp | STHが発行されたUTCのUnix時刻(ミリ秒)。この例では 1746523324303 = 2025年5月6日頃。 |
sha256_root_hash | Merkle Treeのルートハッシュ(base64エンコードされたSHA-256)。 |
tree_head_signature | STH全体に対するログサーバーの署名(base64)。これにより、ルートハッシュの正当性を確認できる。 |
get-entries(特定範囲のログエントリを取得)
ログのエントリ(証明書)をインデックス範囲で取得する。
JSON の構造
{
"entries": [
{
"leaf_input": "<Base64でエンコードされたleaf input>",
"extra_data": "<Base64でエンコードされたextra data>"
}
]
}
[root@localhost CT]# curl 'https://ct.googleapis.com/logs/us1/argon2026h1/ct/v1/get-entries?start=132407315&end=132407315'
{"entries":[{"leaf_input":"AAAAAAGWpObnXgAAAAaFMIIGgTCCBWmgAwIBAgIJANpGvYoaXDgDMA0GCSqGSIb3DQEBCwUAMIG0MQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTI1MDUwNjA5MjE0MloXDTI2MDUwNjA5MjE0MlowGDEWMBQGA1UEAxMNd3d3LmdvaGNpLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANpA9n5LBEeq2UZPOrPbCrLMWO+JEdrkIlR7bpZ3Edp8vp9C8oXSH8WWzDLMeKgAy7k0EhQRHfKHsYbcMMLJcL022ZoyHKrR/JT3l6fCK4fhLTYBG9mdLzcaFVtabWXHnHHFEMDdezDQ668uTkeXo/phixBfDbPwg2kBhPIXlBBO32CfwUj4itt94b1a6yymAu/eIu76cKC0cnkjQEPxKyTjpTO/CArQ3/2k3udwkEzEvlP4UQu1dviLHdSMf8Jmy1Q43V1B6g/hLkl5+ugSDKbOW4zmQqyIFNuVfKBMpgq82kVYW1zCVpoxQJoCSPb7oLFI5pIzOmDZjHSTz+QoCc0CAwEAAaOCAy8wggMrMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA4GA1UdDwEB/wQEAwIFoDA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vY3JsLmdvZGFkZHkuY29tL2dkaWcyczEtNDYxNTQuY3JsMF0GA1UdIARWMFQwSAYLYIZIAYb9bQEHFwEwOTA3BggrBgEFBQcCARYraHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzAIBgZngQwBAgEwdgYIKwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nb2RhZGR5LmNvbS8wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS9nZGlnMi5jcnQwHwYDVR0jBBgwFoAUQMK9J47MNIMwojPX+2yz8LQsgM4wGAYDVR0RBBEwD4INd3d3LmdvaGNpLm5ldDAdBgNVHQ4EFgQUIK39R3sSuKW3rciHg9dy7Pz7XogwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AA5XlLzzrqk+MxssmQez95Dfm8I9cTIl3SGpJaxhxU4hAAABlqTmqv4AAAQDAEgwRgIhAORuYPGhN04uggaazwJI+VTOupWA7JOimNuMnjkXEAPyAiEApLUSftzO8/nD6iO55gzJ9MuBZ6SPqP+jpikwiO6fhc8AdgBkEcRspBLsp4kcogIuALyrTygH1B41J6vq/tUDyX3N8AAAAZak5qv+AAAEAwBHMEUCIQCL22yyj4T2hcUYKFwv9ttyDZy7fLBndk3V+MXPj5BMHgIgf0OwGs2oUyj7J6pfgW2zNZ+KK8MEXd77oSuXpebDVR8AdQDLOPcViXyEoURfW8Hd+8lu8ppZzUcKaQWFsMsUwxRY5wAAAZak5q0kAAAEAwBGMEQCICJo8GB527CRnyI8HH3MiUYCziwD3IdrmOFZzcY20X+fAiB4QUmculwVjfuWQ3KR5ow6unrx9M21VrnNg1Pf7T+/rTANBgkqhkiG9w0BAQsFAAOCAQEAmQ+7BJCCz9R8QNMJgKa2J2El6n9KYfnHW5Q+fqed7CULvkx322u2tmQ3HEc7rmTQ4u1XEFd5cRkPJQY5ZQvy1JLQUNtjd61KKOm96VjfDP/e9Qi4Q55POXDBJIClUeJoub0pj5APezq2IgFetgxrYKoNHcz71q55b1fYM9bH91sbEVLXj2suR8n55/YYNH0tMm/siHTJYZVXTEqKHPRHAUfsxH55lG47Ata8JEzfZXRPV02goTvMvCrgxjiT0pkjOp2ZGP0Jh0e5WIqqp16Qnt1I5IbnlUZOPHRALqtBUt2HzX0dZ94ZbC+zccgePOUkUIqhEWvbocKDcJtrDPpssQAA","extra_data":"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"}]}[root@localhost CT]#