メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#75260active

crypto/x509: add support for Relative OIDs

新規提案

要約

AIによる要約であり、誤りを含む場合があります。

概要

crypto/x509 パッケージが RELATIVE-OID(相対オブジェクト識別子)を含む証明書を正しくパースできない問題を解決するためのプロポーザルです。新興のTLS仕様(Merkle Tree Certificates等)がこの ASN.1 型を使用しており、Go の X.509 パーサーの仕様準拠性を高めることが目的です。

ステータス変更

(新規)active
2026年4月22日のProposal Review Meetingにおいて、@FiloSottile が事前に投稿した具体的な設計方針(オプション6)が議論の俎上に上がり、週次レビューの対象として active 列に追加されました。

技術的背景

現状の問題点

ASN.1(Abstract Syntax Notation One)の型の一つである RELATIVE-OID(タグ番号13)が、encoding/asn1 および x/crypto/cryptobyte パッケージでサポートされていません。x509.ParseCertificate で RELATIVE-OID を属性値に含む証明書を読み込もうとすると、以下のエラーが発生します。

x509: invalid RDNSequence: invalid attribute value: unsupported string type: 13

RFC 5280 の定義上、X.509 の Name 構造における AttributeValueANY 型であり、パーサーは任意の ASN.1 値を受け入れなければなりません。しかし現在の crypto/x509 のパーサーは属性値を PrintableString、UTF8String 等の文字列型のみに限定しており、これは仕様非準拠の状態です。

提案された解決策

@FiloSottile の提案(オプション6)により、pkix.AttributeTypeAndValueValue フィールドに格納される型を、crypto/x509 がパースした場合について明文化します。
具体的には以下の型マッピングをドキュメントとして定義し、パーサーを対応させます:

  • 文字列型(PrintableString, IA5String, NumericString, BMPString, T61String, UTF8String)→ string
  • INTEGERint64
  • BIT STRINGasn1.BitString
  • OCTET STRING[]byte
  • OBJECT IDENTIFIERasn1.ObjectIdentifier
  • UTCTIME / GENERALIZEDTIMEtime.Time
  • BOOLEANbool
  • NULLnil
  • それ以外(RELATIVE-OID 等)asn1.RawValue

これによって何ができるようになるか

Merkle Tree Certificates(MTC)や TLS Trust Anchor IDs といった新しい PKI 仕様が使用する RELATIVE-OID を含む証明書を、x509.ParseCertificate でエラーなく読み込めるようになります。また、OCTET STRING や SEQUENCE など、従来はパースエラーとなっていたあらゆる ASN.1 型の属性値も asn1.RawValue として受け取れるようになり、仕様準拠性と相互運用性が大幅に向上します。

コード例

// Before: RELATIVE-OID を属性値に含む証明書のパースが失敗する
cert, err := x509.ParseCertificate(derBytes)
// => エラー: "x509: invalid RDNSequence: invalid attribute value: unsupported string type: 13"
// After: 未知の ASN.1 型は asn1.RawValue として格納される
cert, err := x509.ParseCertificate(derBytes)
if err != nil {
    log.Fatal(err)
}
for _, attr := range cert.Subject.Names {
    switch v := attr.Value.(type) {
    case string:
        fmt.Printf("文字列属性: %s\n", v)
    case asn1.RawValue:
        // RELATIVE-OID や OCTET STRING など未知の型
        fmt.Printf("その他の属性 (tag=%d): %x\n", v.Tag, v.Bytes)
    }
}

議論のハイライト

  • 問題の根本原因: パーサーの問題は cryptobyte ではなく parseASN1String にあり、属性値を文字列型のみと仮定している点にあると @davidben が指摘。RFC 5280 上 AttributeValueANY 型であるため仕様違反の動作です。
  • Go 1.17 以前の動作との比較: encoding/asn1 を使用していた Go 1.16 以前は偶然にも多くの型を受け入れていましたが、RELATIVE-OID は nil にバインドされるという意図しない動作でした。Go 1.17 の cryptobyte への移行(CL #274234)でこの挙動が壊れました。
  • 型選択の検討: AttributeTypeAndValue.Value の型を asn1.RawValue に変更する案も検討されましたが、既存の API との互換性から、既知の型は従来通りにデコードし、未知の型のみ RawValue にフォールバックする設計が選ばれました。
  • OBJECT IDENTIFIER の型の扱い: OBJECT IDENTIFIER 属性値には x509.OID(大きな OID 成分をサポートする新しい型)ではなく、既存の asn1.ObjectIdentifier を使うことが決定されました。crypto/x509/pkix パッケージが crypto/x509 を参照するのは構造上好ましくないためです。
  • 実装範囲: encoding/asn1 パッケージ自体の変更は最小限に留め、主に crypto/x509 のパーサー(x/crypto/cryptobyte を使用するパス)を修正する方針が示されました。

関連リンク