Go の X.509 証明書認証を欺く方法

2026/06/06 22:52

Go の X.509 証明書認証を欺く方法

RSS: https://news.ycombinator.com/rss

要約

日本語訳:

最も重要な発見は、Go の X.509 バリデーターが、ASN.1 タグタイプの厳密なバイトレベルの不整合により有効な証明書チェーンを誤って拒否している点です。一方、OpenSSL はそれを受け入れます。具体的には、ルート CA(

ca.crt.pem
)は発行元の名称に対して
PRINTABLESTRING
タグを使用しており、リーフ証明書に対応するフィールドは
UTF8String
です。両方の証明書は視覚的に見かけ上同一であり、OpenSSL はこれらを同等とみなして受け付けるため(
openssl verify
が成功します)、Go はタグにおいてバイト単位での完全な整合性を要求します。この不一致の理由は、Go の
CertPool.byName
マップのキーが
cert.RawSubject
から得られる生バイトであることにあります。したがって、リーフの発行元 (
UTF8STRING
) が CA の主題 (
PRINTABLESTRING
) と一致しない場合に不整合が発生します。デバッグ結果では、
buildChains
段階で親証明書として 0 つのマッチが見つかり、「未知の権限によって署名された証明書」というパニックが引き起こされます。この問題はこの特定のケースに限られません。将来、発行元と主題の間でエンコーディングが異なる任意の例でも、おそらく同様の失敗パターンを再現するでしょう。その結果、一貫性のない ASN.1 タグ調和化を採用してシステムを展開する組織は、潜在的な検証の破綻に備えるか、混合された証明書ソースを使用する際に予期せぬ接続障害を防ぐための代替策を実施することを見越す必要があります。

本文

X.509 証明書の互換性問題:OpenSSL と Go で検証結果が不一致する理由

1. 問題の概要

2 つの X.509 証明書(ルート CA とリーフ)を用いた検証において、OpenSSL では正常に認証通过了いますが、Go プログラムでは「不明の権限による署名」としてエラーが返されます。

  • ca.crt.pem: ローター証明書(自己署名)
    • Issuer: Root CA (subject key identifier:
      0x13
      で始まる)
  • leaf.crt.pem: リーフ証明書
    • Issuer: Root CA (UTF8String で定義されている)

Go の検証処理では、以下のエラーが発生します。

panic: x509: certificate signed by unknown authority

2. 根本原因:ASN.1 エンコーディングの違い

一見すると同一の証明書であるかのような

ca.crt.pem
と検証に成功するサンプル
ca.verifies.crt.pem
ですが、バイナリデータ(DER 形式)には細かな差異があります。

バイトレベルでの違い

両方のファイルを表示すると、目視では識別できませんが、ハッシュ比較やバイナリダンプを行うと以下の違いが見つかります。

diff <(openssl x509 -in ca.crt.pem -outform der | xxd) \
     <(openssl x509 -in ca.verifies.crt.pem -outform der | xxd)

出力例:

4c4
< 00000030: 1231 1030 ...
---
> 00000030: 1231 1030 ...
8c8
< 00000070: 1307 526f ...
---
> 00000070: 0c07 526f ...

差異の詳細(Subject/Issuer フィールド)

OpenSSL の解析結果

asn1parse
から、異なるバイトの位置を確認できます。

証明書文字列内容ASN.1 データタイプバイト値 (最初の)備考
ca.crt.pem (失敗)"Root CA"
PRINTABLESTRING
0x13Go で検出に失敗
ca.verifies.crt.pem (成功)"Root CA"UTF8String0x0c標準的な形式
leaf.crt.pem"Root CA"UTF8String0x0cリーフが参照する親は UTF8String

技術的な背景

  • ASN.1 の仕様では、文字列には
    PRINTABLESTRING
    (0x13)、
    UTF8String
    (0x0c) など複数のタグが存在します。
  • Go の検証ロジック は、厳密なバイト列(Raw Subject / Raw Issuer)比較に基づいています。
    • 証明書を
      CertPool
      に登録した際に入力される鍵(キー)は
      cert.RawSubject
      です。
    • リーフが参照しているのは、署名された親の証明書の
      AuthorityKeyId
      です。
  • 問題点: 検証に失敗する CA (
    ca.crt.pem
    ) の Subject は
    PRINTABLESTRING
    で定義されていますが、リーフが期待している(あるいは OpenSSL が許容する)親は
    UTF8STRING
    です。Go のロジックにおいて、異なる ASN.1 タグを持つ文字列を「同一のエンティティ」として扱いません。

補足: 通常、同じツールで生成された証明書ペアならこの不一致は起こりません。しかし、リーフ証明書の寿命が短いため、ツールのバージョン進化や互換性のない生成プロセスとの組み合わせで不整合が生じることがあります。

3. Go の検証ロジック詳細

Go の

crypto/x509
パッケージ内での検証フローを見てみましょう。

検証オプションの構築

opts := x509.VerifyOptions{
    Roots:       roots,      // 登録されたルート証明書プール
    CurrentTime: time.Now(), // 検証時刻
}

チェイン構築と候補検索

Verify()
関数内で、候補の証明書のチェーンを構築します。まず
findPotentialParents()
が呼び出され、Roots プールの中からリーフ(
c
)の親を探す処理が行われます。

// 簡略化されたロジック
for _, root := range opts.Roots.findPotentialParents(c) {
    considerCandidate(rootCertificate, root)
}

エラー発生箇所

最終的に、Subject と Issuer の一致が判定されます。ここでは、証明書の

CertPool
内の
byName
マップを参照しています。

// CertPool の byName map: RawSubject (バイト列のハッシュまたは比較) => インデックス
var candidateChains [][]*Certificate

for _, c := range s.byName[string(cert.RawIssuer)] { // ⚠️ ここが厳密なバイト比較になっている
    candidate, constraint, err := s.cert(c)
    if err != nil {
        continue
    }
    
    // SubjectKeyIdentifier の一致も確認される
    kidMatch := bytes.Equal(candidate.SubjectKeyId, cert.AuthorityKeyId) 
    
    // ... (ロジック分岐略)
}

発生する問題:

  • s.byName[string(cert.RawIssuer)]
    というコードは、Raw Subject(バイト列そのもの)をキーとして使用します。
  • ca.crt.pem
    では Issuer 部分の文字列が
    PRINTABLESTRING
    だが、
    leaf.crt.pem
    の参照元や OpenSSL の許容範囲では
    UTF8String
    と等価と見なす必要があります。
  • バイト列が完全に一致しないため(0x13 vs 0x0c)、ループ内で一致する親が見つからず、検証エラーが発生します。

4. ディバグと確認方法

Go のプログラムをコンパイルし、ブレークポイントでデバッグして挙動を確認できます。

# GDB を使用したデバッグ例
gdb main -ex 'b crypto/x509.(*Certificate).Verify' -ex 'run'

具体的な調査手順

  1. 証明書のバイナリ比較:
    openssl x509 -in ... -outform der | xxd
    で確認。
  2. ASN.1 パース:
    openssl asn1parse -in ...
    でタグの違い(0x13 vs 0x0c)を確認。
  3. Go の内部変数確認: バイト列のハッシュ値や RawSubject を直接確認する。

5. 結論と対策

この現象は、ASN.1 データタイプの厳密比較が Go の証明書検証ロジックにおけるボトルネックとなっています。OpenSSL など他のツールでは柔軟に「同一内容」を許容しますが、Go はバイトレベルの整合性を要求します。

推奨事項

  • 生成ツールの統一: CA サーティフィケートおよびリーフ証明書は、互換性のある設定(UTF8String を優先する等)で同じツール・手順で作成することを強く推奨します。
  • 「Fail-Closed」の理解: Go は「失敗して終了する(fail-closed)」振る舞いを採用しています。検証エラーが出ても原因が不明瞭なケースがあるため、システム設計上当該リスクを許容するか、代替策(手動確認や異なるライブラリの使用)を検討する必要があります。
  • 未来の注意点: 証明書の有効期限(Not After)に注意してください。本記事中の証明書は 2126 年 に切れますが、現在のシステム時刻とは一致しない可能性があります。検証環境の時刻設定も正しいことを確認しましょう。
# 時刻の確認
date

同じ日のほかのニュース

一覧に戻る →

2026/06/09 3:17

Siri AI

## 日本語翻訳: ## サマリー: Apple は、ユーザーのプライバシーとシームレスなクロスデバイス統合を優先する、革新的な人工知能の新しい世代を導入します。この戦略的転換は、クラウド依存型のモデルから高度なオンデバイス処理へと移行し、複雑な計算を行っても iPhone、iPad、Mac の Apple シリコンで動作させることで、個人データが常に安全に保たれ、外部へ保存されることはありません。既存のエコシステムを活用することで、Apple は HomeKit のビデオ分析(視聴前のフットージの説明と AI によるクリップ検索の実現)、アクセシビリティツール(より豊かな VoiceOver 説明、Magnifier テキスト照会、Accessibility Reader の整備、柔軟なボイスコントロール)といった重要な機能の拡大、ならびに画像作成のための Genmoji という新たなクリエイティブユーティリティを実装しています。さらに、Workout Buddy は、近くの iPhone を必要とせずより深い洞察を提供し、スペイン語版も利用可能になります。これらのアップデートは、数年間確立されたインフラストラクチャに基づいて構築されており、「コンテキストグラウンディング」によってあなたの特定の文脈から学習するスマートな AI、およびデータを保存することなく Apple シリコン上で動作する Private Cloud Compute による検証可能なプライバシー保証を実現します。開発者向けには、Foundation Models フレームワーク、App Intents、そして API が独特の利点を提供します:高価なリクエスト課金なしで強力なオフラインモデルを利用でき、データ収集のリスクに直面しないことです。新しい機能は、公式のデバイス互換性リストが公開される年内後半に提供され、個人だけでなくビジネスもまた、機密情報の厳格な管理を保ったまま高度な知性を活用できることになります。

