File: Examining%20Certificates.md

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (170 lines) | stat: -rw-r--r-- 8,981 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# Examining Certificates

Decoding and introspecting certificates.

A common use-case is to have received an X.509 certificate from some source, and to want
to inspect it and make decisions based on its content. X.509 certificates are complex
objects with highly dynamic and nested content, so this article attempts to outline
how to work with ``Certificate`` in order to get the most out of the content of an
X.509 certificate.

## Parsing

Certificates are commonly provided in two formats: PEM and DER. The two formats are closely
related. PEM stands for "Privacy Enhanced Mail", and a PEM certificate commonly looks like
this:

```
-----BEGIN CERTIFICATE-----
MIIB0jCCAXegAwIBAgIUKggFG6vN44MJ3HhCMcypJZTjzyUwCgYIKoZIzj0EAwIw
SjELMAkGA1UEBgwCVVMxGjAYBgNVBAoMEVN3aWZ0IENlcnRpZmljYXRlMR8wHQYD
VQQDDBZHb29kIGpvYiBkZWNvZGluZyB0aGlzMB4XDTIyMTAyODEwMTQyMloXDTIz
MTAyODEwMTQyMlowSjELMAkGA1UEBgwCVVMxGjAYBgNVBAoMEVN3aWZ0IENlcnRp
ZmljYXRlMR8wHQYDVQQDDBZHb29kIGpvYiBkZWNvZGluZyB0aGlzMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAEa0NRgN2LXAvr66LEna3rmJTTn2O1WC6tFJCCw6LD
8TRA9Rz6zYzbpD4+rEoxRTyF6AQuIint1+LmbdmcGhTsUqM7MDkwDwYDVR0TAQH/
BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwFgYDVR0RBA8wDYILZXhhbXBsZS5jb20w
CgYIKoZIzj0EAwIDSQAwRgIhAORCXnM39TRyN5RRmsFgEZgojUf1MqyG/azkAMx1
FGgfAiEA1PMpcTTeK8Hi17N0oaNNIWERLqDketPZG5E3OdEDw9E=
-----END CERTIFICATE-----
```

This format is the DER representation of a certificate, base64-encoded, line wrapped at 64
bytes, and bracketed by the `BEGIN X`/`END X` delimiter. As PEMs can easily be translated
to DER bytes and vice-versa, the rest of this document will focus on the DER format.

To parse a certificate from DER, we can construct it with a helper initializer from `SwiftASN1`:

```swift
let derBytes: [UInt8] = // get these from somewhere
let certificate = try Certificate(derEncoded: derBytes)
```

Assuming the certificate is well-formed, this constructor will build us a ``Certificate`` object
that we can work with.

## Introspecting Certificates

Certificates have a wide range of fields. Many of them are not that interesting (for example,
``Certificate/version-swift.property``). However, a number of them make useful attestations about the
subject of the certificate.

### Names

Certificates have at least two names attached to them: ``Certificate/issuer`` and ``Certificate/subject``.
``Certificate/issuer`` provides the name of the entity that issued the certificate, while
``Certificate/subject`` provides the name of the entity to which the certificate was issued. Both of
these names are represented using ``DistinguishedName``.

``DistinguishedName`` is a fairly complex type that is described in more detail in its API documentation.
In short, it's a hierarchical collection of ``RelativeDistinguishedName``s, each of which is made up
of a set of equivalent ``RelativeDistinguishedName/Attribute``s. These ``RelativeDistinguishedName``s
are arranged in order from least specific to most specific, typically ending with a relative
distinguished name containing a ``CommonName`` ``RelativeDistinguishedName/Attribute``.

