crypto/x509のCertificate・CertificateRequest・RevocationListにRawSignatureAlgorithmフィールドを追加し、未知の署名アルゴリズムでもDERエンコードされたAlgorithmIdentifierを取得できるようにする。
crypto/x509
概要
crypto/x509パッケージのCertificate、CertificateRequest、RevocationListの3つの型に、RawSignatureAlgorithm []byteフィールドが追加される。このフィールドには証明書内の署名アルゴリズムのDERエンコードされたAlgorithmIdentifier(OIDと任意のパラメータからなるASN.1構造体)がそのまま格納される。SignatureAlgorithmフィールドがGoの認識しないアルゴリズムに対してUnknownSignatureAlgorithmとなる場合でも、RawSignatureAlgorithmは常に設定されるため、呼び出し側が期待するアルゴリズム識別子とバイト比較したり、自前でASN.1パースしたりできるようになる。
導入経緯
提案者はMerkle Tree Certificates(MTC)という、証明書透明性(Certificate Transparency)を発行プロセスに統合した新しい証明書形式の実装に取り組んでいた。MTCは独自の署名アルゴリズムOIDを使うが、これはまだIETFドラフト段階でOIDが確定していない実験的な仕様である。crypto/x509.ParseCertificateはこうした未知の署名アルゴリズムを持つ証明書も正常にパースするが、SignatureAlgorithmフィールドはUnknownSignatureAlgorithmになるだけで、実際にどのアルゴリズムかを確認する手段がなかった。
議論の中で、MTC署名アルゴリズム自体をGoに正式サポートさせる案も検討されたが、仕様がまだ確定していない段階でOIDをハードコードするのは時期尚早と判断され、より汎用的に生のアルゴリズム識別子を公開するRawSignatureAlgorithm案が採用された。当初はCertificateのみが対象だったが、RevocationList(CRL)も同じASN.1上のsignatureAlgorithmセマンティクスを持つことが指摘され、続いてCertificateRequest(CSR)も対象に加えられた。
議論タイムライン
- 2026-04-22: プロポーザルレビューグループが本提案をactiveとして週次レビューのアジェンダに追加。
- 2026-04-29: 週次プロポーザルレビューでlikely acceptと判定。
- 2026-05-06: 反対意見が出なかったためacceptedに確定し、実装作業に移行。
議論のハイライト
- 型の選択について、既存の
pkix.AlgorithmIdentifier型は旧来のasn1.ObjectIdentifier型に依存しており、Goチームがそこから離れたい方針であるため、生のDERバイト列([]byte)を公開する設計が選ばれた。 - 署名アルゴリズムはOIDだけでなく任意のANYパラメータを含む
AlgorithmIdentifier全体であるため、OIDのみを公開するのではなくDERバイト列全体を公開する方が柔軟という指摘があった。 - 対象範囲は
Certificateから始まり、CRLも同様の意味論を持つとの指摘でRevocationListが追加され、さらにCertificateRequestにも拡張された。 - 既存の
RawIssuer・RawSubjectなどRaw*フィールド群の設計方針と一貫性を保つアプローチである点が支持された。
使用例
Before
package main
import (
"crypto/x509"
"fmt"
)
func main() {
var mtcCertDER []byte // MTC証明書のDERバイト列を想定
cert, err := x509.ParseCertificate(mtcCertDER)
if err != nil {
panic(err)
}
if cert.SignatureAlgorithm == x509.UnknownSignatureAlgorithm {
// どの署名アルゴリズムなのか確認する手段がない
fmt.Println("unknown signature algorithm")
}
}
After
package main
import (
"bytes"
"crypto/x509"
"fmt"
)
// id-alg-mtcProof (1.3.6.1.4.1.44363.47.0) のDERエンコードされたAlgorithmIdentifier
var mtcAlgID = []byte{0x30, 0x0b, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xda, 0x2b, 0x2f, 0x00}
func main() {
var mtcCertDER []byte // MTC証明書のDERバイト列を想定
cert, err := x509.ParseCertificate(mtcCertDER)
if err != nil {
panic(err)
}
if bytes.Equal(cert.RawSignatureAlgorithm, mtcAlgID) {
fmt.Println("MTC signature algorithm detected")
}
}
移行時の注意
RawSignatureAlgorithmはDERエンコードされたAlgorithmIdentifier(SEQUENCEタグ・長さを含む)がそのまま入るため、OIDだけを取り出したい場合はcryptobyteなどでASN.1パースする必要がある。既存コードへの後方互換性への影響はなく、新規フィールドが追加されるのみである。
実装解説
Certificate.RawSignatureAlgorithm・CertificateRequest.RawSignatureAlgorithm・RevocationList.RawSignatureAlgorithmのフィールド定義はそれぞれx509.go:780、x509.go:2045、x509.go:2466-2468にある。
証明書とCRLのパース(parseCertificate・parseRevocationList、いずれもcryptobyteベースの実装)では、TBS構造体内の署名アルゴリズムをReadASN1ElementでSEQUENCEごと切り出し、その生バイト列をそのままRawSignatureAlgorithmに代入している(parser.go:1016-1020、parser.go:1227-1231)。一方CertificateRequestのパース(parseCertificateRequest)はencoding/asn1ベースの中間構造体certificateRequestを経由しており、in.SignatureAlgorithm.Raw(asn1.RawValueのRawフィールド)をそのまま代入する形で実装されている(x509.go:2368)。パース経路は異なるが、いずれも既存のSignatureAlgorithm解決処理とは独立に、DERエンコード済みのバイト列をそのままコピーするだけの実装になっている。