2026/06/09 0:27

MiMo-v2.5-Pro-UltraSpeed:秒間1000トークンの処理速度を実現する1Tモデル

## 日本語訳: 元のサマリーは高品質ですが、キーポイントリストに見られるより具体的な指標(リアルタイム生成速度(約 1,200 トークン/秒)、価格対効果のトレードオフなど)を盛り込みつつ流れを保つことでやや改善できます。以下に、それらの詳細を統合しつつ箇条書きリストにならないようにした改良版を示します。 ## 改良版サマリー: Xiaomi は**MiMo-V2.5-Pro-UltraSpeed**という画期的な AI モデルを発表しました。このモデルはリアルタイム生成で約 1,200 トークン/秒(標準的な汎用ハードウェア上でも 1,000 よりも高い)の速度を達成し、1 兆パラメータを持つモデルにおける従来の速度記録を更新しました。この革新により、Cerebras や Groq といった高価な専用チップが必要なくなります。コア技術としては、MoE エクスパートにのみ適用される**FP4 量子化**によってメモリ圧力を軽減し、並列推論経路(例:Best-of-N/Tree Search)を可能にするために必要な遅延感度が高いタスク(手術分析、高頻度取引における不正検出、複雑なコーディングエージェントなど)に必要な並列推論経路を実現する**DFlash デコード**を組み合わせています。アクセシビリティについては、モデルは間もなく HuggingFace でオープンソース化されますが、商業 API アクセスには承認された企業およびプロフェッショナル開発者に対してのみ、直ちに 2026 年 6 月 9 日から 6 月 23 日(北京時間)までの 2 週間限定トライアルが用意されています。API のコストは標準的な MiMo-V2.5-Pro バージョンよりも約 3 倍高いものの、生成速度は約 10 倍向上しており、ミッションクリティカルな意思決定ループのための高パフォーマンスツールとなります。

2026/06/09 4:10

細胞が小さい理由とは?

## Japanese Translation: 鍵ポイントリストに含まれており、サマリーには含まれていない特定の定量的データや独自の実例(精子の体積、*E. coli* の統計、および*Thiomargarita* の特定の寸法など)を考慮するため、以下にすべての主要な鍵ポイントが適切な詳細を伴って反映されるよう改善されたバージョンを示します。 ## 改善されたサマリー 人体は約 30 兆個の細胞で構成されており、精子(~30 µm³)から卵母細胞(~4,000,000 µm³)に至る広大なサイズの範囲を示しています。この変化は、進化による必要性によって駆動されており、具体的には早期胚の成長をサポートするための巨大な栄養分貯蔵庫および低い代謝活性が卵母細胞において必要とされる(直径約 100 ミクロン)。逆に、物理学は表面積対体積比を介して厳密に細胞サイズを制限しており、体積が表面面積よりも速く増えるため、大型の細胞はエネルギーを十分に生産したり廃棄物を効率的に排泄したりすることができず、生存リスクが高まります。これは拡散法則によってさらに悪化しており、*E. coli* の代謝産物は数ミリ秒で細胞を横切るものの、大きなタンパク質は非常に遅く移動するため(例:1 センチメートルを越える場合、6 時間以上)、生命維持には頻繁な衝突が不可欠です。これらの制約を克服するために、特定の戦略が進化しました:赤血球は二凹レンズ型盤状の形状(直径~8 ミクロン)を採用し、酸素交換のための表面積を最大化すると同時に毛細血管を航行させることを可能にし、ユカリオット細胞は内部の区画化を利用して機能をモジュール化します。最も顕著な例外は細菌*Thiomargarita magnifica*であり、これは自らの体積の 65–80% を Vacuole で満たすことで長さまで 1 センチメートルに達し、裸眼で確認可能な大きさになります。これにより代謝機構を周縁に配置することで、標準的な表面積対体積則を破っても機能を維持できるようにしています。

Go の X.509 証明書認証を欺く方法 | そっか~ニュース