A string representation of a ``DistinguishedName`` can be obtained by using `String(describing:)`. This
uses the common [RFC4514 format](https://www.rfc-editor.org/rfc/rfc4514) for textual ``DistinguishedName``s.

Generally speaking the specific content of the names in these fields are unimportant to end users, though they do
play a vital role in validating certificates.

### Keys

Certificates are bound to a specific private key, which can be used to authenticate that a specific entity is
the one to whom the certificate was issued. This binding is achieved by embedding the public key corresponding
to the underlying private key into the certificate object itself. This is modelled in the ``Certificate/publicKey-swift.property``
field.

In ``X509`` the ``Certificate/PublicKey-swift.struct`` type is opaque, but they can be compared for equality.

### Extensions

The majority of the semantic information in certificate objects is stored in their X509v3 extensions.
These are a typed key-value dictionary that contain additional information about both the subject of the
certificate and about much of its content.

``X509`` stores the extension information in a ``Certificate/Extensions-swift.struct`` collection. This
is a full-fidelity representation of all extensions found in the certificate. This stores the extensions
as ``Certificate/Extension`` objects, an opaque raw-bytes representation of the extension.

Implementations are generally not required to understand all extensions in a certificate, as many are
strictly informational. However, some extensions must be understood in order for the certificate to safely
fulfil their function. As a result, users of a certificate should always search for extensions that have
the ``Certificate/Extension/critical`` bit set to `true` and, if they do not understand those extensions,
should refuse to trust the certificate.

As the raw representation of extensions is difficult to use, ``X509`` provides a number of higher-level
typed representations of common extensions. These can decode themselves from the ``Certificate/Extension/value``
bytes in an extension, as well as wrap themselves back into the opaque ``Certificate/Extension`` type.

Out of the box, ``X509`` ships support for the following extension types:

- ``AuthorityInformationAccess``
- ``AuthorityKeyIdentifier``
- ``BasicConstraints``
- ``ExtendedKeyUsage``
- ``KeyUsage``
- ``NameConstraints``
- ``SubjectAlternativeNames``
- ``SubjectKeyIdentifier``

To decode an extension usually requires examining its ``Certificate/Extension/oid`` field. For example, to search
for the ``SubjectAlternativeNames``, the typical code would be:

```swift
let opaqueSanExtension = certificate.extensions.first(where: { $0.oid == .X509ExtensionID.subjectAlternativeName })
if let opaqueSanExtension {
    let unwrappedSanExtension = try SubjectAlternativeName(opaqueSanExtension)
}
```

This is verbose and repetitive code, so users are encouraged to use the helper function ``Certificate/Extensions-swift.struct/subscript(oid:)``
to search for a specific extension. The above code could be replaced by:

```swift
if let opaqueSanExtension = certificate.extensions[oid: .X509ExtensionID.subjectAlternativeName] {
    let unwrappedSanExtension = try SubjectAlternativeName(opaqueSanExtension)
}
```

This pattern is itself still somewhat repetitive, so ``Certificate/Extensions-swift.struct`` offers a number of helper properties
that can be used to get a specific typed extension:

- ``Certificate/Extensions-swift.struct/authorityInformationAccess``
- ``Certificate/Extensions-swift.struct/authorityKeyIdentifier``
- ``Certificate/Extensions-swift.struct/basicConstraints``
- ``Certificate/Extensions-swift.struct/extendedKeyUsage``
- ``Certificate/Extensions-swift.struct/keyUsage``
- ``Certificate/Extensions-swift.struct/nameConstraints``
- ``Certificate/Extensions-swift.struct/subjectAlternativeNames``
- ``Certificate/Extensions-swift.struct/subjectKeyIdentifier``

This lets us reduce the above code to a single line:

```swift
let unwrappedSanExtension = try certificate.extensions.subjectAlternativeName
```

This shorthand is most useful when searching for a specific extension. When attempting to work with an entire certificate,
users are encouraged to iterate the ``Certificate/Extensions-swift.struct`` and unwrap each extension in turn. This ensures
that unknown or invalid ``Certificate/Extension/critical`` extensions are not incorrectly tolerated.

### Signatures

All X.509 certificates are signed by their issuer. The issuer calculates the signature over a specific sub-portion
of the certificate called the `TBSCertificate`. The signature is then appended to the `TBSCertificate` along with an
identifier of what the signature algorithm was.

These fields are all exposed on the ``Certificate`` type. The signature itself is represented by ``Certificate/signature-swift.property``,
an opaque object (``Certificate/Signature-swift.struct``) that encapsulates a specific signature field. The signature algorithm is also
represented by ``Certificate/signatureAlgorithm-swift.property`` which stores a simple identifier for the algorithm in question.

The bytes to be signed are also present in the ``Certificate/tbsCertificateBytes`` field. For parsed certificates, these bytes are
the raw certificate bytes as parsed from the wire, which guarantees that there is no encode/decode misrepresentation in these bytes.

Together these objects make it possible to validate that a signature was correctly signed, using
``Certificate/PublicKey-swift.struct/isValidSignature(_:for:)``.

> Warning: While this is a necessary condition for determining the issuer of a certificate, it is not a sufficient one.
> Users are strongly discouraged from hand-rolling their own verification logic, and should instead prefer using an
> existing verifier. Wherever possible, prefer to use the platform verifier.