Certificate Transparency(CT)ログとは

Certificate Transparency(CT)ログとは何か

CTログは、公開鍵証明書の不正発行を防止するために設計された公開ログシステム。Googleによって提唱され、現在ではChromeやSafariなど主要なブラウザが証明書の監視に利用している。

CA(認証局)が新しいTLSサーバ証明書を発行する際、その証明書を一つまたは複数のCTログに登録する。CTログは基本的に公開されており、誰でもログの内容を検証・監視できる。この仕組みにより、不正な証明書や誤って発行された証明書を迅速に検知することが可能になる。

仕組みの概要

  1. 証明書の登録(Submit)
    CAは、発行予定の証明書または証明書チェーンをCTログサーバーに送信する。これにより、サーバーからSCT(Signed Certificate Timestamp)と呼ばれる署名付きタイムスタンプが返ってくる。
  2. SCTの埋め込み
    取得したSCTは証明書本体やTLS拡張、OCSPレスポンスなどに埋め込まれる。クライアント(主にブラウザ)は証明書を検証する際、SCTの存在と有効性を確認する。
  3. ログの検証と監視
    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必須化時期備考
Chrome2018年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ポリシー

また、証明書の有効期間や発行時期に応じて、登録すべきログが指定されるケースもある(例:argon2025h1argon2026h2 など)。これはログの保持期間や信頼期間が制限されているため。


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
timestampSTHが発行されたUTCのUnix時刻(ミリ秒)。この例では 1746523324303 = 2025年5月6日頃。
sha256_root_hashMerkle Treeのルートハッシュ(base64エンコードされたSHA-256)。
tree_head_signatureSTH全体に対するログサーバーの署名(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":"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","extra_data":"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"}]}[root@localhost CT]# 

タイトルとURLをコピーしました