2 * Copyright (c) 2006-2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 /* Allows us to build genanchors against the BaseSDK. */
30 #undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31 #undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
34 #include <Security/SecCertificateInternal.h>
35 #include <utilities/SecIOFormat.h>
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFBundle.h>
40 #include <CoreFoundation/CFDictionary.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFCalendar.h>
43 #include <CoreFoundation/CFTimeZone.h>
44 #include <CoreFoundation/CFXPCBridge.h>
46 #include <AssertMacros.h>
47 #include <libDER/DER_CertCrl.h>
48 #include <libDER/DER_Encode.h>
49 #include <libDER/DER_Keys.h>
50 #include <libDER/asn1Types.h>
51 #include <libDER/oidsPriv.h>
52 #include "SecBasePriv.h"
53 #include "SecRSAKey.h"
54 #include "SecFramework.h"
56 #include "SecItemPriv.h"
57 #include "SecSignatureVerificationSupport.h"
59 #include <utilities/debugging.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <utilities/SecCFError.h>
62 #include <utilities/SecSCTUtils.h>
63 #include <utilities/array_size.h>
65 #include <libkern/OSByteOrder.h>
67 #include <Security/SecInternal.h>
68 #include <Security/SecFrameworkStrings.h>
69 #include "SecBase64.h"
70 #include "AppleBaselineEscrowCertificates.h"
71 #include "AppleiPhoneDeviceCACertificates.h"
72 #include <ipc/securityd_client.h>
73 #include <Security/SecKeyInternal.h>
75 #pragma clang diagnostic ignored "-Wformat=2"
77 /* The minimum key sizes necessary to not be considered "weak" */
78 #define MIN_RSA_KEY_SIZE 128 // 1024-bit
79 #define MIN_EC_KEY_SIZE 20 // 160-bit
81 /* The minimum key sizes necessary to be considered "strong" */
82 #define MIN_STRONG_RSA_KEY_SIZE 256 // 2048-bit
83 #define MIN_STRONG_EC_KEY_SIZE 28 // 224-bit
85 typedef struct SecCertificateExtension
{
89 } SecCertificateExtension
;
92 kSecSelfSignedUnknown
= 0,
97 struct __SecCertificate
{
100 DERItem _der
; /* Entire certificate in DER form. */
101 DERItem _tbs
; /* To Be Signed cert DER bytes. */
102 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
103 DERItem _signature
; /* The content of the sig bit string. */
106 DERItem _serialNum
; /* Integer. */
107 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
108 DERItem _issuer
; /* Sequence of RDN. */
109 CFAbsoluteTime _notBefore
;
110 CFAbsoluteTime _notAfter
;
111 DERItem _subject
; /* Sequence of RDN. */
112 DERItem _subjectPublicKeyInfo
; /* SPKI */
113 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
114 DERItem _pubKeyDER
; /* contents of bit string */
115 DERItem _issuerUniqueID
; /* bit string, optional */
116 DERItem _subjectUniqueID
; /* bit string, optional */
118 bool _foundUnknownCriticalExtension
;
120 /* Well known certificate extensions. */
121 SecCEBasicConstraints _basicConstraints
;
122 SecCEPolicyConstraints _policyConstraints
;
123 SecCEPolicyMappings _policyMappings
;
124 SecCECertificatePolicies _certificatePolicies
;
125 SecCEInhibitAnyPolicy _inhibitAnyPolicySkipCerts
;
127 /* If KeyUsage extension is not present this is 0, otherwise it's
128 the value of the extension. */
129 SecKeyUsage _keyUsage
;
131 /* OCTETS of SubjectKeyIdentifier extensions KeyIdentifier.
132 Length = 0 if not present. */
133 DERItem _subjectKeyIdentifier
;
135 /* OCTETS of AuthorityKeyIdentifier extensions KeyIdentifier.
136 Length = 0 if not present. */
137 DERItem _authorityKeyIdentifier
;
138 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
139 _authorityKeyIdentifierSerialNumber have non zero length if present.
140 Both are either present or absent together. */
141 DERItem _authorityKeyIdentifierIssuer
;
142 DERItem _authorityKeyIdentifierSerialNumber
;
144 /* Subject alt name extension, if present. Not malloced, it's just a
145 pointer to an element in the _extensions array. */
146 const SecCertificateExtension
*_subjectAltName
;
148 /* Parsed extension values. */
150 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
151 CFMutableArrayRef _crlDistributionPoints
;
153 /* Array of CFURLRefs containing the URI values of accessLocations of each
154 id-ad-ocsp AccessDescription in the Authority Information Access
156 CFMutableArrayRef _ocspResponders
;
158 /* Array of CFURLRefs containing the URI values of accessLocations of each
159 id-ad-caIssuers AccessDescription in the Authority Information Access
161 CFMutableArrayRef _caIssuers
;
163 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
165 CFArrayRef _permittedSubtrees
;
167 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
169 CFArrayRef _excludedSubtrees
;
171 CFMutableArrayRef _embeddedSCTs
;
173 /* All other (non known) extensions. The _extensions array is malloced. */
174 CFIndex _extensionCount
;
175 SecCertificateExtension
*_extensions
;
177 /* Optional cached fields. */
180 CFArrayRef _properties
;
181 CFDataRef _serialNumber
;
182 CFDataRef _normalizedIssuer
;
183 CFDataRef _normalizedSubject
;
184 CFDataRef _authorityKeyID
;
185 CFDataRef _subjectKeyID
;
187 CFDataRef _sha1Digest
;
188 CFTypeRef _keychain_item
;
189 uint8_t _isSelfSigned
;
193 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
195 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
196 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
197 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
199 /* Public Constants for property list keys. */
200 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
201 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
202 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
203 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
205 /* Public Constants for property list values. */
206 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
207 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
208 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
209 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
210 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
211 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
212 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
213 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
214 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
216 /* Extension parsing routine. */
217 typedef bool (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
218 const SecCertificateExtension
*extn
);
220 /* Mapping from extension OIDs (as a DERItem *) to
221 SecCertificateExtensionParser extension parsing routines. */
222 static CFDictionaryRef sExtensionParsers
;
224 /* Forward declarations of static functions. */
225 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
);
226 static void SecCertificateDestroy(CFTypeRef cf
);
227 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
228 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
230 /* Static functions. */
231 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
) {
232 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
233 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
234 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
235 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
236 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
237 CFReleaseSafe(issuer
);
238 CFReleaseSafe(subject
);
242 static void SecCertificateDestroy(CFTypeRef cf
) {
243 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
244 if (certificate
->_certificatePolicies
.policies
) {
245 free(certificate
->_certificatePolicies
.policies
);
246 certificate
->_certificatePolicies
.policies
= NULL
;
248 if (certificate
->_policyMappings
.mappings
) {
249 free(certificate
->_policyMappings
.mappings
);
250 certificate
->_policyMappings
.mappings
= NULL
;
252 CFReleaseNull(certificate
->_crlDistributionPoints
);
253 CFReleaseNull(certificate
->_ocspResponders
);
254 CFReleaseNull(certificate
->_caIssuers
);
255 if (certificate
->_extensions
) {
256 free(certificate
->_extensions
);
257 certificate
->_extensions
= NULL
;
259 CFReleaseNull(certificate
->_pubKey
);
260 CFReleaseNull(certificate
->_der_data
);
261 CFReleaseNull(certificate
->_properties
);
262 CFReleaseNull(certificate
->_serialNumber
);
263 CFReleaseNull(certificate
->_normalizedIssuer
);
264 CFReleaseNull(certificate
->_normalizedSubject
);
265 CFReleaseNull(certificate
->_authorityKeyID
);
266 CFReleaseNull(certificate
->_subjectKeyID
);
267 CFReleaseNull(certificate
->_sha1Digest
);
268 CFReleaseNull(certificate
->_keychain_item
);
269 CFReleaseNull(certificate
->_permittedSubtrees
);
270 CFReleaseNull(certificate
->_excludedSubtrees
);
273 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
274 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
275 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
278 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
280 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
283 /* Hash of the certificate is der length + signature length + last 4 bytes
285 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
286 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
287 size_t der_length
= certificate
->_der
.length
;
288 size_t sig_length
= certificate
->_signature
.length
;
289 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
290 CFHashCode hashCode
= 0;
291 for (; ix
< sig_length
; ++ix
)
292 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
294 return (hashCode
+ der_length
+ sig_length
);
299 /************************************************************************/
300 /************************* General Name Parsing *************************/
301 /************************************************************************/
303 GeneralName ::= CHOICE {
304 otherName [0] OtherName,
305 rfc822Name [1] IA5String,
306 dNSName [2] IA5String,
307 x400Address [3] ORAddress,
308 directoryName [4] Name,
309 ediPartyName [5] EDIPartyName,
310 uniformResourceIdentifier [6] IA5String,
311 iPAddress [7] OCTET STRING,
312 registeredID [8] OBJECT IDENTIFIER}
314 OtherName ::= SEQUENCE {
315 type-id OBJECT IDENTIFIER,
316 value [0] EXPLICIT ANY DEFINED BY type-id }
318 EDIPartyName ::= SEQUENCE {
319 nameAssigner [0] DirectoryString OPTIONAL,
320 partyName [1] DirectoryString }
322 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
323 const DERItem
*generalNameContent
,
324 void *context
, parseGeneralNameCallback callback
) {
326 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
327 return callback(context
, GNT_OtherName
, generalNameContent
);
328 case ASN1_CONTEXT_SPECIFIC
| 1:
329 return callback(context
, GNT_RFC822Name
, generalNameContent
);
330 case ASN1_CONTEXT_SPECIFIC
| 2:
331 return callback(context
, GNT_DNSName
, generalNameContent
);
332 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
333 return callback(context
, GNT_X400Address
, generalNameContent
);
334 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
335 return callback(context
, GNT_DirectoryName
, generalNameContent
);
336 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
337 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
338 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
340 /* Technically I don't think this is valid, but there are certs out
341 in the wild that use a constructed IA5String. In particular the
342 VeriSign Time Stamping Authority CA.cer does this. */
343 DERDecodedInfo uriContent
;
344 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
345 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
346 return callback(context
, GNT_URI
, &uriContent
.content
);
348 case ASN1_CONTEXT_SPECIFIC
| 6:
349 return callback(context
, GNT_URI
, generalNameContent
);
350 case ASN1_CONTEXT_SPECIFIC
| 7:
351 return callback(context
, GNT_IPAddress
, generalNameContent
);
352 case ASN1_CONTEXT_SPECIFIC
| 8:
353 return callback(context
, GNT_RegisteredID
, generalNameContent
);
358 return errSecInvalidCertificate
;
361 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
362 void *context
, parseGeneralNameCallback callback
) {
364 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
365 require_noerr_quiet(drtn
, badDER
);
366 DERDecodedInfo generalNameContent
;
367 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
369 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
370 generalNameContent
.tag
, &generalNameContent
.content
, context
,
375 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
376 return errSecSuccess
;
379 return errSecInvalidCertificate
;
382 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
383 parseGeneralNameCallback callback
) {
384 DERDecodedInfo generalNamesContent
;
385 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
386 require_noerr_quiet(drtn
, badDER
);
387 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
388 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
391 return errSecInvalidCertificate
;
397 GeneralName ::= CHOICE {
398 otherName [0] OtherName,
399 rfc822Name [1] IA5String,
400 dNSName [2] IA5String,
401 x400Address [3] ORAddress,
402 directoryName [4] Name,
403 ediPartyName [5] EDIPartyName,
404 uniformResourceIdentifier [6] IA5String,
405 iPAddress [7] OCTET STRING,
406 registeredID [8] OBJECT IDENTIFIER}
408 EDIPartyName ::= SEQUENCE {
409 nameAssigner [0] DirectoryString OPTIONAL,
410 partyName [1] DirectoryString }
412 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
413 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
415 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
416 generalName
->nameType
= GNT_OtherName
;
417 generalName
->berEncoded
= true;
418 generalName
->name
= *generalNameContent
;
420 case ASN1_CONTEXT_SPECIFIC
| 1:
422 generalName
->nameType
= GNT_RFC822Name
;
423 generalName
->berEncoded
= false;
424 generalName
->name
= *generalNameContent
;
426 case ASN1_CONTEXT_SPECIFIC
| 2:
428 generalName
->nameType
= GNT_DNSName
;
429 generalName
->berEncoded
= false;
430 generalName
->name
= *generalNameContent
;
432 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
433 generalName
->nameType
= GNT_X400Address
;
434 generalName
->berEncoded
= true;
435 generalName
->name
= *generalNameContent
;
437 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
438 generalName
->nameType
= GNT_DirectoryName
;
439 generalName
->berEncoded
= true;
440 generalName
->name
= *generalNameContent
;
442 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
443 generalName
->nameType
= GNT_EdiPartyName
;
444 generalName
->berEncoded
= true;
445 generalName
->name
= *generalNameContent
;
447 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
449 /* Technically I don't think this is valid, but there are certs out
450 in the wild that use a constructed IA5String. In particular the
451 VeriSign Time Stamping Authority CA.cer does this. */
452 DERDecodedInfo decoded
;
453 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
454 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
455 generalName
->nameType
= GNT_URI
;
456 generalName
->berEncoded
= false;
457 generalName
->name
= decoded
.content
;
460 case ASN1_CONTEXT_SPECIFIC
| 6:
461 generalName
->nameType
= GNT_URI
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= *generalNameContent
;
465 case ASN1_CONTEXT_SPECIFIC
| 7:
466 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
467 8 octects, addr/mask for ipv6 it's 32. */
468 generalName
->nameType
= GNT_IPAddress
;
469 generalName
->berEncoded
= false;
470 generalName
->name
= *generalNameContent
;
472 case ASN1_CONTEXT_SPECIFIC
| 8:
473 /* name is the content of an OID. */
474 generalName
->nameType
= GNT_RegisteredID
;
475 generalName
->berEncoded
= false;
476 generalName
->name
= *generalNameContent
;
482 return errSecSuccess
;
484 return errSecInvalidCertificate
;
488 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
490 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
491 CFIndex
*count
, SecCEGeneralName
**name
) {
492 SecCEGeneralName
*generalNames
= NULL
;
494 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
495 require_noerr_quiet(drtn
, badDER
);
496 DERDecodedInfo generalNameContent
;
497 CFIndex generalNamesCount
= 0;
498 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
502 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
504 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
506 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
508 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
510 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
511 &generalNameContent
.content
, &generalNames
[ix
])) {
516 *count
= generalNamesCount
;
517 *name
= generalNames
;
518 return errSecSuccess
;
523 return errSecInvalidCertificate
;
526 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
527 CFIndex
*count
, SecCEGeneralName
**name
) {
528 DERDecodedInfo generalNamesContent
;
529 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
530 require_noerr_quiet(drtn
, badDER
);
531 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
533 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
534 return errSecSuccess
;
536 return errSecInvalidCertificate
;
540 /************************************************************************/
541 /************************** X.509 Name Parsing **************************/
542 /************************************************************************/
544 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
545 const DERItem
*value
, CFIndex rdnIX
);
547 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
548 parseX501NameCallback callback
) {
550 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
551 require_noerr_quiet(drtn
, badDER
);
552 DERDecodedInfo atvContent
;
554 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
555 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
556 DERAttributeTypeAndValue atv
;
557 drtn
= DERParseSequenceContent(&atvContent
.content
,
558 DERNumAttributeTypeAndValueItemSpecs
,
559 DERAttributeTypeAndValueItemSpecs
,
561 require_noerr_quiet(drtn
, badDER
);
562 require_quiet(atv
.type
.length
!= 0, badDER
);
563 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
567 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
569 return errSecSuccess
;
571 return errSecInvalidCertificate
;
574 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
575 parseX501NameCallback callback
) {
577 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
578 require_noerr_quiet(drtn
, badDER
);
579 DERDecodedInfo currDecoded
;
580 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
581 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
582 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
587 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
589 return errSecSuccess
;
592 return errSecInvalidCertificate
;
595 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
596 parseX501NameCallback callback
) {
597 DERDecodedInfo x501NameContent
;
598 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
599 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
600 return errSecInvalidCertificate
;
602 return parseX501NameContent(&x501NameContent
.content
, context
,
607 /************************************************************************/
608 /********************** Extension Parsing Routines **********************/
609 /************************************************************************/
611 static bool SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
612 const SecCertificateExtension
*extn
) {
613 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
614 DERDecodedInfo keyIdentifier
;
615 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
616 require_noerr_quiet(drtn
, badDER
);
617 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
618 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
622 secwarning("Invalid SubjectKeyIdentifier Extension");
626 static bool SecCEPKeyUsage(SecCertificateRef certificate
,
627 const SecCertificateExtension
*extn
) {
628 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
629 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
630 DERDecodedInfo bitStringContent
;
631 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
632 require_noerr_quiet(drtn
, badDER
);
633 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
634 DERSize len
= bitStringContent
.content
.length
- 1;
635 require_quiet(len
== 1 || len
== 2, badDER
);
636 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
637 require_quiet(numUnusedBits
< 8, badDER
);
638 /* Flip the bits in the bit string so the first bit in the lsb. */
639 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
640 uint_fast16_t value
= bitStringContent
.content
.data
[1];
643 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
649 for (ix
= 0; ix
< bits
; ++ix
) {
655 certificate
->_keyUsage
= keyUsage
;
658 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
659 secwarning("Invalid KeyUsage Extension");
663 static bool SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
664 const SecCertificateExtension
*extn
) {
665 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
669 static bool SecCEPSubjectAltName(SecCertificateRef certificate
,
670 const SecCertificateExtension
*extn
) {
671 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
672 certificate
->_subjectAltName
= extn
;
676 static bool SecCEPIssuerAltName(SecCertificateRef certificate
,
677 const SecCertificateExtension
*extn
) {
678 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
682 static bool SecCEPBasicConstraints(SecCertificateRef certificate
,
683 const SecCertificateExtension
*extn
) {
684 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
685 DERBasicConstraints basicConstraints
;
686 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
687 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
688 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
689 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
690 &certificate
->_basicConstraints
.isCA
), badDER
);
691 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
692 require_noerr_quiet(DERParseInteger(
693 &basicConstraints
.pathLenConstraint
,
694 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
695 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
697 certificate
->_basicConstraints
.present
= true;
698 certificate
->_basicConstraints
.critical
= extn
->critical
;
701 certificate
->_basicConstraints
.present
= false;
702 secwarning("Invalid BasicConstraints Extension");
708 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
710 * NameConstraints ::= SEQUENCE {
711 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
712 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
714 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
716 * GeneralSubtree ::= SEQUENCE {
718 * minimum [0] BaseDistance DEFAULT 0,
719 * maximum [1] BaseDistance OPTIONAL }
721 * BaseDistance ::= INTEGER (0..MAX)
723 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
724 CFMutableArrayRef gs
= NULL
;
726 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
727 require_noerr_quiet(drtn
, badDER
);
728 DERDecodedInfo gsContent
;
729 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
730 &kCFTypeArrayCallBacks
),
732 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
733 DERGeneralSubtree derGS
;
734 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
735 drtn
= DERParseSequenceContent(&gsContent
.content
,
736 DERNumGeneralSubtreeItemSpecs
,
737 DERGeneralSubtreeItemSpecs
,
738 &derGS
, sizeof(derGS
));
739 require_noerr_quiet(drtn
, badDER
);
742 * Within this profile, the minimum and maximum fields are not used with
743 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
746 * Because minimum DEFAULT 0, absence equivalent to present and 0.
748 if (derGS
.minimum
.length
) {
750 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
752 require_quiet(minimum
== 0, badDER
);
754 require_quiet(derGS
.maximum
.length
== 0, badDER
);
755 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
757 CFDataRef generalName
= NULL
;
758 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
759 derGS
.generalName
.data
,
760 derGS
.generalName
.length
),
762 CFArrayAppendValue(gs
, generalName
);
763 CFReleaseNull(generalName
);
765 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
767 // since generalSubtrees is a pointer to an instance variable,
768 // make sure we release the existing array before assignment.
769 CFReleaseSafe(*generalSubtrees
);
770 *generalSubtrees
= gs
;
776 secdebug("cert","failed to parse GeneralSubtrees");
780 static bool SecCEPNameConstraints(SecCertificateRef certificate
,
781 const SecCertificateExtension
*extn
) {
782 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
783 DERNameConstraints nc
;
785 drtn
= DERParseSequence(&extn
->extnValue
,
786 DERNumNameConstraintsItemSpecs
,
787 DERNameConstraintsItemSpecs
,
789 require_noerr_quiet(drtn
, badDER
);
790 if (nc
.permittedSubtrees
.length
) {
791 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
793 if (nc
.excludedSubtrees
.length
) {
794 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
799 secwarning("Invalid Name Constraints extension");
803 static OSStatus
appendCRLDPFromGeneralNames(void *context
, SecCEGeneralNameType type
,
804 const DERItem
*value
) {
805 CFMutableArrayRef
*crlDPs
= (CFMutableArrayRef
*)context
;
806 if (type
== GNT_URI
) {
808 url
= CFURLCreateWithBytes(NULL
, value
->data
, value
->length
, kCFStringEncodingASCII
, NULL
);
811 *crlDPs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
813 CFArrayAppendValue(*crlDPs
, url
);
817 return errSecSuccess
;
821 id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
823 CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
825 DistributionPoint ::= SEQUENCE {
826 distributionPoint [0] DistributionPointName OPTIONAL,
827 reasons [1] ReasonFlags OPTIONAL,
828 cRLIssuer [2] GeneralNames OPTIONAL }
830 DistributionPointName ::= CHOICE {
831 fullName [0] GeneralNames,
832 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
834 static bool SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
835 const SecCertificateExtension
*extn
) {
836 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
837 DERSequence crlDPSeq
;
839 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &crlDPSeq
);
840 require_noerr_quiet(drtn
, badDER
);
841 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
842 DERDecodedInfo dpContent
;
843 while ((drtn
= DERDecodeSeqNext(&crlDPSeq
, &dpContent
)) == DR_Success
) {
844 require_quiet(dpContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
845 DERDistributionPoint dp
;
846 drtn
= DERParseSequenceContent(&dpContent
.content
, DERNumDistributionPointItemSpecs
,
847 DERDistributionPointItemSpecs
, &dp
, sizeof(dp
));
848 require_noerr_quiet(drtn
, badDER
);
849 require_quiet(dp
.distributionPoint
.data
|| dp
.cRLIssuer
.data
, badDER
);
850 if (dp
.distributionPoint
.data
) {
851 DERDecodedInfo dpName
;
852 drtn
= DERDecodeItem(&dp
.distributionPoint
, &dpName
);
853 require_noerr_quiet(drtn
, badDER
);
854 switch (dpName
.tag
) {
855 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
856 drtn
= parseGeneralNamesContent(&dpName
.content
, &certificate
->_crlDistributionPoints
,
857 appendCRLDPFromGeneralNames
);
858 require_noerr_quiet(drtn
, badDER
);
860 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1:
861 /* RelativeDistinguishName. Nothing we can do with that. */
867 if (dp
.cRLIssuer
.data
) {
868 drtn
= SecCertificateParseGeneralNames(&dp
.cRLIssuer
, &certificate
->_crlDistributionPoints
,
869 appendCRLDPFromGeneralNames
);
870 require_noerr_quiet(drtn
, badDER
);
873 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
876 secwarning("Invalid CRL Distribution Points extension");
881 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
883 PolicyInformation ::= SEQUENCE {
884 policyIdentifier CertPolicyId,
885 policyQualifiers SEQUENCE SIZE (1..MAX) OF
886 PolicyQualifierInfo OPTIONAL }
888 CertPolicyId ::= OBJECT IDENTIFIER
890 PolicyQualifierInfo ::= SEQUENCE {
891 policyQualifierId PolicyQualifierId,
892 qualifier ANY DEFINED BY policyQualifierId }
894 /* maximum number of policies of 8192 seems more than adequate */
895 #define MAX_CERTIFICATE_POLICIES 8192
896 static bool SecCEPCertificatePolicies(SecCertificateRef certificate
,
897 const SecCertificateExtension
*extn
) {
898 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
901 SecCEPolicyInformation
*policies
= NULL
;
902 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
903 require_noerr_quiet(drtn
, badDER
);
904 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
905 DERDecodedInfo piContent
;
906 DERSize policy_count
= 0;
907 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
908 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
909 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
912 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
913 require_quiet(policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
914 * (policy_count
> 0 ? policy_count
: 1)),
916 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
917 require_noerr_quiet(drtn
, badDER
);
918 DERSize policy_ix
= 0;
919 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
920 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
921 DERPolicyInformation pi
;
922 drtn
= DERParseSequenceContent(&piContent
.content
,
923 DERNumPolicyInformationItemSpecs
,
924 DERPolicyInformationItemSpecs
,
926 require_noerr_quiet(drtn
, badDER
);
927 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
928 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
930 certificate
->_certificatePolicies
.present
= true;
931 certificate
->_certificatePolicies
.critical
= extn
->critical
;
932 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
933 certificate
->_certificatePolicies
.policies
= policies
;
938 certificate
->_certificatePolicies
.present
= false;
939 secwarning("Invalid CertificatePolicies Extension");
944 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
946 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
947 issuerDomainPolicy CertPolicyId,
948 subjectDomainPolicy CertPolicyId }
950 #define MAX_POLICY_MAPPINGS 8192
951 static bool SecCEPPolicyMappings(SecCertificateRef certificate
,
952 const SecCertificateExtension
*extn
) {
953 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
956 SecCEPolicyMapping
*mappings
= NULL
;
957 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
958 require_noerr_quiet(drtn
, badDER
);
959 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
960 DERDecodedInfo pmContent
;
961 DERSize mapping_count
= 0;
962 while ((mapping_count
< MAX_POLICY_MAPPINGS
) &&
963 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
964 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
967 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
968 require_quiet(mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
969 * (mapping_count
> 0 ? mapping_count
: 1)),
971 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
972 require_noerr_quiet(drtn
, badDER
);
973 DERSize mapping_ix
= 0;
974 while ((mapping_ix
< (mapping_count
> 0 ? mapping_count
: 1)) &&
975 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
976 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
978 drtn
= DERParseSequenceContent(&pmContent
.content
,
979 DERNumPolicyMappingItemSpecs
,
980 DERPolicyMappingItemSpecs
,
982 require_noerr_quiet(drtn
, badDER
);
983 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
984 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
986 certificate
->_policyMappings
.present
= true;
987 certificate
->_policyMappings
.critical
= extn
->critical
;
988 certificate
->_policyMappings
.numMappings
= mapping_count
;
989 certificate
->_policyMappings
.mappings
= mappings
;
995 certificate
->_policyMappings
.present
= false;
996 secwarning("Invalid CertificatePolicies Extension");
1001 AuthorityKeyIdentifier ::= SEQUENCE {
1002 keyIdentifier [0] KeyIdentifier OPTIONAL,
1003 authorityCertIssuer [1] GeneralNames OPTIONAL,
1004 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
1005 -- authorityCertIssuer and authorityCertSerialNumber MUST both
1006 -- be present or both be absent
1008 KeyIdentifier ::= OCTET STRING
1010 static bool SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
1011 const SecCertificateExtension
*extn
) {
1012 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1013 DERAuthorityKeyIdentifier akid
;
1015 drtn
= DERParseSequence(&extn
->extnValue
,
1016 DERNumAuthorityKeyIdentifierItemSpecs
,
1017 DERAuthorityKeyIdentifierItemSpecs
,
1018 &akid
, sizeof(akid
));
1019 require_noerr_quiet(drtn
, badDER
);
1020 if (akid
.keyIdentifier
.length
) {
1021 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
1023 if (akid
.authorityCertIssuer
.length
||
1024 akid
.authorityCertSerialNumber
.length
) {
1025 require_quiet(akid
.authorityCertIssuer
.length
&&
1026 akid
.authorityCertSerialNumber
.length
, badDER
);
1027 /* Perhaps put in a subsection called Authority Certificate Issuer. */
1028 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
1029 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1034 secwarning("Invalid AuthorityKeyIdentifier Extension");
1038 static bool SecCEPPolicyConstraints(SecCertificateRef certificate
,
1039 const SecCertificateExtension
*extn
) {
1040 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1041 DERPolicyConstraints pc
;
1043 drtn
= DERParseSequence(&extn
->extnValue
,
1044 DERNumPolicyConstraintsItemSpecs
,
1045 DERPolicyConstraintsItemSpecs
,
1047 require_noerr_quiet(drtn
, badDER
);
1048 if (pc
.requireExplicitPolicy
.length
) {
1049 require_noerr_quiet(DERParseInteger(
1050 &pc
.requireExplicitPolicy
,
1051 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1052 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1054 if (pc
.inhibitPolicyMapping
.length
) {
1055 require_noerr_quiet(DERParseInteger(
1056 &pc
.inhibitPolicyMapping
,
1057 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1058 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1061 certificate
->_policyConstraints
.present
= true;
1062 certificate
->_policyConstraints
.critical
= extn
->critical
;
1066 certificate
->_policyConstraints
.present
= false;
1067 secwarning("Invalid PolicyConstraints Extension");
1071 static bool SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1072 const SecCertificateExtension
*extn
) {
1073 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1078 InhibitAnyPolicy ::= SkipCerts
1080 SkipCerts ::= INTEGER (0..MAX)
1082 static bool SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1083 const SecCertificateExtension
*extn
) {
1084 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1085 DERDecodedInfo iapContent
;
1086 require_noerr_quiet(DERDecodeItem(&extn
->extnValue
, &iapContent
), badDER
);
1087 require_quiet(iapContent
.tag
== ASN1_INTEGER
, badDER
);
1088 require_noerr_quiet(DERParseInteger(
1089 &iapContent
.content
,
1090 &certificate
->_inhibitAnyPolicySkipCerts
.skipCerts
), badDER
);
1092 certificate
->_inhibitAnyPolicySkipCerts
.present
= true;
1093 certificate
->_inhibitAnyPolicySkipCerts
.critical
= extn
->critical
;
1096 certificate
->_inhibitAnyPolicySkipCerts
.present
= false;
1097 secwarning("Invalid InhibitAnyPolicy Extension");
1102 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1104 AuthorityInfoAccessSyntax ::=
1105 SEQUENCE SIZE (1..MAX) OF AccessDescription
1107 AccessDescription ::= SEQUENCE {
1108 accessMethod OBJECT IDENTIFIER,
1109 accessLocation GeneralName }
1111 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1113 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1115 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1117 static bool SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1118 const SecCertificateExtension
*extn
) {
1119 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1122 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1123 require_noerr_quiet(drtn
, badDER
);
1124 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1125 DERDecodedInfo adContent
;
1126 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1127 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1128 DERAccessDescription ad
;
1129 drtn
= DERParseSequenceContent(&adContent
.content
,
1130 DERNumAccessDescriptionItemSpecs
,
1131 DERAccessDescriptionItemSpecs
,
1133 require_noerr_quiet(drtn
, badDER
);
1134 CFMutableArrayRef
*urls
;
1135 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1136 urls
= &certificate
->_ocspResponders
;
1137 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1138 urls
= &certificate
->_caIssuers
;
1142 DERDecodedInfo generalNameContent
;
1143 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1144 require_noerr_quiet(drtn
, badDER
);
1145 switch (generalNameContent
.tag
) {
1147 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1148 /* Technically I don't think this is valid, but there are certs out
1149 in the wild that use a constructed IA5String. In particular the
1150 VeriSign Time Stamping Authority CA.cer does this. */
1152 case ASN1_CONTEXT_SPECIFIC
| 6:
1154 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1155 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1156 kCFStringEncodingASCII
, NULL
);
1159 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1160 CFArrayAppendValue(*urls
, url
);
1166 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1167 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1172 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1175 secwarning("Invalid Authority Information Access extension");
1179 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1180 * This is a DER sequence with the leading tag and length bytes removed,
1181 * to match what tbsCert.issuer contains.
1183 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1184 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1185 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1186 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1187 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1188 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1189 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1190 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1191 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1192 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1193 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1196 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1198 certificate
->_crlDistributionPoints
||
1199 certificate
->_ocspResponders
) {
1200 /* We already have an OCSP or CRL URI (or no cert) */
1203 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1204 CFURLRef url
= NULL
;
1205 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1206 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1207 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1208 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1209 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1210 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1211 kCFStringEncodingASCII
, NULL
);
1214 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1215 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1216 CFArrayAppendValue(*urls
, url
);
1221 static bool SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1222 const SecCertificateExtension
*extn
) {
1223 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1227 static bool SecCEPNetscapeCertType(SecCertificateRef certificate
,
1228 const SecCertificateExtension
*extn
) {
1229 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1233 static bool SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1234 const SecCertificateExtension
*extn
) {
1235 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1239 static bool SecCEPEscrowMarker(SecCertificateRef certificate
,
1240 const SecCertificateExtension
*extn
) {
1241 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1246 /* Dictionary key callback for comparing to DERItems. */
1247 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1248 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1251 /* Dictionary key callback calculating the hash of a DERItem. */
1252 static CFHashCode
SecDERItemHash(const void *value
) {
1253 const DERItem
*derItem
= (const DERItem
*)value
;
1254 CFHashCode hash
= derItem
->length
;
1255 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1256 for (; ix
< derItem
->length
; ++ix
) {
1257 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1263 /* Dictionary key callbacks using the above 2 functions. */
1264 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1268 NULL
, /* copyDescription */
1269 SecDERItemEqual
, /* equal */
1270 SecDERItemHash
/* hash */
1273 static void SecCertificateInitializeExtensionParsers(void) {
1274 /* Build a dictionary that maps from extension OIDs to callback functions
1275 which can parse the extension of the type given. */
1276 static const void *extnOIDs
[] = {
1277 &oidSubjectKeyIdentifier
,
1279 &oidPrivateKeyUsagePeriod
,
1282 &oidBasicConstraints
,
1283 &oidNameConstraints
,
1284 &oidCrlDistributionPoints
,
1285 &oidCertificatePolicies
,
1287 &oidAuthorityKeyIdentifier
,
1288 &oidPolicyConstraints
,
1289 &oidExtendedKeyUsage
,
1290 &oidInhibitAnyPolicy
,
1291 &oidAuthorityInfoAccess
,
1292 &oidSubjectInfoAccess
,
1293 &oidNetscapeCertType
,
1294 &oidEntrustVersInfo
,
1295 &oidApplePolicyEscrowService
1297 static const void *extnParsers
[] = {
1298 SecCEPSubjectKeyIdentifier
,
1300 SecCEPPrivateKeyUsagePeriod
,
1301 SecCEPSubjectAltName
,
1302 SecCEPIssuerAltName
,
1303 SecCEPBasicConstraints
,
1304 SecCEPNameConstraints
,
1305 SecCEPCrlDistributionPoints
,
1306 SecCEPCertificatePolicies
,
1307 SecCEPPolicyMappings
,
1308 SecCEPAuthorityKeyIdentifier
,
1309 SecCEPPolicyConstraints
,
1310 SecCEPExtendedKeyUsage
,
1311 SecCEPInhibitAnyPolicy
,
1312 SecCEPAuthorityInfoAccess
,
1313 SecCEPSubjectInfoAccess
,
1314 SecCEPNetscapeCertType
,
1315 SecCEPEntrustVersInfo
,
1318 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1319 extnParsers
, array_size(extnOIDs
),
1320 &SecDERItemKeyCallBacks
, NULL
);
1323 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateCopyDescription
, NULL
, NULL
, ^{
1324 SecCertificateInitializeExtensionParsers();
1327 static bool isAppleExtensionOID(const DERItem
*extnID
)
1329 static const uint8_t appleExtension
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1330 return (extnID
&& extnID
->data
&&
1331 extnID
->length
> sizeof(appleExtension
) &&
1332 !memcmp(extnID
->data
, appleExtension
, sizeof(appleExtension
)));
1335 /* Given the contents of an X.501 Name return the contents of a normalized
1337 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1338 const DERItem
*x501name
) {
1339 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1340 CFIndex length
= x501name
->length
;
1341 CFDataSetLength(result
, length
);
1342 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1345 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1347 require_noerr_quiet(drtn
, badDER
);
1350 /* Always points to last rdn tag. */
1351 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1352 /* Offset relative to base of current rdn set tag. */
1353 CFIndex rdnTagLocation
= 0;
1354 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1355 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1356 /* We don't allow empty RDNs. */
1357 require_quiet(rdn
.content
.length
!= 0, badDER
);
1358 /* Length of the tag and length of the current rdn. */
1359 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1360 CFIndex rdnContentLength
= rdn
.content
.length
;
1361 /* Copy the tag and length of the RDN. */
1362 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1365 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1366 require_quiet(drtn
== DR_Success
, badDER
);
1369 /* Always points to tag of current atv sequence. */
1370 const DERByte
*atvTag
= atvSeq
.nextItem
;
1371 /* Offset relative to base of current atv sequence tag. */
1372 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1373 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1374 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1375 /* Length of the tag and length of the current atv. */
1376 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1377 CFIndex atvContentLength
= atv
.content
.length
;
1378 /* Copy the tag and length of the atv and the atv itself. */
1379 memcpy(base
+ atvTagLocation
, atvTag
,
1380 atvTLLength
+ atv
.content
.length
);
1382 /* Now decode the atv sequence. */
1383 DERAttributeTypeAndValue atvPair
;
1384 drtn
= DERParseSequenceContent(&atv
.content
,
1385 DERNumAttributeTypeAndValueItemSpecs
,
1386 DERAttributeTypeAndValueItemSpecs
,
1387 &atvPair
, sizeof(atvPair
));
1388 require_noerr_quiet(drtn
, badDER
);
1389 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1390 DERDecodedInfo value
;
1391 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1392 require_noerr_quiet(drtn
, badDER
);
1394 /* (c) attribute values in PrintableString are not case sensitive
1395 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1397 (d) attribute values in PrintableString are compared after
1398 removing leading and trailing white space and converting internal
1399 substrings of one or more consecutive white space characters to a
1401 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1402 /* Offset relative to base of current value tag. */
1403 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1404 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1405 CFIndex valueContentLength
= value
.content
.length
;
1407 /* Now copy all the bytes, but convert to upper case while
1408 doing so and convert multiple whitespace chars into a
1410 bool lastWasBlank
= false;
1411 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1412 CFIndex valueCurrentLocation
= valueLocation
;
1414 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1415 UInt8 ch
= value
.content
.data
[ix
];
1420 /* Don't insert a space for first character
1422 if (valueCurrentLocation
> valueLocation
) {
1423 base
[valueCurrentLocation
++] = ' ';
1425 lastWasBlank
= true;
1428 lastWasBlank
= false;
1429 if ('a' <= ch
&& ch
<= 'z') {
1430 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1432 base
[valueCurrentLocation
++] = ch
;
1436 /* Finally if lastWasBlank remove the trailing space. */
1437 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1438 valueCurrentLocation
--;
1440 /* Adjust content length to normalized length. */
1441 valueContentLength
= valueCurrentLocation
- valueLocation
;
1443 /* Number of bytes by which the length should be shorted. */
1444 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1445 if (lengthDiff
== 0) {
1446 /* Easy case no need to adjust lengths. */
1448 /* Hard work we need to go back and fix up length fields
1450 1) The value itself.
1451 2) The ATV Sequence containing type/value
1452 3) The RDN Set containing one or more atv pairs.
1456 /* Step 1 fix up length of value. */
1457 /* Length of value tag and length minus the tag. */
1458 DERSize newValueTLLength
= valueTLLength
- 1;
1459 drtn
= DEREncodeLength(valueContentLength
,
1460 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1461 require(drtn
== DR_Success
, badDER
);
1462 /* Add the length of the tag back in. */
1464 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1466 /* The size of the length field changed, let's slide
1467 the value back by valueLLDiff bytes. */
1468 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1469 base
+ valueTagLocation
+ valueTLLength
,
1470 valueContentLength
);
1471 /* The length diff for the enclosing object. */
1472 lengthDiff
+= valueLLDiff
;
1475 /* Step 2 fix up length of the enclosing ATV Sequence. */
1476 atvContentLength
-= lengthDiff
;
1477 DERSize newATVTLLength
= atvTLLength
- 1;
1478 drtn
= DEREncodeLength(atvContentLength
,
1479 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1480 require(drtn
== DR_Success
, badDER
);
1481 /* Add the length of the tag back in. */
1483 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1485 /* The size of the length field changed, let's slide
1486 the value back by valueLLDiff bytes. */
1487 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1488 base
+ atvTagLocation
+ atvTLLength
,
1490 /* The length diff for the enclosing object. */
1491 lengthDiff
+= atvLLDiff
;
1492 atvTLLength
= newATVTLLength
;
1495 /* Step 3 fix up length of enclosing RDN Set. */
1496 rdnContentLength
-= lengthDiff
;
1497 DERSize newRDNTLLength
= rdnTLLength
- 1;
1498 drtn
= DEREncodeLength(rdnContentLength
,
1499 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1500 require_quiet(drtn
== DR_Success
, badDER
);
1501 /* Add the length of the tag back in. */
1503 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1505 /* The size of the length field changed, let's slide
1506 the value back by valueLLDiff bytes. */
1507 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1508 base
+ rdnTagLocation
+ rdnTLLength
,
1510 /* The length diff for the enclosing object. */
1511 lengthDiff
+= rdnLLDiff
;
1512 rdnTLLength
= newRDNTLLength
;
1514 /* Adjust the locations that might have changed due to
1516 atvTagLocation
-= rdnLLDiff
;
1518 (void) lengthDiff
; // No next object, silence analyzer
1521 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1522 atvTag
= atvSeq
.nextItem
;
1524 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1525 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1526 rdnTag
= rdnSeq
.nextItem
;
1528 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1529 /* Truncate the result to the proper length. */
1530 CFDataSetLength(result
, rdnTagLocation
);
1539 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
1540 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
1541 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
1542 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
1544 CFDataSetLength(sequence
, sequence_length
);
1545 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1546 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
1547 require_noerr_quiet(DEREncodeLength(content
->length
,
1548 sequence_ptr
, &seq_len_length
), out
);
1549 sequence_ptr
+= seq_len_length
;
1550 memcpy(sequence_ptr
, content
->data
, content
->length
);
1553 CFReleaseSafe(sequence
);
1557 static CFDataRef
SecCopySequenceFromContent(CFDataRef content
) {
1559 tmpItem
.data
= (void *)CFDataGetBytePtr(content
);
1560 tmpItem
.length
= CFDataGetLength(content
);
1562 return SecDERItemCopySequence(&tmpItem
);
1565 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1567 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1568 DERDecodedInfo content
;
1569 /* Decode top level sequence into DERItem */
1570 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1571 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1575 CFDataRef
SecDistinguishedNameCopyNormalizedSequence(CFDataRef distinguished_name
)
1577 if (!distinguished_name
) { return NULL
; }
1578 CFDataRef normalizedContent
= SecDistinguishedNameCopyNormalizedContent(distinguished_name
);
1579 if (!normalizedContent
) { return NULL
; }
1580 CFDataRef result
= SecCopySequenceFromContent(normalizedContent
);
1581 CFReleaseNull(normalizedContent
);
1585 /* AUDIT[securityd]:
1586 certificate->_der is a caller provided data of any length (might be 0).
1588 Top level certificate decode.
1590 static bool SecCertificateParse(SecCertificateRef certificate
)
1595 require_quiet(certificate
, badCert
);
1596 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1598 /* top level decode */
1599 DERSignedCertCrl signedCert
;
1600 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1601 DERSignedCertCrlItemSpecs
, &signedCert
,
1602 sizeof(signedCert
));
1603 require_noerr_quiet(drtn
, badCert
);
1604 /* Store tbs since we need to digest it for verification later on. */
1605 certificate
->_tbs
= signedCert
.tbs
;
1607 /* decode the TBSCert - it was saved in full DER form */
1609 drtn
= DERParseSequence(&signedCert
.tbs
,
1610 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1611 &tbsCert
, sizeof(tbsCert
));
1612 require_noerr_quiet(drtn
, badCert
);
1614 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1615 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1616 of the params field. */
1617 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1618 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1619 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1620 require_noerr_quiet(drtn
, badCert
);
1622 /* The contents of signedCert.sig is a bit string whose contents
1623 are the signature itself. */
1624 DERByte numUnusedBits
;
1625 drtn
= DERParseBitString(&signedCert
.sig
,
1626 &certificate
->_signature
, &numUnusedBits
);
1627 require_noerr_quiet(drtn
, badCert
);
1629 /* Now decode the tbsCert. */
1631 /* First we turn the optional version into an int. */
1632 if (tbsCert
.version
.length
) {
1633 DERDecodedInfo decoded
;
1634 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1635 require_noerr_quiet(drtn
, badCert
);
1636 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1637 require_quiet(decoded
.content
.length
== 1, badCert
);
1638 certificate
->_version
= decoded
.content
.data
[0];
1639 if (certificate
->_version
> 2) {
1640 secwarning("Invalid certificate version (%d), must be 0..2",
1641 certificate
->_version
);
1643 require_quiet(certificate
->_version
> 0, badCert
);
1644 require_quiet(certificate
->_version
< 3, badCert
);
1646 certificate
->_version
= 0;
1649 /* The serial number is in the tbsCert.serialNum - it was saved in
1650 INTEGER form without the tag and length. */
1651 certificate
->_serialNum
= tbsCert
.serialNum
;
1653 /* Note: RFC5280 4.1.2.2 limits serial number values to 20 octets.
1654 For now, we warn about larger values, but will still create the
1655 certificate with values up to 36 octets to avoid breaking some
1656 nonconforming certs with slightly longer serial numbers.
1657 We also explicitly allow serial numbers of 21 octets where the
1658 leading byte is 0x00 which is used to make a negative 20 octet
1661 if (tbsCert
.serialNum
.length
< 1 || tbsCert
.serialNum
.length
> 21 ||
1662 (tbsCert
.serialNum
.length
== 21 && tbsCert
.serialNum
.data
[0] != 0x00)) {
1663 secwarning("Invalid serial number length (%ld), must be 1..20",
1664 tbsCert
.serialNum
.length
);
1666 require_quiet(tbsCert
.serialNum
.data
!= NULL
&&
1667 tbsCert
.serialNum
.length
>= 1 &&
1668 tbsCert
.serialNum
.length
<= 37, badCert
);
1669 certificate
->_serialNumber
= CFDataCreate(allocator
,
1670 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1672 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1673 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1674 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1675 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1676 require_noerr_quiet(drtn
, badCert
);
1678 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1679 and length fields. */
1680 certificate
->_issuer
= tbsCert
.issuer
;
1681 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1684 /* sequence we're given: decode the tbsCerts Validity sequence. */
1685 DERValidity validity
;
1686 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1687 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1688 &validity
, sizeof(validity
));
1689 require_noerr_quiet(drtn
, badCert
);
1690 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1691 &certificate
->_notBefore
), badCert
);
1692 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1693 &certificate
->_notAfter
), badCert
);
1695 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1696 and length fields. */
1697 certificate
->_subject
= tbsCert
.subject
;
1698 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1701 /* Keep the SPKI around for CT */
1702 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1704 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1705 DERSubjPubKeyInfo pubKeyInfo
;
1706 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1707 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1708 &pubKeyInfo
, sizeof(pubKeyInfo
));
1709 require_noerr_quiet(drtn
, badCert
);
1711 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1712 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1713 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1714 &certificate
->_algId
, sizeof(certificate
->_algId
));
1715 require_noerr_quiet(drtn
, badCert
);
1717 /* Now we can figure out the key's algorithm id and params based on
1718 certificate->_algId.oid. */
1720 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1721 are a PKCS1 format RSA key. */
1722 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1723 &certificate
->_pubKeyDER
, &numUnusedBits
);
1724 require_noerr_quiet(drtn
, badCert
);
1726 /* The contents of tbsCert.issuerID is a bit string. */
1727 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1729 /* The contents of tbsCert.subjectID is a bit string. */
1730 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1733 if (tbsCert
.extensions
.length
) {
1734 CFIndex extensionCount
= 0;
1737 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1739 require_noerr_quiet(drtn
, badCert
);
1740 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1741 DERDecodedInfo currDecoded
;
1742 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1744 /* ! = MUST recognize ? = SHOULD recognize
1747 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1748 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1749 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1750 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1751 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1752 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1753 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1754 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1756 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1757 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1758 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1759 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1760 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1761 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1762 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1763 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1765 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1766 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1771 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1773 /* Put some upper limit on the number of extensions allowed. */
1774 require_quiet(extensionCount
< 10000, badCert
);
1775 certificate
->_extensionCount
= extensionCount
;
1776 certificate
->_extensions
=
1777 malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1778 require_quiet(certificate
->_extensions
, badCert
);
1781 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1782 require_noerr_quiet(drtn
, badCert
);
1783 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1784 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1785 require_quiet(drtn
== DR_Success
||
1786 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1787 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1789 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1790 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1791 &extn
, sizeof(extn
));
1792 require_noerr_quiet(drtn
, badCert
);
1793 /* Copy stuff into certificate->extensions[ix]. */
1794 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1795 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1796 &certificate
->_extensions
[ix
].critical
), badCert
);
1797 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1799 SecCertificateExtensionParser parser
=
1800 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1801 sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1803 /* Invoke the parser. If the extension is critical and the
1804 * parser fails, fail the cert. */
1805 require_quiet(parser(certificate
, &certificate
->_extensions
[ix
]) ||
1806 !certificate
->_extensions
[ix
].critical
, badCert
);
1807 } else if (certificate
->_extensions
[ix
].critical
) {
1808 if (isAppleExtensionOID(&extn
.extnID
)) {
1811 secdebug("cert", "Found unknown critical extension");
1812 certificate
->_foundUnknownCriticalExtension
= true;
1814 secdebug("cert", "Found unknown non critical extension");
1818 checkForMissingRevocationInfo(certificate
);
1827 /* Public API functions. */
1828 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1829 const UInt8
*der_bytes
, CFIndex der_length
) {
1830 if (der_bytes
== NULL
) return NULL
;
1831 if (der_length
== 0) return NULL
;
1833 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1834 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1835 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1837 memset((char*)result
+ sizeof(result
->_base
), 0,
1838 sizeof(*result
) - sizeof(result
->_base
));
1839 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1840 result
->_der
.length
= der_length
;
1841 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1842 if (!SecCertificateParse(result
)) {
1850 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1851 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1852 const UInt8
*der_bytes
, CFIndex der_length
);
1854 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1855 const UInt8
*der_bytes
, CFIndex der_length
) {
1856 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1858 /* @@@ End of placeholder. */
1860 /* AUDIT[securityd](done):
1861 der_certificate is a caller provided data of any length (might be 0), only
1862 its cf type has been checked.
1864 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1865 CFDataRef der_certificate
) {
1866 if (!der_certificate
) {
1869 CFIndex size
= sizeof(struct __SecCertificate
);
1870 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1871 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1873 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1874 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1875 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1876 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1877 if (!SecCertificateParse(result
)) {
1885 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1886 CFDataRef der_certificate
,
1887 CFTypeRef keychain_item
)
1889 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1891 CFRetainSafe(keychain_item
);
1892 result
->_keychain_item
= keychain_item
;
1897 OSStatus
SecCertificateSetKeychainItem(SecCertificateRef certificate
,
1898 CFTypeRef keychain_item
)
1903 CFRetainSafe(keychain_item
);
1904 CFReleaseSafe(certificate
->_keychain_item
);
1905 certificate
->_keychain_item
= keychain_item
;
1906 return errSecSuccess
;
1909 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1911 CFDataRef result
= NULL
;
1915 if (certificate
->_der_data
) {
1916 CFRetain(certificate
->_der_data
);
1917 result
= certificate
->_der_data
;
1919 result
= CFDataCreate(CFGetAllocator(certificate
),
1920 certificate
->_der
.data
, certificate
->_der
.length
);
1922 /* FIXME: If we wish to cache result we need to lock the certificate.
1923 Also this create 2 copies of the certificate data which is somewhat
1926 certificate
->_der_data
= result
;
1933 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1934 return certificate
->_der
.length
;
1937 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1938 return certificate
->_der
.data
;
1941 /* Used to recreate preCert from cert for Certificate Transparency */
1942 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1944 CFDataRef outData
= NULL
;
1945 DERItem tbsIn
= certificate
->_tbs
;
1946 DERItem tbsOut
= {0,};
1947 DERItem extensionsOut
= {0,};
1948 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1949 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
1953 require_quiet(extensionsList
&& extensionsListSpecs
, out
);
1955 /* decode the TBSCert - it was saved in full DER form */
1956 drtn
= DERParseSequence(&tbsIn
,
1957 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1958 &tbsCert
, sizeof(tbsCert
));
1959 require_noerr_quiet(drtn
, out
);
1961 /* Go over extensions and filter any SCT extension */
1962 CFIndex extensionsCount
= 0;
1964 if (tbsCert
.extensions
.length
) {
1967 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1969 require_noerr_quiet(drtn
, out
);
1970 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
1971 DERDecodedInfo currDecoded
;
1972 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1974 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
1976 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1977 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1978 &extn
, sizeof(extn
));
1979 require_noerr_quiet(drtn
, out
);
1981 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
1982 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
1985 extensionsList
[extensionsCount
] = currDecoded
.content
;
1986 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
1987 extensionsListSpecs
[extensionsCount
].options
= 0;
1988 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
1993 require_quiet(drtn
== DR_EndOfSequence
, out
);
1997 /* Encode extensions */
1998 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
1999 extensionsOut
.data
= malloc(extensionsOut
.length
);
2000 require_quiet(extensionsOut
.data
, out
);
2001 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
2002 require_noerr_quiet(drtn
, out
);
2004 tbsCert
.extensions
= extensionsOut
;
2006 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
2007 tbsOut
.data
= malloc(tbsOut
.length
);
2008 require_quiet(tbsOut
.data
, out
);
2009 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
2010 require_noerr_quiet(drtn
, out
);
2012 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
2015 if (extensionsOut
.data
) free(extensionsOut
.data
);
2016 if (tbsOut
.data
) free(tbsOut
.data
);
2017 if (extensionsList
) free(extensionsList
);
2018 if (extensionsListSpecs
) free(extensionsListSpecs
);
2023 /* From rfc3280 - Appendix B. ASN.1 Notes
2025 Object Identifiers (OIDs) are used throughout this specification to
2026 identify certificate policies, public key and signature algorithms,
2027 certificate extensions, etc. There is no maximum size for OIDs.
2028 This specification mandates support for OIDs which have arc elements
2029 with values that are less than 2^28, that is, they MUST be between 0
2030 and 268,435,455, inclusive. This allows each arc element to be
2031 represented within a single 32 bit word. Implementations MUST also
2032 support OIDs where the length of the dotted decimal (see [RFC 2252],
2033 section 4.1) string representation can be up to 100 bytes
2034 (inclusive). Implementations MUST be able to handle OIDs with up to
2035 20 elements (inclusive). CAs SHOULD NOT issue certificates which
2036 contain OIDs that exceed these requirements. Likewise, CRL issuers
2037 SHOULD NOT issue CRLs which contain OIDs that exceed these
2041 /* Oids longer than this are considered invalid. */
2042 #define MAX_OID_SIZE 32
2044 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
2045 const DERItem
*oid
) {
2047 if (oid
->length
== 0) {
2048 return SecCopyCertString(SEC_NULL_KEY
);
2050 if (oid
->length
> MAX_OID_SIZE
) {
2051 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
2054 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
2056 // The first two levels are encoded into one byte, since the root level
2057 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
2058 // y may be > 39, so we have to add special-case handling for this.
2059 uint32_t x
= oid
->data
[0] / 40;
2060 uint32_t y
= oid
->data
[0] % 40;
2063 // Handle special case for large y if x = 2
2067 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
2070 for (x
= 1; x
< oid
->length
; ++x
)
2072 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
2073 /* @@@ value may not span more than 4 bytes. */
2074 /* A max number of 20 values is allowed. */
2075 if (!(oid
->data
[x
] & 0x80))
2077 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
2084 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
2085 const DERItem
*oid
) {
2086 if (oid
->length
== 0) {
2087 return SecCopyCertString(SEC_NULL_KEY
);
2090 /* Build the key we use to lookup the localized OID description. */
2091 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
2092 oid
->length
* 3 + 5);
2093 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
2095 for (ix
= 0; ix
< oid
->length
; ++ix
)
2096 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
2098 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
2099 if (CFEqual(oidKey
, name
)) {
2101 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2108 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
2109 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
2110 have a length of exactly 4 or 16 octects. */
2111 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
2112 const DERItem
*ip
) {
2113 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
2114 4 octects addr, or 8 octects, addr/mask for ipv6 it's
2115 16 octects addr, or 32 octects addr/mask. */
2116 CFStringRef value
= NULL
;
2117 if (ip
->length
== 4) {
2118 value
= CFStringCreateWithFormat(allocator
, NULL
,
2119 CFSTR("%u.%u.%u.%u"),
2120 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2121 } else if (ip
->length
== 16) {
2122 value
= CFStringCreateWithFormat(allocator
, NULL
,
2123 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
2124 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
2125 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2126 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2127 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2128 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2134 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2135 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
) {
2136 CFDictionaryRef property
;
2139 if (localizedLabel
) {
2142 ll
= localizedLabel
= SecCopyCertString(label
);
2144 const void *all_keys
[4];
2145 all_keys
[0] = kSecPropertyKeyType
;
2146 all_keys
[1] = kSecPropertyKeyLabel
;
2147 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2148 all_keys
[3] = kSecPropertyKeyValue
;
2149 const void *property_values
[] = {
2155 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2156 all_keys
, property_values
, value
? 4 : 3,
2157 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2160 const void *nolabel_keys
[2];
2161 nolabel_keys
[0] = kSecPropertyKeyType
;
2162 nolabel_keys
[1] = kSecPropertyKeyValue
;
2163 const void *property_values
[] = {
2167 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2168 nolabel_keys
, property_values
, 2,
2169 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2172 CFArrayAppendValue(properties
, property
);
2173 CFRelease(property
);
2177 #define UTC_TIME_NOSEC_ZULU_LEN 11
2179 #define UTC_TIME_ZULU_LEN 13
2180 /* YYMMDDhhmmssThhmm */
2181 #define UTC_TIME_LOCALIZED_LEN 17
2182 /* YYYYMMDDhhmmssZ */
2183 #define GENERALIZED_TIME_ZULU_LEN 15
2184 /* YYYYMMDDhhmmssThhmm */
2185 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2187 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2189 static inline int parseDecimalPair(const DERByte
**p
) {
2190 const DERByte
*cp
= *p
;
2192 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2195 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime.
2196 Return a CFErrorRef in the error parameter if decoding fails.
2197 Note that this is needed to distinguish an error condition from a
2198 valid time which specifies 2001-01-01 00:00:00 (i.e. a value of 0).
2200 static CFAbsoluteTime
SecAbsoluteTimeFromDateContentWithError(DERTag tag
,
2201 const uint8_t *bytes
,
2203 CFErrorRef
*error
) {
2207 if (NULL
== bytes
|| 0 == length
) {
2211 bool isUtcLength
= false;
2212 bool isLocalized
= false;
2213 bool noSeconds
= false;
2215 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2219 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2222 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2224 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2227 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2230 default: /* unknown format */
2234 /* Make sure the der tag fits the thing inside it. */
2235 if (tag
== ASN1_UTC_TIME
) {
2239 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2247 const DERByte
*cp
= bytes
;
2248 /* Check that all characters are digits, except if localized the timezone
2249 indicator or if not localized the 'Z' at the end. */
2251 for (ix
= 0; ix
< length
; ++ix
) {
2252 if (!(isdigit(cp
[ix
]))) {
2253 if ((isLocalized
&& ix
== length
- 5 &&
2254 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2255 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2262 /* Parse the date and time fields. */
2263 int year
, month
, day
, hour
, minute
, second
;
2265 year
= parseDecimalPair(&cp
);
2267 /* 0 <= year < 50 : assume century 21 */
2269 } else if (year
< 70) {
2270 /* 50 <= year < 70 : illegal per PKIX */
2273 /* 70 < year <= 99 : assume century 20 */
2277 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2279 month
= parseDecimalPair(&cp
);
2280 day
= parseDecimalPair(&cp
);
2281 hour
= parseDecimalPair(&cp
);
2282 minute
= parseDecimalPair(&cp
);
2286 second
= parseDecimalPair(&cp
);
2289 CFTimeInterval timeZoneOffset
;
2291 /* ZONE INDICATOR */
2292 int multiplier
= *cp
++ == '+' ? 60 : -60;
2293 timeZoneOffset
= multiplier
*
2294 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2299 secdebug("dateparse",
2300 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2301 (int) length
, bytes
, year
, month
,
2302 day
, hour
, minute
, second
,
2303 timeZoneOffset
/ 60);
2305 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2306 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2307 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
2308 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2309 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2314 int dy
= year
- 2001;
2319 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2320 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2322 day
+= is_leap_year
;
2324 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2325 return absTime
- timeZoneOffset
;
2329 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
2334 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2336 return SecAbsoluteTimeFromDateContentWithError(tag
, bytes
, length
, NULL
);
2339 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2340 CFAbsoluteTime
*pabsTime
) {
2341 CFErrorRef error
= NULL
;
2342 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContentWithError(tag
, date
->data
,
2343 date
->length
, &error
);
2345 secwarning("Invalid date specification in certificate (see RFC5280 4.1.2.5)");
2350 *pabsTime
= absTime
;
2354 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2355 true if the date was valid and properly decoded, also return the result in
2356 absTime. Return false otherwise. */
2357 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2358 CFAbsoluteTime
*absTime
) {
2359 if (dateChoice
->length
== 0) return false;
2361 DERDecodedInfo decoded
;
2362 if (DERDecodeItem(dateChoice
, &decoded
))
2365 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2369 static void appendDataProperty(CFMutableArrayRef properties
,
2370 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2371 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2372 der_data
->data
, der_data
->length
);
2373 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2378 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2380 CFStringRef localizedLabel
,
2381 const DERItem
*der_data
,
2382 CFStringRef labelFormat
) {
2383 CFStringRef newLabel
=
2384 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2385 labelFormat
, label
);
2387 if (localizedLabel
) {
2390 ll
= localizedLabel
= SecCopyCertString(label
);
2392 CFStringRef localizedLabelFormat
= SecCopyCertString(labelFormat
);
2393 CFStringRef newLocalizedLabel
=
2394 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2395 localizedLabelFormat
, localizedLabel
);
2397 CFReleaseSafe(localizedLabelFormat
);
2398 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
);
2399 CFReleaseSafe(newLabel
);
2400 CFReleaseSafe(newLocalizedLabel
);
2404 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2405 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2406 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2410 static void appendInvalidProperty(CFMutableArrayRef properties
,
2411 CFStringRef label
, const DERItem
*der_data
) {
2412 appendRelabeledProperty(properties
, label
, NULL
, der_data
, SEC_INVALID_KEY
);
2415 static void appendDateContentProperty(CFMutableArrayRef properties
,
2416 CFStringRef label
, DERTag tag
,
2417 const DERItem
*dateContent
) {
2418 CFAbsoluteTime absTime
;
2419 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2420 /* Date decode failure insert hex bytes instead. */
2421 return appendInvalidProperty(properties
, label
, dateContent
);
2423 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2424 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2428 static void appendDateProperty(CFMutableArrayRef properties
,
2429 CFStringRef label
, CFAbsoluteTime absTime
) {
2430 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2431 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2435 static void appendValidityPeriodProperty(CFMutableArrayRef parent
, CFStringRef label
,
2436 SecCertificateRef certificate
) {
2437 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2438 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2440 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2441 certificate
->_notBefore
);
2442 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2443 certificate
->_notAfter
);
2445 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2446 CFReleaseNull(properties
);
2449 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2450 CFStringRef label
, const DERItem
*ip
) {
2452 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2454 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2457 appendUnparsedProperty(properties
, label
, NULL
, ip
);
2461 static void appendURLContentProperty(CFMutableArrayRef properties
,
2462 CFStringRef label
, const DERItem
*urlContent
) {
2463 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2464 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2466 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
);
2469 appendInvalidProperty(properties
, label
, urlContent
);
2473 static void appendURLProperty(CFMutableArrayRef properties
,
2474 CFStringRef label
, const DERItem
*url
) {
2475 DERDecodedInfo decoded
;
2478 drtn
= DERDecodeItem(url
, &decoded
);
2479 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2480 appendInvalidProperty(properties
, label
, url
);
2482 appendURLContentProperty(properties
, label
, &decoded
.content
);
2486 static void appendOIDProperty(CFMutableArrayRef properties
,
2487 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
) {
2488 CFStringRef oid_string
=
2489 copyLocalizedOidDescription(CFGetAllocator(properties
), oid
);
2490 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2492 CFRelease(oid_string
);
2495 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2496 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2497 CFMutableArrayRef alg_props
=
2498 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2499 &kCFTypeArrayCallBacks
);
2500 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
, &algorithm
->oid
);
2501 if (algorithm
->params
.length
) {
2502 if (algorithm
->params
.length
== 2 &&
2503 algorithm
->params
.data
[0] == ASN1_NULL
&&
2504 algorithm
->params
.data
[1] == 0) {
2505 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2506 appendProperty(alg_props
, kSecPropertyTypeString
,
2507 SEC_PARAMETERS_KEY
, NULL
, value
);
2510 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2511 &algorithm
->params
);
2514 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
, alg_props
);
2515 CFRelease(alg_props
);
2518 static void appendPublicKeyProperty(CFMutableArrayRef parent
, CFStringRef label
,
2519 SecCertificateRef certificate
) {
2520 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2521 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2523 /* Public key algorithm. */
2524 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
2525 &certificate
->_algId
);
2527 /* Public Key Size */
2528 #if TARGET_OS_IPHONE
2529 SecKeyRef publicKey
= SecCertificateCopyPublicKey(certificate
);
2531 SecKeyRef publicKey
= SecCertificateCopyPublicKey_ios(certificate
);
2534 size_t sizeInBytes
= SecKeyGetBlockSize(publicKey
);
2535 CFStringRef sizeInBitsString
= CFStringCreateWithFormat(allocator
, NULL
,
2536 CFSTR("%ld"), (sizeInBytes
*8));
2537 if (sizeInBitsString
) {
2538 appendProperty(properties
, kSecPropertyTypeString
, SEC_PUBLIC_KEY_SIZE_KEY
,
2539 NULL
, sizeInBitsString
);
2541 CFReleaseNull(sizeInBitsString
);
2543 CFReleaseNull(publicKey
);
2545 /* Consider breaking down an RSA public key into modulus and
2547 appendDataProperty(properties
, SEC_PUBLIC_KEY_DATA_KEY
, NULL
,
2548 &certificate
->_pubKeyDER
);
2550 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2551 CFReleaseNull(properties
);
2554 static void appendSignatureProperty(CFMutableArrayRef parent
, CFStringRef label
,
2555 SecCertificateRef certificate
) {
2556 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2557 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2559 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
2560 &certificate
->_tbsSigAlg
);
2562 appendDataProperty(properties
, SEC_SIGNATURE_DATA_KEY
, NULL
,
2563 &certificate
->_signature
);
2565 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2566 CFReleaseNull(properties
);
2569 static void appendFingerprintsProperty(CFMutableArrayRef parent
, CFStringRef label
, SecCertificateRef certificate
) {
2570 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2571 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2573 CFDataRef sha256Fingerprint
= SecCertificateCopySHA256Digest(certificate
);
2574 if (sha256Fingerprint
) {
2575 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA2_FINGERPRINT_KEY
,
2576 NULL
, sha256Fingerprint
);
2578 CFReleaseNull(sha256Fingerprint
);
2580 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA1_FINGERPRINT_KEY
,
2581 NULL
, SecCertificateGetSHA1Digest(certificate
));
2583 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2584 CFReleaseNull(properties
);
2587 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2588 const DERItem
*blob
) {
2589 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2590 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2591 blob
->length
* 3 - 1);
2592 for (ix
= 0; ix
< length
; ++ix
)
2594 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2596 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2601 /* Returns a (localized) blob string. */
2602 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2603 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2604 CFStringRef localizedBlobType
= SecCopyCertString(blobType
);
2605 CFStringRef localizedQuanta
= SecCopyCertString(quanta
);
2606 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2607 "data = 00 00 ...)" */
2608 CFStringRef blobFormat
= SecCopyCertString(SEC_BLOB_KEY
);
2609 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2610 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2611 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2613 CFRelease(blobFormat
);
2614 CFReleaseSafe(localizedQuanta
);
2615 CFReleaseSafe(localizedBlobType
);
2620 /* Return a string verbatim (unlocalized) from a DER field. */
2621 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2622 const DERItem
*string
, CFStringEncoding encoding
,
2623 bool printableOnly
) {
2624 /* Strip potential bogus trailing zero from printable strings. */
2625 DERSize length
= string
->length
;
2626 if (length
&& string
->data
[length
- 1] == 0) {
2627 /* Don't mess with the length of UTF16 strings though. */
2628 if (encoding
!= kCFStringEncodingUTF16
)
2631 /* A zero length string isn't considered printable. */
2632 if (!length
&& printableOnly
)
2635 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2636 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2637 passing false makes it treat it as native endian by default. */
2638 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2639 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2643 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2646 /* From rfc3280 - Appendix B. ASN.1 Notes
2648 CAs MUST force the serialNumber to be a non-negative integer, that
2649 is, the sign bit in the DER encoding of the INTEGER value MUST be
2650 zero - this can be done by adding a leading (leftmost) `00'H octet if
2651 necessary. This removes a potential ambiguity in mapping between a
2652 string of octets and an integer value.
2654 As noted in section 4.1.2.2, serial numbers can be expected to
2655 contain long integers. Certificate users MUST be able to handle
2656 serialNumber values up to 20 octets in length. Conformant CAs MUST
2657 NOT use serialNumber values longer than 20 octets.
2660 /* Return the given numeric data as a string: decimal up to 64 bits,
2662 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2663 const DERItem
*integer
) {
2665 CFIndex ix
, length
= integer
->length
;
2667 if (length
== 0 || length
> 8)
2668 return copyHexDescription(allocator
, integer
);
2670 for(ix
= 0; ix
< length
; ++ix
) {
2672 value
+= integer
->data
[ix
];
2675 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2678 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2679 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2680 if (!derThing
) { return NULL
; }
2684 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2685 case ASN1_PRINTABLE_STRING
:
2686 case ASN1_IA5_STRING
:
2687 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2688 case ASN1_UTF8_STRING
:
2689 case ASN1_GENERAL_STRING
:
2690 case ASN1_UNIVERSAL_STRING
:
2691 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2692 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2693 case ASN1_VIDEOTEX_STRING
: // 21
2694 case ASN1_VISIBLE_STRING
: // 26
2695 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2696 case ASN1_BMP_STRING
: // 30
2697 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2698 case ASN1_OCTET_STRING
:
2699 return printableOnly
? NULL
:
2700 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2702 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2703 case ASN1_BIT_STRING
:
2704 return printableOnly
? NULL
:
2705 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2707 case ASN1_CONSTR_SEQUENCE
:
2708 return printableOnly
? NULL
:
2709 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2711 case ASN1_CONSTR_SET
:
2712 return printableOnly
? NULL
:
2713 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
, derThing
);
2714 case ASN1_OBJECT_ID
:
2715 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2717 if (printableOnly
) {
2720 CFStringRef fmt
= SecCopyCertString(SEC_NOT_DISPLAYED_KEY
);
2721 if (!fmt
) { return NULL
; }
2722 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2723 (unsigned long)tag
, (unsigned long)derThing
->length
);
2730 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2731 const DERItem
*derThing
, bool printableOnly
) {
2732 DERDecodedInfo decoded
;
2735 drtn
= DERDecodeItem(derThing
, &decoded
);
2737 /* TODO: Perhaps put something in the label saying we couldn't parse
2739 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2741 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2742 &decoded
.content
, false);
2746 static void appendDERThingProperty(CFMutableArrayRef properties
,
2747 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*derThing
) {
2748 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2751 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2754 CFReleaseSafe(value
);
2757 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2758 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2759 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2761 /* If there is more than one value pair we create a subsection for the
2762 second pair, and append things to the subsection for subsequent
2764 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2765 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2767 /* Since this is the second rdn pair for a given rdn, we setup a
2768 new subsection for this rdn. We remove the first property
2769 from the properties array and make it the first element in the
2770 subsection instead. */
2771 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2772 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2773 CFArrayAppendValue(rdn_props
, lastValue
);
2774 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2775 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2777 properties
= rdn_props
;
2778 // rdn_props is now retained by the original properties array
2779 CFReleaseSafe(rdn_props
);
2781 /* Since this is the third or later rdn pair we have already
2782 created a subsection in the top level properties array. Instead
2783 of appending to that directly we append to the array inside the
2785 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2786 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2790 /* Finally we append the new rdn value to the property array. */
2791 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(CFGetAllocator(properties
),
2793 CFStringRef localizedLabel
=
2794 copyLocalizedOidDescription(CFGetAllocator(properties
), rdnType
);
2795 appendDERThingProperty(properties
, label
, localizedLabel
, rdnValue
);
2796 CFReleaseSafe(label
);
2797 CFReleaseSafe(localizedLabel
);
2798 return errSecSuccess
;
2801 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2802 const DERItem
*rdnSetContent
) {
2803 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2804 &kCFTypeArrayCallBacks
);
2805 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2808 CFArrayRemoveAllValues(properties
);
2809 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
);
2816 From rfc3739 - 3.1.2. Subject
2818 When parsing the subject here are some tips for a short name of the cert.
2819 Choice I: commonName
2820 Choice II: givenName
2821 Choice III: pseudonym
2823 The commonName attribute value SHALL, when present, contain a name
2824 of the subject. This MAY be in the subject's preferred
2825 presentation format, or a format preferred by the CA, or some
2826 other format. Pseudonyms, nicknames, and names with spelling
2827 other than defined by the registered name MAY be used. To
2828 understand the nature of the name presented in commonName,
2829 complying applications MAY have to examine present values of the
2830 givenName and surname attributes, or the pseudonym attribute.
2833 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2834 const DERItem
*x501NameContent
) {
2835 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2836 &kCFTypeArrayCallBacks
);
2837 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2840 CFArrayRemoveAllValues(properties
);
2841 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501NameContent
);
2847 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2848 const DERItem
*x501Name
) {
2849 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2850 &kCFTypeArrayCallBacks
);
2851 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2853 CFArrayRemoveAllValues(properties
);
2854 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501Name
);
2860 static void appendIntegerProperty(CFMutableArrayRef properties
,
2861 CFStringRef label
, const DERItem
*integer
) {
2862 CFStringRef string
= copyIntegerContentDescription(
2863 CFGetAllocator(properties
), integer
);
2864 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2868 static void appendBoolProperty(CFMutableArrayRef properties
,
2869 CFStringRef label
, bool boolean
) {
2870 CFStringRef value
= SecCopyCertString(boolean
? SEC_YES_KEY
: SEC_NO_KEY
);
2871 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2875 static void appendBooleanProperty(CFMutableArrayRef properties
,
2876 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2878 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2880 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2881 appendInvalidProperty(properties
, label
, boolean
);
2883 appendBoolProperty(properties
, label
, result
);
2887 static void appendSerialNumberProperty(CFMutableArrayRef parent
, CFStringRef label
,
2888 DERItem
*serialNum
) {
2889 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2890 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2892 if (serialNum
->length
) {
2893 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
2895 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2898 CFReleaseNull(properties
);
2901 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2902 CFStringRef label
, const DERItem
*bitStringContent
,
2903 const CFStringRef
*names
, CFIndex namesCount
) {
2904 DERSize len
= bitStringContent
->length
- 1;
2905 require_quiet(len
== 1 || len
== 2, badDER
);
2906 DERByte numUnusedBits
= bitStringContent
->data
[0];
2907 require_quiet(numUnusedBits
< 8, badDER
);
2908 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2909 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2910 uint_fast16_t value
= bitStringContent
->data
[1];
2913 value
= (value
<< 8) + bitStringContent
->data
[2];
2919 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2920 CFStringRef string
= NULL
;
2921 for (ix
= 0; ix
< bits
; ++ix
) {
2925 CFStringCreateWithFormat(CFGetAllocator(properties
),
2926 NULL
, fmt
, string
, names
[ix
]);
2937 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2938 string
? string
: CFSTR(""));
2939 CFReleaseSafe(string
);
2942 appendInvalidProperty(properties
, label
, bitStringContent
);
2945 static void appendBitStringNames(CFMutableArrayRef properties
,
2946 CFStringRef label
, const DERItem
*bitString
,
2947 const CFStringRef
*names
, CFIndex namesCount
) {
2948 DERDecodedInfo bitStringContent
;
2949 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2950 require_noerr_quiet(drtn
, badDER
);
2951 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2952 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2956 appendInvalidProperty(properties
, label
, bitString
);
2959 static void appendKeyUsage(CFMutableArrayRef properties
,
2960 const DERItem
*extnValue
) {
2961 static const CFStringRef usageNames
[] = {
2962 SEC_DIGITAL_SIGNATURE_KEY
,
2963 SEC_NON_REPUDIATION_KEY
,
2964 SEC_KEY_ENCIPHERMENT_KEY
,
2965 SEC_DATA_ENCIPHERMENT_KEY
,
2966 SEC_KEY_AGREEMENT_KEY
,
2969 SEC_ENCIPHER_ONLY_KEY
,
2970 SEC_DECIPHER_ONLY_KEY
2972 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2973 usageNames
, array_size(usageNames
));
2976 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2977 const DERItem
*extnValue
) {
2978 DERPrivateKeyUsagePeriod pkup
;
2979 DERReturn drtn
= DERParseSequence(extnValue
,
2980 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2981 &pkup
, sizeof(pkup
));
2982 require_noerr_quiet(drtn
, badDER
);
2983 if (pkup
.notBefore
.length
) {
2984 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2985 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2987 if (pkup
.notAfter
.length
) {
2988 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2989 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2993 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
, extnValue
);
2996 static void appendStringContentProperty(CFMutableArrayRef properties
,
2997 CFStringRef label
, const DERItem
*stringContent
,
2998 CFStringEncoding encoding
) {
2999 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
3000 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
3002 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
3005 appendInvalidProperty(properties
, label
, stringContent
);
3010 OtherName ::= SEQUENCE {
3011 type-id OBJECT IDENTIFIER,
3012 value [0] EXPLICIT ANY DEFINED BY type-id }
3014 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
3015 const DERItem
*otherNameContent
) {
3017 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
3018 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
3020 require_noerr_quiet(drtn
, badDER
);
3021 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3023 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
3024 CFStringRef localizedLabel
=
3025 copyLocalizedOidDescription(allocator
, &on
.typeIdentifier
);
3026 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
3028 appendProperty(properties
, kSecPropertyTypeString
, label
,
3029 localizedLabel
, value_string
);
3031 appendUnparsedProperty(properties
, label
, localizedLabel
, &on
.value
);
3033 CFReleaseSafe(value_string
);
3034 CFReleaseSafe(label
);
3035 CFReleaseSafe(localizedLabel
);
3038 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
, otherNameContent
);
3042 GeneralName ::= CHOICE {
3043 otherName [0] OtherName,
3044 rfc822Name [1] IA5String,
3045 dNSName [2] IA5String,
3046 x400Address [3] ORAddress,
3047 directoryName [4] Name,
3048 ediPartyName [5] EDIPartyName,
3049 uniformResourceIdentifier [6] IA5String,
3050 iPAddress [7] OCTET STRING,
3051 registeredID [8] OBJECT IDENTIFIER}
3053 EDIPartyName ::= SEQUENCE {
3054 nameAssigner [0] DirectoryString OPTIONAL,
3055 partyName [1] DirectoryString }
3057 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
3058 DERTag tag
, const DERItem
*generalName
) {
3060 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
3061 appendOtherNameContentProperty(properties
, generalName
);
3063 case ASN1_CONTEXT_SPECIFIC
| 1:
3065 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
3066 generalName
, kCFStringEncodingASCII
);
3068 case ASN1_CONTEXT_SPECIFIC
| 2:
3070 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
3071 kCFStringEncodingASCII
);
3073 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
3074 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
3077 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
3079 CFArrayRef directory_plist
=
3080 createPropertiesForX501Name(CFGetAllocator(properties
),
3082 appendProperty(properties
, kSecPropertyTypeSection
,
3083 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
);
3084 CFRelease(directory_plist
);
3087 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
3088 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
3091 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
3092 /* Technically I don't think this is valid, but there are certs out
3093 in the wild that use a constructed IA5String. In particular the
3094 VeriSign Time Stamping Authority CA.cer does this. */
3095 appendURLProperty(properties
, SEC_URI_KEY
, generalName
);
3097 case ASN1_CONTEXT_SPECIFIC
| 6:
3098 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
);
3100 case ASN1_CONTEXT_SPECIFIC
| 7:
3101 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
3104 case ASN1_CONTEXT_SPECIFIC
| 8:
3105 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
, generalName
);
3116 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
3117 const DERItem
*generalName
) {
3118 DERDecodedInfo generalNameContent
;
3119 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
3120 require_noerr_quiet(drtn
, badDER
);
3121 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
3122 &generalNameContent
.content
))
3125 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
, generalName
);
3130 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
3132 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
3133 const DERItem
*generalNamesContent
) {
3135 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
3136 require_noerr_quiet(drtn
, badDER
);
3137 DERDecodedInfo generalNameContent
;
3138 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
3140 if (!appendGeneralNameContentProperty(properties
,
3141 generalNameContent
.tag
, &generalNameContent
.content
)) {
3145 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3148 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3149 generalNamesContent
);
3152 static void appendGeneralNames(CFMutableArrayRef properties
,
3153 const DERItem
*generalNames
) {
3154 DERDecodedInfo generalNamesContent
;
3155 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
3156 require_noerr_quiet(drtn
, badDER
);
3157 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3159 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
3162 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
, generalNames
);
3166 BasicConstraints ::= SEQUENCE {
3167 cA BOOLEAN DEFAULT FALSE,
3168 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3170 static void appendBasicConstraints(CFMutableArrayRef properties
,
3171 const DERItem
*extnValue
) {
3172 DERBasicConstraints basicConstraints
;
3173 DERReturn drtn
= DERParseSequence(extnValue
,
3174 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3175 &basicConstraints
, sizeof(basicConstraints
));
3176 require_noerr_quiet(drtn
, badDER
);
3178 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3179 &basicConstraints
.cA
, false);
3181 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3182 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3183 &basicConstraints
.pathLenConstraint
);
3187 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
, extnValue
);
3191 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3193 * NameConstraints ::= SEQUENCE {
3194 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3195 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3197 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3199 * GeneralSubtree ::= SEQUENCE {
3201 * minimum [0] BaseDistance DEFAULT 0,
3202 * maximum [1] BaseDistance OPTIONAL }
3204 * BaseDistance ::= INTEGER (0..MAX)
3206 static void appendNameConstraints(CFMutableArrayRef properties
,
3207 const DERItem
*extnValue
) {
3208 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3209 DERNameConstraints nc
;
3211 drtn
= DERParseSequence(extnValue
,
3212 DERNumNameConstraintsItemSpecs
,
3213 DERNameConstraintsItemSpecs
,
3215 require_noerr_quiet(drtn
, badDER
);
3216 if (nc
.permittedSubtrees
.length
) {
3218 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3219 DERDecodedInfo gsContent
;
3220 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3221 DERGeneralSubtree derGS
;
3222 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3223 drtn
= DERParseSequenceContent(&gsContent
.content
,
3224 DERNumGeneralSubtreeItemSpecs
,
3225 DERGeneralSubtreeItemSpecs
,
3226 &derGS
, sizeof(derGS
));
3227 require_noerr_quiet(drtn
, badDER
);
3228 if (derGS
.minimum
.length
) {
3229 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
, &derGS
.minimum
);
3231 if (derGS
.maximum
.length
) {
3232 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
, &derGS
.maximum
);
3234 if (derGS
.generalName
.length
) {
3235 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3236 &kCFTypeArrayCallBacks
);
3237 appendProperty(properties
, kSecPropertyTypeSection
,
3238 SEC_PERMITTED_NAME_KEY
, NULL
, base
);
3239 appendGeneralNameProperty(base
, &derGS
.generalName
);
3243 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3245 if (nc
.excludedSubtrees
.length
) {
3247 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3248 DERDecodedInfo gsContent
;
3249 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3250 DERGeneralSubtree derGS
;
3251 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3252 drtn
= DERParseSequenceContent(&gsContent
.content
,
3253 DERNumGeneralSubtreeItemSpecs
,
3254 DERGeneralSubtreeItemSpecs
,
3255 &derGS
, sizeof(derGS
));
3256 require_noerr_quiet(drtn
, badDER
);
3257 if (derGS
.minimum
.length
) {
3258 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
, &derGS
.minimum
);
3260 if (derGS
.maximum
.length
) {
3261 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
, &derGS
.maximum
);
3263 if (derGS
.generalName
.length
) {
3264 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3265 &kCFTypeArrayCallBacks
);
3266 appendProperty(properties
, kSecPropertyTypeSection
,
3267 SEC_EXCLUDED_NAME_KEY
, NULL
, base
);
3268 appendGeneralNameProperty(base
, &derGS
.generalName
);
3272 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3277 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
, extnValue
);
3281 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3283 DistributionPoint ::= SEQUENCE {
3284 distributionPoint [0] DistributionPointName OPTIONAL,
3285 reasons [1] ReasonFlags OPTIONAL,
3286 cRLIssuer [2] GeneralNames OPTIONAL }
3288 DistributionPointName ::= CHOICE {
3289 fullName [0] GeneralNames,
3290 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3292 ReasonFlags ::= BIT STRING {
3296 affiliationChanged (3),
3298 cessationOfOperation (5),
3299 certificateHold (6),
3300 privilegeWithdrawn (7),
3303 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3304 const DERItem
*extnValue
) {
3305 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3308 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3309 require_noerr_quiet(drtn
, badDER
);
3310 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3311 DERDecodedInfo dpSeqContent
;
3312 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3313 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3314 DERDistributionPoint dp
;
3315 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3316 DERNumDistributionPointItemSpecs
,
3317 DERDistributionPointItemSpecs
,
3319 require_noerr_quiet(drtn
, badDER
);
3320 if (dp
.distributionPoint
.length
) {
3321 DERDecodedInfo distributionPointName
;
3322 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3323 require_noerr_quiet(drtn
, badDER
);
3324 if (distributionPointName
.tag
==
3325 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3327 appendGeneralNamesContent(properties
,
3328 &distributionPointName
.content
);
3329 } else if (distributionPointName
.tag
==
3330 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3331 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3333 appendProperty(properties
, kSecPropertyTypeSection
,
3334 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
);
3335 CFRelease(rdn_props
);
3340 if (dp
.reasons
.length
) {
3341 static const CFStringRef reasonNames
[] = {
3343 SEC_KEY_COMPROMISE_KEY
,
3344 SEC_CA_COMPROMISE_KEY
,
3345 SEC_AFFILIATION_CHANGED_KEY
,
3347 SEC_CESSATION_OF_OPER_KEY
,
3348 SEC_CERTIFICATE_HOLD_KEY
,
3349 SEC_PRIV_WITHDRAWN_KEY
,
3350 SEC_AA_COMPROMISE_KEY
3352 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3354 reasonNames
, array_size(reasonNames
));
3356 if (dp
.cRLIssuer
.length
) {
3357 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3358 &kCFTypeArrayCallBacks
);
3359 appendProperty(properties
, kSecPropertyTypeSection
,
3360 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
);
3361 CFRelease(crlIssuer
);
3362 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
3365 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3368 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
, extnValue
);
3371 /* Decode a sequence of integers into a comma separated list of ints. */
3372 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3373 CFStringRef label
, const DERItem
*intSequenceContent
) {
3374 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3376 CFStringRef fmt
= NULL
, value
= NULL
, intDesc
= NULL
, v
= NULL
;
3377 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3378 require_noerr_quiet(drtn
, badDER
);
3379 DERDecodedInfo intContent
;
3380 fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3381 require_quiet(fmt
, badDER
);
3382 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3383 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3384 intDesc
= copyIntegerContentDescription(
3385 allocator
, &intContent
.content
);
3386 require_quiet(intDesc
, badDER
);
3388 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3389 CFReleaseNull(value
);
3390 require_quiet(v
, badDER
);
3393 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
3394 require_quiet(value
, badDER
);
3396 CFReleaseNull(intDesc
);
3399 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3401 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
3405 /* DROPTHOUGH if !value. */
3408 CFReleaseNull(intDesc
);
3409 CFReleaseNull(value
);
3410 appendInvalidProperty(properties
, label
, intSequenceContent
);
3413 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3414 const DERItem
*extnValue
) {
3415 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3416 CFStringRef piLabel
= NULL
, piFmt
= NULL
, lpiLabel
= NULL
;
3417 CFStringRef pqLabel
= NULL
, pqFmt
= NULL
, lpqLabel
= NULL
;
3420 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3421 require_noerr_quiet(drtn
, badDER
);
3422 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3423 DERDecodedInfo piContent
;
3425 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3426 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3427 DERPolicyInformation pi
;
3428 drtn
= DERParseSequenceContent(&piContent
.content
,
3429 DERNumPolicyInformationItemSpecs
,
3430 DERPolicyInformationItemSpecs
,
3432 require_noerr_quiet(drtn
, badDER
);
3433 require_quiet(piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3434 SEC_POLICY_IDENTIFIER_KEY
, pin
), badDER
);
3435 require_quiet(piFmt
= SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
), badDER
);
3436 require_quiet(lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3437 piFmt
, pin
++), badDER
);
3438 CFReleaseNull(piFmt
);
3439 appendOIDProperty(properties
, piLabel
, lpiLabel
, &pi
.policyIdentifier
);
3440 CFReleaseNull(piLabel
);
3441 CFReleaseNull(lpiLabel
);
3442 if (pi
.policyQualifiers
.length
== 0)
3446 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3447 require_noerr_quiet(drtn
, badDER
);
3448 DERDecodedInfo pqContent
;
3450 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3451 DERPolicyQualifierInfo pqi
;
3452 drtn
= DERParseSequenceContent(&pqContent
.content
,
3453 DERNumPolicyQualifierInfoItemSpecs
,
3454 DERPolicyQualifierInfoItemSpecs
,
3456 require_noerr_quiet(drtn
, badDER
);
3457 DERDecodedInfo qualifierContent
;
3458 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3459 require_noerr_quiet(drtn
, badDER
);
3460 require_quiet(pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3461 SEC_POLICY_QUALIFIER_KEY
, pqn
), badDER
);
3462 require_quiet(pqFmt
= SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
), badDER
);
3463 require_quiet(lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3464 pqFmt
, pqn
++), badDER
);
3465 CFReleaseNull(pqFmt
);
3466 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3467 &pqi
.policyQualifierID
);
3468 CFReleaseNull(pqLabel
);
3469 CFReleaseNull(lpqLabel
);
3470 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3471 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3472 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3473 &qualifierContent
.content
);
3474 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3475 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3477 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3478 DERNumUserNoticeItemSpecs
,
3479 DERUserNoticeItemSpecs
,
3481 require_noerr_quiet(drtn
, badDER
);
3482 if (un
.noticeRef
.length
) {
3483 DERNoticeReference nr
;
3484 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3485 DERNumNoticeReferenceItemSpecs
,
3486 DERNoticeReferenceItemSpecs
,
3488 require_noerr_quiet(drtn
, badDER
);
3489 appendDERThingProperty(properties
,
3490 SEC_ORGANIZATION_KEY
, NULL
,
3492 appendIntegerSequenceContent(properties
,
3493 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
);
3495 if (un
.explicitText
.length
) {
3496 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3497 NULL
, &un
.explicitText
);
3500 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3504 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3506 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3509 CFReleaseNull(piFmt
);
3510 CFReleaseNull(piLabel
);
3511 CFReleaseNull(lpiLabel
);
3512 CFReleaseNull(pqFmt
);
3513 CFReleaseNull(pqLabel
);
3514 CFReleaseNull(lpqLabel
);
3515 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
, extnValue
);
3518 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3519 const DERItem
*extnValue
) {
3521 DERDecodedInfo keyIdentifier
;
3522 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3523 require_noerr_quiet(drtn
, badDER
);
3524 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3525 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3526 &keyIdentifier
.content
);
3530 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3535 AuthorityKeyIdentifier ::= SEQUENCE {
3536 keyIdentifier [0] KeyIdentifier OPTIONAL,
3537 authorityCertIssuer [1] GeneralNames OPTIONAL,
3538 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3539 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3540 -- be present or both be absent
3542 KeyIdentifier ::= OCTET STRING
3544 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3545 const DERItem
*extnValue
) {
3546 DERAuthorityKeyIdentifier akid
;
3548 drtn
= DERParseSequence(extnValue
,
3549 DERNumAuthorityKeyIdentifierItemSpecs
,
3550 DERAuthorityKeyIdentifierItemSpecs
,
3551 &akid
, sizeof(akid
));
3552 require_noerr_quiet(drtn
, badDER
);
3553 if (akid
.keyIdentifier
.length
) {
3554 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3555 &akid
.keyIdentifier
);
3557 if (akid
.authorityCertIssuer
.length
||
3558 akid
.authorityCertSerialNumber
.length
) {
3559 require_quiet(akid
.authorityCertIssuer
.length
&&
3560 akid
.authorityCertSerialNumber
.length
, badDER
);
3561 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3562 appendGeneralNamesContent(properties
,
3563 &akid
.authorityCertIssuer
);
3564 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3565 &akid
.authorityCertSerialNumber
);
3570 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
, extnValue
);
3574 PolicyConstraints ::= SEQUENCE {
3575 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3576 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3578 SkipCerts ::= INTEGER (0..MAX)
3580 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3581 const DERItem
*extnValue
) {
3582 DERPolicyConstraints pc
;
3584 drtn
= DERParseSequence(extnValue
,
3585 DERNumPolicyConstraintsItemSpecs
,
3586 DERPolicyConstraintsItemSpecs
,
3588 require_noerr_quiet(drtn
, badDER
);
3589 if (pc
.requireExplicitPolicy
.length
) {
3590 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3591 &pc
.requireExplicitPolicy
);
3593 if (pc
.inhibitPolicyMapping
.length
) {
3594 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3595 &pc
.inhibitPolicyMapping
);
3601 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
, extnValue
);
3605 extendedKeyUsage EXTENSION ::= {
3606 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3607 IDENTIFIED BY id-ce-extKeyUsage }
3609 KeyPurposeId ::= OBJECT IDENTIFIER
3611 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3612 const DERItem
*extnValue
) {
3615 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3616 require_noerr_quiet(drtn
, badDER
);
3617 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3618 DERDecodedInfo currDecoded
;
3619 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3620 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3621 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3622 &currDecoded
.content
);
3624 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3627 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
, extnValue
);
3631 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3633 AuthorityInfoAccessSyntax ::=
3634 SEQUENCE SIZE (1..MAX) OF AccessDescription
3636 AccessDescription ::= SEQUENCE {
3637 accessMethod OBJECT IDENTIFIER,
3638 accessLocation GeneralName }
3640 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3642 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3644 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3646 static void appendInfoAccess(CFMutableArrayRef properties
,
3647 const DERItem
*extnValue
) {
3650 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3651 require_noerr_quiet(drtn
, badDER
);
3652 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3653 DERDecodedInfo adContent
;
3654 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3655 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3656 DERAccessDescription ad
;
3657 drtn
= DERParseSequenceContent(&adContent
.content
,
3658 DERNumAccessDescriptionItemSpecs
,
3659 DERAccessDescriptionItemSpecs
,
3661 require_noerr_quiet(drtn
, badDER
);
3662 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3664 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3665 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3667 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3670 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
, extnValue
);
3673 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3674 const DERItem
*extnValue
) {
3675 static const CFStringRef certTypes
[] = {
3679 SEC_OBJECT_SIGNING_KEY
,
3683 SEC_OBJECT_SIGNING_CA_KEY
3685 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3686 certTypes
, array_size(certTypes
));
3689 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3690 CFStringRef label
, const DERItem
*sequence
) {
3693 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3694 require_noerr_quiet(drtn
, badSequence
);
3695 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3696 DERDecodedInfo currDecoded
;
3697 bool appendedSomething
= false;
3698 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3699 switch (currDecoded
.tag
)
3702 case ASN1_SEQUENCE
: // 16
3703 case ASN1_SET
: // 17
3704 // skip constructed object lengths
3707 case ASN1_UTF8_STRING
: // 12
3708 case ASN1_NUMERIC_STRING
: // 18
3709 case ASN1_PRINTABLE_STRING
: // 19
3710 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3711 case ASN1_VIDEOTEX_STRING
: // 21
3712 case ASN1_IA5_STRING
: // 22
3713 case ASN1_GRAPHIC_STRING
: // 25
3714 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3715 case ASN1_GENERAL_STRING
: // 27
3716 case ASN1_UNIVERSAL_STRING
: // 28
3718 CFStringRef string
=
3719 copyDERThingContentDescription(CFGetAllocator(properties
),
3720 currDecoded
.tag
, &currDecoded
.content
, false);
3721 require_quiet(string
, badSequence
);
3723 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3725 CFReleaseNull(string
);
3726 appendedSomething
= true;
3733 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3734 return appendedSomething
;
3739 static void appendExtension(CFMutableArrayRef parent
,
3740 const SecCertificateExtension
*extn
) {
3741 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3742 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3743 &kCFTypeArrayCallBacks
);
3745 *extnID
= &extn
->extnID
,
3746 *extnValue
= &extn
->extnValue
;
3747 CFStringRef label
= NULL
;
3748 CFStringRef localizedLabel
= NULL
;
3750 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
);
3751 require_quiet(extnID
, xit
);
3753 bool handled
= true;
3754 /* Extensions that we know how to handle ourselves... */
3755 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3756 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3758 switch (extnID
->data
[extnID
->length
- 1]) {
3759 case 14: /* SubjectKeyIdentifier id-ce 14 */
3760 appendSubjectKeyIdentifier(properties
, extnValue
);
3762 case 15: /* KeyUsage id-ce 15 */
3763 appendKeyUsage(properties
, extnValue
);
3765 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3766 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3768 case 17: /* SubjectAltName id-ce 17 */
3769 case 18: /* IssuerAltName id-ce 18 */
3770 appendGeneralNames(properties
, extnValue
);
3772 case 19: /* BasicConstraints id-ce 19 */
3773 appendBasicConstraints(properties
, extnValue
);
3775 case 30: /* NameConstraints id-ce 30 */
3776 appendNameConstraints(properties
, extnValue
);
3778 case 31: /* CRLDistributionPoints id-ce 31 */
3779 appendCrlDistributionPoints(properties
, extnValue
);
3781 case 32: /* CertificatePolicies id-ce 32 */
3782 appendCertificatePolicies(properties
, extnValue
);
3784 case 33: /* PolicyMappings id-ce 33 */
3787 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3788 appendAuthorityKeyIdentifier(properties
, extnValue
);
3790 case 36: /* PolicyConstraints id-ce 36 */
3791 appendPolicyConstraints(properties
, extnValue
);
3793 case 37: /* ExtKeyUsage id-ce 37 */
3794 appendExtendedKeyUsage(properties
, extnValue
);
3796 case 46: /* FreshestCRL id-ce 46 */
3799 case 54: /* InhibitAnyPolicy id-ce 54 */
3806 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3807 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3809 switch (extnID
->data
[extnID
->length
- 1]) {
3810 case 1: /* AuthorityInfoAccess id-pe 1 */
3811 appendInfoAccess(properties
, extnValue
);
3813 case 3: /* QCStatements id-pe 3 */
3816 case 11: /* SubjectInfoAccess id-pe 11 */
3817 appendInfoAccess(properties
, extnValue
);
3823 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3824 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3825 appendNetscapeCertType(properties
, extnValue
);
3831 /* Try to parse and display printable string(s). */
3832 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3833 /* Nothing to do here appendPrintableDERSequence did the work. */
3835 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3836 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3839 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3840 localizedLabel
= copyLocalizedOidDescription(allocator
, extnID
);
3841 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
, properties
);
3844 CFReleaseSafe(localizedLabel
);
3845 CFReleaseSafe(label
);
3846 CFReleaseSafe(properties
);
3849 /* Different types of summary types from least desired to most desired. */
3852 kSummaryTypePrintable
,
3853 kSummaryTypeOrganizationName
,
3854 kSummaryTypeOrganizationalUnitName
,
3855 kSummaryTypeCommonName
,
3859 enum SummaryType type
;
3860 CFStringRef summary
;
3861 CFStringRef description
;
3864 static OSStatus
obtainSummaryFromX501Name(void *context
,
3865 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3866 struct Summary
*summary
= (struct Summary
*)context
;
3867 enum SummaryType stype
= kSummaryTypeNone
;
3868 CFStringRef string
= NULL
;
3869 if (DEROidCompare(type
, &oidCommonName
)) {
3870 stype
= kSummaryTypeCommonName
;
3871 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3872 stype
= kSummaryTypeOrganizationalUnitName
;
3873 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3874 stype
= kSummaryTypeOrganizationName
;
3875 } else if (DEROidCompare(type
, &oidDescription
)) {
3876 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3878 if (summary
->description
) {
3879 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3880 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->description
);
3882 CFRelease(summary
->description
);
3883 summary
->description
= newDescription
;
3885 summary
->description
= string
;
3888 stype
= kSummaryTypePrintable
;
3891 stype
= kSummaryTypePrintable
;
3894 /* Build a string with all instances of the most desired
3895 component type in reverse order encountered comma separated list,
3896 The order of desirability is defined by enum SummaryType. */
3897 if (summary
->type
<= stype
) {
3899 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3902 if (summary
->type
== stype
) {
3903 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3904 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->summary
);
3907 string
= newSummary
;
3909 summary
->type
= stype
;
3911 CFReleaseSafe(summary
->summary
);
3912 summary
->summary
= string
;
3915 CFReleaseSafe(string
);
3918 return errSecSuccess
;
3921 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3922 struct Summary summary
= {};
3923 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3924 /* If we found a description and a common name we change the summary to
3925 CommonName (Description). */
3926 if (summary
.description
) {
3927 if (summary
.type
== kSummaryTypeCommonName
) {
3928 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3929 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3931 CFRelease(summary
.summary
);
3932 summary
.summary
= newSummary
;
3934 CFRelease(summary
.description
);
3937 if (!summary
.summary
) {
3938 /* If we didn't find a suitable printable string in the subject at all, we try
3939 the first email address in the certificate instead. */
3940 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3942 /* If we didn't find any email addresses in the certificate, we try finding
3943 a DNS name instead. */
3944 names
= SecCertificateCopyDNSNames(certificate
);
3947 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3948 CFRetain(summary
.summary
);
3953 return summary
.summary
;
3956 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
3957 struct Summary summary
= {};
3958 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3959 /* If we found a description and a common name we change the summary to
3960 CommonName (Description). */
3961 if (summary
.description
) {
3962 if (summary
.type
== kSummaryTypeCommonName
) {
3963 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3964 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3966 CFRelease(summary
.summary
);
3967 summary
.summary
= newSummary
;
3969 CFRelease(summary
.description
);
3972 return summary
.summary
;
3975 /* Return the earliest date on which all certificates in this chain are still
3977 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3978 SecCertificateRef certificate
) {
3979 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3981 while (certificate
->_parent
) {
3982 certificate
= certificate
->_parent
;
3983 if (earliest
> certificate
->_notAfter
)
3984 earliest
= certificate
->_notAfter
;
3991 /* Return the latest date on which all certificates in this chain will be
3993 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3994 SecCertificateRef certificate
) {
3995 CFAbsoluteTime latest
= certificate
->_notBefore
;
3997 while (certificate
->_parent
) {
3998 certificate
= certificate
->_parent
;
3999 if (latest
< certificate
->_notBefore
)
4000 latest
= certificate
->_notBefore
;
4007 bool SecCertificateIsValid(SecCertificateRef certificate
,
4008 CFAbsoluteTime verifyTime
) {
4009 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
4010 verifyTime
<= certificate
->_notAfter
;
4013 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
4014 return certificate
->_version
+ 1;
4017 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
4018 return certificate
->_notBefore
;
4021 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
4022 return certificate
->_notAfter
;
4025 CFMutableArrayRef
SecCertificateCopySummaryProperties(
4026 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
4027 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4028 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
4029 &kCFTypeArrayCallBacks
);
4031 /* First we put the subject summary name. */
4032 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
4034 appendProperty(summary
, kSecPropertyTypeTitle
,
4035 NULL
, NULL
, ssummary
);
4036 CFRelease(ssummary
);
4039 /* Let see if this certificate is currently valid. */
4041 CFAbsoluteTime when
;
4042 CFStringRef message
;
4044 if (verifyTime
> certificate
->_notAfter
) {
4045 label
= SEC_EXPIRED_KEY
;
4046 when
= certificate
->_notAfter
;
4047 ptype
= kSecPropertyTypeError
;
4048 message
= SEC_CERT_EXPIRED_KEY
;
4049 } else if (certificate
->_notBefore
> verifyTime
) {
4050 label
= SEC_VALID_FROM_KEY
;
4051 when
= certificate
->_notBefore
;
4052 ptype
= kSecPropertyTypeError
;
4053 message
= SEC_CERT_NOT_YET_VALID_KEY
;
4055 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
4056 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
4057 if (verifyTime
> last
) {
4058 label
= SEC_EXPIRED_KEY
;
4060 ptype
= kSecPropertyTypeError
;
4061 message
= SEC_ISSUER_EXPIRED_KEY
;
4062 } else if (verifyTime
< first
) {
4063 label
= SEC_VALID_FROM_KEY
;
4065 ptype
= kSecPropertyTypeError
;
4066 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
4068 label
= SEC_EXPIRES_KEY
;
4069 when
= certificate
->_notAfter
;
4070 ptype
= kSecPropertyTypeSuccess
;
4071 message
= SEC_CERT_VALID_KEY
;
4075 appendDateProperty(summary
, label
, when
);
4076 CFStringRef lmessage
= SecCopyCertString(message
);
4077 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
);
4078 CFRelease(lmessage
);
4083 CFArrayRef
SecCertificateCopyLegacyProperties(SecCertificateRef certificate
) {
4085 This function replicates the content returned by SecCertificateCopyProperties
4086 prior to 10.12.4, providing stable return values for SecCertificateCopyValues.
4087 Unlike SecCertificateCopyProperties, it does not cache the result and
4088 assumes the caller will do so.
4090 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4091 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
,
4092 0, &kCFTypeArrayCallBacks
);
4095 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4096 &certificate
->_subject
);
4097 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Subject Name"),
4098 NULL
, subject_plist
);
4099 CFRelease(subject_plist
);
4102 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4103 &certificate
->_issuer
);
4104 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Issuer Name"),
4105 NULL
, issuer_plist
);
4106 CFRelease(issuer_plist
);
4109 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4110 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
4111 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Version"),
4112 NULL
, versionString
);
4113 CFRelease(versionString
);
4116 if (certificate
->_serialNum
.length
) {
4117 appendIntegerProperty(properties
, CFSTR("Serial Number"),
4118 &certificate
->_serialNum
);
4121 /* Signature Algorithm */
4122 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
4123 &certificate
->_tbsSigAlg
);
4125 /* Validity dates */
4126 appendDateProperty(properties
, CFSTR("Not Valid Before"), certificate
->_notBefore
);
4127 appendDateProperty(properties
, CFSTR("Not Valid After"), certificate
->_notAfter
);
4129 if (certificate
->_subjectUniqueID
.length
) {
4130 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
4131 NULL
, &certificate
->_subjectUniqueID
);
4133 if (certificate
->_issuerUniqueID
.length
) {
4134 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
4135 NULL
, &certificate
->_issuerUniqueID
);
4138 /* Public Key Algorithm */
4139 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
4140 &certificate
->_algId
);
4142 /* Public Key Data */
4143 appendDataProperty(properties
, CFSTR("Public Key Data"),
4144 NULL
, &certificate
->_pubKeyDER
);
4147 appendDataProperty(properties
, CFSTR("Signature"),
4148 NULL
, &certificate
->_signature
);
4152 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4153 appendExtension(properties
, &certificate
->_extensions
[ix
]);
4157 appendFingerprintsProperty(properties
, CFSTR("Fingerprints"), certificate
);
4162 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
4163 if (!certificate
->_properties
) {
4164 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4165 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
4166 &kCFTypeArrayCallBacks
);
4167 require_quiet(properties
, out
);
4170 /* First we put the Subject Name in the property list. */
4171 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4172 &certificate
->_subject
);
4173 if (subject_plist
) {
4174 appendProperty(properties
, kSecPropertyTypeSection
,
4175 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
);
4177 CFReleaseNull(subject_plist
);
4179 /* Next we put the Issuer Name in the property list. */
4180 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4181 &certificate
->_issuer
);
4183 appendProperty(properties
, kSecPropertyTypeSection
,
4184 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
);
4186 CFReleaseNull(issuer_plist
);
4189 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4190 CFStringRef versionString
= NULL
;
4192 versionString
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
4193 certificate
->_version
+ 1);
4196 if (versionString
) {
4197 appendProperty(properties
, kSecPropertyTypeString
,
4198 SEC_VERSION_KEY
, NULL
, versionString
);
4200 CFReleaseNull(versionString
);
4203 appendSerialNumberProperty(properties
, SEC_SERIAL_NUMBER_KEY
, &certificate
->_serialNum
);
4205 /* Validity dates. */
4206 appendValidityPeriodProperty(properties
, SEC_VALIDITY_PERIOD_KEY
, certificate
);
4208 if (certificate
->_subjectUniqueID
.length
) {
4209 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4210 &certificate
->_subjectUniqueID
);
4212 if (certificate
->_issuerUniqueID
.length
) {
4213 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4214 &certificate
->_issuerUniqueID
);
4217 appendPublicKeyProperty(properties
, SEC_PUBLIC_KEY_KEY
, certificate
);
4220 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4221 appendExtension(properties
, &certificate
->_extensions
[ix
]);
4225 appendSignatureProperty(properties
, SEC_SIGNATURE_KEY
, certificate
);
4227 appendFingerprintsProperty(properties
, SEC_FINGERPRINTS_KEY
, certificate
);
4229 certificate
->_properties
= properties
;
4233 CFRetainSafe(certificate
->_properties
);
4234 return certificate
->_properties
;
4237 /* Unified serial number API */
4238 CFDataRef
SecCertificateCopySerialNumberData(
4239 SecCertificateRef certificate
,
4244 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4248 if (certificate
->_serialNumber
) {
4249 CFRetain(certificate
->_serialNumber
);
4251 return certificate
->_serialNumber
;
4255 /* On OS X, the SecCertificateCopySerialNumber API takes two arguments. */
4256 CFDataRef
SecCertificateCopySerialNumber(
4257 SecCertificateRef certificate
,
4258 CFErrorRef
*error
) {
4259 return SecCertificateCopySerialNumberData(certificate
, error
);
4262 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4263 CFDataRef
SecCertificateCopySerialNumber(
4264 SecCertificateRef certificate
) {
4265 return SecCertificateCopySerialNumberData(certificate
, NULL
);
4269 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4270 SecCertificateRef certificate
) {
4271 return certificate
->_normalizedIssuer
;
4274 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4275 SecCertificateRef certificate
) {
4276 return certificate
->_normalizedSubject
;
4279 /* Verify that certificate was signed by issuerKey. */
4280 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4281 SecKeyRef issuerKey
) {
4282 /* Setup algId in SecAsn1AlgId format. */
4284 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4285 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4286 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4287 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4289 /* RFC5280 4.1.1.2, 4.1.2.3 requires the actual signature algorithm
4290 must match the specified algorithm in the TBSCertificate. */
4291 bool sigAlgMatch
= DEROidCompare(&certificate
->_sigAlg
.oid
,
4292 &certificate
->_tbsSigAlg
.oid
);
4294 secwarning("Signature algorithm mismatch in certificate (see RFC5280 4.1.1.2)");
4297 CFErrorRef error
= NULL
;
4299 !SecVerifySignatureWithPublicKey(issuerKey
, &algId
,
4300 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4301 certificate
->_signature
.data
, certificate
->_signature
.length
, &error
))
4303 #if !defined(NDEBUG)
4304 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, (error
) ? (OSStatus
)CFErrorGetCode(error
) : errSecNotSigner
);
4306 CFReleaseSafe(error
);
4307 return errSecNotSigner
;
4310 return errSecSuccess
;
4313 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4314 if (!certificate
->_subjectAltName
) {
4317 return &certificate
->_subjectAltName
->extnValue
;
4320 static bool convertIPAddress(CFStringRef name
, CFDataRef
*dataIP
) {
4321 /* IPv4: 4 octets in decimal separated by dots. We don't support matching IPv6 already. */
4322 bool result
= false;
4324 if (CFStringGetLength(name
) < 7 || /* min size is #.#.#.# */
4325 CFStringGetLength(name
) > 15) { /* max size is ###.###.###.### */
4329 CFCharacterSetRef decimals
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
4330 CFCharacterSetRef nonDecimals
= CFCharacterSetCreateInvertedSet(NULL
, decimals
);
4331 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4332 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, name
, CFSTR("."));
4334 /* Check character set */
4335 if (CFStringFindCharacterFromSet(name
, nonDecimals
,
4336 CFRangeMake(0, CFStringGetLength(name
)),
4337 kCFCompareForcedOrdering
, NULL
)) {
4341 /* Check number of labels */
4342 if (CFArrayGetCount(parts
) != 4) {
4346 /* Check each label and convert */
4347 CFIndex i
, count
= CFArrayGetCount(parts
);
4348 for (i
= 0; i
< count
; i
++) {
4349 CFStringRef octet
= CFArrayGetValueAtIndex(parts
, i
);
4350 char *cString
= CFStringToCString(octet
);
4351 uint32_t value
= atoi(cString
);
4356 uint8_t byte
= value
;
4357 CFDataAppendBytes(data
, &byte
, 1);
4362 *dataIP
= CFRetain(data
);
4366 CFReleaseNull(data
);
4367 CFReleaseNull(parts
);
4368 CFReleaseNull(decimals
);
4369 CFReleaseNull(nonDecimals
);
4373 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4374 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4375 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4376 if (gnType
== GNT_IPAddress
) {
4377 CFStringRef string
= copyIPAddressContentDescription(
4378 kCFAllocatorDefault
, generalName
);
4380 CFArrayAppendValue(ipAddresses
, string
);
4383 return errSecInvalidCertificate
;
4386 return errSecSuccess
;
4389 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4390 /* These can only exist in the subject alt name. */
4391 if (!certificate
->_subjectAltName
)
4394 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4395 0, &kCFTypeArrayCallBacks
);
4396 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4397 ipAddresses
, appendIPAddressesFromGeneralNames
);
4398 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4399 CFRelease(ipAddresses
);
4405 static OSStatus
appendIPAddressesFromX501Name(void *context
, const DERItem
*type
,
4406 const DERItem
*value
, CFIndex rdnIX
) {
4407 CFMutableArrayRef addrs
= (CFMutableArrayRef
)context
;
4408 if (DEROidCompare(type
, &oidCommonName
)) {
4409 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4412 CFDataRef data
= NULL
;
4413 if (convertIPAddress(string
, &data
)) {
4414 CFArrayAppendValue(addrs
, data
);
4415 CFReleaseNull(data
);
4419 return errSecInvalidCertificate
;
4422 return errSecSuccess
;
4425 CFArrayRef
SecCertificateCopyIPAddressesFromSubject(SecCertificateRef certificate
) {
4426 CFMutableArrayRef addrs
= CFArrayCreateMutable(kCFAllocatorDefault
,
4427 0, &kCFTypeArrayCallBacks
);
4428 OSStatus status
= parseX501NameContent(&certificate
->_subject
, addrs
,
4429 appendIPAddressesFromX501Name
);
4430 if (status
|| CFArrayGetCount(addrs
) == 0) {
4431 CFReleaseNull(addrs
);
4437 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4438 const DERItem
*generalName
) {
4439 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4440 if (gnType
== GNT_DNSName
) {
4441 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4442 generalName
->data
, generalName
->length
,
4443 kCFStringEncodingUTF8
, FALSE
);
4445 CFArrayAppendValue(dnsNames
, string
);
4448 return errSecInvalidCertificate
;
4451 return errSecSuccess
;
4454 /* Return true if the passed in string matches the
4455 Preferred name syntax from sections 2.3.1. in RFC 1035.
4456 With the added check that we disallow empty dns names.
4457 Also in order to support wildcard DNSNames we allow for the '*'
4458 character anywhere in a dns component where we currently allow
4461 <domain> ::= <subdomain> | " "
4463 <subdomain> ::= <label> | <subdomain> "." <label>
4465 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4467 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4469 <let-dig-hyp> ::= <let-dig> | "-"
4471 <let-dig> ::= <letter> | <digit>
4473 <letter> ::= any one of the 52 alphabetic characters A through Z in
4474 upper case and a through z in lower case
4476 <digit> ::= any one of the ten digits 0 through 9
4478 static bool isDNSName(CFStringRef string
) {
4479 CFStringInlineBuffer buf
= {};
4480 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4481 /* From RFC 1035 2.3.4. Size limits:
4482 labels 63 octets or less
4483 names 255 octets or less */
4484 require_quiet(length
<= 255, notDNS
);
4485 CFRange range
= { 0, length
};
4486 CFStringInitInlineBuffer(string
, &buf
, range
);
4490 kDNSStateAfterAlpha
,
4491 kDNSStateAfterDigit
,
4493 } state
= kDNSStateInital
;
4495 for (ix
= 0; ix
< length
; ++ix
) {
4496 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4499 require_quiet(labelLength
<= 64 &&
4500 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4502 state
= kDNSStateAfterDot
;
4504 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4506 state
= kDNSStateAfterAlpha
;
4507 } else if ('0' <= ch
&& ch
<= '9') {
4509 /* The requirement for labels to start with a letter was
4510 dropped so we don't check this anymore. */
4511 require_quiet(state
== kDNSStateAfterAlpha
||
4512 state
== kDNSStateAfterDigit
||
4513 state
== kDNSStateAfterDash
, notDNS
);
4515 state
= kDNSStateAfterDigit
;
4516 } else if (ch
== '-') {
4517 require_quiet(state
== kDNSStateAfterAlpha
||
4518 state
== kDNSStateAfterDigit
||
4519 state
== kDNSStateAfterDash
, notDNS
);
4520 state
= kDNSStateAfterDash
;
4526 /* We don't allow a dns name to end in a dot or dash. */
4527 require_quiet(labelLength
<= 63 &&
4528 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4536 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4537 const DERItem
*value
, CFIndex rdnIX
) {
4538 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4539 if (DEROidCompare(type
, &oidCommonName
)) {
4540 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4543 if (isDNSName(string
)) {
4544 /* We found a common name that is formatted like a valid
4546 CFArrayAppendValue(dnsNames
, string
);
4550 return errSecInvalidCertificate
;
4553 return errSecSuccess
;
4556 CFArrayRef
SecCertificateCopyDNSNamesFromSubject(SecCertificateRef certificate
) {
4557 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4558 0, &kCFTypeArrayCallBacks
);
4559 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4560 appendDNSNamesFromX501Name
);
4561 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4562 CFReleaseNull(dnsNames
);
4566 /* appendDNSNamesFromX501Name allows IP addresses, we don't want those for this function */
4567 __block CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4568 CFArrayForEach(dnsNames
, ^(const void *value
) {
4569 CFStringRef name
= (CFStringRef
)value
;
4570 if (!convertIPAddress(name
, NULL
)) {
4571 CFArrayAppendValue(result
, name
);
4574 CFReleaseNull(dnsNames
);
4575 if (CFArrayGetCount(result
) == 0) {
4576 CFReleaseNull(result
);
4582 /* Not everything returned by this function is going to be a proper DNS name,
4583 we also return the certificates common name entries from the subject,
4584 assuming they look like dns names as specified in RFC 1035. */
4585 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4586 /* These can exist in the subject alt name or in the subject. */
4587 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4588 0, &kCFTypeArrayCallBacks
);
4589 OSStatus status
= errSecSuccess
;
4590 if (certificate
->_subjectAltName
) {
4591 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4592 dnsNames
, appendDNSNamesFromGeneralNames
);
4594 /* RFC 2818 section 3.1. Server Identity
4596 If a subjectAltName extension of type dNSName is present, that MUST
4597 be used as the identity. Otherwise, the (most specific) Common Name
4598 field in the Subject field of the certificate MUST be used. Although
4599 the use of the Common Name is existing practice, it is deprecated and
4600 Certification Authorities are encouraged to use the dNSName instead.
4603 This implies that if we found 1 or more DNSNames in the
4604 subjectAltName, we should not use the Common Name of the subject as
4607 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4608 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4609 appendDNSNamesFromX501Name
);
4611 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4612 CFRelease(dnsNames
);
4618 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4619 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4620 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4621 if (gnType
== GNT_RFC822Name
) {
4622 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4623 generalName
->data
, generalName
->length
,
4624 kCFStringEncodingASCII
, FALSE
);
4626 CFArrayAppendValue(dnsNames
, string
);
4629 return errSecInvalidCertificate
;
4632 return errSecSuccess
;
4635 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4636 const DERItem
*value
, CFIndex rdnIX
) {
4637 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4638 if (DEROidCompare(type
, &oidEmailAddress
)) {
4639 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4642 CFArrayAppendValue(dnsNames
, string
);
4645 return errSecInvalidCertificate
;
4648 return errSecSuccess
;
4651 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4652 /* These can exist in the subject alt name or in the subject. */
4653 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4654 0, &kCFTypeArrayCallBacks
);
4655 OSStatus status
= errSecSuccess
;
4656 if (certificate
->_subjectAltName
) {
4657 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4658 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4661 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4662 appendRFC822NamesFromX501Name
);
4664 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4665 CFRelease(rfc822Names
);
4671 OSStatus
SecCertificateCopyEmailAddresses(SecCertificateRef certificate
, CFArrayRef
* __nonnull CF_RETURNS_RETAINED emailAddresses
) {
4672 if (!certificate
|| !emailAddresses
) {
4675 *emailAddresses
= SecCertificateCopyRFC822Names(certificate
);
4676 if (*emailAddresses
== NULL
) {
4677 *emailAddresses
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
4679 return errSecSuccess
;
4682 CFArrayRef
SecCertificateCopyRFC822NamesFromSubject(SecCertificateRef certificate
) {
4683 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4684 0, &kCFTypeArrayCallBacks
);
4685 OSStatus status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4686 appendRFC822NamesFromX501Name
);
4687 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4688 CFRelease(rfc822Names
);
4694 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4695 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4696 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4697 if (DEROidCompare(type
, &oidCommonName
)) {
4698 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4701 CFArrayAppendValue(commonNames
, string
);
4704 return errSecInvalidCertificate
;
4707 return errSecSuccess
;
4710 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4711 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4712 0, &kCFTypeArrayCallBacks
);
4714 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4715 appendCommonNamesFromX501Name
);
4716 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4717 CFRelease(commonNames
);
4723 OSStatus
SecCertificateCopyCommonName(SecCertificateRef certificate
, CFStringRef
*commonName
)
4728 CFArrayRef commonNames
= SecCertificateCopyCommonNames(certificate
);
4730 return errSecInternal
;
4734 CFIndex count
= CFArrayGetCount(commonNames
);
4735 *commonName
= CFRetainSafe(CFArrayGetValueAtIndex(commonNames
, count
-1));
4737 CFReleaseSafe(commonNames
);
4738 return errSecSuccess
;
4741 static OSStatus
appendOrganizationFromX501Name(void *context
,
4742 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4743 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4744 if (DEROidCompare(type
, &oidOrganizationName
)) {
4745 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4748 CFArrayAppendValue(organization
, string
);
4751 return errSecInvalidCertificate
;
4754 return errSecSuccess
;
4757 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4758 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4759 0, &kCFTypeArrayCallBacks
);
4761 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4762 appendOrganizationFromX501Name
);
4763 if (status
|| CFArrayGetCount(organization
) == 0) {
4764 CFRelease(organization
);
4765 organization
= NULL
;
4767 return organization
;
4770 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4771 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4772 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4773 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4774 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4777 CFArrayAppendValue(organizationalUnit
, string
);
4780 return errSecInvalidCertificate
;
4783 return errSecSuccess
;
4786 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4787 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4788 0, &kCFTypeArrayCallBacks
);
4790 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4791 appendOrganizationalUnitFromX501Name
);
4792 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4793 CFRelease(organizationalUnit
);
4794 organizationalUnit
= NULL
;
4796 return organizationalUnit
;
4799 static OSStatus
appendCountryFromX501Name(void *context
,
4800 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4801 CFMutableArrayRef countries
= (CFMutableArrayRef
)context
;
4802 if (DEROidCompare(type
, &oidCountryName
)) {
4803 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4806 CFArrayAppendValue(countries
, string
);
4809 return errSecInvalidCertificate
;
4812 return errSecSuccess
;
4815 CFArrayRef
SecCertificateCopyCountry(SecCertificateRef certificate
) {
4816 CFMutableArrayRef countries
= CFArrayCreateMutable(kCFAllocatorDefault
,
4817 0, &kCFTypeArrayCallBacks
);
4819 status
= parseX501NameContent(&certificate
->_subject
, countries
,
4820 appendCountryFromX501Name
);
4821 if (status
|| CFArrayGetCount(countries
) == 0) {
4822 CFRelease(countries
);
4828 const SecCEBasicConstraints
*
4829 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4830 if (certificate
->_basicConstraints
.present
)
4831 return &certificate
->_basicConstraints
;
4836 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
4837 return (certificate
->_permittedSubtrees
);
4840 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
4841 return (certificate
->_excludedSubtrees
);
4844 const SecCEPolicyConstraints
*
4845 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4846 if (certificate
->_policyConstraints
.present
)
4847 return &certificate
->_policyConstraints
;
4852 const SecCEPolicyMappings
*
4853 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4854 if (certificate
->_policyMappings
.present
) {
4855 return &certificate
->_policyMappings
;
4861 const SecCECertificatePolicies
*
4862 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4863 if (certificate
->_certificatePolicies
.present
)
4864 return &certificate
->_certificatePolicies
;
4869 const SecCEInhibitAnyPolicy
*
4870 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4871 if (certificate
->_inhibitAnyPolicySkipCerts
.present
) {
4872 return &certificate
->_inhibitAnyPolicySkipCerts
;
4878 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4879 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4880 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4881 if (gnType
== GNT_OtherName
) {
4883 DERReturn drtn
= DERParseSequenceContent(generalName
,
4884 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4886 require_noerr_quiet(drtn
, badDER
);
4887 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4889 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4890 &on
.value
, true), badDER
);
4891 CFArrayAppendValue(ntPrincipalNames
, string
);
4895 return errSecSuccess
;
4898 return errSecInvalidCertificate
;
4902 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
4903 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4904 0, &kCFTypeArrayCallBacks
);
4905 OSStatus status
= errSecSuccess
;
4906 if (certificate
->_subjectAltName
) {
4907 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4908 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4910 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4911 CFRelease(ntPrincipalNames
);
4912 ntPrincipalNames
= NULL
;
4914 return ntPrincipalNames
;
4917 static OSStatus
appendToRFC2253String(void *context
,
4918 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4919 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4923 ST stateOrProvinceName
4925 OU organizationalUnitName
4927 STREET streetAddress
4931 /* Prepend a + if this is not the first RDN in an RDN set.
4932 Otherwise prepend a , if this is not the first RDN. */
4934 CFStringAppend(string
, CFSTR("+"));
4935 else if (CFStringGetLength(string
)) {
4936 CFStringAppend(string
, CFSTR(","));
4939 CFStringRef label
, oid
= NULL
;
4940 /* @@@ Consider changing this to a dictionary lookup keyed by the
4941 decimal representation. */
4942 if (DEROidCompare(type
, &oidCommonName
)) {
4943 label
= CFSTR("CN");
4944 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4946 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4947 label
= CFSTR("ST");
4948 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4950 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4951 label
= CFSTR("OU");
4952 } else if (DEROidCompare(type
, &oidCountryName
)) {
4955 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4956 label
= CFSTR("STREET");
4957 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4958 label
= CFSTR("DC");
4959 } else if (DEROidCompare(type
, &oidUserID
)) {
4960 label
= CFSTR("UID");
4963 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4966 CFStringAppend(string
, label
);
4967 CFStringAppend(string
, CFSTR("="));
4968 CFStringRef raw
= NULL
;
4970 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4973 /* Append raw to string while escaping:
4974 a space or "#" character occurring at the beginning of the string
4975 a space character occurring at the end of the string
4976 one of the characters ",", "+", """, "\", "<", ">" or ";"
4978 CFStringInlineBuffer buffer
= {};
4979 CFIndex ix
, length
= CFStringGetLength(raw
);
4980 CFRange range
= { 0, length
};
4981 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4982 for (ix
= 0; ix
< length
; ++ix
) {
4983 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4985 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4986 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4987 ch
== '<' || ch
== '>' || ch
== ';' ||
4988 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4989 (ch
== '#' && ix
== 0)) {
4990 UniChar chars
[] = { '\\', ch
};
4991 CFStringAppendCharacters(string
, chars
, 2);
4993 CFStringAppendCharacters(string
, &ch
, 1);
4998 /* Append the value in hex. */
4999 CFStringAppend(string
, CFSTR("#"));
5001 for (ix
= 0; ix
< value
->length
; ++ix
)
5002 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
5007 return errSecSuccess
;
5010 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
5011 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5012 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
5013 if (status
|| CFStringGetLength(string
) == 0) {
5020 static OSStatus
appendToCompanyNameString(void *context
,
5021 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
5022 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5023 if (CFStringGetLength(string
) != 0)
5024 return errSecSuccess
;
5026 if (!DEROidCompare(type
, &oidOrganizationName
))
5027 return errSecSuccess
;
5030 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
5032 return errSecSuccess
;
5033 CFStringAppend(string
, raw
);
5036 return errSecSuccess
;
5039 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
5040 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5041 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
5042 appendToCompanyNameString
);
5043 if (status
|| CFStringGetLength(string
) == 0) {
5050 CFDataRef
SecCertificateCopyIssuerSequence(
5051 SecCertificateRef certificate
) {
5052 return SecDERItemCopySequence(&certificate
->_issuer
);
5055 CFDataRef
SecCertificateCopySubjectSequence(
5056 SecCertificateRef certificate
) {
5057 return SecDERItemCopySequence(&certificate
->_subject
);
5060 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(SecCertificateRef certificate
) {
5061 if (!certificate
|| !certificate
->_normalizedIssuer
) {
5064 return SecCopySequenceFromContent(certificate
->_normalizedIssuer
);
5067 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(SecCertificateRef certificate
) {
5068 if (!certificate
|| !certificate
->_normalizedSubject
) {
5071 return SecCopySequenceFromContent(certificate
->_normalizedSubject
);
5074 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
5075 SecCertificateRef certificate
) {
5076 return &certificate
->_algId
;
5079 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
5080 return &certificate
->_pubKeyDER
;
5084 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
5085 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
5087 __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
5089 __nullable SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
5092 if (certificate
->_pubKey
== NULL
) {
5093 const DERAlgorithmId
*algId
=
5094 SecCertificateGetPublicKeyAlgorithm(certificate
);
5095 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
5096 const DERItem
*params
= NULL
;
5097 if (algId
->params
.length
!= 0) {
5098 params
= &algId
->params
;
5100 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
5101 SecAsn1Item params1
= {
5102 .Data
= params
? params
->data
: NULL
,
5103 .Length
= params
? params
->length
: 0
5105 SecAsn1Item keyData1
= {
5106 .Data
= keyData
? keyData
->data
: NULL
,
5107 .Length
= keyData
? keyData
->length
: 0
5109 certificate
->_pubKey
= SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
5113 return CFRetainSafe(certificate
->_pubKey
);
5116 static CFIndex
SecCertificateGetPublicKeyAlgorithmIdAndSize(SecCertificateRef certificate
, size_t *keySizeInBytes
) {
5117 CFIndex keyAlgID
= kSecNullAlgorithmID
;
5120 SecKeyRef pubKey
= NULL
;
5121 require_quiet(certificate
, out
);
5123 require_quiet(pubKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
5125 require_quiet(pubKey
= SecCertificateCopyPublicKey(certificate
) ,out
);
5127 size
= SecKeyGetBlockSize(pubKey
);
5128 keyAlgID
= SecKeyGetAlgorithmId(pubKey
);
5131 CFReleaseNull(pubKey
);
5132 if (keySizeInBytes
) { *keySizeInBytes
= size
; }
5137 * Public keys in certificates may be considered "weak" or "strong" or neither
5138 * (that is, in between). Certificates using weak keys are not trusted at all.
5139 * Certificates using neither strong nor weak keys are only trusted in certain
5140 * contexts. SecPolicy and SecPolicyServer define the contexts by which we enforce
5141 * these (or stronger) key size trust policies.
5143 bool SecCertificateIsWeakKey(SecCertificateRef certificate
) {
5144 if (!certificate
) { return true; }
5148 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5149 case kSecRSAAlgorithmID
:
5150 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
5152 case kSecECDSAAlgorithmID
:
5153 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
5161 bool SecCertificateIsStrongKey(SecCertificateRef certificate
) {
5162 if (!certificate
) { return false; }
5164 bool strong
= false;
5166 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5167 case kSecRSAAlgorithmID
:
5168 if (MIN_STRONG_RSA_KEY_SIZE
<= size
) strong
= true;
5170 case kSecECDSAAlgorithmID
:
5171 if (MIN_STRONG_EC_KEY_SIZE
<= size
) strong
= true;
5179 bool SecCertificateIsWeakHash(SecCertificateRef certificate
) {
5180 if (!certificate
) { return true; }
5181 SecSignatureHashAlgorithm certAlg
= 0;
5182 certAlg
= SecCertificateGetSignatureHashAlgorithm(certificate
);
5183 if (certAlg
== kSecSignatureHashAlgorithmUnknown
||
5184 certAlg
== kSecSignatureHashAlgorithmMD2
||
5185 certAlg
== kSecSignatureHashAlgorithmMD4
||
5186 certAlg
== kSecSignatureHashAlgorithmMD5
||
5187 certAlg
== kSecSignatureHashAlgorithmSHA1
) {
5193 bool SecCertificateIsAtLeastMinKeySize(SecCertificateRef certificate
,
5194 CFDictionaryRef keySizes
) {
5195 if (!certificate
) { return false; }
5197 bool goodSize
= false;
5199 CFNumberRef minSize
;
5200 size_t minSizeInBits
;
5201 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5202 case kSecRSAAlgorithmID
:
5203 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeRSA
, (const void**)&minSize
)
5204 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5205 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5208 case kSecECDSAAlgorithmID
:
5209 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeEC
, (const void**)&minSize
)
5210 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5211 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5220 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
5221 if (!certificate
|| !certificate
->_der
.data
) {
5224 if (!certificate
->_sha1Digest
) {
5225 certificate
->_sha1Digest
=
5226 SecSHA1DigestCreate(CFGetAllocator(certificate
),
5227 certificate
->_der
.data
, certificate
->_der
.length
);
5229 return certificate
->_sha1Digest
;
5232 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
5233 if (!certificate
|| !certificate
->_der
.data
) {
5236 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
5237 certificate
->_der
.data
, certificate
->_der
.length
);
5240 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
5241 CFDataRef digest
= NULL
;
5242 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
5244 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
5245 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
5251 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
5252 if (!certificate
|| !certificate
->_pubKeyDER
.data
) {
5255 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
5256 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
5259 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA1Digest(SecCertificateRef certificate
) {
5260 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
5263 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
5264 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
5267 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
5268 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
5271 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
5272 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
5275 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
5280 CFRetainSafe(certificate
->_keychain_item
);
5281 return certificate
->_keychain_item
;
5284 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
5288 if (!certificate
->_authorityKeyID
&&
5289 certificate
->_authorityKeyIdentifier
.length
) {
5290 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
5291 certificate
->_authorityKeyIdentifier
.data
,
5292 certificate
->_authorityKeyIdentifier
.length
);
5295 return certificate
->_authorityKeyID
;
5298 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
5302 if (!certificate
->_subjectKeyID
&&
5303 certificate
->_subjectKeyIdentifier
.length
) {
5304 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5305 certificate
->_subjectKeyIdentifier
.data
,
5306 certificate
->_subjectKeyIdentifier
.length
);
5309 return certificate
->_subjectKeyID
;
5312 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5316 return certificate
->_crlDistributionPoints
;
5319 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5323 return certificate
->_ocspResponders
;
5326 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5330 return certificate
->_caIssuers
;
5333 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5337 return certificate
->_subjectAltName
&&
5338 certificate
->_subjectAltName
->critical
;
5341 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5345 /* Since the _subject field is the content of the subject and not the
5346 whole thing, we can simply check for a 0 length subject here. */
5347 return certificate
->_subject
.length
!= 0;
5350 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5354 return certificate
->_foundUnknownCriticalExtension
;
5357 /* Private API functions. */
5358 void SecCertificateShow(SecCertificateRef certificate
) {
5360 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5361 fprintf(stderr
, "\n");
5365 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5366 SecCertificateRef certificate
) {
5367 if (!certificate
|| !(CFGetTypeID(certificate
) == SecCertificateGetTypeID())) {
5370 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5371 CFNumberRef certificateType
= NULL
;
5372 CFNumberRef certificateEncoding
= NULL
;
5373 CFStringRef label
= NULL
;
5374 CFStringRef alias
= NULL
;
5375 CFDataRef skid
= NULL
;
5376 CFDataRef pubKeyDigest
= NULL
;
5377 CFDataRef certData
= NULL
;
5378 CFDictionaryRef dict
= NULL
;
5382 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5383 SInt32 ctv
= certificate
->_version
+ 1;
5384 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5385 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5386 require_quiet(certificateType
!= NULL
, out
);
5387 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5388 require_quiet(certificateEncoding
!= NULL
, out
);
5389 certData
= SecCertificateCopyData(certificate
);
5390 require_quiet(certData
!= NULL
, out
);
5391 skid
= SecCertificateGetSubjectKeyID(certificate
);
5392 require_quiet(certificate
->_pubKeyDER
.data
!= NULL
&& certificate
->_pubKeyDER
.length
> 0, out
);
5393 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5394 certificate
->_pubKeyDER
.length
);
5395 require_quiet(pubKeyDigest
!= NULL
, out
);
5397 /* We still need to figure out how to deal with multi valued attributes. */
5398 alias
= SecCertificateCopyRFC822Names(certificate
);
5399 label
= SecCertificateCopySubjectSummary(certificate
);
5405 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5406 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5407 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5409 DICT_ADDPAIR(kSecAttrLabel
, label
);
5412 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5414 if (isData(certificate
->_normalizedSubject
)) {
5415 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5417 require_quiet(isData(certificate
->_normalizedIssuer
), out
);
5418 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5419 require_quiet(isData(certificate
->_serialNumber
), out
);
5420 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5422 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5424 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5425 DICT_ADDPAIR(kSecValueData
, certData
);
5426 dict
= DICT_CREATE(allocator
);
5429 CFReleaseSafe(label
);
5430 CFReleaseSafe(alias
);
5431 CFReleaseSafe(pubKeyDigest
);
5432 CFReleaseSafe(certData
);
5433 CFReleaseSafe(certificateEncoding
);
5434 CFReleaseSafe(certificateType
);
5439 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5440 CFDictionaryRef refAttributes
) {
5441 /* @@@ Support having an allocator in refAttributes. */
5442 CFAllocatorRef allocator
= NULL
;
5443 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5444 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5448 static bool _SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
5449 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
5450 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
5451 SecKeyRef publicKey
= NULL
;
5452 require(certificate
&& (CFGetTypeID(certificate
) == SecCertificateGetTypeID()), out
);
5454 require(publicKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
5456 require(publicKey
= SecCertificateCopyPublicKey(certificate
), out
);
5458 CFDataRef normalizedIssuer
=
5459 SecCertificateGetNormalizedIssuerContent(certificate
);
5460 CFDataRef normalizedSubject
=
5461 SecCertificateGetNormalizedSubjectContent(certificate
);
5462 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5463 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5465 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5466 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5467 if (authorityKeyID
) {
5468 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5471 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5473 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
5475 CFReleaseSafe(publicKey
);
5478 return (certificate
->_isSelfSigned
== kSecSelfSignedTrue
);
5481 bool SecCertificateIsCA(SecCertificateRef certificate
) {
5482 bool result
= false;
5483 require(certificate
&& (CFGetTypeID(certificate
) == SecCertificateGetTypeID()), out
);
5484 if (SecCertificateVersion(certificate
) >= 3) {
5485 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5486 result
= (basicConstraints
&& basicConstraints
->isCA
);
5489 result
= _SecCertificateIsSelfSigned(certificate
);
5495 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5496 return (_SecCertificateIsSelfSigned(certificate
) && SecCertificateIsCA(certificate
));
5499 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5500 if (!certificate
|| (CFGetTypeID(certificate
) != SecCertificateGetTypeID())) {
5501 return errSecInvalidCertificate
;
5503 if (!isSelfSigned
) {
5506 *isSelfSigned
= _SecCertificateIsSelfSigned(certificate
);
5507 return errSecSuccess
;
5510 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5512 return kSecKeyUsageUnspecified
;
5514 return certificate
->_keyUsage
;
5517 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5519 CFMutableArrayRef extended_key_usage_oids
=
5520 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5521 require_quiet(certificate
&& extended_key_usage_oids
, out
);
5523 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5524 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5525 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5526 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5529 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5530 require_noerr_quiet(drtn
, out
);
5531 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5532 DERDecodedInfo currDecoded
;
5534 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5535 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5536 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5537 currDecoded
.content
.data
, currDecoded
.content
.length
);
5538 require_quiet(oid
, out
);
5539 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5542 require_quiet(drtn
== DR_EndOfSequence
, out
);
5543 return extended_key_usage_oids
;
5547 CFReleaseSafe(extended_key_usage_oids
);
5551 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5553 require_quiet(certificate
, out
);
5556 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5557 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5558 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5559 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5560 /* Got the SCT oid */
5561 DERDecodedInfo sctList
;
5562 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5563 require_noerr_quiet(drtn
, out
);
5564 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5565 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5573 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5574 if (der
.length
> 1) {
5575 DERDecodedInfo decoded
;
5576 DERDecodeItem(&der
, &decoded
);
5577 switch (decoded
.tag
) {
5580 return decoded
.content
.length
== 0 && expected
== NULL
;
5584 case ASN1_IA5_STRING
:
5585 case ASN1_UTF8_STRING
: {
5586 if (isString(expected
)) {
5587 CFStringRef expectedString
= (CFStringRef
) expected
;
5588 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5590 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5591 CFReleaseNull(itemString
);
5597 case ASN1_OCTET_STRING
: {
5598 if (isData(expected
)) {
5599 CFDataRef expectedData
= (CFDataRef
) expected
;
5600 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5602 bool result
= CFEqual(expectedData
, itemData
);
5603 CFReleaseNull(itemData
);
5609 case ASN1_INTEGER
: {
5610 SInt32 expected_value
= 0;
5611 if (isString(expected
))
5613 CFStringRef aStr
= (CFStringRef
)expected
;
5614 expected_value
= CFStringGetIntValue(aStr
);
5616 else if (isNumber(expected
))
5618 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5621 uint32_t num_value
= 0;
5622 if (!DERParseInteger(&decoded
.content
, &num_value
))
5624 return ((uint32_t)expected_value
== num_value
);
5637 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5640 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5641 size_t oid_len
= CFDataGetLength(oid
);
5643 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5644 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5645 if (extn
->extnID
.length
== oid_len
5646 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5648 return matches_expected(extn
->extnValue
, expectedValue
);
5654 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5656 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5659 struct search_context
{
5661 SecCertificateRef certificate
;
5664 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5666 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5667 bool result
= false;
5669 if ( CFStringGetLength(string
) > 0
5670 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5673 *value
= CFStringGetIntValue(string
);
5677 CFReleaseNull(nonDecimalDigit
);
5682 bool SecCertificateIsOidString(CFStringRef oid
)
5684 if (!oid
) return false;
5685 if (2 >= CFStringGetLength(oid
)) return false;
5688 /* oid string only has the allowed characters */
5689 CFCharacterSetRef decimalOid
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
5690 CFCharacterSetRef nonDecimalOid
= CFCharacterSetCreateInvertedSet(NULL
, decimalOid
);
5691 if (CFStringFindCharacterFromSet(oid
, nonDecimalOid
, CFRangeMake(0, CFStringGetLength(oid
)), kCFCompareForcedOrdering
, NULL
)) {
5695 /* first arc is allowed */
5696 UniChar firstArc
[2];
5697 CFRange firstTwo
= {0, 2};
5698 CFStringGetCharacters(oid
, firstTwo
, firstArc
);
5699 if (firstArc
[1] != '.' ||
5700 (firstArc
[0] != '0' && firstArc
[0] != '1' && firstArc
[0] != '2')) {
5704 CFReleaseNull(decimalOid
);
5705 CFReleaseNull(nonDecimalOid
);
5710 CFDataRef
SecCertificateCreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
5712 CFMutableDataRef currentResult
= NULL
;
5713 CFDataRef encodedResult
= NULL
;
5715 CFArrayRef parts
= NULL
;
5718 if (!string
|| !SecCertificateIsOidString(string
))
5721 parts
= CFStringCreateArrayBySeparatingStrings(NULL
, string
, CFSTR("."));
5726 count
= CFArrayGetCount(parts
);
5730 // assume no more than 5 bytes needed to represent any part of the oid,
5731 // since we limit parts to 32-bit values,
5732 // but the first two parts only need 1 byte
5733 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
5739 part
= CFArrayGetValueAtIndex(parts
, 0);
5741 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
5748 part
= CFArrayGetValueAtIndex(parts
, 1);
5750 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
5756 CFDataAppendBytes(currentResult
, &firstByte
, 1);
5758 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
5759 uint8_t b
[5] = {0, 0, 0, 0, 0};
5761 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
5762 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
5763 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
5764 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
5766 // Skip the unused extension bytes.
5767 size_t skipBytes
= 0;
5768 while (b
[skipBytes
] == 0x80)
5771 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
5774 encodedResult
= currentResult
;
5775 currentResult
= NULL
;
5778 CFReleaseNull(parts
);
5779 CFReleaseNull(currentResult
);
5781 return encodedResult
;
5784 static void check_for_marker(const void *key
, const void *value
, void *context
)
5786 struct search_context
* search_ctx
= (struct search_context
*) context
;
5787 CFStringRef key_string
= (CFStringRef
) key
;
5788 CFTypeRef value_ref
= (CFTypeRef
) value
;
5790 // If we could have short circuted the iteration
5791 // we would have, but the best we can do
5792 // is not waste time comparing once a match
5794 if (search_ctx
->found
)
5797 if (CFGetTypeID(key_string
) != CFStringGetTypeID())
5800 CFDataRef key_data
= SecCertificateCreateOidDataFromString(NULL
, key_string
);
5802 if (NULL
== key_data
)
5805 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
5806 search_ctx
->found
= true;
5808 CFReleaseNull(key_data
);
5812 // CFType Ref is either:
5814 // CFData - OID to match with no data permitted
5815 // CFString - decimal OID to match
5816 // CFDictionary - OID -> Value table for expected values Single Object or Array
5817 // CFArray - Array of the above.
5819 // This returns true if any of the requirements are met.
5820 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
5822 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
5823 CFIndex ix
, length
= CFArrayGetCount(oids
);
5824 for (ix
= 0; ix
< length
; ix
++)
5825 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
5827 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
5828 struct search_context context
= { .found
= false, .certificate
= certificate
};
5829 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
5830 return context
.found
;
5831 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
5832 return cert_contains_marker_extension(certificate
, oids
);
5833 } else if (CFGetTypeID(oids
) == CFStringGetTypeID()) {
5834 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oids
);
5835 if (dataOid
== NULL
) return false;
5836 bool result
= cert_contains_marker_extension(certificate
, dataOid
);
5837 CFReleaseNull(dataOid
);
5843 static DERItem
*cert_extension_value_for_marker(SecCertificateRef certificate
, CFDataRef oid
) {
5845 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5846 size_t oid_len
= CFDataGetLength(oid
);
5848 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5849 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5850 if (extn
->extnID
.length
== oid_len
5851 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5853 return (DERItem
*)&extn
->extnValue
;
5860 // CFType Ref is either:
5862 // CFData - OID to match with no data permitted
5863 // CFString - decimal OID to match
5865 DERItem
*SecCertificateGetExtensionValue(SecCertificateRef certificate
, CFTypeRef oid
) {
5866 if (!certificate
|| !oid
) {
5870 if(CFGetTypeID(oid
) == CFDataGetTypeID()) {
5871 return cert_extension_value_for_marker(certificate
, oid
);
5872 } else if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
5873 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oid
);
5874 if (dataOid
== NULL
) return NULL
;
5875 DERItem
*result
= cert_extension_value_for_marker(certificate
, dataOid
);
5876 CFReleaseNull(dataOid
);
5883 CFDataRef
SecCertificateCopyiAPAuthCapabilities(SecCertificateRef certificate
) {
5887 CFDataRef extensionData
= NULL
;
5888 DERItem
*extensionValue
= NULL
;
5889 extensionValue
= SecCertificateGetExtensionValue(certificate
,
5890 CFSTR("1.2.840.113635.100.6.36"));
5891 require_quiet(extensionValue
, out
);
5892 /* The extension is a octet string containing the DER-encoded 32-byte octet string */
5893 require_quiet(extensionValue
->length
== 34, out
);
5894 DERDecodedInfo decodedValue
;
5895 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
5896 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
5897 require_quiet(decodedValue
.content
.length
== 32, out
);
5898 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
5899 decodedValue
.content
.length
);
5901 require_quiet(extensionValue
->data
[33] == 0x00 &&
5902 extensionValue
->data
[32] == 0x00, out
);
5903 extensionData
= CFDataCreate(NULL
, extensionValue
->data
, 32);
5906 return extensionData
;
5910 /* From iapd IAPAuthenticationTypes.h */
5911 typedef struct IapCertSerialNumber
5913 uint8_t xservID
; // Xserver ID
5914 uint8_t hsmID
; // Hardware security module ID (generated cert)
5915 uint8_t delimiter01
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5916 uint8_t dateYear
; // Date year cert was issued
5917 uint8_t dateMonth
; // Date month cert was issued
5918 uint8_t dateDay
; // Date day cert was issued
5919 uint8_t delimiter02
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5920 uint8_t devClass
; // iAP device class (maps to lingo permissions)
5921 uint8_t delimiter03
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5922 uint8_t batchNumHi
; // Batch number high byte (15:08)
5923 uint8_t batchNumLo
; // Batch number low byte (07:00)
5924 uint8_t delimiter04
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5925 uint8_t serialNumHi
; // Serial number high byte (23:16)
5926 uint8_t serialNumMid
; // Serial number middle byte (15:08)
5927 uint8_t serialNumLo
; // Serial number low byte (07:00)
5929 } IapCertSerialNumber_t
, *pIapCertSerialNumber_t
;
5932 #define IAP_CERT_FIELD_DELIMITER 0xAA // "Apple_Accessory" delimiter
5933 SeciAuthVersion
SecCertificateGetiAuthVersion(SecCertificateRef certificate
) {
5935 return kSeciAuthInvalid
;
5937 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
5938 CFSTR("1.2.840.113635.100.6.36"))) {
5939 return kSeciAuthVersion3
;
5941 DERItem serialNumber
= certificate
->_serialNum
;
5942 require_quiet(serialNumber
.data
, out
);
5943 require_quiet(serialNumber
.length
== 15, out
);
5944 require_quiet(serialNumber
.data
[2] == IAP_CERT_FIELD_DELIMITER
&&
5945 serialNumber
.data
[6] == IAP_CERT_FIELD_DELIMITER
&&
5946 serialNumber
.data
[8] == IAP_CERT_FIELD_DELIMITER
&&
5947 serialNumber
.data
[11] == IAP_CERT_FIELD_DELIMITER
, out
);
5948 return kSeciAuthVersion2
;
5950 return kSeciAuthInvalid
;
5953 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
5954 CFDataRef pem_certificate
)
5956 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
5957 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
5958 uint8_t *base64_data
= NULL
;
5959 SecCertificateRef cert
= NULL
;
5960 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
5961 //const size_t length = CFDataGetLength(pem_certificate);
5962 char *begin
= strstr((const char *)data
, begin_cert
);
5963 char *end
= strstr((const char *)data
, end_cert
);
5966 begin
+= sizeof(begin_cert
) - 1;
5967 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
5968 if (base64_length
&& (base64_length
< (size_t)CFDataGetLength(pem_certificate
))) {
5969 require_quiet(base64_data
= calloc(1, base64_length
), out
);
5970 require_action_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
, free(base64_data
));
5971 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
5980 // -- MARK -- XPC encoding/decoding
5983 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5985 return true; // NOOP
5987 size_t length
= SecCertificateGetLength(certificate
);
5988 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
5989 #if SECTRUST_VERBOSE_DEBUG
5990 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
5992 if (!length
|| !bytes
) {
5993 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
5995 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
5999 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
6000 SecCertificateRef certificate
= NULL
;
6002 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
6004 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
6007 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
6012 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
6013 xpc_object_t xpc_certificates
;
6014 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
6015 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
6016 CFIndex ix
, count
= CFArrayGetCount(certificates
);
6017 for (ix
= 0; ix
< count
; ++ix
) {
6018 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
6019 #if SECTRUST_VERBOSE_DEBUG
6020 CFIndex length
= SecCertificateGetLength(certificate
);
6021 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
6022 secerror("idx=%d of %d; cert=0x%lX length=%ld bytes=0x%lX", (int)ix
, (int)count
, (uintptr_t)certificate
, (size_t)length
, (uintptr_t)bytes
);
6024 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
6025 xpc_release(xpc_certificates
);
6026 xpc_certificates
= NULL
;
6032 return xpc_certificates
;
6035 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6036 CFMutableArrayRef certificates
= NULL
;
6037 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
6038 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
6039 size_t count
= xpc_array_get_count(xpc_certificates
);
6040 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
6041 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
6044 for (ix
= 0; ix
< count
; ++ix
) {
6045 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
6047 CFRelease(certificates
);
6050 CFArraySetValueAtIndex(certificates
, ix
, cert
);
6055 return certificates
;
6058 #define do_if_registered(sdp, ...) if (gTrustd && gTrustd->sdp) { return gTrustd->sdp(__VA_ARGS__); }
6061 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
6063 __block CFArrayRef result
= NULL
;
6065 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
6067 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
6068 ^bool(xpc_object_t message
, CFErrorRef
*error
)
6070 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
6073 ^bool(xpc_object_t response
, CFErrorRef
*error
)
6075 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6077 if (response
&& (NULL
!= xpc_array
)) {
6078 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
6081 return SecError(errSecInternal
, error
, CFSTR("Did not get the Escrow certificates"));
6083 return result
!= NULL
;
6088 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
6090 CFArrayRef result
= NULL
;
6092 CFDataRef certData
= NULL
;
6095 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
6096 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
||
6097 kSecCertificateBaselineEscrowBackupRoot
== escrowRootType
||
6098 kSecCertificateBaselineEscrowEnrollmentRoot
== escrowRootType
)
6100 // The request is for the base line certificates.
6101 // Use the hard coded data to generate the return array.
6102 struct RootRecord
** pEscrowRoots
;
6103 switch (escrowRootType
) {
6104 case kSecCertificateBaselineEscrowRoot
:
6105 numRoots
= kNumberOfBaseLineEscrowRoots
;
6106 pEscrowRoots
= kBaseLineEscrowRoots
;
6108 case kSecCertificateBaselinePCSEscrowRoot
:
6109 numRoots
= kNumberOfBaseLinePCSEscrowRoots
;
6110 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
6112 case kSecCertificateBaselineEscrowBackupRoot
:
6113 numRoots
= kNumberOfBaseLineEscrowBackupRoots
;
6114 pEscrowRoots
= kBaseLineEscrowBackupRoots
;
6116 case kSecCertificateBaselineEscrowEnrollmentRoot
:
6118 numRoots
= kNumberOfBaseLineEscrowEnrollmentRoots
;
6119 pEscrowRoots
= kBaseLineEscrowEnrollmentRoots
;
6123 // Get the hard coded set of roots
6124 SecCertificateRef baseLineCerts
[numRoots
];
6125 struct RootRecord
* pRootRecord
= NULL
;
6127 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6128 pRootRecord
= pEscrowRoots
[iCnt
];
6129 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
) {
6130 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
6131 if (NULL
!= certData
) {
6132 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6133 CFRelease(certData
);
6137 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6138 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6139 if (NULL
!= baseLineCerts
[iCnt
]) {
6140 CFRelease(baseLineCerts
[iCnt
]);
6145 // The request is for the current certificates.
6146 CFErrorRef error
= NULL
;
6147 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
6148 if (NULL
!= error
|| NULL
== cert_datas
) {
6149 if (NULL
!= error
) {
6152 if (NULL
!= cert_datas
) {
6153 CFRelease(cert_datas
);
6158 numRoots
= (int)(CFArrayGetCount(cert_datas
));
6160 SecCertificateRef assetCerts
[numRoots
];
6161 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6162 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
6163 if (NULL
!= certData
) {
6164 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6165 assetCerts
[iCnt
] = aCertRef
;
6168 assetCerts
[iCnt
] = NULL
;
6173 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6174 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6175 if (NULL
!= assetCerts
[iCnt
]) {
6176 CFRelease(assetCerts
[iCnt
]);
6180 CFReleaseSafe(cert_datas
);
6185 SEC_CONST_DECL (kSecSignatureDigestAlgorithmUnknown
, "SignatureDigestUnknown");
6186 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD2
, "SignatureDigestMD2");
6187 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD4
, "SignatureDigestMD4");
6188 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD5
, "SignatureDigestMD5");
6189 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA1
, "SignatureDigestSHA1");
6190 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA224
, "SignatureDigestSHA224");
6191 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA256
, "SignatureDigestSHA256");
6192 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA384
, "SignatureDigestSHA284");
6193 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA512
, "SignatureDigestSHA512");
6195 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
6197 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
6198 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
6199 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
6201 if (!algOid
->data
|| !algOid
->length
) {
6204 /* classify the signature algorithm OID into one of our known types */
6205 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
6206 DEROidCompare(algOid
, &oidSha512Rsa
) ||
6207 DEROidCompare(algOid
, &oidSha512
)) {
6208 result
= kSecSignatureHashAlgorithmSHA512
;
6211 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
6212 DEROidCompare(algOid
, &oidSha384Rsa
) ||
6213 DEROidCompare(algOid
, &oidSha384
)) {
6214 result
= kSecSignatureHashAlgorithmSHA384
;
6217 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
6218 DEROidCompare(algOid
, &oidSha256Rsa
) ||
6219 DEROidCompare(algOid
, &oidSha256
)) {
6220 result
= kSecSignatureHashAlgorithmSHA256
;
6223 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
6224 DEROidCompare(algOid
, &oidSha224Rsa
) ||
6225 DEROidCompare(algOid
, &oidSha224
)) {
6226 result
= kSecSignatureHashAlgorithmSHA224
;
6229 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
6230 DEROidCompare(algOid
, &oidSha1Rsa
) ||
6231 DEROidCompare(algOid
, &oidSha1Dsa
) ||
6232 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
6233 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
6234 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
6235 DEROidCompare(algOid
, &oidSha1Fee
) ||
6236 DEROidCompare(algOid
, &oidSha1
)) {
6237 result
= kSecSignatureHashAlgorithmSHA1
;
6240 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
6241 DEROidCompare(algOid
, &oidMd5Fee
) ||
6242 DEROidCompare(algOid
, &oidMd5
)) {
6243 result
= kSecSignatureHashAlgorithmMD5
;
6246 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
6247 DEROidCompare(algOid
, &oidMd4
)) {
6248 result
= kSecSignatureHashAlgorithmMD4
;
6251 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
6252 DEROidCompare(algOid
, &oidMd2
)) {
6253 result
= kSecSignatureHashAlgorithmMD2
;
6262 CFArrayRef
SecCertificateCopyiPhoneDeviceCAChain(void) {
6263 CFMutableArrayRef result
= NULL
;
6264 SecCertificateRef iPhoneDeviceCA
= NULL
, iPhoneCA
= NULL
, appleRoot
= NULL
;
6266 require_quiet(iPhoneDeviceCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneDeviceCA
, sizeof(_AppleiPhoneDeviceCA
)),
6268 require_quiet(iPhoneCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneCA
, sizeof(_AppleiPhoneCA
)),
6270 require_quiet(appleRoot
= SecCertificateCreateWithBytes(NULL
, _AppleRootCA
, sizeof(_AppleRootCA
)),
6273 require_quiet(result
= CFArrayCreateMutable(NULL
, 3, &kCFTypeArrayCallBacks
), errOut
);
6274 CFArrayAppendValue(result
, iPhoneDeviceCA
);
6275 CFArrayAppendValue(result
, iPhoneCA
);
6276 CFArrayAppendValue(result
, appleRoot
);
6279 CFReleaseNull(iPhoneDeviceCA
);
6280 CFReleaseNull(iPhoneCA
);
6281 CFReleaseNull(appleRoot
);