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

Go Proposal Weekly Digest

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

#75260likely_accept

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

ステータス変更: active likely_accept

要約

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

概要

crypto/x509 パッケージが X.509 証明書を解析する際に、Subject/Issuer の識別名(DN)フィールドの属性値として文字列型以外の ASN.1 型を受け入れられるよう、パーサの挙動とドキュメントを改善するproposalです。

ステータス変更

activelikely_accept
RFC 5280 では属性値(AttributeValue)は ANY 型として定義されており、文字列以外の ASN.1 型を含んでも仕様準拠であるにもかかわらず、Go の crypto/x509 パーサがこれらを拒否していました。Merkle Tree Certificate のような新しいTLS関連ドラフト仕様への対応必要性と、設計の妥当性についてコアチームで合意が得られたため、2026年5月6日の週次提案レビューミーティングで likely_accept と判断されました。

技術的背景

現状の問題点

RFC 5280 の X.509 証明書仕様では、RDN(Relative Distinguished Name)の属性値(AttributeValue)は ANY 型として定義されています。しかし現在の crypto/x509parseName 関数(parseASN1String 経由)は、属性値として以下の文字列型しか受け入れていません: T61StringPrintableStringUTF8StringBMPStringIA5StringNumericString
TLS のドラフト仕様 draft-ietf-tls-trust-anchor-ids および Merkle Tree Certificates で使用される RELATIVE-OID を属性値に含む証明書を x509.ParseCertificate で解析しようとすると、以下のエラーが発生します:

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

歴史的背景として、Go 1.16 以前は encoding/asn1 を使った汎用的な Unmarshal により解析できていましたが、Go 1.17 で x/crypto/cryptobyte を使った専用パーサへの移行(CL #274234)に伴い、この問題が生じました。

提案された解決策

crypto/x509parseName 関数が、文字列型以外の ASN.1 型も pkix.AttributeTypeAndValue.Value に格納できるよう拡張します。具体的には以下の型マッピングを採用します。

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
また、pkix.AttributeTypeAndValue のドキュメントに、Value フィールドが取り得る Go の型を明記します。

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

Merkle Tree Certificate や TLS Trust Anchor IDs などの新しいTLS仕様に準拠した証明書を Go でエラーなく解析できるようになります。また、OCTET STRINGINTEGEROBJECT IDENTIFIER など文字列以外の属性値を含む独自 X.509 証明書の処理も可能になります。

コード例

// Before: RELATIVE-OID を含む証明書の解析が失敗する
cert, err := x509.ParseCertificate(derBytes)
// => error: x509: invalid RDNSequence: invalid attribute value: unsupported string type: 13
// After: 非文字列属性も含む証明書を正常に解析できる
cert, err := x509.ParseCertificate(derBytes)
if err != nil {
    panic(err)
}
for _, attr := range cert.Subject.Names {
    switch v := attr.Value.(type) {
    case string:
        fmt.Printf("文字列属性: %s = %s\n", attr.Type, v)
    case []byte:
        fmt.Printf("OCTET STRING属性: %s = %x\n", attr.Type, v)
    case asn1.RawValue:
        // RELATIVE-OID などの未知の型はRawValueとして取得可能
        fmt.Printf("その他ASN.1属性: %s, タグ=%d\n", attr.Type, v.Tag)
    }
}

議論のハイライト

  • 複数の解決案の検討: FiloSottile は6つの選択肢を列挙しました。(5) 旧 encoding/asn1 の挙動への回帰、(6) 全 ASN.1 型を認識し未知型は asn1.RawValue にフォールバック、という2案が最終候補となり、より情報量の多い (6) が採用されました。
  • 後方互換性: pkix.AttributeTypeAndValue.Value はもともと any 型のため、型定義の変更は不要です。現在は解析エラーで失敗していたコードがエラーなく動くようになるため、後方非互換ではありません。ただし、将来型を変更する機会は失われます(FiloSottile 指摘)。
  • Go 1.16 からの退行の確認: davidben が詳細な調査を行い、Go 1.16 では encoding/asn1 経由で一部の型(OCTET STRINGOBJECT IDENTIFIER など)が解析できていたことを証明しました。RELATIVE-OID や NULL などは nil にバインドされていたという興味深い挙動も明らかになりました。
  • ドキュメントの場所: rolandshoemaker は「pkix.AttributeTypeAndValueany 型であり冗長では」と疑問を呈しましたが、aclements が「利用者の最も見つけやすい場所にドキュメントを置くべき」と結論付けました。
  • 将来の X.509 DN API の議論: rolandshoemaker は pkix.Name のAPI全体の刷新(新 DN 型の導入)を示唆しましたが、現在の提案の妨げにならないよう、別途の議論とすることになりました。

関連リンク