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

Go Proposal Weekly Digest

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

新機能

crypto/x509のCertificate・CertificateRequest・RevocationListにRawSignatureAlgorithmフィールドを追加し、未知の署名アルゴリズムでもDERエンコードされたAlgorithmIdentifierを取得できるようにする。

crypto/x509

この項目の注釈は AI により生成されており、誤りを含む場合があります。
使用例のコンパイル検証: 検証済み

概要

crypto/x509パッケージのCertificateCertificateRequestRevocationListの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にも拡張された。
  • 既存のRawIssuerRawSubjectなど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.RawSignatureAlgorithmCertificateRequest.RawSignatureAlgorithmRevocationList.RawSignatureAlgorithmのフィールド定義はそれぞれx509.go:780x509.go:2045x509.go:2466-2468にある。

証明書とCRLのパース(parseCertificateparseRevocationList、いずれもcryptobyteベースの実装)では、TBS構造体内の署名アルゴリズムをReadASN1ElementでSEQUENCEごと切り出し、その生バイト列をそのままRawSignatureAlgorithmに代入している(parser.go:1016-1020parser.go:1227-1231)。一方CertificateRequestのパース(parseCertificateRequest)はencoding/asn1ベースの中間構造体certificateRequestを経由しており、in.SignatureAlgorithm.Raw(asn1.RawValueRawフィールド)をそのまま代入する形で実装されている(x509.go:2368)。パース経路は異なるが、いずれも既存のSignatureAlgorithm解決処理とは独立に、DERエンコード済みのバイト列をそのままコピーするだけの実装になっている。

関連リンク