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

Go Proposal Weekly Digest

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

#75260accepted

crypto/x509: accept non-string pkix.Name attributes

ステータス変更: likely_accept accepted

要約

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

概要

crypto/x509 パッケージのX.509証明書パーサーが、Subject/Issuer名の属性値(pkix.AttributeTypeAndValue.Value)として文字列型以外のASN.1型を含む証明書を拒否してしまう問題を修正するproposalです。文字列型以外の属性値を持つ証明書を正常にパースできるよう、パーサーの動作を改善し、許容される型の対応関係をドキュメント化します。

ステータス変更

likely_acceptaccepted
2026年5月6日に likely_accept となり、2026年5月13日のプロポーザルレビューミーティングで合意に変更なしとして正式に accepted となりました。提案内容は明確で後方互換性を損なわず、RFC 5280の仕様に準拠する方向への変更であることが支持されました。

技術的背景

現状の問題点

RFC 5280において、X.509のDistinguished Name(DN)内の属性値(AttributeValue)は ANY 型として定義されており、あらゆるASN.1型を取りうることが仕様上の要件です。

AttributeTypeAndValue ::= SEQUENCE {
  type     AttributeType,          -- OBJECT IDENTIFIER
  value    AttributeValue }        -- ANY

しかし現在の crypto/x509 の内部関数 parseNameParseCertificate などの公開APIから呼ばれる)は、属性値として PrintableStringIA5StringNumericStringBMPStringT61StringUTF8String の6つの文字列型しか受け付けず、それ以外の型が含まれると即座にエラーを返します。
たとえば、Merkle Tree Certificate(MTC)の仕様(draft-davidben-tls-merkle-tree-certs)では RELATIVE-OID 型の属性値が使われており、現在のGoで x509.ParseCertificate を呼ぶと次のエラーが発生します。

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

OCTET STRINGINTEGERNULL などを属性値に持つ証明書も同様にパース不能でした。なお Go 1.17以前は encoding/asn1 を使った実装で既知の型はパースできていましたが、その後の実装変更(CL 274234)で現在の厳格な挙動になりました。

提案された解決策

6つの選択肢が議論された末、選択肢 (6) が採用されました。

  • crypto/x509 の内部パーサー(parseName)を拡張し、文字列型以外のASN.1型も pkix.AttributeTypeAndValue.Value として格納できるようにする。
  • pkix.AttributeTypeAndValue のドキュメントに、crypto/x509 でパースした場合に Value フィールドが取りうる型を明示する。

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

文字列型以外のASN.1属性値を含むX.509証明書を、エラーなくパースできるようになります。

コード例

// Before: 文字列以外の属性値(OCTET STRING, INTEGER, RELATIVE-OID 等)を持つ証明書は
// ParseCertificate がエラーを返し、証明書を取得できない
cert, err := x509.ParseCertificate(block.Bytes)
// err: "x509: invalid RDNSequence: invalid attribute value: unsupported string type: 4"
// After: 同じ証明書が正常にパースされ、各属性値は対応するGoの型で格納される
cert, err := x509.ParseCertificate(block.Bytes)
// err == nil
for _, attr := range cert.Subject.Names {
    switch v := attr.Value.(type) {
    case string:
        // PrintableString, IA5String, UTF8String 等
    case []byte:
        // OCTET STRING
    case int64:
        // INTEGER
    case asn1.ObjectIdentifier:
        // OBJECT IDENTIFIER
    case bool:
        // BOOLEAN
    case time.Time:
        // UTCTime, GeneralizedTime
    case asn1.BitString:
        // BIT STRING
    case nil:
        // NULL
    case asn1.RawValue:
        // 上記以外の未知のASN.1型
    }
}

ドキュメントに明記される型の対応関係(crypto/x509 でのパース時):

ASN.1型 Go型
PrintableString / IA5String / NumericString / BMPString / T61String / UTF8String string
INTEGER int64
BIT STRING asn1.BitString
OCTET STRING []byte
OBJECT IDENTIFIER asn1.ObjectIdentifier
UTCTime / GeneralizedTime time.Time
BOOLEAN bool
NULL nil
それ以外 asn1.RawValue

議論のハイライト

  • 仕様適合性が主な動機: RFC 5280でも AttributeValueANY として定義されており、現在の実装は仕様上許可されている証明書を拒否していた。MTC(Merkle Tree Certificate)の普及に向けて、RELATIVE-OID を属性値に持つ証明書を受け付けられるようにすることが直接の契機となった。
  • 歴史的な経緯: Go 1.17以前は encoding/asn1 を経由して既知の型(OCTET STRING、OBJECT IDENTIFIER 等)はパースできていたが、CL 274234 によるパーサーの書き換えで文字列型のみに制限される動作へと変化した。この変更が意図的かどうかは不明。
  • asn1.RawValue の採用: 未知の型には asn1.RawValue を使う方針が @FiloSottile によって提案・採用された。encoding/asn1any 型にアンマーシャルした場合 nil を返していた既存の問題を踏まえ、情報を失わない表現として選択された。
  • pkix.Name 全体の設計課題: X.509のDNは SEQUENCE OF SET OF AttributeTypeAndValue という2層構造だが pkix.Name はこれを平坦化しており、文字列型も元のASN.1型情報を失う等の問題が指摘された。@rolandshoemaker からは全面的に新しいDN型を導入すべきとの意見も出たが、今回はまず既存の問題修正に絞ることになった。
  • ドキュメント化の場所: @aclements は pkix.AttributeTypeAndValue が最も発見しやすい場所であるとして、同型のドキュメントに型対応表を記載することを支持した。

関連リンク