Browse Source

Merge "Add certificate validation"

Jenkins 1 year ago
parent
commit
ad25a4016c

+ 350
- 0
cursive/certificate_utils.py View File

@@ -0,0 +1,350 @@
1
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
2
+#    not use this file except in compliance with the License. You may obtain
3
+#    a copy of the License at
4
+#
5
+#         http://www.apache.org/licenses/LICENSE-2.0
6
+#
7
+#    Unless required by applicable law or agreed to in writing, software
8
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10
+#    License for the specific language governing permissions and limitations
11
+#    under the License.
12
+
13
+"""Support certificate validation."""
14
+
15
+from cryptography.hazmat.primitives.asymmetric import ec
16
+from cryptography.hazmat.primitives.asymmetric import padding
17
+from cryptography.hazmat.primitives.asymmetric import rsa
18
+from cryptography import x509, exceptions as cryptography_exceptions
19
+from oslo_log import log as logging
20
+from oslo_utils import timeutils
21
+
22
+from cursive import exception
23
+from cursive import signature_utils
24
+
25
+LOG = logging.getLogger(__name__)
26
+
27
+
28
+def is_within_valid_dates(certificate):
29
+    """Determine if the certificate is outside its valid date range.
30
+
31
+    :param certificate: the cryptography certificate object
32
+    :return: False if the certificate valid time range does not include
33
+             now, True otherwise.
34
+    """
35
+    # Get now in UTC, since certificate returns times in UTC
36
+    now = timeutils.utcnow()
37
+
38
+    # Confirm the certificate valid time range includes now
39
+    if now < certificate.not_valid_before:
40
+        return False
41
+    elif now > certificate.not_valid_after:
42
+        return False
43
+    return True
44
+
45
+
46
+def is_issuer(issuing_certificate, issued_certificate):
47
+    """Determine if the issuing cert is the parent of the issued cert.
48
+
49
+    Determine if the issuing certificate is the parent of the issued
50
+    certificate by:
51
+    * conducting subject and issuer name matching, and
52
+    * verifying the signature of the issued certificate with the issuing
53
+      certificate's public key
54
+
55
+    :param issuing_certificate: the cryptography certificate object that
56
+           is the potential parent of the issued certificate
57
+    :param issued_certificate: the cryptography certificate object that
58
+           is the potential child of the issuing certificate
59
+    :return: True if the issuing certificate is the parent of the issued
60
+             certificate, False otherwise.
61
+    """
62
+    if (issuing_certificate is None) or (issued_certificate is None):
63
+        return False
64
+    elif issuing_certificate.subject != issued_certificate.issuer:
65
+        return False
66
+    else:
67
+        try:
68
+            verify_certificate_signature(
69
+                issuing_certificate,
70
+                issued_certificate
71
+            )
72
+        except cryptography_exceptions.InvalidSignature:
73
+            # If verification fails, an exception is expected.
74
+            return False
75
+        return True
76
+
77
+
78
+def can_sign_certificates(certificate, certificate_uuid=''):
79
+    """Determine if the certificate can sign other certificates.
80
+
81
+    :param certificate: the cryptography certificate object
82
+    :param certificate_uuid: the uuid of the certificate
83
+    :return: False if the certificate cannot sign other certificates,
84
+             True otherwise.
85
+    """
86
+    try:
87
+        basic_constraints = certificate.extensions.get_extension_for_oid(
88
+            x509.oid.ExtensionOID.BASIC_CONSTRAINTS
89
+        ).value
90
+    except x509.extensions.ExtensionNotFound:
91
+        LOG.debug(
92
+            "Certificate '%s' does not have a basic constraints extension.",
93
+            certificate_uuid)
94
+        return False
95
+
96
+    try:
97
+        key_usage = certificate.extensions.get_extension_for_oid(
98
+            x509.oid.ExtensionOID.KEY_USAGE
99
+        ).value
100
+    except x509.extensions.ExtensionNotFound:
101
+        LOG.debug(
102
+            "Certificate '%s' does not have a key usage extension.",
103
+            certificate_uuid)
104
+        return False
105
+
106
+    if basic_constraints.ca and key_usage.key_cert_sign:
107
+        return True
108
+
109
+    if not basic_constraints.ca:
110
+        LOG.debug(
111
+            "Certificate '%s' is not marked as a CA in its basic constraints "
112
+            "extension.",
113
+            certificate_uuid)
114
+    if not key_usage.key_cert_sign:
115
+        LOG.debug(
116
+            "Certificate '%s' is not marked for verifying certificate "
117
+            "signatures in its key usage extension.",
118
+            certificate_uuid)
119
+
120
+    return False
121
+
122
+
123
+def verify_certificate_signature(signing_certificate, certificate):
124
+    """Verify that the certificate was signed correctly.
125
+
126
+    :param signing_certificate: the cryptography certificate object used to
127
+           sign the certificate
128
+    :param certificate: the cryptography certificate object that was signed
129
+           by the signing certificate
130
+    :raises: cryptography.exceptions.InvalidSignature if certificate signature
131
+             verification fails.
132
+    """
133
+    signature_hash_algorithm = certificate.signature_hash_algorithm
134
+    signature_bytes = certificate.signature
135
+    signer_public_key = signing_certificate.public_key()
136
+
137
+    if isinstance(signer_public_key, rsa.RSAPublicKey):
138
+        verifier = signer_public_key.verifier(
139
+            signature_bytes, padding.PKCS1v15(), signature_hash_algorithm
140
+        )
141
+    elif isinstance(signer_public_key, ec.EllipticCurvePublicKey):
142
+        verifier = signer_public_key.verifier(
143
+            signature_bytes, ec.ECDSA(signature_hash_algorithm)
144
+        )
145
+    else:
146
+        verifier = signer_public_key.verifier(
147
+            signature_bytes, signature_hash_algorithm
148
+        )
149
+
150
+    verifier.update(certificate.tbs_certificate_bytes)
151
+    verifier.verify()
152
+
153
+
154
+def verify_certificate(context, certificate_uuid,
155
+                       trusted_certificate_uuids,
156
+                       enforce_valid_dates=True,
157
+                       enforce_signing_extensions=True,
158
+                       enforce_path_length=True):
159
+    """Validate a certificate against a set of trusted certificates.
160
+
161
+    From the key manager, load the set of trusted certificates and the
162
+    certificate to validate. Store the trusted certificates in a certificate
163
+    verification context. Use the context to verify that the certificate is
164
+    cryptographically linked to at least one of the trusted certificates.
165
+
166
+    :param context: the user context for authentication
167
+    :param certificate_uuid: the uuid of a certificate to validate, stored in
168
+           the key manager
169
+    :param trusted_certificate_uuids: a list containing the uuids of trusted
170
+           certificates stored in the key manager
171
+    :param enforce_valid_dates: a boolean indicating whether date checking
172
+           should be enforced during certificate verification, defaults to
173
+           True
174
+    :param enforce_signing_extensions: a boolean indicating whether extension
175
+           checking should be enforced during certificate verification,
176
+           defaults to True
177
+    :param enforce_path_length: a boolean indicating whether path length
178
+           constraints should be enforced during certificate verification,
179
+           defaults to True
180
+    :raises: SignatureVerificationError if the certificate verification fails
181
+             for any reason.
182
+    """
183
+    trusted_certificates = list()
184
+    for uuid in trusted_certificate_uuids:
185
+        try:
186
+            trusted_certificates.append(
187
+                (uuid, signature_utils.get_certificate(context, uuid))
188
+            )
189
+        except exception.SignatureVerificationError:
190
+            LOG.warning("Skipping trusted certificate: %(id)s" % {'id': uuid})
191
+
192
+    certificate = signature_utils.get_certificate(context, certificate_uuid)
193
+    certificate_context = CertificateVerificationContext(
194
+        trusted_certificates,
195
+        enforce_valid_dates=enforce_valid_dates,
196
+        enforce_signing_extensions=enforce_signing_extensions,
197
+        enforce_path_length=enforce_path_length
198
+    )
199
+    certificate_context.update(certificate)
200
+    certificate_context.verify()
201
+
202
+
203
+class CertificateVerificationContext(object):
204
+    """A collection of signing certificates.
205
+
206
+    A collection of signing certificates that may be used to verify the
207
+    signatures of other certificates.
208
+    """
209
+
210
+    def __init__(self, certificate_tuples, enforce_valid_dates=True,
211
+                 enforce_signing_extensions=True,
212
+                 enforce_path_length=True):
213
+        self._signing_certificates = []
214
+        for certificate_tuple in certificate_tuples:
215
+            certificate_uuid, certificate = certificate_tuple
216
+            if not isinstance(certificate, x509.Certificate):
217
+                LOG.error(
218
+                    "A signing certificate must be an x509.Certificate object."
219
+                )
220
+                continue
221
+
222
+            if enforce_valid_dates:
223
+                if not is_within_valid_dates(certificate):
224
+                    LOG.warning(
225
+                        "Certificate '%s' is outside its valid date range and "
226
+                        "cannot be used as a signing certificate.",
227
+                        certificate_uuid)
228
+                    continue
229
+
230
+            if enforce_signing_extensions:
231
+                if not can_sign_certificates(certificate, certificate_uuid):
232
+                    LOG.warning(
233
+                        "Certificate '%s' is not configured to act as a "
234
+                        "signing certificate. It will not be used as a "
235
+                        "signing certificate.",
236
+                        certificate_uuid)
237
+                    continue
238
+            self._signing_certificates.append(certificate_tuple)
239
+
240
+        self._signed_certificate = None
241
+        self._enforce_valid_dates = enforce_valid_dates
242
+        self._enforce_path_length = enforce_path_length
243
+
244
+    def update(self, certificate):
245
+        """Process the certificate to be verified.
246
+
247
+        Raises an exception if the certificate is invalid. Stores it
248
+        otherwise.
249
+
250
+        :param certificate: the cryptography certificate to be verified
251
+        :raises: SignatureVerificationError if the certificate is not of the
252
+                 right type or if it is outside its valid date range.
253
+        """
254
+        if not isinstance(certificate, x509.Certificate):
255
+            raise exception.SignatureVerificationError(
256
+                "The certificate must be an x509.Certificate object."
257
+            )
258
+
259
+        if self._enforce_valid_dates:
260
+            if not is_within_valid_dates(certificate):
261
+                raise exception.SignatureVerificationError(
262
+                    "The certificate is outside its valid date range."
263
+                )
264
+
265
+        self._signed_certificate = certificate
266
+
267
+    def verify(self):
268
+        """Locate the certificate's signing certificate and verify it.
269
+
270
+        Locate the certificate's signing certificate in the context
271
+        certificate cache, using both subject/issuer name matching and
272
+        signature verification. If the certificate is self-signed, verify that
273
+        it is also located in the context's certificate cache. Construct the
274
+        certificate chain from certificates in the context certificate cache.
275
+        Verify that the signing certificate can have a sufficient number of
276
+        child certificates to support the chain.
277
+
278
+        :raises: SignatureVerificationError if certificate validation fails
279
+                 for any reason, including mismatched signatures or a failure
280
+                 to find the required signing certificate.
281
+        """
282
+        signed_certificate = self._signed_certificate
283
+        certificate_chain = [('base', signed_certificate)]
284
+
285
+        # Build the certificate chain.
286
+        while True:
287
+            signing_certificate_tuple = None
288
+
289
+            # Search for the signing certificate
290
+            for certificate_tuple in self._signing_certificates:
291
+                _, candidate = certificate_tuple
292
+                if is_issuer(candidate, signed_certificate):
293
+                    signing_certificate_tuple = certificate_tuple
294
+                    break
295
+
296
+            # If a valid signing certificate is found, prepare to find the
297
+            # next link in the certificate chain. Otherwise, raise an error.
298
+            if signing_certificate_tuple:
299
+                # If the certificate is self-signed, the root of the
300
+                # certificate chain has been found. Otherwise, repeat the
301
+                # verification process using the newly found signing
302
+                # certificate.
303
+                if signed_certificate == signing_certificate_tuple[1]:
304
+                    break
305
+                else:
306
+                    certificate_chain.insert(0, signing_certificate_tuple)
307
+                    signed_certificate = signing_certificate_tuple[1]
308
+            else:
309
+                uuid = certificate_chain[0][0]
310
+                raise exception.SignatureVerificationError(
311
+                    "Certificate chain building failed. Could not locate the "
312
+                    "signing certificate for %s in the set of trusted "
313
+                    "certificates." %
314
+                    "the base certificate" if uuid == 'base'
315
+                    else "certificate '%s'" % uuid
316
+                )
317
+
318
+        if self._enforce_path_length:
319
+            # Verify that each certificate's path length constraint allows
320
+            # for it to support the rest of the certificate chain.
321
+            for i in range(len(certificate_chain)):
322
+                certificate = certificate_chain[i][1]
323
+
324
+                # No need to check the last certificate in the chain.
325
+                if certificate == certificate_chain[-1][1]:
326
+                    break
327
+
328
+                try:
329
+                    constraints = certificate.extensions.get_extension_for_oid(
330
+                        x509.oid.ExtensionOID.BASIC_CONSTRAINTS
331
+                    ).value
332
+                except x509.extensions.ExtensionNotFound:
333
+                    raise exception.SignatureVerificationError(
334
+                        "Certificate validation failed. The signing "
335
+                        "certificate '%s' does not have a basic constraints "
336
+                        "extension." % certificate_chain[i][0]
337
+                    )
338
+
339
+                # Path length only applies to non-self-issued intermediate
340
+                # certificates. Do not include the current or end certificates
341
+                # when computing path length.
342
+                chain_length = len(certificate_chain[i:])
343
+                chain_length = (chain_length - 2) if chain_length > 2 else 0
344
+                if constraints.path_length < chain_length:
345
+                    raise exception.SignatureVerificationError(
346
+                        "Certificate validation failed. The signing "
347
+                        "certificate '%s' is not configured to support "
348
+                        "certificate chains of sufficient "
349
+                        "length." % certificate_chain[i][0]
350
+                    )

