[インデックス 18498] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/x509
パッケージに、PKCS #10 (Certificate Signing Request: CSR) のサポートを追加するものです。具体的には、CSRのパース(解析)とシリアライズ(直列化)の機能が導入され、これによりGoアプリケーション内で証明書署名要求の生成と処理が可能になります。
コミット
Author: Kyle Isom kyle@gokyle.net Date: Thu Feb 13 12:54:04 2014 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fc8e77ca65ab7d4dfd6fd58ad67145f253aab829
元コミット内容
crypto/x509: Add certificate signature request (CSR) support.
This change adds support for parsing and serialisation of PKCS #10,
certificate signature requests.
LGTM=agl
R=golang-codereviews, agl
CC=agl, golang-codereviews, nick
https://golang.org/cl/49830048
変更の背景
この変更が導入される以前のGo言語の crypto/x509
パッケージは、X.509証明書の生成、パース、検証の機能を提供していましたが、PKCS #10形式の証明書署名要求 (CSR) を直接扱う機能がありませんでした。CSRは、公開鍵インフラストラクチャ (PKI) において、証明書発行局 (CA) に証明書の発行を要求する際に用いられる標準的なフォーマットです。
CSRのサポートがない場合、Goアプリケーションから証明書を要求するには、外部ツール(例: OpenSSL)を使用するか、手動でCSRを作成する必要がありました。これは、自動化された証明書管理システムや、Go言語でセキュアな通信を必要とするサービスを構築する上で大きな制約となっていました。
このコミットは、Go言語エコシステム内でCSRの生成と解析を完結させることで、開発者がより簡単にセキュアなアプリケーションを構築できるようにすることを目的としています。これにより、Go言語の crypto/x509
パッケージが提供する機能が拡張され、より包括的なPKI操作が可能になりました。
前提知識の解説
PKCS #10 (Certificate Signing Request: CSR)
PKCS #10は、公開鍵暗号標準 (PKCS) の一つで、証明書署名要求 (CSR) のフォーマットを定義しています。CSRは、公開鍵と、その公開鍵の所有者に関する情報(識別名、Subject Alternative Nameなど)をCAに提出し、デジタル証明書の発行を要求するために使用されます。CSRには、要求者の秘密鍵で署名が施されており、これにより要求の正当性が保証されます。
X.509 証明書
X.509は、公開鍵証明書のフォーマットを定義するITU-Tの標準です。デジタル証明書は、公開鍵と、その公開鍵が属するエンティティ(人、組織、デバイスなど)の識別情報を結びつけ、CAによって署名されたものです。これにより、公開鍵の信頼性を保証し、安全な通信を可能にします。
ASN.1 (Abstract Syntax Notation One)
ASN.1は、データ構造を記述するための標準的な記法です。通信プロトコルやデータストレージにおいて、異なるシステム間でデータを交換する際に、データの表現方法を統一するために使用されます。X.509証明書やPKCS #10 CSRなど、多くの暗号関連の標準がASN.1で定義されています。
DER (Distinguished Encoding Rules)
DERは、ASN.1で定義されたデータ構造をバイト列にエンコードするための規則の一つです。DERは、特定のASN.1構造に対して一意のバイト列表現を保証するため、デジタル署名やハッシュ計算など、データの同一性が厳密に要求される場面で広く利用されます。
Subject Alternative Name (SAN)
X.509証明書において、Subject Alternative Name (SAN) 拡張は、証明書が保護する追加の識別子を指定するために使用されます。従来の証明書では「Subject Common Name (CN)」フィールドが主要な識別子でしたが、SANは複数のDNS名(ウェブサイトのドメイン名)、IPアドレス、メールアドレスなどを含めることができ、より柔軟な識別子を提供します。特に、単一の証明書で複数のドメインを保護する(例: example.com
と www.example.com
)場合や、IPアドレスで識別されるサーバーの証明書を発行する場合に不可欠です。
暗号学的ハッシュ関数
暗号学的ハッシュ関数は、任意の長さの入力データ(メッセージ)を受け取り、固定長の短いバイト列(ハッシュ値またはメッセージダイジェスト)を出力する関数です。以下の特性を持ちます。
- 一方向性: ハッシュ値から元のメッセージを効率的に復元することは困難です。
- 衝突耐性: 異なるメッセージから同じハッシュ値が生成されること(衝突)は、計算上非常に困難です。
- 改ざん検出: メッセージが少しでも変更されると、ハッシュ値が大きく変化するため、データの改ざんを検出できます。 デジタル署名では、メッセージ全体ではなく、そのハッシュ値に署名することで、効率的にデータの完全性と認証性を保証します。
デジタル署名
デジタル署名は、公開鍵暗号技術を用いて、電子文書の作成者の身元を証明し、文書が改ざんされていないことを保証する仕組みです。署名者は自身の秘密鍵で文書のハッシュ値を暗号化し、その結果を署名として添付します。検証者は署名者の公開鍵を用いて署名を復号し、得られたハッシュ値と、受信した文書から計算したハッシュ値を比較することで、署名の正当性と文書の完全性を確認します。
RSA と ECDSA
- RSA (Rivest–Shamir–Adleman): 最も広く使用されている公開鍵暗号アルゴリズムの一つで、素因数分解の困難性に基づいています。デジタル署名、鍵交換、暗号化に利用されます。
- ECDSA (Elliptic Curve Digital Signature Algorithm): 楕円曲線暗号 (ECC) に基づくデジタル署名アルゴリズムです。RSAと比較して、同等のセキュリティレベルをより短い鍵長で実現できるため、計算リソースが限られた環境や、より高いパフォーマンスが求められる場面で有利です。
技術的詳細
このコミットは、crypto/x509
パッケージにPKCS #10 CSRの生成と解析のための新しいデータ構造と関数を導入しています。
新しいデータ構造
-
type CertificateRequest struct
:- CSRの主要な情報を保持する構造体です。
Raw
: 完全なDERエンコードされたCSRバイト列。RawTBSCertificateRequest
: TBSCertificateRequest(署名される部分)のDERバイト列。RawSubjectPublicKeyInfo
: 公開鍵情報のDERバイト列。RawSubject
: Subject(識別名)のDERバイト列。Version
: CSRのバージョン(通常は0)。Signature
: 署名値。SignatureAlgorithm
: 署名アルゴリズム。PublicKeyAlgorithm
: 公開鍵アルゴリズム。PublicKey
: パースされた公開鍵(*rsa.PublicKey
または*ecdsa.PublicKey
)。Subject
:pkix.Name
型で、要求者の識別名。Attributes
: PKCS #10で定義される属性のコレクション。これには、要求された拡張(例: SAN)が含まれることがあります。Extensions
: パース時に抽出されるX.509拡張。ExtraExtensions
: CSRを生成する際に、追加で含める拡張。DNSNames
,EmailAddresses
,IPAddresses
: Subject Alternative Name (SAN) 拡張から抽出された、またはCSR生成時に指定されるDNS名、メールアドレス、IPアドレス。
-
type tbsCertificateRequest struct
:- CSRの「To Be Signed Certificate Request」部分を表す内部構造体。この部分が秘密鍵で署名されます。
Version
,Subject
,PublicKey
,Attributes
を含みます。
-
type certificateRequest struct
:- DERエンコードされたCSR全体の構造を表す内部構造体。
TBSCSR
(tbsCertificateRequest),SignatureAlgorithm
,SignatureValue
を含みます。
-
type AttributeTypeAndValueSET struct
(insrc/pkg/crypto/x509/pkix/pkix.go
):- RFC 2986 (PKCS #10) で定義される
AttributeTypeAndValue
のセットを表す新しい構造体。CSRの属性に使用されます。
- RFC 2986 (PKCS #10) で定義される
新しい関数
-
func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv interface{}) (csr []byte, err error)
:CertificateRequest
テンプレートと秘密鍵を使用して、新しいCSRをDERエンコードされたバイト列として生成します。- 内部で
signingParamsForPrivateKey
を呼び出して署名パラメータを決定し、公開鍵をマーシャルし、tbsCertificateRequest
を構築して署名します。 - SANs (
DNSNames
,EmailAddresses
,IPAddresses
) が指定されている場合、それらをSubjectAltName
拡張としてCSRに含めます。 ExtraExtensions
とAttributes
の処理ロジックが含まれており、特にoidExtensionRequest
(PKCS #9 Extension Request) 属性を通じて拡張がどのように組み込まれるかが実装されています。
-
func ParseCertificateRequest(asn1Data []byte) (*CertificateRequest, error)
:- DERエンコードされたCSRバイト列をパースし、
CertificateRequest
構造体を返します。 - 内部で
parseCertificateRequest
を呼び出し、ASN.1のデコード、公開鍵のパース、Subjectの抽出、および拡張(特にSAN)の処理を行います。
- DERエンコードされたCSRバイト列をパースし、
-
func marshalSANs(dnsNames, emailAddresses []string, ipAddresses []net.IP) (derBytes []byte, err error)
:- DNS名、メールアドレス、IPアドレスのリストを、X.509
SubjectAlternativeName
拡張のDERエンコードされた内容にマーシャルするヘルパー関数。 - IPv4アドレスは可能な限り4バイトでエンコードされます。
- DNS名、メールアドレス、IPアドレスのリストを、X.509
-
func signingParamsForPrivateKey(priv interface{}, requestedSigAlgo SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error)
:- 与えられた秘密鍵と要求された署名アルゴリズムに基づいて、ハッシュ関数と署名アルゴリズムの識別子を決定するヘルパー関数。
- RSAおよびECDSA鍵に対応し、適切なハッシュ関数とOID (Object Identifier) を選択します。
既存関数の変更
-
func parseCertificate(in *certificate) (*Certificate, error)
(insrc/pkg/crypto/x509/x509.go
):SubjectAltName
拡張のパースロジックがparseSANExtension
関数に切り出され、コードの重複が解消されました。
-
func buildExtensions(template *Certificate) (ret []pkix.Extension, err error)
(insrc/pkg/crypto/x509/x509.go
):SubjectAltName
拡張の構築にmarshalSANs
ヘルパー関数が使用されるようになり、コードが簡素化されました。
-
func CreateCertificate(...)
(insrc/pkg/crypto/x509/x509.go
):- 署名パラメータの決定ロジックが
signingParamsForPrivateKey
関数に切り出され、コードの再利用性が向上しました。
- 署名パラメータの決定ロジックが
src/pkg/encoding/asn1/asn1.go
の変更
ObjectIdentifier
型にString()
メソッドが追加され、OIDをドット区切りの文字列形式で表現できるようになりました。これはデバッグやログ出力に便利です。
テストの追加 (src/pkg/crypto/x509/x509_test.go
)
TestCreateCertificateRequest
: さまざまな鍵タイプ(RSA, ECDSA)と署名アルゴリズムを使用してCSRを生成し、その結果をパースして、元のテンプレートと一致するかどうかを検証するテストが追加されました。TestCertificateRequestOverrides
:ExtraExtensions
やAttributes
を使用して、CSRの拡張がどのようにオーバーライドされるかを検証するテストが追加されました。TestParseCertificateRequest
: 既存のBase64エンコードされたCSRをパースし、その内容(メールアドレス、DNS名、Subjectなど)が正しく抽出されるかを検証するテストが追加されました。
これらの変更により、Go言語の crypto/x509
パッケージは、証明書ライフサイクル管理においてより完全な機能セットを提供するようになりました。
コアとなるコードの変更箇所
このコミットにおける主要な変更は、以下のファイルとコードブロックに集中しています。
-
src/pkg/crypto/x509/x509.go
:CertificateRequest
構造体の追加 (約1559行目から)tbsCertificateRequest
およびcertificateRequest
内部構造体の追加oidExtensionRequest
OIDの定義CreateCertificateRequest
関数の追加 (約1600行目から)ParseCertificateRequest
関数の追加 (約1720行目から)parseCertificateRequest
ヘルパー関数の追加marshalSANs
ヘルパー関数の追加 (約1157行目から)signingParamsForPrivateKey
ヘルパー関数の追加 (約1354行目から)- 既存の
parseCertificate
関数におけるSubjectAltName
拡張のパースロジックのparseSANExtension
関数への切り出し (約790行目から) - 既存の
buildExtensions
関数におけるmarshalSANs
の利用 (約1279行目) - 既存の
CreateCertificate
関数におけるsigningParamsForPrivateKey
の利用 (約1420行目)
-
src/pkg/crypto/x509/pkix/pkix.go
:AttributeTypeAndValueSET
構造体の追加 (約30行目から)
-
src/pkg/crypto/x509/x509_test.go
:TestCreateCertificateRequest
関数の追加 (約735行目から)TestCertificateRequestOverrides
関数の追加 (約800行目から)TestParseCertificateRequest
関数の追加 (約880行目から)- テスト用のBase64エンコードされたCSRデータ
csrBase64
の追加
-
src/pkg/encoding/asn1/asn1.go
:ObjectIdentifier
型へのString()
メソッドの追加 (約197行目から)
コアとなるコードの解説
CertificateRequest
構造体
type CertificateRequest struct {
Raw []byte // Complete ASN.1 DER content (CSR, signature algorithm and signature).
RawTBSCertificateRequest []byte // Certificate request info part of raw ASN.1 DER content.
RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo.
RawSubject []byte // DER encoded Subject.
Version int
Signature []byte
SignatureAlgorithm SignatureAlgorithm
PublicKeyAlgorithm PublicKeyAlgorithm
PublicKey interface{}
Subject pkix.Name
// Attributes is a collection of attributes providing
// additional information about the subject of the certificate.
// See RFC 2986 section 4.1.
Attributes []pkix.AttributeTypeAndValueSET
// Extensions contains raw X.509 extensions. When parsing CSRs, this
// can be used to extract extensions that are not parsed by this
// package.
Extensions []pkix.Extension
// ExtraExtensions contains extensions to be copied, raw, into any
// marshaled CSR. Values override any extensions that would otherwise
// be produced based on the other fields but are overridden by any
// extensions specified in Attributes.
//
// The ExtraExtensions field is not populated when parsing CSRs, see
// Extensions.
ExtraExtensions []pkix.Extension
// Subject Alternate Name values.
DNSNames []string
EmailAddresses []string
IPAddresses []net.IP
}
この構造体は、PKCS #10 CSRのすべての関連情報をカプセル化します。CSRの生成時には、Subject
、DNSNames
、EmailAddresses
、IPAddresses
、Attributes
、ExtraExtensions
などのフィールドを設定してテンプレートとして使用します。パース時には、これらのフィールドにCSRから抽出されたデータが格納されます。Raw
フィールドは、完全なDERエンコードされたCSRバイト列を保持し、署名検証などに利用されます。
CreateCertificateRequest
関数
func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv interface{}) (csr []byte, err error) {
hashFunc, sigAlgo, err := signingParamsForPrivateKey(priv, template.SignatureAlgorithm)
if err != nil {
return nil, err
}
// ... (公開鍵のマーシャル、SANsの処理、拡張の処理) ...
tbsCSR := tbsCertificateRequest{
Version: 0, // PKCS #10, RFC 2986
Subject: asn1.RawValue{FullBytes: asn1Subject},
PublicKey: publicKeyInfo{
Algorithm: publicKeyAlgorithm,
PublicKey: asn1.BitString{
Bytes: publicKeyBytes,
BitLength: len(publicKeyBytes) * 8,
},
},
Attributes: attributes,
}
tbsCSRContents, err := asn1.Marshal(tbsCSR)
if err != nil {
return
}
tbsCSR.Raw = tbsCSRContents
h := hashFunc.New()
h.Write(tbsCSRContents)
digest := h.Sum(nil)
var signature []byte
switch priv := priv.(type) {
case *rsa.PrivateKey:
signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest)
case *ecdsa.PrivateKey:
var r, s *big.Int
if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil {
signature, err = asn1.Marshal(ecdsaSignature{r, s})
}
default:
panic("internal error")
}
if err != nil {
return
}
return asn1.Marshal(certificateRequest{
TBSCSR: tbsCSR,
SignatureAlgorithm: sigAlgo,
SignatureValue: asn1.BitString{
Bytes: signature,
BitLength: len(signature) * 8,
},
})
}
この関数はCSR生成の中心的なロジックを担います。
- 署名パラメータの決定:
signingParamsForPrivateKey
を呼び出し、使用するハッシュ関数と署名アルゴリズムを決定します。 - 公開鍵のマーシャル: テンプレートから公開鍵情報を抽出し、DERエンコードします。
- 拡張の処理:
DNSNames
,EmailAddresses
,IPAddresses
が指定されていれば、marshalSANs
を使ってSubjectAltName
拡張を生成します。また、ExtraExtensions
やAttributes
に含まれる拡張も適切に処理し、PKCS #9ExtensionRequest
属性としてCSRに含めます。 tbsCertificateRequest
の構築と署名: CSRの署名対象となる部分 (tbsCSR
) を構築し、そのDERエンコードされたバイト列をハッシュ化します。その後、提供された秘密鍵 (priv
) を使用してハッシュ値にデジタル署名を行います(RSAまたはECDSA)。- 最終的なCSRのエンコード: 署名された
tbsCSR
と署名情報 (SignatureAlgorithm
,SignatureValue
) を含むcertificateRequest
構造体をASN.1 DER形式でエンコードし、完成したCSRバイト列を返します。
ParseCertificateRequest
関数
func ParseCertificateRequest(asn1Data []byte) (*CertificateRequest, error) {
var csr certificateRequest
rest, err := asn1.Unmarshal(asn1Data, &csr)
if err != nil {
return nil, err
} else if len(rest) != 0 {
return nil, asn1.SyntaxError{Msg: "trailing data"}
}
return parseCertificateRequest(&csr)
}
func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error) {
out := &CertificateRequest{
Raw: in.Raw,
RawTBSCertificateRequest: in.TBSCSR.Raw,
RawSubjectPublicKeyInfo: in.TBSCSR.PublicKey.Raw,
RawSubject: in.TBSCSR.Subject.FullBytes,
Signature: in.SignatureValue.RightAlign(),
SignatureAlgorithm: getSignatureAlgorithmFromOID(in.SignatureAlgorithm.Algorithm),
PublicKeyAlgorithm: getPublicKeyAlgorithmFromOID(in.TBSCSR.PublicKey.Algorithm.Algorithm),
Version: in.TBSCSR.Version,
Attributes: in.TBSCSR.Attributes,
}
// ... (公開鍵のパース、Subjectのパース、拡張の抽出とSANsのパース) ...
return out, nil
}
この関数は、DERエンコードされたCSRバイト列を受け取り、それを CertificateRequest
構造体にデコードします。
- ASN.1デコード: まず、入力されたバイト列を
certificateRequest
内部構造体にASN.1デコードします。 - 基本情報の抽出:
certificateRequest
からRaw
、RawTBSCertificateRequest
、RawSubjectPublicKeyInfo
、RawSubject
、Signature
、SignatureAlgorithm
、PublicKeyAlgorithm
、Version
、Attributes
などの基本情報をCertificateRequest
構造体にコピーします。 - 公開鍵のパース:
parsePublicKey
を使用して、CSRに含まれる公開鍵を適切なGoの公開鍵型(*rsa.PublicKey
または*ecdsa.PublicKey
)にパースします。 - Subjectのパース:
in.TBSCSR.Subject.FullBytes
からpkix.Name
構造体を構築し、要求者の識別名を抽出します。 - 拡張の抽出とSANsのパース: CSRの
Attributes
フィールドからoidExtensionRequest
属性を探し、その中に含まれるX.509拡張を抽出します。特に、SubjectAltName
拡張が見つかった場合は、parseSANExtension
を呼び出してDNSNames
,EmailAddresses
,IPAddresses
をパースし、CertificateRequest
構造体の対応するフィールドに格納します。
これらの関数と構造体の導入により、Go言語の crypto/x509
パッケージは、PKCS #10 CSRの生成と解析という重要な機能を提供できるようになり、PKI関連のアプリケーション開発がより容易になりました。
関連リンク
- RFC 2986 - PKCS #10: Certification Request Syntax Specification Version 1.7: https://datatracker.ietf.org/doc/html/rfc2986
- RFC 5280 - Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile: https://datatracker.ietf.org/doc/html/rfc5280
- Go言語
crypto/x509
パッケージドキュメント: https://pkg.go.dev/crypto/x509
参考にした情報源リンク
- Wikipedia - Certificate signing request: https://en.wikipedia.org/wiki/Certificate_signing_request
- Wikipedia - X.509: https://en.wikipedia.org/wiki/X.509
- Wikipedia - Abstract Syntax Notation One: https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
- Wikipedia - Distinguished Encoding Rules: https://en.wikipedia.org/wiki/Distinguished_Encoding_Rules
- Wikipedia - Subject Alternative Name: https://en.wikipedia.org/wiki/Subject_Alternative_Name
- Wikipedia - Digital signature: https://en.wikipedia.org/wiki/Digital_signature
- Wikipedia - RSA (cryptosystem): https://en.wikipedia.org/wiki/RSA_(cryptosystem)
- Wikipedia - Elliptic Curve Digital Signature Algorithm: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm