// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package pkix contains shared, low level structures used for ASN.1 parsing // and serialization of X.509 certificates, CRL and OCSP.
package pkix import ( ) // AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC // 5280, section 4.1.1.2. type AlgorithmIdentifier struct { Algorithm asn1.ObjectIdentifier Parameters asn1.RawValue `asn1:"optional"` } type RDNSequence []RelativeDistinguishedNameSET var attributeTypeNames = map[string]string{ "2.5.4.6": "C", "2.5.4.10": "O", "2.5.4.11": "OU", "2.5.4.3": "CN", "2.5.4.5": "SERIALNUMBER", "2.5.4.7": "L", "2.5.4.8": "ST", "2.5.4.9": "STREET", "2.5.4.17": "POSTALCODE", } // String returns a string representation of the sequence r, // roughly following the RFC 2253 Distinguished Names syntax. func ( RDNSequence) () string { := "" for := 0; < len(); ++ { := [len()-1-] if > 0 { += "," } for , := range { if > 0 { += "+" } := .Type.String() , := attributeTypeNames[] if ! { , := asn1.Marshal(.Value) if == nil { += + "=#" + hex.EncodeToString() continue // No value escaping necessary. } = } := fmt.Sprint(.Value) := make([]rune, 0, len()) for , := range { := false switch { case ',', '+', '"', '\\', '<', '>', ';': = true case ' ': = == 0 || == len()-1 case '#': = == 0 } if { = append(, '\\', ) } else { = append(, ) } } += + "=" + string() } } return } type RelativeDistinguishedNameSET []AttributeTypeAndValue // AttributeTypeAndValue mirrors the ASN.1 structure of the same name in // RFC 5280, Section 4.1.2.4. type AttributeTypeAndValue struct { Type asn1.ObjectIdentifier Value interface{} } // AttributeTypeAndValueSET represents a set of ASN.1 sequences of // AttributeTypeAndValue sequences from RFC 2986 (PKCS #10). type AttributeTypeAndValueSET struct { Type asn1.ObjectIdentifier Value [][]AttributeTypeAndValue `asn1:"set"` } // Extension represents the ASN.1 structure of the same name. See RFC // 5280, section 4.2. type Extension struct { Id asn1.ObjectIdentifier Critical bool `asn1:"optional"` Value []byte } // Name represents an X.509 distinguished name. This only includes the common // elements of a DN. Note that Name is only an approximation of the X.509 // structure. If an accurate representation is needed, asn1.Unmarshal the raw // subject or issuer as an RDNSequence. type Name struct { Country, Organization, OrganizationalUnit []string Locality, Province []string StreetAddress, PostalCode []string SerialNumber, CommonName string // Names contains all parsed attributes. When parsing distinguished names, // this can be used to extract non-standard attributes that are not parsed // by this package. When marshaling to RDNSequences, the Names field is // ignored, see ExtraNames. Names []AttributeTypeAndValue // ExtraNames contains attributes to be copied, raw, into any marshaled // distinguished names. Values override any attributes with the same OID. // The ExtraNames field is not populated when parsing, see Names. ExtraNames []AttributeTypeAndValue } // FillFromRDNSequence populates n from the provided RDNSequence. // Multi-entry RDNs are flattened, all entries are added to the // relevant n fields, and the grouping is not preserved. func ( *Name) ( *RDNSequence) { for , := range * { if len() == 0 { continue } for , := range { .Names = append(.Names, ) , := .Value.(string) if ! { continue } := .Type if len() == 4 && [0] == 2 && [1] == 5 && [2] == 4 { switch [3] { case 3: .CommonName = case 5: .SerialNumber = case 6: .Country = append(.Country, ) case 7: .Locality = append(.Locality, ) case 8: .Province = append(.Province, ) case 9: .StreetAddress = append(.StreetAddress, ) case 10: .Organization = append(.Organization, ) case 11: .OrganizationalUnit = append(.OrganizationalUnit, ) case 17: .PostalCode = append(.PostalCode, ) } } } } } var ( oidCountry = []int{2, 5, 4, 6} oidOrganization = []int{2, 5, 4, 10} oidOrganizationalUnit = []int{2, 5, 4, 11} oidCommonName = []int{2, 5, 4, 3} oidSerialNumber = []int{2, 5, 4, 5} oidLocality = []int{2, 5, 4, 7} oidProvince = []int{2, 5, 4, 8} oidStreetAddress = []int{2, 5, 4, 9} oidPostalCode = []int{2, 5, 4, 17} ) // appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence // and returns the new value. The relativeDistinguishedNameSET contains an // attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and // search for AttributeTypeAndValue. func ( Name) ( RDNSequence, []string, asn1.ObjectIdentifier) RDNSequence { if len() == 0 || oidInAttributeTypeAndValue(, .ExtraNames) { return } := make([]AttributeTypeAndValue, len()) for , := range { [].Type = [].Value = } return append(, ) } // ToRDNSequence converts n into a single RDNSequence. The following // attributes are encoded as multi-value RDNs: // // - Country // - Organization // - OrganizationalUnit // - Locality // - Province // - StreetAddress // - PostalCode // // Each ExtraNames entry is encoded as an individual RDN. func ( Name) () ( RDNSequence) { = .appendRDNs(, .Country, oidCountry) = .appendRDNs(, .Province, oidProvince) = .appendRDNs(, .Locality, oidLocality) = .appendRDNs(, .StreetAddress, oidStreetAddress) = .appendRDNs(, .PostalCode, oidPostalCode) = .appendRDNs(, .Organization, oidOrganization) = .appendRDNs(, .OrganizationalUnit, oidOrganizationalUnit) if len(.CommonName) > 0 { = .appendRDNs(, []string{.CommonName}, oidCommonName) } if len(.SerialNumber) > 0 { = .appendRDNs(, []string{.SerialNumber}, oidSerialNumber) } for , := range .ExtraNames { = append(, []AttributeTypeAndValue{}) } return } // String returns the string form of n, roughly following // the RFC 2253 Distinguished Names syntax. func ( Name) () string { var RDNSequence // If there are no ExtraNames, surface the parsed value (all entries in // Names) instead. if .ExtraNames == nil { for , := range .Names { := .Type if len() == 4 && [0] == 2 && [1] == 5 && [2] == 4 { switch [3] { case 3, 5, 6, 7, 8, 9, 10, 11, 17: // These attributes were already parsed into named fields. continue } } // Place non-standard parsed values at the beginning of the sequence // so they will be at the end of the string. See Issue 39924. = append(, []AttributeTypeAndValue{}) } } = append(, .ToRDNSequence()...) return .String() } // oidInAttributeTypeAndValue reports whether a type with the given OID exists // in atv. func oidInAttributeTypeAndValue( asn1.ObjectIdentifier, []AttributeTypeAndValue) bool { for , := range { if .Type.Equal() { return true } } return false } // CertificateList represents the ASN.1 structure of the same name. See RFC // 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the // signature. type CertificateList struct { TBSCertList TBSCertificateList SignatureAlgorithm AlgorithmIdentifier SignatureValue asn1.BitString } // HasExpired reports whether certList should have been updated by now. func ( *CertificateList) ( time.Time) bool { return !.Before(.TBSCertList.NextUpdate) } // TBSCertificateList represents the ASN.1 structure of the same name. See RFC // 5280, section 5.1. type TBSCertificateList struct { Raw asn1.RawContent Version int `asn1:"optional,default:0"` Signature AlgorithmIdentifier Issuer RDNSequence ThisUpdate time.Time NextUpdate time.Time `asn1:"optional"` RevokedCertificates []RevokedCertificate `asn1:"optional"` Extensions []Extension `asn1:"tag:0,optional,explicit"` } // RevokedCertificate represents the ASN.1 structure of the same name. See RFC // 5280, section 5.1. type RevokedCertificate struct { SerialNumber *big.Int RevocationTime time.Time Extensions []Extension `asn1:"optional"` }