+ 1
- 25
cursive/signature_utils.py View File

@@ -26,7 +26,6 @@ from cryptography import x509
26 26
 from oslo_log import log as logging
27 27
 from oslo_serialization import base64
28 28
 from oslo_utils import encodeutils
29
-from oslo_utils import timeutils
30 29
 
31 30
 from cursive import exception
32 31
 from cursive.i18n import _, _LE
@@ -70,6 +69,7 @@ MASK_GEN_ALGORITHMS = {
70 69
     'MGF1': padding.MGF1,
71 70
 }
72 71
 
72
+
73 73
 # Required image property names
74 74
 (SIGNATURE, HASH_METHOD, KEY_TYPE, CERT_UUID) = (
75 75
     'img_signature',
@@ -336,28 +336,4 @@ def get_certificate(context, signature_certificate_uuid):
336 336
         certificate = x509.load_der_x509_certificate(cert_data,
337 337
                                                      default_backend())
338 338
 
339
-    # verify the certificate
340
-    verify_certificate(certificate)
341
-
342 339
     return certificate
343
-
344
-
345
-def verify_certificate(certificate):
346
-    """Verify that the certificate has not expired.
347
-
348
-    :param certificate: the cryptography certificate object
349
-    :raises: SignatureVerificationError if the certificate valid time range
350
-             does not include now
351
-    """
352
-    # Get now in UTC, since certificate returns times in UTC
353
-    now = timeutils.utcnow()
354
-
355
-    # Confirm the certificate valid time range includes now
356
-    if now < certificate.not_valid_before:
357
-        raise exception.SignatureVerificationError(
358
-            reason=_('Certificate is not valid before: %s UTC')
359
-            % certificate.not_valid_before)
360
-    elif now > certificate.not_valid_after:
361
-        raise exception.SignatureVerificationError(
362
-            reason=_('Certificate is not valid after: %s UTC')
363
-            % certificate.not_valid_after)

+ 87
- 0
cursive/tests/unit/data/child_cert.pem View File

@@ -0,0 +1,87 @@
1
+Certificate:
2
+    Data:
3
+        Version: 3 (0x2)
4
+        Serial Number: 10 (0xa)
5
+    Signature Algorithm: sha256WithRSAEncryption
6
+        Issuer: C=US, ST=Test, L=Test, O=Test, CN=Test Parent
7
+        Validity
8
+            Not Before: Oct  3 18:02:45 2017 GMT
9
+            Not After : Oct  1 18:02:45 2027 GMT
10
+        Subject: C=US, ST=Test, L=Test, O=Test, CN=Test Child
11
+        Subject Public Key Info:
12
+            Public Key Algorithm: rsaEncryption
13
+                Public-Key: (2048 bit)
14
+                Modulus:
15
+                    00:b2:45:31:e4:99:12:1f:0f:c3:5b:78:98:39:a5:
16
+                    d0:da:c6:9f:38:23:09:df:fd:35:b6:95:b6:37:5d:
17
+                    b6:49:f2:a5:f1:62:75:62:41:09:9d:36:e5:53:c8:
18
+                    82:1a:5c:9d:2a:fd:03:9c:a9:00:6d:28:b3:29:bb:
19
+                    cf:f3:eb:0f:5c:c9:81:8d:69:e1:04:f7:9a:1c:09:
20
+                    33:ab:54:c1:ac:0c:d7:d1:11:79:6c:6f:c0:2b:54:
21
+                    9e:c2:86:85:05:a3:e4:70:06:84:42:eb:8b:c0:0e:
22
+                    3a:73:16:cd:13:79:a5:43:e6:89:8b:c3:7f:6b:04:
23
+                    cd:7f:34:6b:4a:47:65:c3:4a:6a:d3:ea:8e:57:34:
24
+                    5d:39:18:fc:d0:8e:e4:f6:ff:74:86:a0:98:06:67:
25
+                    40:0c:8f:a6:5e:46:9d:ed:b9:25:99:7c:4c:62:b8:
26
+                    19:ae:12:1e:33:0b:d3:43:b9:3c:bc:5a:f3:6b:c6:
27
+                    a9:1c:c1:ce:99:1f:64:b7:a3:8d:ed:c8:3e:95:75:
28
+                    19:e5:ce:51:f1:11:f1:c0:58:76:87:ee:42:12:a4:
29
+                    ff:8e:c6:e8:42:3d:b4:df:c7:be:a6:c7:ea:6c:88:
30
+                    04:4b:d3:f3:9b:7f:d4:db:87:21:55:36:2e:3c:1c:
31
+                    c9:21:4a:2f:7f:51:f0:08:d7:21:ea:75:c4:e2:78:
32
+                    91:9d
33
+                Exponent: 65537 (0x10001)
34
+        X509v3 extensions:
35
+            X509v3 Subject Key Identifier: 
36
+                C2:03:EA:FC:7E:70:7F:34:21:C1:BE:33:0E:8A:E0:7E:C6:A2:21:1B
37
+            X509v3 Authority Key Identifier: 
38
+                keyid:7A:BE:7D:09:5A:5F:5C:DE:CC:82:1A:3B:FE:A8:ED:CA:BA:16:58:49
39
+
40
+            X509v3 Basic Constraints: 
41
+                CA:TRUE, pathlen:0
42
+            X509v3 Key Usage: 
43
+                Certificate Sign, CRL Sign
44
+            X509v3 Subject Alternative Name: 
45
+                DNS:example.com, DNS:www.example.com, DNS:mail.example.com, DNS:ftp.example.com
46
+            Netscape Comment: 
47
+                OpenSSL Generated Certificate
48
+    Signature Algorithm: sha256WithRSAEncryption
49
+         10:46:2e:1e:37:b8:10:4a:8c:e3:76:7c:05:57:76:34:05:0b:
50
+         e2:ed:b3:1b:28:20:2b:56:9b:2d:59:70:e5:4e:5e:ce:a8:11:
51
+         d5:c1:9b:e7:c8:0e:61:2b:63:ae:d2:1b:ec:cf:75:31:d0:4f:
52
+         35:86:c2:51:22:64:c3:07:a7:c4:6b:13:57:cc:e5:d9:86:8d:
53
+         b4:73:45:c5:ca:48:b7:b6:02:1e:c7:de:71:c6:5f:2a:64:7d:
54
+         b5:5b:16:9a:27:7d:5f:3c:8a:5e:95:38:7f:c0:7e:d4:39:3f:
55
+         36:60:7d:7d:8e:9f:72:06:d4:69:7a:e5:45:3f:e2:c9:eb:7f:
56
+         5f:74:1a:6b:6c:b8:a1:08:05:d9:25:ee:d4:97:db:5a:72:1f:
57
+         4a:06:a9:86:76:41:58:34:0b:5a:39:be:65:ec:26:b1:13:41:
58
+         6b:86:58:fa:2e:cd:ab:06:d2:59:0e:bb:e4:44:2c:de:21:d1:
59
+         8c:9c:93:a5:d5:ae:fc:af:37:b0:91:1f:46:61:28:b9:a5:c8:
60
+         b4:3c:28:33:b1:d9:ca:49:53:fe:14:80:82:de:06:c1:ab:21:
61
+         e7:44:76:04:d8:85:b4:60:72:30:7a:28:b7:6f:4d:9e:52:70:
62
+         21:df:4e:71:aa:01:d6:ba:fa:4b:4a:61:75:9c:57:67:a6:b2:
63
+         e7:ab:24:6c
64
+-----BEGIN CERTIFICATE-----
65
+MIID9jCCAt6gAwIBAgIBCjANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEN
66
+MAsGA1UECAwEVGVzdDENMAsGA1UEBwwEVGVzdDENMAsGA1UECgwEVGVzdDEUMBIG
67
+A1UEAwwLVGVzdCBQYXJlbnQwHhcNMTcxMDAzMTgwMjQ1WhcNMjcxMDAxMTgwMjQ1
68
+WjBPMQswCQYDVQQGEwJVUzENMAsGA1UECAwEVGVzdDENMAsGA1UEBwwEVGVzdDEN
69
+MAsGA1UECgwEVGVzdDETMBEGA1UEAwwKVGVzdCBDaGlsZDCCASIwDQYJKoZIhvcN
70
+AQEBBQADggEPADCCAQoCggEBALJFMeSZEh8Pw1t4mDml0NrGnzgjCd/9NbaVtjdd
71
+tknypfFidWJBCZ025VPIghpcnSr9A5ypAG0osym7z/PrD1zJgY1p4QT3mhwJM6tU
72
+wawM19EReWxvwCtUnsKGhQWj5HAGhELri8AOOnMWzRN5pUPmiYvDf2sEzX80a0pH
73
+ZcNKatPqjlc0XTkY/NCO5Pb/dIagmAZnQAyPpl5Gne25JZl8TGK4Ga4SHjML00O5
74
+PLxa82vGqRzBzpkfZLejje3IPpV1GeXOUfER8cBYdofuQhKk/47G6EI9tN/HvqbH
75
+6myIBEvT85t/1NuHIVU2LjwcySFKL39R8AjXIep1xOJ4kZ0CAwEAAaOB2zCB2DAd
76
+BgNVHQ4EFgQUwgPq/H5wfzQhwb4zDorgfsaiIRswHwYDVR0jBBgwFoAUer59CVpf
77
+XN7Mgho7/qjtyroWWEkwDwYDVR0TBAgwBgEB/wIBADALBgNVHQ8EBAMCAQYwSgYD
78
+VR0RBEMwQYILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNvbYIQbWFpbC5leGFt
79
+cGxlLmNvbYIPZnRwLmV4YW1wbGUuY29tMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM
80
+IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAQEAEEYuHje4
81
+EEqM43Z8BVd2NAUL4u2zGyggK1abLVlw5U5ezqgR1cGb58gOYStjrtIb7M91MdBP
82
+NYbCUSJkwwenxGsTV8zl2YaNtHNFxcpIt7YCHsfeccZfKmR9tVsWmid9XzyKXpU4
83
+f8B+1Dk/NmB9fY6fcgbUaXrlRT/iyet/X3Qaa2y4oQgF2SXu1JfbWnIfSgaphnZB
84
+WDQLWjm+ZewmsRNBa4ZY+i7NqwbSWQ675EQs3iHRjJyTpdWu/K83sJEfRmEouaXI
85
+tDwoM7HZyklT/hSAgt4Gwash50R2BNiFtGByMHoot29NnlJwId9OcaoB1rr6S0ph
86
+dZxXZ6ay56skbA==
87
+-----END CERTIFICATE-----

+ 87
- 0
cursive/tests/unit/data/grandchild_cert.pem View File

@@ -0,0 +1,87 @@
1
+Certificate:
2
+    Data:
3
+        Version: 3 (0x2)
4
+        Serial Number: 11 (0xb)
5
+    Signature Algorithm: sha256WithRSAEncryption
6
+        Issuer: C=US, ST=Test, L=Test, O=Test, CN=Test Child
7
+        Validity
8
+            Not Before: Oct  3 18:09:07 2017 GMT
9
+            Not After : Oct  1 18:09:07 2027 GMT
10
+        Subject: C=US, ST=Test, L=Test, O=Test, CN=Test Grandchild
11
+        Subject Public Key Info:
12
+            Public Key Algorithm: rsaEncryption
13
+                Public-Key: (2048 bit)
14
+                Modulus:
15
+                    00:bd:3c:4b:2a:e8:03:5d:07:ae:94:f4:19:ed:00:
16
+                    21:20:dd:1c:54:f1:dc:44:d8:bf:66:b4:bf:ce:21:
17
+                    7b:bf:b4:15:7b:b3:4f:0e:d5:ef:fa:f1:31:ab:2a:
18
+                    22:78:72:20:7d:ce:58:c3:45:0d:2f:5c:23:7c:87:
19
+                    07:bf:ee:8c:8c:9f:ae:31:70:19:61:dc:92:b5:8f:
20
+                    fb:36:16:1c:08:d4:2c:c0:0c:86:e0:ee:a8:31:20:
21
+                    21:16:41:b2:78:bc:88:a8:ef:4c:3a:34:4f:a0:08:
22
+                    25:e7:35:e8:bc:66:d3:c3:b5:2a:05:34:91:b0:d0:
23
+                    ae:02:f2:a1:58:22:af:43:42:d8:40:82:0c:e3:26:
24
+                    72:22:06:d2:b1:13:87:04:83:70:f6:b0:99:39:bf:
25
+                    79:26:f6:e2:ff:24:c3:72:48:9f:68:0a:c1:c9:aa:
26
+                    b1:a8:b4:f7:cf:44:38:4a:77:bf:56:20:fa:7e:08:
27
+                    75:26:04:fb:5e:d5:4f:ff:b8:45:1f:80:12:fb:7e:
28
+                    61:7e:52:f0:dc:71:ee:72:91:27:fa:60:93:96:e5:
29
+                    78:1d:d9:fd:5a:b8:00:b9:97:46:12:b5:2a:93:0e:
30
+                    c3:1b:30:6e:b2:67:5d:c5:ca:40:3f:36:0c:7c:4f:
31
+                    d4:48:e0:1f:32:a9:28:0c:37:35:7c:5d:42:f5:cb:
32
+                    54:b9
33
+                Exponent: 65537 (0x10001)
34
+        X509v3 extensions:
35
+            X509v3 Subject Key Identifier: 
36
+                4F:6C:A8:1F:80:F0:A6:EE:41:85:B9:A2:3F:EC:3A:B2:93:B4:0E:86
37
+            X509v3 Authority Key Identifier: 
38
+                keyid:C2:03:EA:FC:7E:70:7F:34:21:C1:BE:33:0E:8A:E0:7E:C6:A2:21:1B
39
+
40
+            X509v3 Basic Constraints: 
41
+                CA:FALSE
42
+            X509v3 Key Usage: 
43
+                Digital Signature, Key Encipherment
44
+            X509v3 Subject Alternative Name: 
45
+                DNS:example.com, DNS:www.example.com, DNS:mail.example.com, DNS:ftp.example.com
46
+            Netscape Comment: 
47
+                OpenSSL Generated Certificate
48
+    Signature Algorithm: sha256WithRSAEncryption
49
+         ae:a7:62:9e:f6:b7:e3:02:84:0f:fe:c6:7c:c1:0b:74:8e:95:
50
+         c3:2e:e9:5f:c0:8b:fc:79:45:53:5c:34:9d:b0:de:e6:cf:ed:
51
+         52:4c:3f:6a:3f:e9:8d:a3:58:d4:ae:4d:31:30:57:d5:31:f9:
52
+         a2:ed:82:e2:ae:1a:65:a5:ab:de:64:35:c9:0b:d1:86:b0:83:
53
+         57:8a:e4:ca:21:d5:9a:79:5b:44:42:ff:52:9a:51:b6:f4:6e:
54
+         f1:da:dd:3b:ca:12:cb:4c:e5:9f:a5:12:4f:13:99:85:79:c8:
55
+         00:3b:2c:25:7f:02:07:a3:4e:59:0b:4d:8e:f8:43:08:a9:91:
56
+         30:0a:17:1c:ff:91:c0:16:d5:c0:1e:ec:a5:24:c8:cc:f0:2c:
57
+         0e:30:b9:bb:34:11:83:e7:4d:02:e4:2d:2a:90:98:eb:d8:ae:
58
+         7b:2f:19:31:db:63:fc:0c:0b:47:f5:8f:7b:cf:99:0b:30:91:
59
+         a6:44:19:51:7f:15:4f:ab:8c:08:e2:bd:91:42:e4:e7:88:8e:
60
+         c0:ea:fd:09:ac:96:c6:14:ef:0e:7d:75:6a:05:b0:b5:4d:43:
61
+         60:62:31:85:61:cb:c3:0f:81:24:d6:de:10:42:54:ff:c0:63:
62
+         95:40:3d:89:52:f9:00:2a:a5:74:1c:b1:42:be:a1:2f:de:90:
63
+         cb:d5:a7:3d
64
+-----BEGIN CERTIFICATE-----
65
+MIID9DCCAtygAwIBAgIBCzANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEN
66
+MAsGA1UECAwEVGVzdDENMAsGA1UEBwwEVGVzdDENMAsGA1UECgwEVGVzdDETMBEG
67
+A1UEAwwKVGVzdCBDaGlsZDAeFw0xNzEwMDMxODA5MDdaFw0yNzEwMDExODA5MDda
68
+MFQxCzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARUZXN0MQ0wCwYDVQQHDARUZXN0MQ0w
69
+CwYDVQQKDARUZXN0MRgwFgYDVQQDDA9UZXN0IEdyYW5kY2hpbGQwggEiMA0GCSqG
70
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9PEsq6ANdB66U9BntACEg3RxU8dxE2L9m
71
+tL/OIXu/tBV7s08O1e/68TGrKiJ4ciB9zljDRQ0vXCN8hwe/7oyMn64xcBlh3JK1
72
+j/s2FhwI1CzADIbg7qgxICEWQbJ4vIio70w6NE+gCCXnNei8ZtPDtSoFNJGw0K4C
73
+8qFYIq9DQthAggzjJnIiBtKxE4cEg3D2sJk5v3km9uL/JMNySJ9oCsHJqrGotPfP
74
+RDhKd79WIPp+CHUmBPte1U//uEUfgBL7fmF+UvDcce5ykSf6YJOW5Xgd2f1auAC5
75
+l0YStSqTDsMbMG6yZ13FykA/Ngx8T9RI4B8yqSgMNzV8XUL1y1S5AgMBAAGjgdUw
76
+gdIwHQYDVR0OBBYEFE9sqB+A8KbuQYW5oj/sOrKTtA6GMB8GA1UdIwQYMBaAFMID
77
+6vx+cH80IcG+Mw6K4H7GoiEbMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMEoGA1Ud
78
+EQRDMEGCC2V4YW1wbGUuY29tgg93d3cuZXhhbXBsZS5jb22CEG1haWwuZXhhbXBs
79
+ZS5jb22CD2Z0cC5leGFtcGxlLmNvbTAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBH
80
+ZW5lcmF0ZWQgQ2VydGlmaWNhdGUwDQYJKoZIhvcNAQELBQADggEBAK6nYp72t+MC
81
+hA/+xnzBC3SOlcMu6V/Ai/x5RVNcNJ2w3ubP7VJMP2o/6Y2jWNSuTTEwV9Ux+aLt
82
+guKuGmWlq95kNckL0Yawg1eK5Moh1Zp5W0RC/1KaUbb0bvHa3TvKEstM5Z+lEk8T
83
+mYV5yAA7LCV/AgejTlkLTY74QwipkTAKFxz/kcAW1cAe7KUkyMzwLA4wubs0EYPn
84
+TQLkLSqQmOvYrnsvGTHbY/wMC0f1j3vPmQswkaZEGVF/FU+rjAjivZFC5OeIjsDq
85
+/QmslsYU7w59dWoFsLVNQ2BiMYVhy8MPgSTW3hBCVP/AY5VAPYlS+QAqpXQcsUK+
86
+oS/ekMvVpz0=
87
+-----END CERTIFICATE-----

+ 34
- 0
cursive/tests/unit/data/grandparent_cert.pem View File

@@ -0,0 +1,34 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIIF7jCCA9agAwIBAgIJANHiL5B0pUVmMA0GCSqGSIb3DQEBCwUAMIGDMQswCQYD
3
+VQQGEwJVUzENMAsGA1UECAwEVGVzdDENMAsGA1UEBwwEVGVzdDENMAsGA1UECgwE
4
+VGVzdDENMAsGA1UECwwEVGVzdDEZMBcGA1UEAwwQVGVzdCBHcmFuZHBhcmVudDEd
5
+MBsGCSqGSIb3DQEJARYOZ3BAZXhhbXBsZS5jb20wHhcNMTcxMDAzMTc0NzMyWhcN
6
+MTcxMTAyMTc0NzMyWjCBgzELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFRlc3QxDTAL
7
+BgNVBAcMBFRlc3QxDTALBgNVBAoMBFRlc3QxDTALBgNVBAsMBFRlc3QxGTAXBgNV
8
+BAMMEFRlc3QgR3JhbmRwYXJlbnQxHTAbBgkqhkiG9w0BCQEWDmdwQGV4YW1wbGUu
9
+Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAn4m1O+fffNTSnGE5
10
+MPac07jjMNrKHEjARS4aM222C8wCPiXXrs1diTQlvtxrLFOzc0gtCH6xVl59Zcis
11
+H7kBf8oV9wNIHcfy2BmGkP7Wv68p3UsIF1IzGSHPyKJWG+l/xNexuXFaVG9y+siu
12
+5Z3bx9DMBPFfXalxwRGoS0fyBOG/tXlqicf/aojF1U3UolML58URQqQ7IvGjEq22
13
+iqAfduEwLlLb99iJ8uiFgO6Rl/hwxvy9gmrGWJGHpHQKJ2Dx37Zc8MMcAJ5yos7c
14
+GAs3e31TvRJgyEBcPKtl+xmh36wNC+V1KKRYKAfENqB6v7b1GDZrVtH7uHvSPCDQ
15
+ccKklF2thomO4cm6UpbCF7/5i50OLwtr1TcUI/YT+nR/YsCuc/PKdyXITpP0CNR9
16
+Wcw6pb5LsWgLisFh4my4PzTbwTcPGFUJHq0CUUsRP1YijMNRtiZsoMJwspMZyg9d
17
+9Ufxf7HjbugazUfvfIKMfX/s/pIZLiDLx5lV9RbHmjvlCt4OfH3FhqBveguqDLq7
18
+LbBC6tYm1E21izUqXy4Zh+oogvZeZGUBQL/JUJ6XOkUnffv1nf2HDsCmlW64NLce
19
+9gc25BtXAAf5/tMQL2J3t5SZ+Ladk+nklQXz6eClFcLEbRcd15bZ3QFXCajeLWcP
20
+Y5TZFgDQFFr7/FjDhr2bByMOJEkCAwEAAaNjMGEwHQYDVR0OBBYEFKfrobRNKmLV
21
+OJDtRPp3f/m66pkEMB8GA1UdIwQYMBaAFKfrobRNKmLVOJDtRPp3f/m66pkEMBIG
22
+A1UdEwEB/wQIMAYBAf8CAQEwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IC
23
+AQByRvLd0xO238AwgDep4hBshcFH9tcSg5hDPFFeJWRnC4c63vNGXMVV7iEAoPSH
24
+LPbuELCIRqiZFYW5A8Olv2MGGZ3kjiFrWYfbLFU0/z1y82/E2NtM65cvOQKYxMk1
25
+HBmaGF8s43LdDiGUZ0yFMTwe+da+zWcfDPgSYml36ReCsn2dGFmfkPFhqSf810kI
26
+yl2EKQnjEf51AGDfrA6fmEafsQy8eFf0uH6cR8nrsa+0aXIkTHZ9erXrXujD30iL
27
+9M4T3uW/0Qk/kqSN3wUgHYWDBRyTKxCDPMiEixXDq21Jm1VzSKJAFE+xEuFHtqXl
28
+nfZQCzihdx3ckZnH3qfrJt0V0cu6qSNr6sbyrb7FVO8aCNyumdCDM9VdJ64UFAme
29
+Xd/1/195PcoFOVEokoH15EH0mPr+/DDWA39c+FaRHH0A3LmuzX/P5rTRLO3wldpL
30
+XiZkLrfG43UNq3PIdh3YZEabpFcQYTmab7N8nZnmoMRM6YoEnHjdqPcDv3xs4gJS
31
+U24bVnFqzgSW3V1GfZGnlQyGXFrlrU+wWJ55eJ59ucQn8PDYlrSz7+x9RiqoFZcQ
32
+c9L8j7dFMBx6zI4dI5Ddx5q9KNtxPJb4Hk9HEd4C5OQ0qqdBR/hSD9mDjBpqEyc8
33
+aXIzmrTpGm7A9lbyXCEaOzN+2Jvdq5KtWh/halEgqgToqQ==
34
+-----END CERTIFICATE-----

+ 1
- 0
cursive/tests/unit/data/not_a_cert.txt View File

@@ -0,0 +1 @@
1
+This is not a certificate.

+ 24
- 0
cursive/tests/unit/data/orphaned_cert.pem View File

@@ -0,0 +1,24 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIID8zCCAtugAwIBAgIJAMDwfYBIkXEGMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD
3
+VQQGEwJVUzERMA8GA1UECAwIVGVzdG9uaWExDTALBgNVBAcMBFRlc3QxJDAiBgNV
4
+BAoMG0NvbXByZWhlbnNpdmUgVGVzdGluZywgSW5jLjEaMBgGA1UECwwRVGVzdGlu
5
+ZyBPdmVyc2lnaHQxHDAaBgNVBAMME1Rlc3RpbmcgQ2VydGlmaWNhdGUwHhcNMTYw
6
+NjMwMTcyOTUwWhcNMTcwNjMwMTcyOTUwWjCBjzELMAkGA1UEBhMCVVMxETAPBgNV
7
+BAgMCFRlc3RvbmlhMQ0wCwYDVQQHDARUZXN0MSQwIgYDVQQKDBtDb21wcmVoZW5z
8
+aXZlIFRlc3RpbmcsIEluYy4xGjAYBgNVBAsMEVRlc3RpbmcgT3ZlcnNpZ2h0MRww
9
+GgYDVQQDDBNUZXN0aW5nIENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOC
10
+AQ8AMIIBCgKCAQEAm+W78acV27U11m7E3iUUUGb+JXMW0okP8epD9OsLtVHxR+oq
11
+iOt19rgNIH/wJzaT+CnJ1jUerjzjFu2RwGhEr8Ph2KrWWQ7vxkhJzuXmKmGBZJm3
12
+FJcADrxcmZ8V3Yqxf3zO36Rg27jqDgxSy3uzxgO7ZXrkrJjrgrg+x8wVQ/pkhd8Y
13
+gQ/YQ2r1DF1GcpS/tSkCSc3lbIpCCHhORaRmHZXURML5q7vibLmc55Ad90WxtS1d
14
+WI8RAsWnQMvP1OmZcRcPKrUlRc/w+nIrxNF9HdeOweQv2tcnNlxBOcr6MwIL+Gle
15
+N4TmmthyVYCXxNWhW1VFA3atfEfmyEpiKIcQGwIDAQABo1AwTjAdBgNVHQ4EFgQU
16
+IkPrrGyB6+XUlWbd287uFbfCvkkwHwYDVR0jBBgwFoAUIkPrrGyB6+XUlWbd287u
17
+FbfCvkkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAMWi3C19pjWs9
18
+9hg1rTC0D/5C9K/0nmQ1pstVMXOKn9Z3ndUqRvLzxhHZHhQ/ATQwHKeSM2vpCmKa
19
+eV7PGivF6W+CAXJImvgNrsP2fMBnTsg2Q3hBHSIkTgwJxAHlYZ3NXSxWoDSuozvU
20
++qjRY3hMpYLSXpfGFKh73GHBNWXyjlo7pn+I4gAEoHOqDKTelOONz6PiKKi4Un2g
21
+j5FqmLZEq9VvzqSEC5VuFLZs4BpGmsKBM16+q+8JWMa025wNcdq4DxuNAkb3Zsty
22
+QZkgVYJgIeuEKOCubCQfDOya5W7ik3mtZZm9dFD5dZ3+CDB53a/AlKdi9YUAJOUW
23
+xBJzlRBlLg==
24
+-----END CERTIFICATE-----

+ 107
- 0
cursive/tests/unit/data/parent_cert.pem View File

@@ -0,0 +1,107 @@
1
+Certificate:
2
+    Data:
3
+        Version: 3 (0x2)
4
+        Serial Number: 9 (0x9)
5
+    Signature Algorithm: sha256WithRSAEncryption
6
+        Issuer: C=US, ST=Test, L=Test, O=Test, OU=Test, CN=Test Grandparent/emailAddress=gp@example.com
7
+        Validity
8
+            Not Before: Oct  3 17:58:30 2017 GMT
9
+            Not After : Oct  1 17:58:30 2027 GMT
10
+        Subject: C=US, ST=Test, L=Test, O=Test, CN=Test Parent
11
+        Subject Public Key Info:
12
+            Public Key Algorithm: rsaEncryption
13
+                Public-Key: (2048 bit)
14
+                Modulus:
15
+                    00:9f:9d:95:c4:a3:2f:37:52:e4:7c:cf:0b:0e:7f:
16
+                    14:69:63:1e:7a:cc:a8:19:a7:88:59:c8:17:f2:21:
17
+                    13:1b:45:21:fa:cc:93:40:71:cf:77:52:5a:1e:2e:
18
+                    5a:91:16:a9:67:3a:a3:6a:ea:cb:a2:bf:24:9b:8c:
19
+                    08:96:33:19:46:f9:7a:04:f9:c2:ee:87:f3:c3:23:
20
+                    73:37:59:0e:c0:71:f4:cd:0b:ad:23:63:51:0a:4f:
21
+                    dc:d2:9b:ab:ab:8a:99:07:d4:c8:c8:70:fd:18:73:
22
+                    25:0a:48:82:32:0d:64:46:b1:63:84:24:03:0b:3c:
23
+                    b8:17:92:78:6c:2b:4d:21:1b:46:3e:c1:cf:98:0b:
24
+                    a8:43:91:c0:39:48:f5:4e:71:77:c5:43:0e:68:8f:
25
+                    01:c6:fb:59:77:d5:b3:f3:fe:95:27:ea:6e:ae:fc:
26
+                    8e:59:ad:06:97:0c:f7:a6:e7:61:df:23:91:26:d0:
27
+                    bc:80:c6:2b:02:9b:fa:0f:e6:32:69:5a:90:29:c9:
28
+                    9c:34:eb:50:ed:1d:e3:eb:0f:67:88:e3:ec:2b:1a:
29
+                    ab:41:c3:fa:d6:e8:aa:e3:7b:6a:16:3d:d8:da:6b:
30
+                    af:92:81:32:98:2f:f7:c0:bd:c4:25:bb:02:60:43:
31
+                    d5:e6:0c:29:7f:31:5d:09:4b:6a:a9:31:9b:92:24:
32
+                    09:8f
33
+                Exponent: 65537 (0x10001)
34
+        X509v3 extensions:
35
+            X509v3 Subject Key Identifier: 
36
+                7A:BE:7D:09:5A:5F:5C:DE:CC:82:1A:3B:FE:A8:ED:CA:BA:16:58:49
37
+            X509v3 Authority Key Identifier: 
38
+                keyid:A7:EB:A1:B4:4D:2A:62:D5:38:90:ED:44:FA:77:7F:F9:BA:EA:99:04
39
+
40
+            X509v3 Basic Constraints: 
41
+                CA:TRUE, pathlen:0
42
+            X509v3 Key Usage: 
43
+                Certificate Sign, CRL Sign
44
+            X509v3 Subject Alternative Name: 
45
+                DNS:example.com, DNS:www.example.com, DNS:mail.example.com, DNS:ftp.example.com
46
+            Netscape Comment: 
47
+                OpenSSL Generated Certificate
48
+    Signature Algorithm: sha256WithRSAEncryption
49
+         81:55:0d:1d:73:54:1d:72:73:72:dc:cf:ed:c1:47:c8:38:2a:
50
+         78:33:5e:55:6f:02:cc:c0:6a:6f:7e:c9:fa:4c:3d:a0:5b:25:
51
+         37:5e:87:69:7f:d8:66:73:4f:58:7d:c7:3e:6d:be:2a:85:43:
52
+         6a:cb:ff:68:59:1d:72:d2:68:ad:e9:5b:2f:8d:f6:95:31:ba:
53
+         1d:de:16:45:d9:12:51:85:12:bb:fb:89:fc:3a:7c:f5:e4:75:
54
+         64:b4:7d:ff:9f:f6:15:fa:1e:cb:18:4a:9d:e8:d8:5e:5a:d7:
55
+         dd:78:c7:df:3d:21:2d:99:ef:b4:2c:78:2f:fb:fa:a0:7e:f3:
56
+         cb:3b:05:5a:65:7d:9b:0f:9b:a3:9b:a9:ad:25:f8:32:cb:08:
57
+         fd:c2:68:d3:92:15:09:59:5f:8b:c4:84:01:5f:75:7b:f0:55:
58
+         5f:20:39:f1:26:65:3d:d8:a2:19:de:fb:79:a0:27:2a:24:ae:
59
+         95:02:84:61:72:7a:47:37:4e:9f:af:20:5b:21:ec:c4:bf:ee:
60
+         80:5b:35:4e:ee:20:46:e6:cb:a6:e2:2f:c6:3e:5a:fa:f9:97:
61
+         c3:97:09:1d:ce:08:a3:e9:09:cb:c3:59:3f:98:f3:b6:bf:00:
62
+         8b:a7:40:de:0a:1c:09:88:f7:74:fa:b1:1c:05:44:ff:ba:73:
63
+         84:3b:93:8d:a8:51:d0:d8:59:e6:cd:a8:79:d3:db:0a:1d:99:
64
+         3f:7c:a0:f9:d5:9e:dd:13:58:ee:ef:0d:3d:e2:4a:8b:85:18:
65
+         0c:86:f8:97:4d:18:54:c0:52:b8:10:38:1a:b8:8a:06:71:a5:
66
+         e7:78:11:00:5b:9f:19:92:34:28:0f:19:3f:b0:57:ea:11:69:
67
+         29:ca:ed:05:36:08:f6:8d:ec:5d:34:79:92:8e:4c:e0:1c:a4:
68
+         ad:1a:31:90:b7:16:60:da:e3:8f:ee:ea:66:df:13:e8:46:8d:
69
+         a3:e2:3b:0a:f5:87:14:3d:4b:14:ea:da:89:c7:ae:e0:60:e3:
70
+         a0:4c:04:2f:a1:0f:a9:84:5a:5a:f7:3d:4f:7b:d4:7c:e1:cd:
71
+         ef:8b:28:45:19:ea:a9:4c:9e:59:f8:41:43:10:77:89:09:3e:
72
+         30:d0:e9:58:96:45:07:50:0e:4d:cc:6a:53:9e:64:c4:8a:e0:
73
+         51:96:3a:c6:8a:e2:94:af:9c:26:9a:fe:e3:7a:cd:cc:55:60:
74
+         f0:dc:bf:f3:0d:e8:69:e4:cf:49:e1:f4:d2:87:91:31:cf:42:
75
+         f7:2c:a7:f7:7b:88:90:e4:17:96:f6:34:d2:bf:a1:66:3c:03:
76
+         db:aa:07:fa:a6:c3:4b:d3:29:d1:d1:40:6f:a7:88:a5:7f:bd:
77
+         5f:f5:00:94:db:53:5d:24
78
+-----BEGIN CERTIFICATE-----
79
+MIIFKzCCAxOgAwIBAgIBCTANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
80
+DTALBgNVBAgMBFRlc3QxDTALBgNVBAcMBFRlc3QxDTALBgNVBAoMBFRlc3QxDTAL
81
+BgNVBAsMBFRlc3QxGTAXBgNVBAMMEFRlc3QgR3JhbmRwYXJlbnQxHTAbBgkqhkiG
82
+9w0BCQEWDmdwQGV4YW1wbGUuY29tMB4XDTE3MTAwMzE3NTgzMFoXDTI3MTAwMTE3
83
+NTgzMFowUDELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFRlc3QxDTALBgNVBAcMBFRl
84
+c3QxDTALBgNVBAoMBFRlc3QxFDASBgNVBAMMC1Rlc3QgUGFyZW50MIIBIjANBgkq
85
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn52VxKMvN1LkfM8LDn8UaWMeesyoGaeI
86
+WcgX8iETG0Uh+syTQHHPd1JaHi5akRapZzqjaurLor8km4wIljMZRvl6BPnC7ofz
87
+wyNzN1kOwHH0zQutI2NRCk/c0purq4qZB9TIyHD9GHMlCkiCMg1kRrFjhCQDCzy4
88
+F5J4bCtNIRtGPsHPmAuoQ5HAOUj1TnF3xUMOaI8BxvtZd9Wz8/6VJ+purvyOWa0G
89
+lwz3pudh3yORJtC8gMYrApv6D+YyaVqQKcmcNOtQ7R3j6w9niOPsKxqrQcP61uiq
90
+43tqFj3Y2muvkoEymC/3wL3EJbsCYEPV5gwpfzFdCUtqqTGbkiQJjwIDAQABo4Hb
91
+MIHYMB0GA1UdDgQWBBR6vn0JWl9c3syCGjv+qO3KuhZYSTAfBgNVHSMEGDAWgBSn
92
+66G0TSpi1TiQ7UT6d3/5uuqZBDAPBgNVHRMECDAGAQH/AgEAMAsGA1UdDwQEAwIB
93
+BjBKBgNVHREEQzBBggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUuY29tghBtYWls
94
+LmV4YW1wbGUuY29tgg9mdHAuZXhhbXBsZS5jb20wLAYJYIZIAYb4QgENBB8WHU9w
95
+ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMA0GCSqGSIb3DQEBCwUAA4ICAQCB
96
+VQ0dc1QdcnNy3M/twUfIOCp4M15VbwLMwGpvfsn6TD2gWyU3Xodpf9hmc09Yfcc+
97
+bb4qhUNqy/9oWR1y0mit6VsvjfaVMbod3hZF2RJRhRK7+4n8Onz15HVktH3/n/YV
98
++h7LGEqd6NheWtfdeMffPSEtme+0LHgv+/qgfvPLOwVaZX2bD5ujm6mtJfgyywj9
99
+wmjTkhUJWV+LxIQBX3V78FVfIDnxJmU92KIZ3vt5oCcqJK6VAoRhcnpHN06fryBb
100
+IezEv+6AWzVO7iBG5sum4i/GPlr6+ZfDlwkdzgij6QnLw1k/mPO2vwCLp0DeChwJ
101
+iPd0+rEcBUT/unOEO5ONqFHQ2Fnmzah509sKHZk/fKD51Z7dE1ju7w094kqLhRgM
102
+hviXTRhUwFK4EDgauIoGcaXneBEAW58ZkjQoDxk/sFfqEWkpyu0FNgj2jexdNHmS
103
+jkzgHKStGjGQtxZg2uOP7upm3xPoRo2j4jsK9YcUPUsU6tqJx67gYOOgTAQvoQ+p
104
+hFpa9z1Pe9R84c3viyhFGeqpTJ5Z+EFDEHeJCT4w0OlYlkUHUA5NzGpTnmTEiuBR
105
+ljrGiuKUr5wmmv7jes3MVWDw3L/zDehp5M9J4fTSh5Exz0L3LKf3e4iQ5BeW9jTS
106
+v6FmPAPbqgf6psNL0ynR0UBvp4ilf71f9QCU21NdJA==
107
+-----END CERTIFICATE-----

BIN
cursive/tests/unit/data/self_signed_cert.der View File


+ 22
- 0
cursive/tests/unit/data/self_signed_cert.pem View File

@@ -0,0 +1,22 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIIDmTCCAoGgAwIBAgIJAKmiuSJghxIGMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNV
3
+BAYTAlVTMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ0wCwYDVQQKEwRU
4
+ZXN0MQ0wCwYDVQQLEwRUZXN0MQ0wCwYDVQQDEwRUZXN0MSEwHwYJKoZIhvcNAQkB
5
+FhJ0ZXN0QGVtYWlsLmFkZHJlc3MwHhcNMTYwNjMwMTc1NzU1WhcNMTcwNjMwMTc1
6
+NzU1WjB7MQswCQYDVQQGEwJVUzENMAsGA1UECBMEVGVzdDENMAsGA1UEBxMEVGVz
7
+dDENMAsGA1UEChMEVGVzdDENMAsGA1UECxMEVGVzdDENMAsGA1UEAxMEVGVzdDEh
8
+MB8GCSqGSIb3DQEJARYSdGVzdEBlbWFpbC5hZGRyZXNzMIIBIjANBgkqhkiG9w0B
9
+AQEFAAOCAQ8AMIIBCgKCAQEA4KHdygQZ2/posNLLE/9lR0U57/iQKyFAfzO70RA5
10
+9mYZWSQTk8yI4wsDQi75xBjyuhYExsl+9lX6dgV0uEElhkycRemTN9pHmdGLi6of
11
+OIpVd5drZGcK19ndPeG5IzFvCpWXfsKuZ2kJf8p9i5XDNhigtYNq5rfLZBOIE3FY
12
+HPKBbx9cBaPOL8kjyX8LPwG7tpmNRLAF4XgQZu/AbfWx0jg8UqqJhOKwPQz+YOPY
13
+1eJ55BDyDYYiRj70qhQ1jIbfmYbWjg1VOv7LKzzwQWI8gTnKND26+L0D1tAy4joO
14
+cV/XM9lWheBCvzWTULqKpy95hyUMTz9mdG3xb5yFEccYwwIDAQABoyAwHjAPBgNV
15
+HRMECDAGAQH/AgECMAsGA1UdDwQEAwICBDANBgkqhkiG9w0BAQsFAAOCAQEAPwKA
16
+rC4S7P//VbffPNYOdmlu1dOjSSoNgtXC2lwpCy2iuvakasIAaDtseEjHgvcqJ/ty
17
+mHeOQu23qAP584ss+GoR7JUjlaTRoXRt/5PQ66HyrJXl/2jWLtT+7yU2+UtOxWSa
18
+fE3xUnzZlW4ES0hi+pWCpK+WaEya8q1+ak+i5oF8kQ3nRcT1f0IcOgyYnhvu8GGI
19
+Zd7BA8boUhR+L+X52zk6loaOEIwsmsfero9i2pn+JGZKyQfFKI8+bsnYuc7elIbY
20
+PER8fGWHid/DoIgQety153LLKtfR/20rYBrlnNtatg3ePTRdFZ1p7lnLPfA+AiV5
21
+e1Y6hSfZYXpZRV+Qtw==
22
+-----END CERTIFICATE-----

+ 22
- 0
cursive/tests/unit/data/self_signed_cert_invalid_ca_constraint.pem View File

@@ -0,0 +1,22 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIIDkzCCAnugAwIBAgIJAKnOrxg9gSXNMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNV
3
+BAYTAlVTMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ0wCwYDVQQKEwRU
4
+ZXN0MQ0wCwYDVQQLEwRUZXN0MQ0wCwYDVQQDEwRUZXN0MSEwHwYJKoZIhvcNAQkB
5
+FhJ0ZXN0QGVtYWlsLmFkZHJlc3MwHhcNMTYwNjMwMTgxOTQyWhcNMTcwNjMwMTgx
6
+OTQyWjB7MQswCQYDVQQGEwJVUzENMAsGA1UECBMEVGVzdDENMAsGA1UEBxMEVGVz
7
+dDENMAsGA1UEChMEVGVzdDENMAsGA1UECxMEVGVzdDENMAsGA1UEAxMEVGVzdDEh
8
+MB8GCSqGSIb3DQEJARYSdGVzdEBlbWFpbC5hZGRyZXNzMIIBIjANBgkqhkiG9w0B
9
+AQEFAAOCAQ8AMIIBCgKCAQEAnhmtSiir3aB5igvVEQlHqIw2K98q336cyjipFq9b
10
+Pt1YslTwLfUAagr7224i0tny45PIZ3o1YlBxhEwd/i1tMnCz2+DQyat+p+vVbbiI
11
+ceN1ZzRFE4zJV0QjG+H+TOWqzjtdtq04jkdrKMOsp3Lv4NHIuEuLocQLPLuT79wP
12
+VUO+BCHlU/0bUQHhAU/Jx9B81GQ1/4lYS400AYtANSEccMR1djUTjFha4wiwSDH2
13
+QZQBgmiqmDhf22uoioFgay9+yhOJ3SJx/lIiMavM2LMgNbns1DcbAD8oKGS69Mmo
14
+TsQlgOVMQTIDbwsm3WaxIcY8BipUACSe3E+RdqDrP/MTDwIDAQABoxowGDAJBgNV
15
+HRMEAjAAMAsGA1UdDwQEAwIDCDANBgkqhkiG9w0BAQsFAAOCAQEACP8ZAbyAtffS
16
+sWZ6WbLcm+BT7FFdUm62gGqC2knaIZQud3AV5/wtj2CAw1lXgGbaCqfWNQrTDY9z
17
+PpGXyQ5tvNUtBZG23K7nMvid+U1WDh1fhlRC0kxCK2MsPwv9T5BM3tj/YF0MGWuQ
18
+3GlFL8wU8UoAP3alUhxQl5qXqfXc6qfMW2ec4Jb3j6nbezL7ttn15LiBCsvoJ6/V
19
+Go9bbox81UrbtxnVin5+cYczUdB+Q9+fe23B/6MG99hzWU9arkkU7ZOlnI/bW9Zb
20
+fx4+atZfpi18nyd2ljgiTapB6Ex6uxVPrzwuKxpGMt9wU1++ZYc3YDke4EOT+OIX
21
+nKc7UFfjyg==
22
+-----END CERTIFICATE-----

+ 22
- 0
cursive/tests/unit/data/self_signed_cert_invalid_key_usage.pem View File

@@ -0,0 +1,22 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIIDmTCCAoGgAwIBAgIJAKOf0EhCGUdQMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNV
3
+BAYTAlVTMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ0wCwYDVQQKEwRU
4
+ZXN0MQ0wCwYDVQQLEwRUZXN0MQ0wCwYDVQQDEwRUZXN0MSEwHwYJKoZIhvcNAQkB
5
+FhJ0ZXN0QGVtYWlsLmFkZHJlc3MwHhcNMTYwNjMwMTgxNDMwWhcNMTcwNjMwMTgx
6
+NDMwWjB7MQswCQYDVQQGEwJVUzENMAsGA1UECBMEVGVzdDENMAsGA1UEBxMEVGVz
7
+dDENMAsGA1UEChMEVGVzdDENMAsGA1UECxMEVGVzdDENMAsGA1UEAxMEVGVzdDEh
8
+MB8GCSqGSIb3DQEJARYSdGVzdEBlbWFpbC5hZGRyZXNzMIIBIjANBgkqhkiG9w0B
9
+AQEFAAOCAQ8AMIIBCgKCAQEA575xHdxVuT5CYPom/PbFdDLt0PgG3CZFkiRNxPAG
10
+rDN8cG5ouTw0R9RMBFA+nYcTx/4GnPJmSBEBVqMoPSzsB6Rx9k21KymNlaEs2O1W
11
+jSMsYd9gW4NlHdyoomYw0nXQjkstmtdJxDNWg0zSZrHMPnkOVh+JNV58i4FXOx5O
12
+bxWo4sSyAvNjAEH9GwDwy+Jz0X4RdFGQrGjm+/v+ohvy8JqU5ZKpz2oP2oQURjDj
13
++AH3ghmgNAVAk0syjtSqydEJd9aeMLTmTaUtP+gPnXdj/ZBj+TQH01RNlSECH2l4
14
+WrymS3g4+X5xsA7DeLIbiXB4K1xjbJFCSfDYrV52H/fE6QIDAQABoyAwHjAPBgNV
15
+HRMECDAGAQH/AgECMAsGA1UdDwQEAwIDCDANBgkqhkiG9w0BAQsFAAOCAQEARNRE
16
+XC8y/RoPLUVAVKJ/RwcH4cwaHoSSu6HnygIpg9Qs7Xc7u1aKCL0dRF1NqfmaqHZ6
17
+ZjllxDi5t0CFIXPfDQIYchfSzOafhJGEH3gilBwfmN43N4/eCSvdRKfhRbRFOD9j
18
+0JHRAHkn2JRcwSTTjJEcJUJJETAIIbX1ovobJZuJOY0faI1O/Z2KILYrwdmcfnZ5
19
+3i0kUps5BWrBrcs70gBsDBugeM24ANa7hJzFk+9TztfLWF1AUfjpZ4Bj/rb21+Gp
20
+08FRjvn80Y5bGlNh0Q7Qbu8NS8VbAHHF3t3PUVRymJhIycpvBBBS7dcQqHf+v1gs
21
+Z/UpzuobJvnhq+7mcQ==
22
+-----END CERTIFICATE-----

+ 21
- 0
cursive/tests/unit/data/self_signed_cert_missing_ca_constraint.pem View File

@@ -0,0 +1,21 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIIDiDCCAnCgAwIBAgIJAOYNT7MuoypuMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNV
3
+BAYTAlVTMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ0wCwYDVQQKEwRU
4
+ZXN0MQ0wCwYDVQQLEwRUZXN0MQ0wCwYDVQQDEwRUZXN0MSEwHwYJKoZIhvcNAQkB
5
+FhJ0ZXN0QGVtYWlsLmFkZHJlc3MwHhcNMTYwNjMwMTgyMTMyWhcNMTcwNjMwMTgy
6
+MTMyWjB7MQswCQYDVQQGEwJVUzENMAsGA1UECBMEVGVzdDENMAsGA1UEBxMEVGVz
7
+dDENMAsGA1UEChMEVGVzdDENMAsGA1UECxMEVGVzdDENMAsGA1UEAxMEVGVzdDEh
8
+MB8GCSqGSIb3DQEJARYSdGVzdEBlbWFpbC5hZGRyZXNzMIIBIjANBgkqhkiG9w0B
9
+AQEFAAOCAQ8AMIIBCgKCAQEA3qypxOJ2X0k47SIEkRGvA/ECqzVAX3nsC0yPnbF3
10
+14SGe7xFBzi0VMAXOcpVj0BL+G5TL95O5loVN/UnU5/xtjSa712HOOlJfvnqmv63
11
+BLq9cMS2strwufeOK3YUtQExtJdxMjcEYuCMt+NlQ3Hl+xNfBc0LXWNBdlusP4fs
12
+6sLEgyD4ywSLC9oHzyzgDxi0pr52itu+KnZv2iET/Wotg/8Aiw5Q5fiTc8DoysdZ
13
+MF1ix56oGo1SFFGFf+n2iwYbImtNGt6//jKEDP8P4iLdLAxHxmfsXXnl+Zs/6VoA
14
+RrnS3Xt7F5xj4CWuoZy4CLo8YXhXdznRQZ2r5Qha65cFNwIDAQABow8wDTALBgNV
15
+HQ8EBAMCAwgwDQYJKoZIhvcNAQELBQADggEBALOBnrWz3xMg4Yh92zsXfrSm2uAL
16
+P8jgXQtQdsYWSEWcfYNYOSlmLnICweDnAn0V5Kzp0E5wZSHP8Ut4IYEKwe+IF7HA
17
+pv3mpg1CYtwVbVsN0dhlLHDVuF27i0r8LuOv9yh0wxY3hPYrd2WvQ/qTP8NO0EoD
18
+fM8w2fjNeTu2jB+lYWhGWOHfzEvltosxMZIPWBJxrh3PbYdbuJJZlm/NPqj5Urxx
19
+nRB89AEBHKEKHlmNIMMOM3mQ+ShssgGrbRV6U6iJ5qv4H5RdaZtmXMGhjt8dFJrD
20
+A2YZYW15QLQDEyud4flSztaw6UMHJi+4FChBAnuJuOcNRZA83v3szuM9t64=
21
+-----END CERTIFICATE-----

+ 21
- 0
cursive/tests/unit/data/self_signed_cert_missing_key_usage.pem View File

@@ -0,0 +1,21 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIIDXTCCAkWgAwIBAgIJAI20mCsVbBjpMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
3
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
4
+aWRnaXRzIFB0eSBMdGQwHhcNMTYwNjMwMTM0NTU5WhcNMTcwNjMwMTM0NTU5WjBF
5
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
6
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
7
+CgKCAQEA5NjoTq0D6LM6z7V16E1+Eul25L6X1BSLcppsHhVMB/NM1e7hjoUtAODv
8
+71L/bAAgV4ky4aphOfwYQLfeAP8nkq4CU30LaAyAQwrT4RyW1NG7AA00xylauebt
9
+sc2GUUy06gQ0Z6OjjEfA5HA4W+HYfeyuNzQpWXHWz/6K8xcKb9w10qAjDhNilHbj
10
+3RVtL6u6rsZgQi0DlMxpHsp6gLezIRMN72B/AOKrzaobw4nY4hOVkqbRlOHB/tsk
11
+4BJLUuW5WM30TVpfsKe07jCBgqUwb9XD9lZa1alkFRsSTZoWijeQBM6kiLD/VFNC
12
+YlIKBrN7HZtfOlqhftMknCsoyrWsjQIDAQABo1AwTjAdBgNVHQ4EFgQUnDu25deh
13
+bIOsCaj3LAcE9r2gx0QwHwYDVR0jBBgwFoAUnDu25dehbIOsCaj3LAcE9r2gx0Qw
14
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAowntOCmRXKgz7M6hnbQc
15
+sEyIt5tN3QILHSeprVO7/ONVIiPJMCfB+S8gJKJ0d5R0xXDYN/6+HyYlgfaL33Gt
16
+HY75y8MRnfqpgbEWhXWsBkxgeuqWiM/OFMTqLtgkVbxsVzoUl6V+tHsZaaM9yuyb
17
+iUBM9McAPGIgodpMGG86BV6qg07VjqWjl5pBUU4B2zvvzZjwrC8jqUYksVESHB9U
18
+WBzwfPLXoj0PUfAog34ZtT33UXX8M3oXTw+yb/hx0rContYMc78Lnlk6mV9gGG+X
19
++3gSwAHn0SMZNNkKc3gdb1CLfluvHw2Od2jat0yfHHawh1JBtnfHrAU0px3Kzw5U
20
+0A==
21
+-----END CERTIFICATE-----

+ 26
- 0
cursive/tests/unit/data/signed_cert.pem View File

@@ -0,0 +1,26 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIIEWzCCA0OgAwIBAgIBATANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJVUzEN
3
+MAsGA1UECBMEVGVzdDENMAsGA1UEBxMEVGVzdDENMAsGA1UEChMEVGVzdDENMAsG
4
+A1UECxMEVGVzdDENMAsGA1UEAxMEVGVzdDEhMB8GCSqGSIb3DQEJARYSdGVzdEBl
5
+bWFpbC5hZGRyZXNzMB4XDTE2MDYzMDE5NDA1N1oXDTE3MDYzMDE5NDA1N1owbzEL
6
+MAkGA1UEBhMCVVMxDTALBgNVBAgMBFRlc3QxDTALBgNVBAoMBFRlc3QxDTALBgNV
7
+BAsMBFRlc3QxDzANBgNVBAMMBkNsaWVudDEiMCAGCSqGSIb3DQEJARYTdGVzdEBj
8
+bGllbnQuYWRkcmVzczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALOK
9
+dKWvu8NLDee4KvHf5WmmJsNeqrfZt+5EBXlp9wEJ3i//6vRpZe9Gr/k3xfbQPVng
10
+PS8LUBancZ/zPos6ZibUuJi+ZjgVXUm61S18536wq4S1LH4Hkb4RgJW+IKqlqi0z
11
+RVC3xeNcUhGprcH9JtjinOusQ1HLWy4mSr5aaCfCVshj3YEN5uCrfDOXPkS5B1kd
12
+kpnEZJt2tAUPLlIKD4Ytjq9A84bL6wHTrUg5NmZ8j+yfXfD0qE1rN69AZFQ2V72R
13
+uKUbxvvx2T8ObJh1VpiSlMLLEeoEY1OUCZWB+Xz6GX9uL9B9zeD0+f9WcswI3UCw
14
+9WEFgDHYWxgFciu7IYsCAwEAAaOB9TCB8jAJBgNVHRMEAjAAMCwGCWCGSAGG+EIB
15
+DQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUDs4X
16
+d7VArujtnCJ3Ppht7hSfdL0wgZcGA1UdIwSBjzCBjKF/pH0wezELMAkGA1UEBhMC
17
+VVMxDTALBgNVBAgTBFRlc3QxDTALBgNVBAcTBFRlc3QxDTALBgNVBAoTBFRlc3Qx
18
+DTALBgNVBAsTBFRlc3QxDTALBgNVBAMTBFRlc3QxITAfBgkqhkiG9w0BCQEWEnRl
19
+c3RAZW1haWwuYWRkcmVzc4IJAKmiuSJghxIGMA0GCSqGSIb3DQEBCwUAA4IBAQBm
20
+f2VVj4Eqb+5pAgimkejDrYRzDgDQ4Eyr45vdUtu7JoGovGmkxg5z3izW/UKKj8GC
21
+04aXIJiIu8d7mn5ZxuaIS0/mtVN167tVVI0wBlkQRK5dJNjn47fTixymEy4lwdUl
22
+0iSb1JP6beVmSMIywD5lFxGPiW/MEJSvDCdlOT2Ojiv/Sbn9Q09PsXei0fAmNGZn
23
+FEUSnlqgWkeGIIv3+//kY8pHlZ1RyYSShQ+3Vb8Qifx0lbiFQWDP82EgETu7JKWn
24
+fKCoogSDybcLqB/WeGOQ0myXgEth5Lhkdo0n08J/FYL/bA1thADVnV66ZpERgb3h
25
+38P4rEcobzZdVPcS4zwP
26
+-----END CERTIFICATE-----

+ 403
- 0
cursive/tests/unit/test_certificate_utils.py View File

@@ -0,0 +1,403 @@
1
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
2
+#    not use this file except in compliance with the License. You may obtain
3
+#    a copy of the License at
4
+#
5
+#         http://www.apache.org/licenses/LICENSE-2.0
6
+#
7
+#    Unless required by applicable law or agreed to in writing, software
8
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10
+#    License for the specific language governing permissions and limitations
11
+#    under the License.
12
+
13
+import datetime
14
+import mock
15
+import os
16
+
17
+from cryptography.hazmat.backends import default_backend
18
+from cryptography import x509
19
+
20
+from cursive import certificate_utils
21
+from cursive import exception
22
+from cursive.tests import base
23
+
24
+
25
+class TestCertificateUtils(base.TestCase):
26
+    """Test methods for the certificate verification context and utilities"""
27
+
28
+    def setUp(self):
29
+        super(TestCertificateUtils, self).setUp()
30
+
31
+        self.cert_path = os.path.join(
32
+            os.path.dirname(os.path.realpath(__file__)),
33
+            'data'
34
+        )
35
+
36
+    def tearDown(self):
37
+        super(TestCertificateUtils, self).tearDown()
38
+
39
+    def load_certificate(self, cert_name):
40
+        # Load the raw certificate file data.
41
+        path = os.path.join(self.cert_path, cert_name)
42
+        with open(path, 'rb') as cert_file:
43
+            data = cert_file.read()
44
+
45
+        # Convert the raw certificate data into a certificate object, first
46
+        # as a PEM-encoded certificate and, if that fails, then as a
47
+        # DER-encoded certificate. If both fail, the certificate cannot be
48
+        # loaded.
49
+        try:
50
+            return x509.load_pem_x509_certificate(data, default_backend())
51
+        except Exception:
52
+            try:
53
+                return x509.load_der_x509_certificate(data, default_backend())
54
+            except Exception:
55
+                raise exception.SignatureVerificationError(
56
+                    "Failed to load certificate: %s" % path
57
+                )
58
+
59
+    def load_certificates(self, cert_names):
60
+        certs = list()
61
+        for cert_name in cert_names:
62
+            cert = self.load_certificate(cert_name)
63
+            certs.append(cert)
64
+        return certs
65
+
66
+    @mock.patch('oslo_utils.timeutils.utcnow')
67
+    def test_is_within_valid_dates(self, mock_utcnow):
68
+        # Verify a certificate is valid at a time within its valid date range
69
+        cert = self.load_certificate('self_signed_cert.pem')
70
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
71
+        result = certificate_utils.is_within_valid_dates(cert)
72
+        self.assertEqual(True, result)
73
+
74
+    @mock.patch('oslo_utils.timeutils.utcnow')
75
+    def test_is_before_valid_dates(self, mock_utcnow):
76
+        # Verify a certificate is invalid at a time before its valid date range
77
+        cert = self.load_certificate('self_signed_cert.pem')
78
+        mock_utcnow.return_value = datetime.datetime(2000, 1, 1)
79
+        result = certificate_utils.is_within_valid_dates(cert)
80
+        self.assertEqual(False, result)
81
+
82
+    @mock.patch('oslo_utils.timeutils.utcnow')
83
+    def test_is_after_valid_dates(self, mock_utcnow):
84
+        # Verify a certificate is invalid at a time after its valid date range
85
+        cert = self.load_certificate('self_signed_cert.pem')
86
+        mock_utcnow.return_value = datetime.datetime(2100, 1, 1)
87
+        result = certificate_utils.is_within_valid_dates(cert)
88
+        self.assertEqual(False, result)
89
+
90
+    def test_is_issuer(self):
91
+        # Test issuer and subject name matching for a self-signed certificate.
92
+        cert = self.load_certificate('self_signed_cert.pem')
93
+        result = certificate_utils.is_issuer(cert, cert)
94
+        self.assertEqual(True, result)
95
+
96
+    def test_is_not_issuer(self):
97
+        # Test issuer and subject name mismatching.
98
+        cert = self.load_certificate('self_signed_cert.pem')
99
+        alt = self.load_certificate('orphaned_cert.pem')
100
+        result = certificate_utils.is_issuer(cert, alt)
101
+        self.assertEqual(False, result)
102
+
103
+    def test_is_issuer_with_invalid_certs(self):
104
+        # Test issuer check with invalid certificates
105
+        cert = self.load_certificate('self_signed_cert.pem')
106
+        result = certificate_utils.is_issuer(cert, None)
107
+        self.assertEqual(False, result)
108
+        result = certificate_utils.is_issuer(None, cert)
109
+        self.assertEqual(False, result)
110
+
111
+    def test_can_sign_certificates(self):
112
+        # Test that a well-formatted certificate can sign
113
+        cert = self.load_certificate('self_signed_cert.pem')
114
+        result = certificate_utils.can_sign_certificates(cert, 'test-ID')
115
+        self.assertEqual(True, result)
116
+
117
+    def test_cannot_sign_certificates_without_basic_constraints(self):
118
+        # Verify a certificate without basic constraints cannot sign
119
+        cert = self.load_certificate(
120
+            'self_signed_cert_missing_ca_constraint.pem'
121
+        )
122
+        result = certificate_utils.can_sign_certificates(cert, 'test-ID')
123
+        self.assertEqual(False, result)
124
+
125
+    def test_cannot_sign_certificates_with_invalid_basic_constraints(self):
126
+        # Verify a certificate with invalid basic constraints cannot sign
127
+        cert = self.load_certificate(
128
+            'self_signed_cert_invalid_ca_constraint.pem'
129
+        )
130
+        result = certificate_utils.can_sign_certificates(cert, 'test-ID')
131
+        self.assertEqual(False, result)
132
+
133
+    def test_cannot_sign_certificates_without_key_usage(self):
134
+        # Verify a certificate without key usage cannot sign
135
+        cert = self.load_certificate('self_signed_cert_missing_key_usage.pem')
136
+        result = certificate_utils.can_sign_certificates(cert, 'test-ID')
137
+        self.assertEqual(False, result)
138
+
139
+    def test_cannot_sign_certificates_with_invalid_key_usage(self):
140
+        # Verify a certificate with invalid key usage cannot sign
141
+        cert = self.load_certificate('self_signed_cert_invalid_key_usage.pem')
142
+        result = certificate_utils.can_sign_certificates(cert, 'test-ID')
143
+        self.assertEqual(False, result)
144
+
145
+    def test_verify_signing_certificate(self):
146
+        signing_certificate = self.load_certificate('self_signed_cert.pem')
147
+        signed_certificate = self.load_certificate('signed_cert.pem')
148
+
149
+        certificate_utils.verify_certificate_signature(
150
+            signing_certificate,
151
+            signed_certificate
152
+        )
153
+
154
+    @mock.patch('cursive.signature_utils.get_certificate')
155
+    @mock.patch('oslo_utils.timeutils.utcnow')
156
+    def test_verify_valid_certificate(self, mock_utcnow, mock_get_cert):
157
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
158
+        certs = self.load_certificates(
159
+            ['self_signed_cert.pem', 'self_signed_cert.der',
160
+             'signed_cert.pem']
161
+        )
162
+        mock_get_cert.side_effect = certs
163
+        cert_uuid = '3'
164
+        trusted_cert_uuids = ['1', '2']
165
+        certificate_utils.verify_certificate(
166
+            None, cert_uuid, trusted_cert_uuids
167
+        )
168
+
169
+    @mock.patch('cursive.signature_utils.get_certificate')
170
+    @mock.patch('oslo_utils.timeutils.utcnow')
171
+    def test_verify_invalid_certificate(self, mock_utcnow, mock_get_cert):
172
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
173
+        certs = self.load_certificates(
174
+            ['self_signed_cert.pem', 'self_signed_cert.der',
175
+             'orphaned_cert.pem']
176
+        )
177
+        mock_get_cert.side_effect = certs
178
+        cert_uuid = '3'
179
+        trusted_cert_uuids = ['1', '2']
180
+        self.assertRaisesRegex(
181
+            exception.SignatureVerificationError,
182
+            "Certificate chain building failed. Could not locate the "
183
+            "signing certificate for the base certificate in the set of "
184
+            "trusted certificates.",
185
+            certificate_utils.verify_certificate,
186
+            None,
187
+            cert_uuid,
188
+            trusted_cert_uuids
189
+        )
190
+
191
+    @mock.patch('cursive.signature_utils.get_certificate')
192
+    @mock.patch('oslo_utils.timeutils.utcnow')
193
+    def test_verify_valid_certificate_with_no_root(self, mock_utcnow,
194
+                                                   mock_get_cert):
195
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
196
+
197
+        # Test verifying a valid certificate against an empty list of trusted
198
+        # certificates.
199
+        certs = self.load_certificates(['signed_cert.pem'])
200
+        mock_get_cert.side_effect = certs
201
+        cert_uuid = '3'
202
+        trusted_cert_uuids = []
203
+        self.assertRaisesRegex(
204
+            exception.SignatureVerificationError,
205
+            "Certificate chain building failed. Could not locate the "
206
+            "signing certificate for the base certificate in the set of "
207
+            "trusted certificates.",
208
+            certificate_utils.verify_certificate,
209
+            None,
210
+            cert_uuid,
211
+            trusted_cert_uuids
212
+        )
213
+
214
+    @mock.patch('oslo_utils.timeutils.utcnow')
215
+    def test_context_init(self, mock_utcnow):
216
+        # Test constructing a context object with a valid set of certificates
217
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
218
+        certs = self.load_certificates(
219
+            ['self_signed_cert.pem', 'self_signed_cert.der']
220
+        )
221
+        cert_tuples = [('1', certs[0]), ('2', certs[1])]
222
+        context = certificate_utils.CertificateVerificationContext(
223
+            cert_tuples
224
+        )
225
+        self.assertEqual(2, len(context._signing_certificates))
226
+        for t in cert_tuples:
227
+            path, cert = t
228
+            self.assertIn(cert, [x[1] for x in context._signing_certificates])
229
+
230
+    @mock.patch('cursive.certificate_utils.LOG')
231
+    @mock.patch('oslo_utils.timeutils.utcnow')
232
+    def test_context_init_with_invalid_certificate(self, mock_utcnow,
233
+                                                   mock_log):
234
+        # Test constructing a context object with an invalid certificate
235
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
236
+        alt_cert_tuples = [('path', None)]
237
+        context = certificate_utils.CertificateVerificationContext(
238
+            alt_cert_tuples
239
+        )
240
+        self.assertEqual(0, len(context._signing_certificates))
241
+        self.assertEqual(1, mock_log.error.call_count)
242
+
243
+    @mock.patch('cursive.certificate_utils.LOG')
244
+    @mock.patch('oslo_utils.timeutils.utcnow')
245
+    def test_context_init_with_non_signing_certificate(self, mock_utcnow,
246
+                                                       mock_log):
247
+        # Test constructing a context object with an non-signing certificate
248
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
249
+        non_signing_cert = self.load_certificate(
250
+            'self_signed_cert_missing_key_usage.pem'
251
+        )
252
+        alt_cert_tuples = [('path', non_signing_cert)]
253
+        context = certificate_utils.CertificateVerificationContext(
254
+            alt_cert_tuples
255
+        )
256
+        self.assertEqual(0, len(context._signing_certificates))
257
+        self.assertEqual(1, mock_log.warning.call_count)
258
+
259
+    @mock.patch('cursive.certificate_utils.LOG')
260
+    @mock.patch('oslo_utils.timeutils.utcnow')
261
+    def test_context_init_with_out_of_date_certificate(self, mock_utcnow,
262
+                                                       mock_log):
263
+        # Test constructing a context object with out-of-date certificates
264
+        mock_utcnow.return_value = datetime.datetime(2100, 1, 1)
265
+        certs = self.load_certificates(
266
+            ['self_signed_cert.pem', 'self_signed_cert.der']
267
+        )
268
+        cert_tuples = [('1', certs[0]), ('2', certs[1])]
269
+        context = certificate_utils.CertificateVerificationContext(cert_tuples)
270
+        self.assertEqual(0, len(context._signing_certificates))
271
+        self.assertEqual(2, mock_log.warning.call_count)
272
+
273
+    @mock.patch('oslo_utils.timeutils.utcnow')
274
+    def test_context_update_with_valid_certificate(self, mock_utcnow):
275
+        # Test updating the context with a valid certificate
276
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
277
+        certs = self.load_certificates(
278
+            ['self_signed_cert.pem', 'self_signed_cert.der']
279
+        )
280
+        cert_tuples = [('1', certs[0]), ('2', certs[1])]
281
+        context = certificate_utils.CertificateVerificationContext(cert_tuples)
282
+        cert = self.load_certificate('orphaned_cert.pem')
283
+        context.update(cert)
284
+        self.assertEqual(cert, context._signed_certificate)
285
+
286
+    @mock.patch('oslo_utils.timeutils.utcnow')
287
+    def test_context_update_with_date_invalid_certificate(self, mock_utcnow):
288
+        # Test updating the context with an out-of-date certificate
289
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
290
+        certs = self.load_certificates(
291
+            ['self_signed_cert.pem', 'self_signed_cert.der']
292
+        )
293
+        cert_tuples = [('1', certs[0]), ('2', certs[1])]
294
+        context = certificate_utils.CertificateVerificationContext(cert_tuples)
295
+        cert = self.load_certificate('orphaned_cert.pem')
296
+        mock_utcnow.return_value = datetime.datetime(2100, 1, 1)
297
+        self.assertRaisesRegex(
298
+            exception.SignatureVerificationError,
299
+            "The certificate is outside its valid date range.",
300
+            context.update,
301
+            cert
302
+        )
303
+
304
+    def test_context_update_with_invalid_certificate(self):
305
+        # Test updating the context with an invalid certificate
306
+        certs = self.load_certificates(
307
+            ['self_signed_cert.pem', 'self_signed_cert.der']
308
+        )
309
+        cert_tuples = [('1', certs[0]), ('2', certs[1])]
310
+        context = certificate_utils.CertificateVerificationContext(
311
+            cert_tuples
312
+        )
313
+
314
+        self.assertRaisesRegex(
315
+            exception.SignatureVerificationError,
316
+            "The certificate must be an x509.Certificate object.",
317
+            context.update,
318
+            None
319
+        )
320
+
321
+    @mock.patch('oslo_utils.timeutils.utcnow')
322
+    def test_context_verify(self, mock_utcnow):
323
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
324
+        certs = self.load_certificates(
325
+            ['self_signed_cert.pem', 'self_signed_cert.der']
326
+        )
327
+        cert_tuples = [('1', certs[0]), ('2', certs[1])]
328
+
329
+        # Test verification with a two-link certificate chain.
330
+        context = certificate_utils.CertificateVerificationContext(
331
+            cert_tuples
332
+        )
333
+        cert = self.load_certificate('signed_cert.pem')
334
+        context.update(cert)
335
+        context.verify()
336
+
337
+        # Test verification with a single-link certificate chain.
338
+        context = certificate_utils.CertificateVerificationContext(
339
+            cert_tuples
340
+        )
341
+        context.update(certs[0])
342
+        context.verify()
343
+
344
+    @mock.patch('oslo_utils.timeutils.utcnow')
345
+    def test_context_verify_disable_checks(self, mock_utcnow):
346
+        mock_utcnow.return_value = datetime.datetime(2017, 1, 1)
347
+        certs = self.load_certificates(
348
+            ['self_signed_cert.pem', 'self_signed_cert.der']
349
+        )
350
+        cert_tuples = [('1', certs[0]), ('2', certs[1])]
351
+
352
+        # Test verification with a two-link certificate chain.
353
+        context = certificate_utils.CertificateVerificationContext(
354
+            cert_tuples,
355
+            enforce_valid_dates=False,
356
+            enforce_signing_extensions=False,
357
+            enforce_path_length=False
358
+        )
359
+        cert = self.load_certificate('signed_cert.pem')
360
+        context.update(cert)
361
+        context.verify()
362
+
363
+        # Test verification with a single-link certificate chain.
364
+        context = certificate_utils.CertificateVerificationContext(
365
+            cert_tuples,
366
+            enforce_valid_dates=False,
367
+            enforce_signing_extensions=False,
368
+            enforce_path_length=False
369
+        )
370
+        context.update(certs[0])
371
+        context.verify()
372
+
373
+    @mock.patch('oslo_utils.timeutils.utcnow')
374
+    def test_context_verify_invalid_chain_length(self, mock_utcnow):
375
+        mock_utcnow.return_value = datetime.datetime(2017, 11, 1)
376
+        certs = self.load_certificates(
377
+            ['grandparent_cert.pem', 'parent_cert.pem', 'child_cert.pem']
378
+        )
379
+        cert_tuples = [
380
+            ('1', certs[0]),
381
+            ('2', certs[1]),
382
+            ('3', certs[2])
383
+        ]
384
+        cert = self.load_certificate('grandchild_cert.pem')
385
+
386
+        context = certificate_utils.CertificateVerificationContext(
387
+            cert_tuples
388
+        )
389
+        context.update(cert)
390
+        self.assertRaisesRegex(
391
+            exception.SignatureVerificationError,
392
+            "Certificate validation failed. The signing certificate '1' is "
393
+            "not configured to support certificate chains of sufficient "
394
+            "length.",
395
+            context.verify
396
+        )
397
+
398
+        context = certificate_utils.CertificateVerificationContext(
399
+            cert_tuples,
400
+            enforce_path_length=False
401
+        )
402
+        context.update(cert)
403
+        context.verify()

+ 16
- 33
cursive/tests/unit/test_signature_utils.py View File

@@ -12,6 +12,7 @@
12 12
 
13 13
 import base64
14 14
 import datetime
15
+import mock
15 16
 
16 17
 from castellan.common.exception import KeyManagerError
17 18
 import cryptography.exceptions as crypto_exceptions
@@ -20,7 +21,6 @@ from cryptography.hazmat.primitives.asymmetric import dsa
20 21
 from cryptography.hazmat.primitives.asymmetric import ec
21 22
 from cryptography.hazmat.primitives.asymmetric import padding
22 23
 from cryptography.hazmat.primitives.asymmetric import rsa
23
-import mock
24 24
 from oslo_utils import timeutils
25 25
 
26 26
 from cursive import exception
@@ -110,6 +110,12 @@ class BadPublicKey(object):
110 110
 class TestSignatureUtils(base.TestCase):
111 111
     """Test methods of signature_utils"""
112 112
 
113
+    def setUp(self):
114
+        super(TestSignatureUtils, self).setUp()
115
+
116
+    def tearDown(self):
117
+        super(TestSignatureUtils, self).tearDown()
118
+
113 119
     def test_should_create_verifier(self):
114 120
         image_props = {CERT_UUID: 'CERT_UUID',
115 121
                        HASH_METHOD: 'HASH_METHOD',
@@ -283,7 +289,8 @@ class TestSignatureUtils(base.TestCase):
283 289
                                'RSB-PSS')
284 290
 
285 291
     @mock.patch('cursive.signature_utils.get_certificate')
286
-    def test_get_public_key_rsa(self, mock_get_cert):
292
+    @mock.patch('cursive.certificate_utils.verify_certificate')
293
+    def test_get_public_key_rsa(self, mock_verify_cert, mock_get_cert):
287 294
         fake_cert = FakeCryptoCertificate()
288 295
         mock_get_cert.return_value = fake_cert
289 296
         sig_key_type = signature_utils.SignatureKeyType.lookup(
@@ -294,7 +301,8 @@ class TestSignatureUtils(base.TestCase):
294 301
         self.assertEqual(fake_cert.public_key(), result_pub_key)
295 302
 
296 303
     @mock.patch('cursive.signature_utils.get_certificate')
297
-    def test_get_public_key_ecc(self, mock_get_cert):
304
+    @mock.patch('cursive.certificate_utils.verify_certificate')
305
+    def test_get_public_key_ecc(self, mock_verify_cert, mock_get_cert):
298 306
         fake_cert = FakeCryptoCertificate(TEST_ECC_PRIVATE_KEY.public_key())
299 307
         mock_get_cert.return_value = fake_cert
300 308
         sig_key_type = signature_utils.SignatureKeyType.lookup('ECC_SECP521R1')
@@ -303,7 +311,8 @@ class TestSignatureUtils(base.TestCase):
303 311
         self.assertEqual(fake_cert.public_key(), result_pub_key)
304 312
 
305 313
     @mock.patch('cursive.signature_utils.get_certificate')
306
-    def test_get_public_key_dsa(self, mock_get_cert):
314
+    @mock.patch('cursive.certificate_utils.verify_certificate')
315
+    def test_get_public_key_dsa(self, mock_verify_cert, mock_get_cert):
307 316
         fake_cert = FakeCryptoCertificate(TEST_DSA_PRIVATE_KEY.public_key())
308 317
         mock_get_cert.return_value = fake_cert
309 318
         sig_key_type = signature_utils.SignatureKeyType.lookup(
@@ -314,7 +323,9 @@ class TestSignatureUtils(base.TestCase):
314 323
         self.assertEqual(fake_cert.public_key(), result_pub_key)
315 324
 
316 325
     @mock.patch('cursive.signature_utils.get_certificate')
317
-    def test_get_public_key_invalid_key(self, mock_get_certificate):
326
+    @mock.patch('cursive.certificate_utils.verify_certificate')
327
+    def test_get_public_key_invalid_key(self, mock_verify_certificate,
328
+                                        mock_get_certificate):
318 329
         bad_pub_key = 'A' * 256
319 330
         mock_get_certificate.return_value = FakeCryptoCertificate(bad_pub_key)
320 331
         sig_key_type = signature_utils.SignatureKeyType.lookup(
@@ -335,34 +346,6 @@ class TestSignatureUtils(base.TestCase):
335 346
         self.assertEqual(x509_cert,
336 347
                          signature_utils.get_certificate(None, cert_uuid))
337 348
 
338
-    @mock.patch('cryptography.x509.load_der_x509_certificate')
339
-    @mock.patch('castellan.key_manager.API', return_value=FakeKeyManager())
340
-    def test_get_expired_certificate(self, mock_key_manager_API,
341
-                                     mock_load_cert):
342
-        cert_uuid = 'valid_format_cert'
343
-        x509_cert = FakeCryptoCertificate(
344
-            not_valid_after=timeutils.utcnow() -
345
-            datetime.timedelta(hours=1))
346
-        mock_load_cert.return_value = x509_cert
347
-        self.assertRaisesRegex(exception.SignatureVerificationError,
348
-                               'Certificate is not valid after: .*',
349
-                               signature_utils.get_certificate, None,
350
-                               cert_uuid)
351
-
352
-    @mock.patch('cryptography.x509.load_der_x509_certificate')
353
-    @mock.patch('castellan.key_manager.API', return_value=FakeKeyManager())
354
-    def test_get_not_yet_valid_certificate(self, mock_key_manager_API,
355
-                                           mock_load_cert):
356
-        cert_uuid = 'valid_format_cert'
357
-        x509_cert = FakeCryptoCertificate(
358
-            not_valid_before=timeutils.utcnow() +
359
-            datetime.timedelta(hours=1))
360
-        mock_load_cert.return_value = x509_cert
361
-        self.assertRaisesRegex(exception.SignatureVerificationError,
362
-                               'Certificate is not valid before: .*',
363
-                               signature_utils.get_certificate, None,
364
-                               cert_uuid)
365
-
366 349
     @mock.patch('castellan.key_manager.API', return_value=FakeKeyManager())
367 350
     def test_get_certificate_key_manager_fail(self, mock_key_manager_API):
368 351
         bad_cert_uuid = 'fea14bc2-d75f-4ba5-bccc-b5c924ad0695'

+ 37
- 0
releasenotes/notes/add-certificate-validation-68a1ffbd5369a8d1.yaml View File

@@ -0,0 +1,37 @@
1
+---
2
+prelude: >
3
+    The cursive library supports the verification of digital signatures.
4
+    However, there is no way currently to validate the certificate used to
5
+    generate a given signature. Adding certificate validation improves the
6
+    security of signature verification when each is used together.
7
+features:
8
+  - Adds a variety of certificate utility functions that inspect certificate
9
+    attributes and extensions for different settings.
10
+  - Adds the CertificateVerificationContext class which uses a set of
11
+    trusted certificates to conduct certificate validation, verifying that a
12
+    given certificate is part of a certificate chain rooted with a trusted
13
+    certificate.
14
+  - Adds a verify_certificate method that loads all certificates needed for
15
+    certificate validation from the key manager and uses them to create a
16
+    CertificateVerificationContext object. The context is then used to
17
+    determine if a certificate is valid.
18
+upgrade:
19
+  - The addition of certificate validation as a separate operation from the
20
+    signature verification process preserves backwards compatibility.
21
+    Signatures previously verifiable with cursive will still be verifiable.
22
+    However, their signing certificates may not be valid. Each signing
23
+    certificate should be checked for validity before it is used to conduct
24
+    signature verification.
25
+security:
26
+  - The usage of certificate validation with the signature verification
27
+    process improves the security of signature verification. A signature
28
+    should not be considered valid unless its corresponding certificate is
29
+    also valid.
30
+other:
31
+  - The CertificateVerificationContext is built using a set of trusted
32
+    certificates. However, to conduct certificate verification the context
33
+    builds the full certificate chain, starting with the certificate to
34
+    validate and ending with the self-signed root certificate. If this
35
+    self-signed root certificate is not present in the context, or if one
36
+    of the intermediate certificates is not present in the context, the
37
+    certificate chain cannot be built and certificate validation will fail.

Loading…
Cancel
Save