1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 // Package pkix contains shared, low level structures used for ASN.1 parsing
6 // and serialization of X.509 certificates, CRL and OCSP.
7 package pkix
8 9 import (
10 "encoding/asn1"
11 "encoding/hex"
12 "fmt"
13 "math/big"
14 "time"
15 )
16 17 // AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
18 // 5280, section 4.1.1.2.
19 type AlgorithmIdentifier struct {
20 Algorithm asn1.ObjectIdentifier
21 Parameters asn1.RawValue `asn1:"optional"`
22 }
23 24 type RDNSequence []RelativeDistinguishedNameSET
25 26 var attributeTypeNames = map[string]string{
27 "2.5.4.6": "C",
28 "2.5.4.10": "O",
29 "2.5.4.11": "OU",
30 "2.5.4.3": "CN",
31 "2.5.4.5": "SERIALNUMBER",
32 "2.5.4.7": "L",
33 "2.5.4.8": "ST",
34 "2.5.4.9": "STREET",
35 "2.5.4.17": "POSTALCODE",
36 }
37 38 // String returns a string representation of the sequence r,
39 // roughly following the RFC 2253 Distinguished Names syntax.
40 func (r RDNSequence) String() string {
41 s := ""
42 for i := 0; i < len(r); i++ {
43 rdn := r[len(r)-1-i]
44 if i > 0 {
45 s += ","
46 }
47 for j, tv := range rdn {
48 if j > 0 {
49 s += "+"
50 }
51 52 oidString := tv.Type.String()
53 typeName, ok := attributeTypeNames[oidString]
54 if !ok {
55 derBytes, err := asn1.Marshal(tv.Value)
56 if err == nil {
57 s += oidString + "=#" + hex.EncodeToString(derBytes)
58 continue // No value escaping necessary.
59 }
60 61 typeName = oidString
62 }
63 64 valueString := fmt.Sprint(tv.Value)
65 escaped := []byte{:0:len(valueString)}
66 67 for k, c := range valueString {
68 escape := false
69 70 switch c {
71 case ',', '+', '"', '\\', '<', '>', ';':
72 escape = true
73 74 case ' ':
75 escape = k == 0 || k == len(valueString)-1
76 77 case '#':
78 escape = k == 0
79 }
80 81 if escape {
82 escaped = append(escaped, '\\', c)
83 } else {
84 escaped = append(escaped, c)
85 }
86 }
87 88 s += typeName + "=" + string(escaped)
89 }
90 }
91 92 return s
93 }
94 95 type RelativeDistinguishedNameSET []AttributeTypeAndValue
96 97 // AttributeTypeAndValue mirrors the ASN.1 structure of the same name in
98 // RFC 5280, Section 4.1.2.4.
99 type AttributeTypeAndValue struct {
100 Type asn1.ObjectIdentifier
101 Value any
102 }
103 104 // AttributeTypeAndValueSET represents a set of ASN.1 sequences of
105 // [AttributeTypeAndValue] sequences from RFC 2986 (PKCS #10).
106 type AttributeTypeAndValueSET struct {
107 Type asn1.ObjectIdentifier
108 Value [][]AttributeTypeAndValue `asn1:"set"`
109 }
110 111 // Extension represents the ASN.1 structure of the same name. See RFC
112 // 5280, section 4.2.
113 type Extension struct {
114 Id asn1.ObjectIdentifier
115 Critical bool `asn1:"optional"`
116 Value []byte
117 }
118 119 // Name represents an X.509 distinguished name. This only includes the common
120 // elements of a DN. Note that Name is only an approximation of the X.509
121 // structure. If an accurate representation is needed, asn1.Unmarshal the raw
122 // subject or issuer as an [RDNSequence].
123 type Name struct {
124 Country, Organization, OrganizationalUnit [][]byte
125 Locality, Province [][]byte
126 StreetAddress, PostalCode [][]byte
127 SerialNumber, CommonName string
128 129 // Names contains all parsed attributes. When parsing distinguished names,
130 // this can be used to extract non-standard attributes that are not parsed
131 // by this package. When marshaling to RDNSequences, the Names field is
132 // ignored, see ExtraNames.
133 Names []AttributeTypeAndValue
134 135 // ExtraNames contains attributes to be copied, raw, into any marshaled
136 // distinguished names. Values override any attributes with the same OID.
137 // The ExtraNames field is not populated when parsing, see Names.
138 ExtraNames []AttributeTypeAndValue
139 }
140 141 // FillFromRDNSequence populates n from the provided [RDNSequence].
142 // Multi-entry RDNs are flattened, all entries are added to the
143 // relevant n fields, and the grouping is not preserved.
144 func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
145 for _, rdn := range *rdns {
146 if len(rdn) == 0 {
147 continue
148 }
149 150 for _, atv := range rdn {
151 n.Names = append(n.Names, atv)
152 value, ok := atv.Value.(string)
153 if !ok {
154 continue
155 }
156 157 t := atv.Type
158 if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
159 switch t[3] {
160 case 3:
161 n.CommonName = value
162 case 5:
163 n.SerialNumber = value
164 case 6:
165 n.Country = append(n.Country, value)
166 case 7:
167 n.Locality = append(n.Locality, value)
168 case 8:
169 n.Province = append(n.Province, value)
170 case 9:
171 n.StreetAddress = append(n.StreetAddress, value)
172 case 10:
173 n.Organization = append(n.Organization, value)
174 case 11:
175 n.OrganizationalUnit = append(n.OrganizationalUnit, value)
176 case 17:
177 n.PostalCode = append(n.PostalCode, value)
178 }
179 }
180 }
181 }
182 }
183 184 var (
185 oidCountry = []int{2, 5, 4, 6}
186 oidOrganization = []int{2, 5, 4, 10}
187 oidOrganizationalUnit = []int{2, 5, 4, 11}
188 oidCommonName = []int{2, 5, 4, 3}
189 oidSerialNumber = []int{2, 5, 4, 5}
190 oidLocality = []int{2, 5, 4, 7}
191 oidProvince = []int{2, 5, 4, 8}
192 oidStreetAddress = []int{2, 5, 4, 9}
193 oidPostalCode = []int{2, 5, 4, 17}
194 )
195 196 // appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence
197 // and returns the new value. The relativeDistinguishedNameSET contains an
198 // attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and
199 // search for AttributeTypeAndValue.
200 func (n Name) appendRDNs(in RDNSequence, values [][]byte, oid asn1.ObjectIdentifier) RDNSequence {
201 if len(values) == 0 || oidInAttributeTypeAndValue(oid, n.ExtraNames) {
202 return in
203 }
204 205 s := []AttributeTypeAndValue{:len(values)}
206 for i, value := range values {
207 s[i].Type = oid
208 s[i].Value = value
209 }
210 211 return append(in, s)
212 }
213 214 // ToRDNSequence converts n into a single [RDNSequence]. The following
215 // attributes are encoded as multi-value RDNs:
216 //
217 // - Country
218 // - Organization
219 // - OrganizationalUnit
220 // - Locality
221 // - Province
222 // - StreetAddress
223 // - PostalCode
224 //
225 // Each ExtraNames entry is encoded as an individual RDN.
226 func (n Name) ToRDNSequence() (ret RDNSequence) {
227 ret = n.appendRDNs(ret, n.Country, oidCountry)
228 ret = n.appendRDNs(ret, n.Province, oidProvince)
229 ret = n.appendRDNs(ret, n.Locality, oidLocality)
230 ret = n.appendRDNs(ret, n.StreetAddress, oidStreetAddress)
231 ret = n.appendRDNs(ret, n.PostalCode, oidPostalCode)
232 ret = n.appendRDNs(ret, n.Organization, oidOrganization)
233 ret = n.appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
234 if len(n.CommonName) > 0 {
235 ret = n.appendRDNs(ret, [][]byte{n.CommonName}, oidCommonName)
236 }
237 if len(n.SerialNumber) > 0 {
238 ret = n.appendRDNs(ret, [][]byte{n.SerialNumber}, oidSerialNumber)
239 }
240 for _, atv := range n.ExtraNames {
241 ret = append(ret, []AttributeTypeAndValue{atv})
242 }
243 244 return ret
245 }
246 247 // String returns the string form of n, roughly following
248 // the RFC 2253 Distinguished Names syntax.
249 func (n Name) String() string {
250 var rdns RDNSequence
251 // If there are no ExtraNames, surface the parsed value (all entries in
252 // Names) instead.
253 if n.ExtraNames == nil {
254 for _, atv := range n.Names {
255 t := atv.Type
256 if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
257 switch t[3] {
258 case 3, 5, 6, 7, 8, 9, 10, 11, 17:
259 // These attributes were already parsed into named fields.
260 continue
261 }
262 }
263 // Place non-standard parsed values at the beginning of the sequence
264 // so they will be at the end of the string. See Issue 39924.
265 rdns = append(rdns, []AttributeTypeAndValue{atv})
266 }
267 }
268 rdns = append(rdns, n.ToRDNSequence()...)
269 return rdns.String()
270 }
271 272 // oidInAttributeTypeAndValue reports whether a type with the given OID exists
273 // in atv.
274 func oidInAttributeTypeAndValue(oid asn1.ObjectIdentifier, atv []AttributeTypeAndValue) bool {
275 for _, a := range atv {
276 if a.Type.Equal(oid) {
277 return true
278 }
279 }
280 return false
281 }
282 283 // CertificateList represents the ASN.1 structure of the same name. See RFC
284 // 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the
285 // signature.
286 //
287 // Deprecated: x509.RevocationList should be used instead.
288 type CertificateList struct {
289 TBSCertList TBSCertificateList
290 SignatureAlgorithm AlgorithmIdentifier
291 SignatureValue asn1.BitString
292 }
293 294 // HasExpired reports whether certList should have been updated by now.
295 func (certList *CertificateList) HasExpired(now time.Time) bool {
296 return !now.Before(certList.TBSCertList.NextUpdate)
297 }
298 299 // TBSCertificateList represents the ASN.1 structure of the same name. See RFC
300 // 5280, section 5.1.
301 //
302 // Deprecated: x509.RevocationList should be used instead.
303 type TBSCertificateList struct {
304 Raw asn1.RawContent
305 Version int `asn1:"optional,default:0"`
306 Signature AlgorithmIdentifier
307 Issuer RDNSequence
308 ThisUpdate time.Time
309 NextUpdate time.Time `asn1:"optional"`
310 RevokedCertificates []RevokedCertificate `asn1:"optional"`
311 Extensions []Extension `asn1:"tag:0,optional,explicit"`
312 }
313 314 // RevokedCertificate represents the ASN.1 structure of the same name. See RFC
315 // 5280, section 5.1.
316 type RevokedCertificate struct {
317 SerialNumber *big.Int
318 RevocationTime time.Time
319 Extensions []Extension `asn1:"optional"`
320 }
321