2 * Copyright (c) 2006-2015 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"
58 #include <utilities/debugging.h>
59 #include <utilities/SecCFWrappers.h>
60 #include <utilities/SecCFError.h>
61 #include <utilities/SecSCTUtils.h>
62 #include <utilities/array_size.h>
64 #include <libkern/OSByteOrder.h>
66 #include <Security/SecInternal.h>
67 #include <Security/SecFrameworkStrings.h>
68 #include "SecBase64.h"
69 #include "AppleBaselineEscrowCertificates.h"
70 #include <ipc/securityd_client.h>
71 #include <Security/SecKeyInternal.h>
73 /* The minimum key sizes necessary to not be considered "weak" */
74 #define MIN_RSA_KEY_SIZE 128 // 1024-bit
75 #define MIN_EC_KEY_SIZE 20 // 160-bit
77 typedef struct SecCertificateExtension
{
81 } SecCertificateExtension
;
84 typedef struct KnownExtension
{
90 kSecSelfSignedUnknown
= 0,
96 struct __SecCertificate
{
99 DERItem _der
; /* Entire certificate in DER form. */
100 DERItem _tbs
; /* To Be Signed cert DER bytes. */
101 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
102 DERItem _signature
; /* The content of the sig bit string. */
105 DERItem _serialNum
; /* Integer. */
106 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
107 DERItem _issuer
; /* Sequence of RDN. */
108 CFAbsoluteTime _notBefore
;
109 CFAbsoluteTime _notAfter
;
110 DERItem _subject
; /* Sequence of RDN. */
111 DERItem _subjectPublicKeyInfo
; /* SPKI */
112 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
113 DERItem _pubKeyDER
; /* contents of bit string */
114 DERItem _issuerUniqueID
; /* bit string, optional */
115 DERItem _subjectUniqueID
; /* bit string, optional */
118 /* Known extensions if the certificate contains them,
119 extnValue.length will be > 0. */
120 KnownExtension _authorityKeyID
;
122 /* This extension is used to uniquely identify a certificate from among
123 several that have the same subject name. If the extension is not
124 present, its value is calculated by performing a SHA-1 hash of the
125 certificate's DER encoded subjectPublicKeyInfo, as recommended by
127 KnownExtension _subjectKeyID
;
128 KnownExtension _keyUsage
;
129 KnownExtension _extendedKeyUsage
;
130 KnownExtension _basicConstraints
;
131 KnownExtension _netscapeCertType
;
132 KnownExtension _subjectAltName
;
133 KnownExtension _qualCertStatements
;
136 bool _foundUnknownCriticalExtension
;
138 /* Well known certificate extensions. */
139 SecCEBasicConstraints _basicConstraints
;
140 SecCEPolicyConstraints _policyConstraints
;
141 CFDictionaryRef _policyMappings
;
142 SecCECertificatePolicies _certificatePolicies
;
144 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
145 value of the SkipCerts field of the InhibitAnyPolicy extension
147 uint32_t _inhibitAnyPolicySkipCerts
;
149 /* If KeyUsage extension is not present this is 0, otherwise it's
150 the value of the extension. */
151 SecKeyUsage _keyUsage
;
153 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
154 Length = 0 if not present. */
155 DERItem _subjectKeyIdentifier
;
157 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
158 Length = 0 if not present. */
159 DERItem _authorityKeyIdentifier
;
160 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
161 _authorityKeyIdentifierSerialNumber have non zero length if present.
162 Both are either present or absent together. */
163 DERItem _authorityKeyIdentifierIssuer
;
164 DERItem _authorityKeyIdentifierSerialNumber
;
166 /* Subject alt name extension, if present. Not malloced, it's just a
167 pointer to an element in the _extensions array. */
168 const SecCertificateExtension
*_subjectAltName
;
170 /* Parsed extension values. */
172 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
173 CFMutableArrayRef _crlDistributionPoints
;
175 /* Array of CFURLRefs containing the URI values of accessLocations of each
176 id-ad-ocsp AccessDescription in the Authority Information Access
178 CFMutableArrayRef _ocspResponders
;
180 /* Array of CFURLRefs containing the URI values of accessLocations of each
181 id-ad-caIssuers AccessDescription in the Authority Information Access
183 CFMutableArrayRef _caIssuers
;
185 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
187 CFArrayRef _permittedSubtrees
;
189 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
191 CFArrayRef _excludedSubtrees
;
193 CFMutableArrayRef _embeddedSCTs
;
195 /* All other (non known) extensions. The _extensions array is malloced. */
196 CFIndex _extensionCount
;
197 SecCertificateExtension
*_extensions
;
199 /* Optional cached fields. */
200 SecKeyRef _pubKey
; /* never set, never used */
202 CFArrayRef _properties
;
203 CFDataRef _serialNumber
;
204 CFDataRef _normalizedIssuer
;
205 CFDataRef _normalizedSubject
;
206 CFDataRef _authorityKeyID
;
207 CFDataRef _subjectKeyID
;
209 CFDataRef _sha1Digest
;
210 CFTypeRef _keychain_item
;
211 uint8_t _isSelfSigned
;
215 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
217 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
218 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
219 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
221 /* Public Constants for property list keys. */
222 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
223 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
224 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
225 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
227 /* Public Constants for property list values. */
228 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
229 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
230 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
231 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
232 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
233 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
234 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
235 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
236 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
238 /* Extension parsing routine. */
239 typedef void (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
240 const SecCertificateExtension
*extn
);
242 /* Mapping from extension OIDs (as a DERItem *) to
243 SecCertificateExtensionParser extension parsing routines. */
244 static CFDictionaryRef sExtensionParsers
;
246 /* Forward declarations of static functions. */
247 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
);
248 static void SecCertificateDestroy(CFTypeRef cf
);
249 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
250 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
252 /* Static functions. */
253 static CF_RETURNS_RETAINED CFStringRef
SecCertificateDescribe(CFTypeRef cf
) {
254 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
255 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
256 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
257 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
258 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
259 CFReleaseSafe(issuer
);
260 CFReleaseSafe(subject
);
264 static void SecCertificateDestroy(CFTypeRef cf
) {
265 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
266 if (certificate
->_certificatePolicies
.policies
)
267 free(certificate
->_certificatePolicies
.policies
);
268 CFReleaseSafe(certificate
->_policyMappings
);
269 CFReleaseSafe(certificate
->_crlDistributionPoints
);
270 CFReleaseSafe(certificate
->_ocspResponders
);
271 CFReleaseSafe(certificate
->_caIssuers
);
272 if (certificate
->_extensions
) {
273 free(certificate
->_extensions
);
275 CFReleaseSafe(certificate
->_pubKey
);
276 CFReleaseSafe(certificate
->_der_data
);
277 CFReleaseSafe(certificate
->_properties
);
278 CFReleaseSafe(certificate
->_serialNumber
);
279 CFReleaseSafe(certificate
->_normalizedIssuer
);
280 CFReleaseSafe(certificate
->_normalizedSubject
);
281 CFReleaseSafe(certificate
->_authorityKeyID
);
282 CFReleaseSafe(certificate
->_subjectKeyID
);
283 CFReleaseSafe(certificate
->_sha1Digest
);
284 CFReleaseSafe(certificate
->_keychain_item
);
285 CFReleaseSafe(certificate
->_permittedSubtrees
);
286 CFReleaseSafe(certificate
->_excludedSubtrees
);
289 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
290 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
291 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
294 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
296 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
299 /* Hash of the certificate is der length + signature length + last 4 bytes
301 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
302 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
303 size_t der_length
= certificate
->_der
.length
;
304 size_t sig_length
= certificate
->_signature
.length
;
305 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
306 CFHashCode hashCode
= 0;
307 for (; ix
< sig_length
; ++ix
)
308 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
310 return (hashCode
+ der_length
+ sig_length
);
315 /************************************************************************/
316 /************************* General Name Parsing *************************/
317 /************************************************************************/
319 GeneralName ::= CHOICE {
320 otherName [0] OtherName,
321 rfc822Name [1] IA5String,
322 dNSName [2] IA5String,
323 x400Address [3] ORAddress,
324 directoryName [4] Name,
325 ediPartyName [5] EDIPartyName,
326 uniformResourceIdentifier [6] IA5String,
327 iPAddress [7] OCTET STRING,
328 registeredID [8] OBJECT IDENTIFIER}
330 OtherName ::= SEQUENCE {
331 type-id OBJECT IDENTIFIER,
332 value [0] EXPLICIT ANY DEFINED BY type-id }
334 EDIPartyName ::= SEQUENCE {
335 nameAssigner [0] DirectoryString OPTIONAL,
336 partyName [1] DirectoryString }
338 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
339 const DERItem
*generalNameContent
,
340 void *context
, parseGeneralNameCallback callback
) {
342 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
343 return callback(context
, GNT_OtherName
, generalNameContent
);
344 case ASN1_CONTEXT_SPECIFIC
| 1:
345 return callback(context
, GNT_RFC822Name
, generalNameContent
);
346 case ASN1_CONTEXT_SPECIFIC
| 2:
347 return callback(context
, GNT_DNSName
, generalNameContent
);
348 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
349 return callback(context
, GNT_X400Address
, generalNameContent
);
350 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
351 return callback(context
, GNT_DirectoryName
, generalNameContent
);
352 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
353 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
354 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
356 /* Technically I don't think this is valid, but there are certs out
357 in the wild that use a constructed IA5String. In particular the
358 VeriSign Time Stamping Authority CA.cer does this. */
359 DERDecodedInfo uriContent
;
360 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
361 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
362 return callback(context
, GNT_URI
, &uriContent
.content
);
364 case ASN1_CONTEXT_SPECIFIC
| 6:
365 return callback(context
, GNT_URI
, generalNameContent
);
366 case ASN1_CONTEXT_SPECIFIC
| 7:
367 return callback(context
, GNT_IPAddress
, generalNameContent
);
368 case ASN1_CONTEXT_SPECIFIC
| 8:
369 return callback(context
, GNT_RegisteredID
, generalNameContent
);
374 return errSecInvalidCertificate
;
377 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
378 void *context
, parseGeneralNameCallback callback
) {
380 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
381 require_noerr_quiet(drtn
, badDER
);
382 DERDecodedInfo generalNameContent
;
383 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
385 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
386 generalNameContent
.tag
, &generalNameContent
.content
, context
,
391 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
392 return errSecSuccess
;
395 return errSecInvalidCertificate
;
398 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
399 parseGeneralNameCallback callback
) {
400 DERDecodedInfo generalNamesContent
;
401 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
402 require_noerr_quiet(drtn
, badDER
);
403 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
404 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
407 return errSecInvalidCertificate
;
413 GeneralName ::= CHOICE {
414 otherName [0] OtherName,
415 rfc822Name [1] IA5String,
416 dNSName [2] IA5String,
417 x400Address [3] ORAddress,
418 directoryName [4] Name,
419 ediPartyName [5] EDIPartyName,
420 uniformResourceIdentifier [6] IA5String,
421 iPAddress [7] OCTET STRING,
422 registeredID [8] OBJECT IDENTIFIER}
424 EDIPartyName ::= SEQUENCE {
425 nameAssigner [0] DirectoryString OPTIONAL,
426 partyName [1] DirectoryString }
428 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
429 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
431 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
432 generalName
->nameType
= GNT_OtherName
;
433 generalName
->berEncoded
= true;
434 generalName
->name
= *generalNameContent
;
436 case ASN1_CONTEXT_SPECIFIC
| 1:
438 generalName
->nameType
= GNT_RFC822Name
;
439 generalName
->berEncoded
= false;
440 generalName
->name
= *generalNameContent
;
442 case ASN1_CONTEXT_SPECIFIC
| 2:
444 generalName
->nameType
= GNT_DNSName
;
445 generalName
->berEncoded
= false;
446 generalName
->name
= *generalNameContent
;
448 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
449 generalName
->nameType
= GNT_X400Address
;
450 generalName
->berEncoded
= true;
451 generalName
->name
= *generalNameContent
;
453 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
454 generalName
->nameType
= GNT_DirectoryName
;
455 generalName
->berEncoded
= true;
456 generalName
->name
= *generalNameContent
;
458 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
459 generalName
->nameType
= GNT_EdiPartyName
;
460 generalName
->berEncoded
= true;
461 generalName
->name
= *generalNameContent
;
463 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
465 /* Technically I don't think this is valid, but there are certs out
466 in the wild that use a constructed IA5String. In particular the
467 VeriSign Time Stamping Authority CA.cer does this. */
468 DERDecodedInfo decoded
;
469 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
470 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
471 generalName
->nameType
= GNT_URI
;
472 generalName
->berEncoded
= false;
473 generalName
->name
= decoded
.content
;
476 case ASN1_CONTEXT_SPECIFIC
| 6:
477 generalName
->nameType
= GNT_URI
;
478 generalName
->berEncoded
= false;
479 generalName
->name
= *generalNameContent
;
481 case ASN1_CONTEXT_SPECIFIC
| 7:
482 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
483 8 octects, addr/mask for ipv6 it's 32. */
484 generalName
->nameType
= GNT_IPAddress
;
485 generalName
->berEncoded
= false;
486 generalName
->name
= *generalNameContent
;
488 case ASN1_CONTEXT_SPECIFIC
| 8:
489 /* name is the content of an OID. */
490 generalName
->nameType
= GNT_RegisteredID
;
491 generalName
->berEncoded
= false;
492 generalName
->name
= *generalNameContent
;
498 return errSecSuccess
;
500 return errSecInvalidCertificate
;
504 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
506 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
507 CFIndex
*count
, SecCEGeneralName
**name
) {
508 SecCEGeneralName
*generalNames
= NULL
;
510 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
511 require_noerr_quiet(drtn
, badDER
);
512 DERDecodedInfo generalNameContent
;
513 CFIndex generalNamesCount
= 0;
514 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
518 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
520 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
522 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
524 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
526 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
527 &generalNameContent
.content
, &generalNames
[ix
])) {
532 *count
= generalNamesCount
;
533 *name
= generalNames
;
534 return errSecSuccess
;
539 return errSecInvalidCertificate
;
542 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
543 CFIndex
*count
, SecCEGeneralName
**name
) {
544 DERDecodedInfo generalNamesContent
;
545 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
546 require_noerr_quiet(drtn
, badDER
);
547 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
549 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
550 return errSecSuccess
;
552 return errSecInvalidCertificate
;
556 /************************************************************************/
557 /************************** X.509 Name Parsing **************************/
558 /************************************************************************/
560 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
561 const DERItem
*value
, CFIndex rdnIX
);
563 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
564 parseX501NameCallback callback
) {
566 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
567 require_noerr_quiet(drtn
, badDER
);
568 DERDecodedInfo atvContent
;
570 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
571 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
572 DERAttributeTypeAndValue atv
;
573 drtn
= DERParseSequenceContent(&atvContent
.content
,
574 DERNumAttributeTypeAndValueItemSpecs
,
575 DERAttributeTypeAndValueItemSpecs
,
577 require_noerr_quiet(drtn
, badDER
);
578 require_quiet(atv
.type
.length
!= 0, badDER
);
579 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
583 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
585 return errSecSuccess
;
587 return errSecInvalidCertificate
;
590 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
591 parseX501NameCallback callback
) {
593 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
594 require_noerr_quiet(drtn
, badDER
);
595 DERDecodedInfo currDecoded
;
596 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
597 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
598 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
603 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
605 return errSecSuccess
;
608 return errSecInvalidCertificate
;
611 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
612 parseX501NameCallback callback
) {
613 DERDecodedInfo x501NameContent
;
614 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
615 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
616 return errSecInvalidCertificate
;
618 return parseX501NameContent(&x501NameContent
.content
, context
,
623 /************************************************************************/
624 /********************** Extension Parsing Routines **********************/
625 /************************************************************************/
627 static void SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
628 const SecCertificateExtension
*extn
) {
629 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
630 DERDecodedInfo keyIdentifier
;
631 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
632 require_noerr_quiet(drtn
, badDER
);
633 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
634 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
638 secwarning("Invalid SubjectKeyIdentifier Extension");
641 static void SecCEPKeyUsage(SecCertificateRef certificate
,
642 const SecCertificateExtension
*extn
) {
643 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
644 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
645 DERDecodedInfo bitStringContent
;
646 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
647 require_noerr_quiet(drtn
, badDER
);
648 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
649 DERSize len
= bitStringContent
.content
.length
- 1;
650 require_quiet(len
== 1 || len
== 2, badDER
);
651 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
652 require_quiet(numUnusedBits
< 8, badDER
);
653 /* Flip the bits in the bit string so the first bit in the lsb. */
654 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
655 uint_fast16_t value
= bitStringContent
.content
.data
[1];
658 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
664 for (ix
= 0; ix
< bits
; ++ix
) {
670 certificate
->_keyUsage
= keyUsage
;
673 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
676 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
677 const SecCertificateExtension
*extn
) {
678 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
681 static void SecCEPSubjectAltName(SecCertificateRef certificate
,
682 const SecCertificateExtension
*extn
) {
683 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
684 certificate
->_subjectAltName
= extn
;
687 static void SecCEPIssuerAltName(SecCertificateRef certificate
,
688 const SecCertificateExtension
*extn
) {
689 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
692 static void SecCEPBasicConstraints(SecCertificateRef certificate
,
693 const SecCertificateExtension
*extn
) {
694 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
695 DERBasicConstraints basicConstraints
;
696 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
697 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
698 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
699 require_noerr_quiet(DERParseBoolean(&basicConstraints
.cA
, false,
700 &certificate
->_basicConstraints
.isCA
), badDER
);
701 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
702 require_noerr_quiet(DERParseInteger(
703 &basicConstraints
.pathLenConstraint
,
704 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
705 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
707 certificate
->_basicConstraints
.present
= true;
708 certificate
->_basicConstraints
.critical
= extn
->critical
;
711 certificate
->_basicConstraints
.present
= false;
712 secwarning("Invalid BasicConstraints Extension");
717 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
719 * NameConstraints ::= SEQUENCE {
720 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
721 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
723 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
725 * GeneralSubtree ::= SEQUENCE {
727 * minimum [0] BaseDistance DEFAULT 0,
728 * maximum [1] BaseDistance OPTIONAL }
730 * BaseDistance ::= INTEGER (0..MAX)
732 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
733 CFMutableArrayRef gs
= NULL
;
735 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
736 require_noerr_quiet(drtn
, badDER
);
737 DERDecodedInfo gsContent
;
738 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
739 &kCFTypeArrayCallBacks
),
741 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
742 DERGeneralSubtree derGS
;
743 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
744 drtn
= DERParseSequenceContent(&gsContent
.content
,
745 DERNumGeneralSubtreeItemSpecs
,
746 DERGeneralSubtreeItemSpecs
,
747 &derGS
, sizeof(derGS
));
748 require_noerr_quiet(drtn
, badDER
);
751 * Within this profile, the minimum and maximum fields are not used with
752 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
755 * Because minimum DEFAULT 0, absence equivalent to present and 0.
757 if (derGS
.minimum
.length
) {
759 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
761 require_quiet(minimum
== 0, badDER
);
763 require_quiet(derGS
.maximum
.length
== 0, badDER
);
764 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
766 CFDataRef generalName
= NULL
;
767 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
768 derGS
.generalName
.data
,
769 derGS
.generalName
.length
),
771 CFArrayAppendValue(gs
, generalName
);
772 CFReleaseNull(generalName
);
775 *generalSubtrees
= gs
;
777 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
782 secdebug("cert","failed to parse GeneralSubtrees");
786 static void SecCEPNameConstraints(SecCertificateRef certificate
,
787 const SecCertificateExtension
*extn
) {
788 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
789 DERNameConstraints nc
;
791 drtn
= DERParseSequence(&extn
->extnValue
,
792 DERNumNameConstraintsItemSpecs
,
793 DERNameConstraintsItemSpecs
,
795 require_noerr_quiet(drtn
, badDER
);
796 if (nc
.permittedSubtrees
.length
) {
797 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
799 if (nc
.excludedSubtrees
.length
) {
800 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
805 secdebug("cert", "failed to parse Name Constraints extension");
808 static void SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
809 const SecCertificateExtension
*extn
) {
810 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
814 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
816 PolicyInformation ::= SEQUENCE {
817 policyIdentifier CertPolicyId,
818 policyQualifiers SEQUENCE SIZE (1..MAX) OF
819 PolicyQualifierInfo OPTIONAL }
821 CertPolicyId ::= OBJECT IDENTIFIER
823 PolicyQualifierInfo ::= SEQUENCE {
824 policyQualifierId PolicyQualifierId,
825 qualifier ANY DEFINED BY policyQualifierId }
827 /* maximum number of policies of 8192 seems more than adequate */
828 #define MAX_CERTIFICATE_POLICIES 8192
829 static void SecCEPCertificatePolicies(SecCertificateRef certificate
,
830 const SecCertificateExtension
*extn
) {
831 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
834 SecCEPolicyInformation
*policies
= NULL
;
835 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
836 require_noerr_quiet(drtn
, badDER
);
837 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
838 DERDecodedInfo piContent
;
839 DERSize policy_count
= 0;
840 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
841 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
842 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
845 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
846 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
847 * (policy_count
> 0 ? policy_count
: 1));
848 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
849 DERSize policy_ix
= 0;
850 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
851 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
852 DERPolicyInformation pi
;
853 drtn
= DERParseSequenceContent(&piContent
.content
,
854 DERNumPolicyInformationItemSpecs
,
855 DERPolicyInformationItemSpecs
,
857 require_noerr_quiet(drtn
, badDER
);
858 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
859 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
861 certificate
->_certificatePolicies
.present
= true;
862 certificate
->_certificatePolicies
.critical
= extn
->critical
;
863 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
864 certificate
->_certificatePolicies
.policies
= policies
;
869 certificate
->_certificatePolicies
.present
= false;
870 secwarning("Invalid CertificatePolicies Extension");
874 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
876 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
877 issuerDomainPolicy CertPolicyId,
878 subjectDomainPolicy CertPolicyId }
881 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
882 const SecCertificateExtension
*extn
) {
883 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
886 SecCEPolicyMapping
*mappings
= NULL
;
887 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
888 require_noerr_quiet(drtn
, badDER
);
889 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
890 DERDecodedInfo pmContent
;
891 DERSize mapping_count
= 0;
892 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
893 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
896 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
898 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
899 DERSize mapping_ix
= 0;
900 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
902 drtn
= DERParseSequenceContent(&pmContent
.content
,
903 DERNumPolicyMappingItemSpecs
,
904 DERPolicyMappingItemSpecs
,
906 require_noerr_quiet(drtn
, badDER
);
907 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
908 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
910 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
911 certificate
->_policyMappings
.present
= true;
912 certificate
->_policyMappings
.critical
= extn
->critical
;
913 certificate
->_policyMappings
.numMappings
= mapping_count
;
914 certificate
->_policyMappings
.mappings
= mappings
;
919 CFReleaseSafe(mappings
);
920 certificate
->_policyMappings
.present
= false;
921 secwarning("Invalid CertificatePolicies Extension");
924 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
925 const SecCertificateExtension
*extn
) {
926 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
929 CFMutableDictionaryRef mappings
= NULL
;
930 CFDataRef idp
= NULL
, sdp
= NULL
;
931 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
932 require_noerr_quiet(drtn
, badDER
);
933 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
934 DERDecodedInfo pmContent
;
935 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
936 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
938 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
939 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
941 drtn
= DERParseSequenceContent(&pmContent
.content
,
942 DERNumPolicyMappingItemSpecs
,
943 DERPolicyMappingItemSpecs
,
945 require_noerr_quiet(drtn
, badDER
);
946 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
947 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
948 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
949 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
950 CFMutableArrayRef sdps
=
951 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
953 CFArrayAppendValue(sdps
, sdp
);
955 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
956 &kCFTypeArrayCallBacks
), badDER
);
957 CFDictionarySetValue(mappings
, idp
, sdps
);
963 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
964 certificate
->_policyMappings
= mappings
;
969 CFReleaseSafe(mappings
);
970 certificate
->_policyMappings
= NULL
;
971 secwarning("Invalid CertificatePolicies Extension");
976 AuthorityKeyIdentifier ::= SEQUENCE {
977 keyIdentifier [0] KeyIdentifier OPTIONAL,
978 authorityCertIssuer [1] GeneralNames OPTIONAL,
979 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
980 -- authorityCertIssuer and authorityCertSerialNumber MUST both
981 -- be present or both be absent
983 KeyIdentifier ::= OCTET STRING
985 static void SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
986 const SecCertificateExtension
*extn
) {
987 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
988 DERAuthorityKeyIdentifier akid
;
990 drtn
= DERParseSequence(&extn
->extnValue
,
991 DERNumAuthorityKeyIdentifierItemSpecs
,
992 DERAuthorityKeyIdentifierItemSpecs
,
993 &akid
, sizeof(akid
));
994 require_noerr_quiet(drtn
, badDER
);
995 if (akid
.keyIdentifier
.length
) {
996 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
998 if (akid
.authorityCertIssuer
.length
||
999 akid
.authorityCertSerialNumber
.length
) {
1000 require_quiet(akid
.authorityCertIssuer
.length
&&
1001 akid
.authorityCertSerialNumber
.length
, badDER
);
1002 /* Perhaps put in a subsection called Authority Certificate Issuer. */
1003 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
1004 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1009 secwarning("Invalid AuthorityKeyIdentifier Extension");
1012 static void SecCEPPolicyConstraints(SecCertificateRef certificate
,
1013 const SecCertificateExtension
*extn
) {
1014 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1015 DERPolicyConstraints pc
;
1017 drtn
= DERParseSequence(&extn
->extnValue
,
1018 DERNumPolicyConstraintsItemSpecs
,
1019 DERPolicyConstraintsItemSpecs
,
1021 require_noerr_quiet(drtn
, badDER
);
1022 if (pc
.requireExplicitPolicy
.length
) {
1023 require_noerr_quiet(DERParseInteger(
1024 &pc
.requireExplicitPolicy
,
1025 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1026 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1028 if (pc
.inhibitPolicyMapping
.length
) {
1029 require_noerr_quiet(DERParseInteger(
1030 &pc
.inhibitPolicyMapping
,
1031 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1032 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1035 certificate
->_policyConstraints
.present
= true;
1036 certificate
->_policyConstraints
.critical
= extn
->critical
;
1040 certificate
->_policyConstraints
.present
= false;
1041 secwarning("Invalid PolicyConstraints Extension");
1044 static void SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1045 const SecCertificateExtension
*extn
) {
1046 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1050 InhibitAnyPolicy ::= SkipCerts
1052 SkipCerts ::= INTEGER (0..MAX)
1054 static void SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1055 const SecCertificateExtension
*extn
) {
1056 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1057 require_noerr_quiet(DERParseInteger(
1059 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
1062 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
1063 secwarning("Invalid InhibitAnyPolicy Extension");
1067 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1069 AuthorityInfoAccessSyntax ::=
1070 SEQUENCE SIZE (1..MAX) OF AccessDescription
1072 AccessDescription ::= SEQUENCE {
1073 accessMethod OBJECT IDENTIFIER,
1074 accessLocation GeneralName }
1076 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1078 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1080 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1082 static void SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1083 const SecCertificateExtension
*extn
) {
1084 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1087 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1088 require_noerr_quiet(drtn
, badDER
);
1089 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1090 DERDecodedInfo adContent
;
1091 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1092 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1093 DERAccessDescription ad
;
1094 drtn
= DERParseSequenceContent(&adContent
.content
,
1095 DERNumAccessDescriptionItemSpecs
,
1096 DERAccessDescriptionItemSpecs
,
1098 require_noerr_quiet(drtn
, badDER
);
1099 CFMutableArrayRef
*urls
;
1100 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1101 urls
= &certificate
->_ocspResponders
;
1102 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1103 urls
= &certificate
->_caIssuers
;
1107 DERDecodedInfo generalNameContent
;
1108 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1109 require_noerr_quiet(drtn
, badDER
);
1110 switch (generalNameContent
.tag
) {
1112 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1113 /* Technically I don't think this is valid, but there are certs out
1114 in the wild that use a constructed IA5String. In particular the
1115 VeriSign Time Stamping Authority CA.cer does this. */
1117 case ASN1_CONTEXT_SPECIFIC
| 6:
1119 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1120 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1121 kCFStringEncodingASCII
, NULL
);
1124 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1125 CFArrayAppendValue(*urls
, url
);
1131 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02x v: %.*s",
1132 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1137 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1140 secdebug("cert", "failed to parse Authority Information Access extension");
1143 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1144 * This is a DER sequence with the leading tag and length bytes removed,
1145 * to match what tbsCert.issuer contains.
1147 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1148 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1149 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1150 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1151 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1152 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1153 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1154 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1155 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1156 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1157 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1160 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1162 certificate
->_crlDistributionPoints
||
1163 certificate
->_ocspResponders
) {
1164 /* We already have an OCSP or CRL URI (or no cert) */
1167 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1168 CFURLRef url
= NULL
;
1169 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1170 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1171 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1172 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1173 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1174 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1175 kCFStringEncodingASCII
, NULL
);
1178 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1179 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1180 CFArrayAppendValue(*urls
, url
);
1185 static void SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1186 const SecCertificateExtension
*extn
) {
1187 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1190 static void SecCEPNetscapeCertType(SecCertificateRef certificate
,
1191 const SecCertificateExtension
*extn
) {
1192 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1195 static void SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1196 const SecCertificateExtension
*extn
) {
1197 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1200 static void SecCEPEscrowMarker(SecCertificateRef certificate
,
1201 const SecCertificateExtension
*extn
) {
1202 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1206 /* Dictionary key callback for comparing to DERItems. */
1207 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1208 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1211 /* Dictionary key callback calculating the hash of a DERItem. */
1212 static CFHashCode
SecDERItemHash(const void *value
) {
1213 const DERItem
*derItem
= (const DERItem
*)value
;
1214 CFHashCode hash
= derItem
->length
;
1215 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1216 for (; ix
< derItem
->length
; ++ix
) {
1217 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1223 /* Dictionary key callbacks using the above 2 functions. */
1224 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1228 NULL
, /* copyDescription */
1229 SecDERItemEqual
, /* equal */
1230 SecDERItemHash
/* hash */
1233 static void SecCertificateInitializeExtensionParsers(void) {
1234 /* Build a dictionary that maps from extension OIDs to callback functions
1235 which can parse the extension of the type given. */
1236 static const void *extnOIDs
[] = {
1237 &oidSubjectKeyIdentifier
,
1239 &oidPrivateKeyUsagePeriod
,
1242 &oidBasicConstraints
,
1243 &oidNameConstraints
,
1244 &oidCrlDistributionPoints
,
1245 &oidCertificatePolicies
,
1247 &oidAuthorityKeyIdentifier
,
1248 &oidPolicyConstraints
,
1249 &oidExtendedKeyUsage
,
1250 &oidInhibitAnyPolicy
,
1251 &oidAuthorityInfoAccess
,
1252 &oidSubjectInfoAccess
,
1253 &oidNetscapeCertType
,
1254 &oidEntrustVersInfo
,
1255 &oidApplePolicyEscrowService
1257 static const void *extnParsers
[] = {
1258 SecCEPSubjectKeyIdentifier
,
1260 SecCEPPrivateKeyUsagePeriod
,
1261 SecCEPSubjectAltName
,
1262 SecCEPIssuerAltName
,
1263 SecCEPBasicConstraints
,
1264 SecCEPNameConstraints
,
1265 SecCEPCrlDistributionPoints
,
1266 SecCEPCertificatePolicies
,
1267 SecCEPPolicyMappings
,
1268 SecCEPAuthorityKeyIdentifier
,
1269 SecCEPPolicyConstraints
,
1270 SecCEPExtendedKeyUsage
,
1271 SecCEPInhibitAnyPolicy
,
1272 SecCEPAuthorityInfoAccess
,
1273 SecCEPSubjectInfoAccess
,
1274 SecCEPNetscapeCertType
,
1275 SecCEPEntrustVersInfo
,
1278 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1279 extnParsers
, array_size(extnOIDs
),
1280 &SecDERItemKeyCallBacks
, NULL
);
1283 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateDescribe
, NULL
, NULL
, ^{
1284 SecCertificateInitializeExtensionParsers();
1287 static bool isAppleExtensionOID(const DERItem
*extnID
)
1289 static const uint8_t appleExtension
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1290 return (extnID
&& extnID
->data
&&
1291 extnID
->length
> sizeof(appleExtension
) &&
1292 !memcmp(extnID
->data
, appleExtension
, sizeof(appleExtension
)));
1295 /* Given the contents of an X.501 Name return the contents of a normalized
1297 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1298 const DERItem
*x501name
) {
1299 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1300 CFIndex length
= x501name
->length
;
1301 CFDataSetLength(result
, length
);
1302 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1305 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1307 require_noerr_quiet(drtn
, badDER
);
1310 /* Always points to last rdn tag. */
1311 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1312 /* Offset relative to base of current rdn set tag. */
1313 CFIndex rdnTagLocation
= 0;
1314 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1315 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1316 /* We don't allow empty RDNs. */
1317 require_quiet(rdn
.content
.length
!= 0, badDER
);
1318 /* Length of the tag and length of the current rdn. */
1319 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1320 CFIndex rdnContentLength
= rdn
.content
.length
;
1321 /* Copy the tag and length of the RDN. */
1322 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1325 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1326 require_quiet(drtn
== DR_Success
, badDER
);
1329 /* Always points to tag of current atv sequence. */
1330 const DERByte
*atvTag
= atvSeq
.nextItem
;
1331 /* Offset relative to base of current atv sequence tag. */
1332 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1333 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1334 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1335 /* Length of the tag and length of the current atv. */
1336 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1337 CFIndex atvContentLength
= atv
.content
.length
;
1338 /* Copy the tag and length of the atv and the atv itself. */
1339 memcpy(base
+ atvTagLocation
, atvTag
,
1340 atvTLLength
+ atv
.content
.length
);
1342 /* Now decode the atv sequence. */
1343 DERAttributeTypeAndValue atvPair
;
1344 drtn
= DERParseSequenceContent(&atv
.content
,
1345 DERNumAttributeTypeAndValueItemSpecs
,
1346 DERAttributeTypeAndValueItemSpecs
,
1347 &atvPair
, sizeof(atvPair
));
1348 require_noerr_quiet(drtn
, badDER
);
1349 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1350 DERDecodedInfo value
;
1351 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1352 require_noerr_quiet(drtn
, badDER
);
1354 /* (c) attribute values in PrintableString are not case sensitive
1355 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1357 (d) attribute values in PrintableString are compared after
1358 removing leading and trailing white space and converting internal
1359 substrings of one or more consecutive white space characters to a
1361 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1362 /* Offset relative to base of current value tag. */
1363 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1364 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1365 CFIndex valueContentLength
= value
.content
.length
;
1367 /* Now copy all the bytes, but convert to upper case while
1368 doing so and convert multiple whitespace chars into a
1370 bool lastWasBlank
= false;
1371 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1372 CFIndex valueCurrentLocation
= valueLocation
;
1374 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1375 UInt8 ch
= value
.content
.data
[ix
];
1380 /* Don't insert a space for first character
1382 if (valueCurrentLocation
> valueLocation
) {
1383 base
[valueCurrentLocation
++] = ' ';
1385 lastWasBlank
= true;
1388 lastWasBlank
= false;
1389 if ('a' <= ch
&& ch
<= 'z') {
1390 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1392 base
[valueCurrentLocation
++] = ch
;
1396 /* Finally if lastWasBlank remove the trailing space. */
1397 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1398 valueCurrentLocation
--;
1400 /* Adjust content length to normalized length. */
1401 valueContentLength
= valueCurrentLocation
- valueLocation
;
1403 /* Number of bytes by which the length should be shorted. */
1404 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1405 if (lengthDiff
== 0) {
1406 /* Easy case no need to adjust lengths. */
1408 /* Hard work we need to go back and fix up length fields
1410 1) The value itself.
1411 2) The ATV Sequence containing type/value
1412 3) The RDN Set containing one or more atv pairs.
1416 /* Step 1 fix up length of value. */
1417 /* Length of value tag and length minus the tag. */
1418 DERSize newValueTLLength
= valueTLLength
- 1;
1419 drtn
= DEREncodeLength(valueContentLength
,
1420 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1421 require(drtn
== DR_Success
, badDER
);
1422 /* Add the length of the tag back in. */
1424 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1426 /* The size of the length field changed, let's slide
1427 the value back by valueLLDiff bytes. */
1428 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1429 base
+ valueTagLocation
+ valueTLLength
,
1430 valueContentLength
);
1431 /* The length diff for the enclosing object. */
1432 lengthDiff
+= valueLLDiff
;
1435 /* Step 2 fix up length of the enclosing ATV Sequence. */
1436 atvContentLength
-= lengthDiff
;
1437 DERSize newATVTLLength
= atvTLLength
- 1;
1438 drtn
= DEREncodeLength(atvContentLength
,
1439 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1440 require(drtn
== DR_Success
, badDER
);
1441 /* Add the length of the tag back in. */
1443 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1445 /* The size of the length field changed, let's slide
1446 the value back by valueLLDiff bytes. */
1447 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1448 base
+ atvTagLocation
+ atvTLLength
,
1450 /* The length diff for the enclosing object. */
1451 lengthDiff
+= atvLLDiff
;
1452 atvTLLength
= newATVTLLength
;
1455 /* Step 3 fix up length of enclosing RDN Set. */
1456 rdnContentLength
-= lengthDiff
;
1457 DERSize newRDNTLLength
= rdnTLLength
- 1;
1458 drtn
= DEREncodeLength(rdnContentLength
,
1459 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1460 require_quiet(drtn
== DR_Success
, badDER
);
1461 /* Add the length of the tag back in. */
1463 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1465 /* The size of the length field changed, let's slide
1466 the value back by valueLLDiff bytes. */
1467 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1468 base
+ rdnTagLocation
+ rdnTLLength
,
1470 /* The length diff for the enclosing object. */
1471 lengthDiff
+= rdnLLDiff
;
1472 rdnTLLength
= newRDNTLLength
;
1474 /* Adjust the locations that might have changed due to
1476 atvTagLocation
-= rdnLLDiff
;
1478 (void) lengthDiff
; // No next object, silence analyzer
1481 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1482 atvTag
= atvSeq
.nextItem
;
1484 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1485 rdnTag
= rdnSeq
.nextItem
;
1487 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1488 /* Truncate the result to the proper length. */
1489 CFDataSetLength(result
, rdnTagLocation
);
1498 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1500 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1501 DERDecodedInfo content
;
1502 /* Decode top level sequence into DERItem */
1503 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1504 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1508 /* AUDIT[securityd]:
1509 certificate->_der is a caller provided data of any length (might be 0).
1511 Top level certificate decode.
1513 static bool SecCertificateParse(SecCertificateRef certificate
)
1518 require_quiet(certificate
, badCert
);
1519 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1521 /* top level decode */
1522 DERSignedCertCrl signedCert
;
1523 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1524 DERSignedCertCrlItemSpecs
, &signedCert
,
1525 sizeof(signedCert
));
1526 require_noerr_quiet(drtn
, badCert
);
1527 /* Store tbs since we need to digest it for verification later on. */
1528 certificate
->_tbs
= signedCert
.tbs
;
1530 /* decode the TBSCert - it was saved in full DER form */
1532 drtn
= DERParseSequence(&signedCert
.tbs
,
1533 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1534 &tbsCert
, sizeof(tbsCert
));
1535 require_noerr_quiet(drtn
, badCert
);
1537 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1538 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1539 of the params field. */
1540 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1541 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1542 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1543 require_noerr_quiet(drtn
, badCert
);
1545 /* The contents of signedCert.sig is a bit string whose contents
1546 are the signature itself. */
1547 DERByte numUnusedBits
;
1548 drtn
= DERParseBitString(&signedCert
.sig
,
1549 &certificate
->_signature
, &numUnusedBits
);
1550 require_noerr_quiet(drtn
, badCert
);
1552 /* Now decode the tbsCert. */
1554 /* First we turn the optional version into an int. */
1555 if (tbsCert
.version
.length
) {
1556 DERDecodedInfo decoded
;
1557 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1558 require_noerr_quiet(drtn
, badCert
);
1559 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1560 require_quiet(decoded
.content
.length
== 1, badCert
);
1561 certificate
->_version
= decoded
.content
.data
[0];
1562 require_quiet(certificate
->_version
> 0, badCert
);
1563 require_quiet(certificate
->_version
< 3, badCert
);
1565 certificate
->_version
= 0;
1568 /* The serial number is in the tbsCert.serialNum - it was saved in
1569 INTEGER form without the tag and length. */
1570 certificate
->_serialNum
= tbsCert
.serialNum
;
1571 certificate
->_serialNumber
= CFDataCreate(allocator
,
1572 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1574 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1575 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1576 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1577 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1578 require_noerr_quiet(drtn
, badCert
);
1580 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1581 and length fields. */
1582 certificate
->_issuer
= tbsCert
.issuer
;
1583 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1586 /* sequence we're given: decode the tbsCerts Validity sequence. */
1587 DERValidity validity
;
1588 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1589 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1590 &validity
, sizeof(validity
));
1591 require_noerr_quiet(drtn
, badCert
);
1592 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1593 &certificate
->_notBefore
), badCert
);
1594 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1595 &certificate
->_notAfter
), badCert
);
1597 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1598 and length fields. */
1599 certificate
->_subject
= tbsCert
.subject
;
1600 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1603 /* Keep the SPKI around for CT */
1604 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1606 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1607 DERSubjPubKeyInfo pubKeyInfo
;
1608 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1609 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1610 &pubKeyInfo
, sizeof(pubKeyInfo
));
1611 require_noerr_quiet(drtn
, badCert
);
1613 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1614 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1615 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1616 &certificate
->_algId
, sizeof(certificate
->_algId
));
1617 require_noerr_quiet(drtn
, badCert
);
1619 /* Now we can figure out the key's algorithm id and params based on
1620 certificate->_algId.oid. */
1622 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1623 are a PKCS1 format RSA key. */
1624 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1625 &certificate
->_pubKeyDER
, &numUnusedBits
);
1626 require_noerr_quiet(drtn
, badCert
);
1628 /* The contents of tbsCert.issuerID is a bit string. */
1629 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1631 /* The contents of tbsCert.subjectID is a bit string. */
1632 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1635 if (tbsCert
.extensions
.length
) {
1636 CFIndex extensionCount
= 0;
1639 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1641 require_noerr_quiet(drtn
, badCert
);
1642 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1643 DERDecodedInfo currDecoded
;
1644 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1646 /* ! = MUST recognize ? = SHOULD recognize
1649 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1650 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1651 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1652 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1653 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1654 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1655 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1656 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1658 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1659 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1660 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1661 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1662 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1663 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1664 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1665 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1667 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1668 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1673 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1675 /* Put some upper limit on the number of extensions allowed. */
1676 require_quiet(extensionCount
< 10000, badCert
);
1677 certificate
->_extensionCount
= extensionCount
;
1678 certificate
->_extensions
=
1679 malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1682 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1683 require_noerr_quiet(drtn
, badCert
);
1684 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1685 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1686 require_quiet(drtn
== DR_Success
||
1687 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1688 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1690 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1691 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1692 &extn
, sizeof(extn
));
1693 require_noerr_quiet(drtn
, badCert
);
1694 /* Copy stuff into certificate->extensions[ix]. */
1695 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1696 require_noerr_quiet(drtn
= DERParseBoolean(&extn
.critical
, false,
1697 &certificate
->_extensions
[ix
].critical
), badCert
);
1698 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1700 SecCertificateExtensionParser parser
=
1701 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1702 sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1704 /* Invoke the parser. */
1705 parser(certificate
, &certificate
->_extensions
[ix
]);
1706 } else if (certificate
->_extensions
[ix
].critical
) {
1707 if (isAppleExtensionOID(&extn
.extnID
)) {
1710 secdebug("cert", "Found unknown critical extension");
1711 certificate
->_foundUnknownCriticalExtension
= true;
1713 secdebug("cert", "Found unknown non critical extension");
1717 checkForMissingRevocationInfo(certificate
);
1726 /* Public API functions. */
1727 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1728 const UInt8
*der_bytes
, CFIndex der_length
) {
1729 if (der_bytes
== NULL
) return NULL
;
1730 if (der_length
== 0) return NULL
;
1732 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1733 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1734 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1736 memset((char*)result
+ sizeof(result
->_base
), 0,
1737 sizeof(*result
) - sizeof(result
->_base
));
1738 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1739 result
->_der
.length
= der_length
;
1740 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1741 if (!SecCertificateParse(result
)) {
1749 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1750 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1751 const UInt8
*der_bytes
, CFIndex der_length
);
1753 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1754 const UInt8
*der_bytes
, CFIndex der_length
) {
1755 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1757 /* @@@ End of placeholder. */
1759 /* AUDIT[securityd](done):
1760 der_certificate is a caller provided data of any length (might be 0), only
1761 its cf type has been checked.
1763 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1764 CFDataRef der_certificate
) {
1765 check(der_certificate
);
1766 CFIndex size
= sizeof(struct __SecCertificate
);
1767 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1768 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1770 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1771 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1772 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1773 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1774 if (!SecCertificateParse(result
)) {
1782 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1783 CFDataRef der_certificate
,
1784 CFTypeRef keychain_item
)
1786 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1788 CFRetainSafe(keychain_item
);
1789 result
->_keychain_item
= keychain_item
;
1794 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1796 CFDataRef result
= NULL
;
1800 if (certificate
->_der_data
) {
1801 CFRetain(certificate
->_der_data
);
1802 result
= certificate
->_der_data
;
1804 result
= CFDataCreate(CFGetAllocator(certificate
),
1805 certificate
->_der
.data
, certificate
->_der
.length
);
1807 /* FIXME: If we wish to cache result we need to lock the certificate.
1808 Also this create 2 copies of the certificate data which is somewhat
1811 certificate
->_der_data
= result
;
1818 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1819 return certificate
->_der
.length
;
1822 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1823 return certificate
->_der
.data
;
1826 /* Used to recreate preCert from cert for Certificate Transparency */
1827 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1829 CFDataRef outData
= NULL
;
1830 DERItem tbsIn
= certificate
->_tbs
;
1831 DERItem tbsOut
= {0,};
1832 DERItem extensionsOut
= {0,};
1833 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1834 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
1838 /* decode the TBSCert - it was saved in full DER form */
1839 drtn
= DERParseSequence(&tbsIn
,
1840 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1841 &tbsCert
, sizeof(tbsCert
));
1842 require_noerr_quiet(drtn
, out
);
1844 /* Go over extensions and filter any SCT extension */
1845 CFIndex extensionsCount
= 0;
1847 if (tbsCert
.extensions
.length
) {
1850 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1852 require_noerr_quiet(drtn
, out
);
1853 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
1854 DERDecodedInfo currDecoded
;
1855 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1857 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
1859 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1860 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1861 &extn
, sizeof(extn
));
1862 require_noerr_quiet(drtn
, out
);
1864 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
1865 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
1868 extensionsList
[extensionsCount
] = currDecoded
.content
;
1869 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
1870 extensionsListSpecs
[extensionsCount
].options
= 0;
1871 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
1876 require_quiet(drtn
== DR_EndOfSequence
, out
);
1880 /* Encode extensions */
1881 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
1882 extensionsOut
.data
= malloc(extensionsOut
.length
);
1883 require_quiet(extensionsOut
.data
, out
);
1884 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
1885 require_noerr_quiet(drtn
, out
);
1887 tbsCert
.extensions
= extensionsOut
;
1889 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
1890 tbsOut
.data
= malloc(tbsOut
.length
);
1891 require_quiet(tbsOut
.data
, out
);
1892 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
1893 require_noerr_quiet(drtn
, out
);
1895 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
1898 free(extensionsOut
.data
);
1900 free(extensionsList
);
1901 free(extensionsListSpecs
);
1906 /* From rfc3280 - Appendix B. ASN.1 Notes
1908 Object Identifiers (OIDs) are used throughout this specification to
1909 identify certificate policies, public key and signature algorithms,
1910 certificate extensions, etc. There is no maximum size for OIDs.
1911 This specification mandates support for OIDs which have arc elements
1912 with values that are less than 2^28, that is, they MUST be between 0
1913 and 268,435,455, inclusive. This allows each arc element to be
1914 represented within a single 32 bit word. Implementations MUST also
1915 support OIDs where the length of the dotted decimal (see [RFC 2252],
1916 section 4.1) string representation can be up to 100 bytes
1917 (inclusive). Implementations MUST be able to handle OIDs with up to
1918 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1919 contain OIDs that exceed these requirements. Likewise, CRL issuers
1920 SHOULD NOT issue CRLs which contain OIDs that exceed these
1924 /* Oids longer than this are considered invalid. */
1925 #define MAX_OID_SIZE 32
1927 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1928 const DERItem
*oid
) {
1930 if (oid
->length
== 0) {
1931 return SecCopyCertString(SEC_NULL_KEY
);
1933 if (oid
->length
> MAX_OID_SIZE
) {
1934 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
1937 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1939 // The first two levels are encoded into one byte, since the root level
1940 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1941 // y may be > 39, so we have to add special-case handling for this.
1942 uint32_t x
= oid
->data
[0] / 40;
1943 uint32_t y
= oid
->data
[0] % 40;
1946 // Handle special case for large y if x = 2
1950 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1953 for (x
= 1; x
< oid
->length
; ++x
)
1955 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1956 /* @@@ value may not span more than 4 bytes. */
1957 /* A max number of 20 values is allowed. */
1958 if (!(oid
->data
[x
] & 0x80))
1960 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
1967 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1968 const DERItem
*oid
) {
1969 if (oid
->length
== 0) {
1970 return SecCopyCertString(SEC_NULL_KEY
);
1973 /* Build the key we use to lookup the localized OID description. */
1974 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1975 oid
->length
* 3 + 5);
1976 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
1978 for (ix
= 0; ix
< oid
->length
; ++ix
)
1979 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1981 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1982 if (CFEqual(oidKey
, name
)) {
1984 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1991 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1992 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1993 have a length of exactly 4 or 16 octects. */
1994 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1995 const DERItem
*ip
) {
1996 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1997 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1998 16 octects addr, or 32 octects addr/mask. */
1999 CFStringRef value
= NULL
;
2000 if (ip
->length
== 4) {
2001 value
= CFStringCreateWithFormat(allocator
, NULL
,
2002 CFSTR("%u.%u.%u.%u"),
2003 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2004 } else if (ip
->length
== 16) {
2005 value
= CFStringCreateWithFormat(allocator
, NULL
,
2006 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
2007 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
2008 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2009 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2010 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2011 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2018 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
2019 const DERItem
*oid
) {
2020 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2021 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
2022 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
2023 CFSTR("%@ (%@)"), name
, decimal
);
2030 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2031 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
) {
2032 CFDictionaryRef property
;
2035 if (localizedLabel
) {
2038 ll
= localizedLabel
= SecCopyCertString(label
);
2040 const void *all_keys
[4];
2041 all_keys
[0] = kSecPropertyKeyType
;
2042 all_keys
[1] = kSecPropertyKeyLabel
;
2043 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2044 all_keys
[3] = kSecPropertyKeyValue
;
2045 const void *property_values
[] = {
2051 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2052 all_keys
, property_values
, value
? 4 : 3,
2053 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2056 const void *nolabel_keys
[2];
2057 nolabel_keys
[0] = kSecPropertyKeyType
;
2058 nolabel_keys
[1] = kSecPropertyKeyValue
;
2059 const void *property_values
[] = {
2063 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2064 nolabel_keys
, property_values
, 2,
2065 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2068 CFArrayAppendValue(properties
, property
);
2069 CFRelease(property
);
2073 #define UTC_TIME_NOSEC_ZULU_LEN 11
2075 #define UTC_TIME_ZULU_LEN 13
2076 /* YYMMDDhhmmssThhmm */
2077 #define UTC_TIME_LOCALIZED_LEN 17
2078 /* YYYYMMDDhhmmssZ */
2079 #define GENERALIZED_TIME_ZULU_LEN 15
2080 /* YYYYMMDDhhmmssThhmm */
2081 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2083 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2085 static inline int parseDecimalPair(const DERByte
**p
) {
2086 const DERByte
*cp
= *p
;
2088 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2091 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2092 true if the date was valid and properly decoded, also return the result in
2093 absTime. Return false otherwise. */
2094 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2101 bool isUtcLength
= false;
2102 bool isLocalized
= false;
2103 bool noSeconds
= false;
2105 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2109 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2112 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2114 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2117 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2120 default: /* unknown format */
2124 /* Make sure the der tag fits the thing inside it. */
2125 if (tag
== ASN1_UTC_TIME
) {
2128 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2135 const DERByte
*cp
= bytes
;
2136 /* Check that all characters are digits, except if localized the timezone
2137 indicator or if not localized the 'Z' at the end. */
2139 for (ix
= 0; ix
< length
; ++ix
) {
2140 if (!(isdigit(cp
[ix
]))) {
2141 if ((isLocalized
&& ix
== length
- 5 &&
2142 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2143 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2150 /* Parse the date and time fields. */
2151 int year
, month
, day
, hour
, minute
, second
;
2153 year
= parseDecimalPair(&cp
);
2155 /* 0 <= year < 50 : assume century 21 */
2157 } else if (year
< 70) {
2158 /* 50 <= year < 70 : illegal per PKIX */
2161 /* 70 < year <= 99 : assume century 20 */
2165 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2167 month
= parseDecimalPair(&cp
);
2168 day
= parseDecimalPair(&cp
);
2169 hour
= parseDecimalPair(&cp
);
2170 minute
= parseDecimalPair(&cp
);
2174 second
= parseDecimalPair(&cp
);
2177 CFTimeInterval timeZoneOffset
;
2179 /* ZONE INDICATOR */
2180 int multiplier
= *cp
++ == '+' ? 60 : -60;
2181 timeZoneOffset
= multiplier
*
2182 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2187 secdebug("dateparse",
2188 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2189 (int) length
, bytes
, year
, month
,
2190 day
, hour
, minute
, second
,
2191 timeZoneOffset
/ 60);
2193 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2194 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2195 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
2196 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2197 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2202 int dy
= year
- 2001;
2207 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2208 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2210 day
+= is_leap_year
;
2212 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2213 return absTime
- timeZoneOffset
;
2216 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2217 CFAbsoluteTime
*pabsTime
) {
2218 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
2220 if (absTime
== NULL_TIME
)
2223 *pabsTime
= absTime
;
2227 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2228 true if the date was valid and properly decoded, also return the result in
2229 absTime. Return false otherwise. */
2230 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2231 CFAbsoluteTime
*absTime
) {
2232 if (dateChoice
->length
== 0) return false;
2234 DERDecodedInfo decoded
;
2235 if (DERDecodeItem(dateChoice
, &decoded
))
2238 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2242 static void appendDataProperty(CFMutableArrayRef properties
,
2243 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2244 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2245 der_data
->data
, der_data
->length
);
2246 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2251 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2253 CFStringRef localizedLabel
,
2254 const DERItem
*der_data
,
2255 CFStringRef labelFormat
) {
2256 CFStringRef newLabel
=
2257 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2258 labelFormat
, label
);
2260 if (localizedLabel
) {
2263 ll
= localizedLabel
= SecCopyCertString(label
);
2265 CFStringRef localizedLabelFormat
= SecCopyCertString(labelFormat
);
2266 CFStringRef newLocalizedLabel
=
2267 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2268 localizedLabelFormat
, localizedLabel
);
2270 CFReleaseSafe(localizedLabelFormat
);
2271 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
);
2272 CFReleaseSafe(newLabel
);
2273 CFReleaseSafe(newLocalizedLabel
);
2277 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2278 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2279 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2283 static void appendInvalidProperty(CFMutableArrayRef properties
,
2284 CFStringRef label
, const DERItem
*der_data
) {
2285 appendRelabeledProperty(properties
, label
, NULL
, der_data
, SEC_INVALID_KEY
);
2288 static void appendDateContentProperty(CFMutableArrayRef properties
,
2289 CFStringRef label
, DERTag tag
,
2290 const DERItem
*dateContent
) {
2291 CFAbsoluteTime absTime
;
2292 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2293 /* Date decode failure insert hex bytes instead. */
2294 return appendInvalidProperty(properties
, label
, dateContent
);
2296 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2297 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2301 static void appendDateProperty(CFMutableArrayRef properties
,
2302 CFStringRef label
, CFAbsoluteTime absTime
) {
2303 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2304 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2308 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2309 CFStringRef label
, const DERItem
*ip
) {
2311 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2313 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2316 appendUnparsedProperty(properties
, label
, NULL
, ip
);
2320 static void appendURLContentProperty(CFMutableArrayRef properties
,
2321 CFStringRef label
, const DERItem
*urlContent
) {
2322 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2323 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2325 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
);
2328 appendInvalidProperty(properties
, label
, urlContent
);
2332 static void appendURLProperty(CFMutableArrayRef properties
,
2333 CFStringRef label
, const DERItem
*url
) {
2334 DERDecodedInfo decoded
;
2337 drtn
= DERDecodeItem(url
, &decoded
);
2338 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2339 appendInvalidProperty(properties
, label
, url
);
2341 appendURLContentProperty(properties
, label
, &decoded
.content
);
2345 static void appendOIDProperty(CFMutableArrayRef properties
,
2346 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
) {
2347 CFStringRef oid_string
=
2348 copyLocalizedOidDescription(CFGetAllocator(properties
), oid
);
2349 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2351 CFRelease(oid_string
);
2354 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2355 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2356 CFMutableArrayRef alg_props
=
2357 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2358 &kCFTypeArrayCallBacks
);
2359 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
, &algorithm
->oid
);
2360 if (algorithm
->params
.length
) {
2361 if (algorithm
->params
.length
== 2 &&
2362 algorithm
->params
.data
[0] == ASN1_NULL
&&
2363 algorithm
->params
.data
[1] == 0) {
2364 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2365 appendProperty(alg_props
, kSecPropertyTypeString
,
2366 SEC_PARAMETERS_KEY
, NULL
, value
);
2369 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2370 &algorithm
->params
);
2373 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
, alg_props
);
2374 CFRelease(alg_props
);
2377 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2378 const DERItem
*blob
) {
2379 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2380 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2381 blob
->length
* 3 - 1);
2382 for (ix
= 0; ix
< length
; ++ix
)
2384 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2386 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2391 /* Returns a (localized) blob string. */
2392 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2393 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2394 CFStringRef localizedBlobType
= SecCopyCertString(blobType
);
2395 CFStringRef localizedQuanta
= SecCopyCertString(quanta
);
2396 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2397 "data = 00 00 ...)" */
2398 CFStringRef blobFormat
= SecCopyCertString(SEC_BLOB_KEY
);
2399 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2400 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2401 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2403 CFRelease(blobFormat
);
2404 CFReleaseSafe(localizedQuanta
);
2405 CFReleaseSafe(localizedBlobType
);
2410 /* Return a string verbatim (unlocalized) from a DER field. */
2411 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2412 const DERItem
*string
, CFStringEncoding encoding
,
2413 bool printableOnly
) {
2414 /* Strip potential bogus trailing zero from printable strings. */
2415 DERSize length
= string
->length
;
2416 if (length
&& string
->data
[length
- 1] == 0) {
2417 /* Don't mess with the length of UTF16 strings though. */
2418 if (encoding
!= kCFStringEncodingUTF16
)
2421 /* A zero length string isn't considered printable. */
2422 if (!length
&& printableOnly
)
2425 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2426 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2427 passing false makes it treat it as native endian by default. */
2428 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2429 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2433 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2436 /* From rfc3280 - Appendix B. ASN.1 Notes
2438 CAs MUST force the serialNumber to be a non-negative integer, that
2439 is, the sign bit in the DER encoding of the INTEGER value MUST be
2440 zero - this can be done by adding a leading (leftmost) `00'H octet if
2441 necessary. This removes a potential ambiguity in mapping between a
2442 string of octets and an integer value.
2444 As noted in section 4.1.2.2, serial numbers can be expected to
2445 contain long integers. Certificate users MUST be able to handle
2446 serialNumber values up to 20 octets in length. Conformant CAs MUST
2447 NOT use serialNumber values longer than 20 octets.
2450 /* Return the given numeric data as a string: decimal up to 64 bits,
2452 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2453 const DERItem
*integer
) {
2455 CFIndex ix
, length
= integer
->length
;
2457 if (length
== 0 || length
> 8)
2458 return copyHexDescription(allocator
, integer
);
2460 for(ix
= 0; ix
< length
; ++ix
) {
2462 value
+= integer
->data
[ix
];
2465 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2468 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2469 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2473 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2474 case ASN1_PRINTABLE_STRING
:
2475 case ASN1_IA5_STRING
:
2476 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2477 case ASN1_UTF8_STRING
:
2478 case ASN1_GENERAL_STRING
:
2479 case ASN1_UNIVERSAL_STRING
:
2480 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2481 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2482 case ASN1_VIDEOTEX_STRING
: // 21
2483 case ASN1_VISIBLE_STRING
: // 26
2484 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2485 case ASN1_BMP_STRING
: // 30
2486 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2487 case ASN1_OCTET_STRING
:
2488 return printableOnly
? NULL
:
2489 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2491 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2492 case ASN1_BIT_STRING
:
2493 return printableOnly
? NULL
:
2494 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2496 case ASN1_CONSTR_SEQUENCE
:
2497 return printableOnly
? NULL
:
2498 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2500 case ASN1_CONSTR_SET
:
2501 return printableOnly
? NULL
:
2502 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
, derThing
);
2503 case ASN1_OBJECT_ID
:
2504 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2506 if (printableOnly
) {
2509 CFStringRef fmt
= SecCopyCertString(SEC_NOT_DISPLAYED_KEY
);
2510 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2511 tag
, derThing
->length
);
2518 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2519 const DERItem
*derThing
, bool printableOnly
) {
2520 DERDecodedInfo decoded
;
2523 drtn
= DERDecodeItem(derThing
, &decoded
);
2525 /* TODO: Perhaps put something in the label saying we couldn't parse
2527 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2529 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2530 &decoded
.content
, false);
2534 static void appendDERThingProperty(CFMutableArrayRef properties
,
2535 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*derThing
) {
2536 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2538 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2540 CFReleaseSafe(value
);
2543 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2544 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2545 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2547 /* If there is more than one value pair we create a subsection for the
2548 second pair, and append things to the subsection for subsequent
2550 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2551 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2553 /* Since this is the second rdn pair for a given rdn, we setup a
2554 new subsection for this rdn. We remove the first property
2555 from the properties array and make it the first element in the
2556 subsection instead. */
2557 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2558 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2559 CFArrayAppendValue(rdn_props
, lastValue
);
2560 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2561 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2563 properties
= rdn_props
;
2565 /* Since this is the third or later rdn pair we have already
2566 created a subsection in the top level properties array. Instead
2567 of appending to that directly we append to the array inside the
2569 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2570 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2574 /* Finally we append the new rdn value to the property array. */
2575 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(
2576 CFGetAllocator(properties
), rdnType
);
2577 CFStringRef localizedLabel
=
2578 copyLocalizedOidDescription(CFGetAllocator(properties
), rdnType
);
2579 appendDERThingProperty(properties
, label
, localizedLabel
, rdnValue
);
2580 CFReleaseSafe(label
);
2581 CFReleaseSafe(localizedLabel
);
2582 return errSecSuccess
;
2585 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2586 const DERItem
*rdnSetContent
) {
2587 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2588 &kCFTypeArrayCallBacks
);
2589 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2592 CFArrayRemoveAllValues(properties
);
2593 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
);
2600 From rfc3739 - 3.1.2. Subject
2602 When parsing the subject here are some tips for a short name of the cert.
2603 Choice I: commonName
2604 Choice II: givenName
2605 Choice III: pseudonym
2607 The commonName attribute value SHALL, when present, contain a name
2608 of the subject. This MAY be in the subject's preferred
2609 presentation format, or a format preferred by the CA, or some
2610 other format. Pseudonyms, nicknames, and names with spelling
2611 other than defined by the registered name MAY be used. To
2612 understand the nature of the name presented in commonName,
2613 complying applications MAY have to examine present values of the
2614 givenName and surname attributes, or the pseudonym attribute.
2617 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2618 const DERItem
*x501NameContent
) {
2619 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2620 &kCFTypeArrayCallBacks
);
2621 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2624 CFArrayRemoveAllValues(properties
);
2625 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501NameContent
);
2631 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2632 const DERItem
*x501Name
) {
2633 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2634 &kCFTypeArrayCallBacks
);
2635 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2637 CFArrayRemoveAllValues(properties
);
2638 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501Name
);
2644 static void appendIntegerProperty(CFMutableArrayRef properties
,
2645 CFStringRef label
, const DERItem
*integer
) {
2646 CFStringRef string
= copyIntegerContentDescription(
2647 CFGetAllocator(properties
), integer
);
2648 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2652 static void appendBoolProperty(CFMutableArrayRef properties
,
2653 CFStringRef label
, bool boolean
) {
2654 CFStringRef value
= SecCopyCertString(boolean
? SEC_YES_KEY
: SEC_NO_KEY
);
2655 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2659 static void appendBooleanProperty(CFMutableArrayRef properties
,
2660 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2662 DERReturn drtn
= DERParseBoolean(boolean
, defaultValue
, &result
);
2664 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2665 appendInvalidProperty(properties
, label
, boolean
);
2667 appendBoolProperty(properties
, label
, result
);
2671 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2672 CFStringRef label
, const DERItem
*bitStringContent
,
2673 const CFStringRef
*names
, CFIndex namesCount
) {
2674 DERSize len
= bitStringContent
->length
- 1;
2675 require_quiet(len
== 1 || len
== 2, badDER
);
2676 DERByte numUnusedBits
= bitStringContent
->data
[0];
2677 require_quiet(numUnusedBits
< 8, badDER
);
2678 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2679 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2680 uint_fast16_t value
= bitStringContent
->data
[1];
2683 value
= (value
<< 8) + bitStringContent
->data
[2];
2689 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2690 CFStringRef string
= NULL
;
2691 for (ix
= 0; ix
< bits
; ++ix
) {
2695 CFStringCreateWithFormat(CFGetAllocator(properties
),
2696 NULL
, fmt
, string
, names
[ix
]);
2707 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2708 string
? string
: CFSTR(""));
2709 CFReleaseSafe(string
);
2712 appendInvalidProperty(properties
, label
, bitStringContent
);
2715 static void appendBitStringNames(CFMutableArrayRef properties
,
2716 CFStringRef label
, const DERItem
*bitString
,
2717 const CFStringRef
*names
, CFIndex namesCount
) {
2718 DERDecodedInfo bitStringContent
;
2719 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2720 require_noerr_quiet(drtn
, badDER
);
2721 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2722 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2726 appendInvalidProperty(properties
, label
, bitString
);
2730 typedef uint16_t SecKeyUsage
;
2732 #define kSecKeyUsageDigitalSignature 0x8000
2733 #define kSecKeyUsageNonRepudiation 0x4000
2734 #define kSecKeyUsageKeyEncipherment 0x2000
2735 #define kSecKeyUsageDataEncipherment 0x1000
2736 #define kSecKeyUsageKeyAgreement 0x0800
2737 #define kSecKeyUsageKeyCertSign 0x0400
2738 #define kSecKeyUsageCRLSign 0x0200
2739 #define kSecKeyUsageEncipherOnly 0x0100
2740 #define kSecKeyUsageDecipherOnly 0x0080
2743 KeyUsage ::= BIT STRING {
2744 digitalSignature (0),
2746 keyEncipherment (2),
2747 dataEncipherment (3),
2754 static void appendKeyUsage(CFMutableArrayRef properties
,
2755 const DERItem
*extnValue
) {
2756 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2757 extnValue
->data
[0] != ASN1_BIT_STRING
||
2758 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2759 extnValue
->data
[2] > 7) {
2760 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2763 CFMutableStringRef string
=
2764 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2765 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2766 if (extnValue
->length
== 5)
2767 usage
+= extnValue
->data
[4];
2768 secdebug("keyusage", "keyusage: %04X", usage
);
2769 static const CFStringRef usageNames
[] = {
2770 CFSTR("Digital Signature"),
2771 CFSTR("Non-Repudiation"),
2772 CFSTR("Key Encipherment"),
2773 CFSTR("Data Encipherment"),
2774 CFSTR("Key Agreement"),
2780 bool didOne
= false;
2781 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2782 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2783 for (ix
= 0; ix
< bits
; ++ix
) {
2786 CFStringAppend(string
, CFSTR(", "));
2790 /* @@@ Localize usageNames[ix]. */
2791 CFStringAppend(string
, usageNames
[ix
]);
2795 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2801 static void appendKeyUsage(CFMutableArrayRef properties
,
2802 const DERItem
*extnValue
) {
2803 static const CFStringRef usageNames
[] = {
2804 SEC_DIGITAL_SIGNATURE_KEY
,
2805 SEC_NON_REPUDIATION_KEY
,
2806 SEC_KEY_ENCIPHERMENT_KEY
,
2807 SEC_DATA_ENCIPHERMENT_KEY
,
2808 SEC_KEY_AGREEMENT_KEY
,
2811 SEC_ENCIPHER_ONLY_KEY
,
2812 SEC_DECIPHER_ONLY_KEY
2814 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2815 usageNames
, array_size(usageNames
));
2819 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2820 const DERItem
*extnValue
) {
2821 DERPrivateKeyUsagePeriod pkup
;
2822 DERReturn drtn
= DERParseSequence(extnValue
,
2823 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2824 &pkup
, sizeof(pkup
));
2825 require_noerr_quiet(drtn
, badDER
);
2826 if (pkup
.notBefore
.length
) {
2827 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2828 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2830 if (pkup
.notAfter
.length
) {
2831 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2832 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2836 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
, extnValue
);
2839 static void appendStringContentProperty(CFMutableArrayRef properties
,
2840 CFStringRef label
, const DERItem
*stringContent
,
2841 CFStringEncoding encoding
) {
2842 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2843 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2845 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2848 appendInvalidProperty(properties
, label
, stringContent
);
2853 OtherName ::= SEQUENCE {
2854 type-id OBJECT IDENTIFIER,
2855 value [0] EXPLICIT ANY DEFINED BY type-id }
2857 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2858 const DERItem
*otherNameContent
) {
2860 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2861 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2863 require_noerr_quiet(drtn
, badDER
);
2864 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2866 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
2867 CFStringRef localizedLabel
=
2868 copyLocalizedOidDescription(allocator
, &on
.typeIdentifier
);
2869 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2871 appendProperty(properties
, kSecPropertyTypeString
, label
,
2872 localizedLabel
, value_string
);
2874 appendUnparsedProperty(properties
, label
, localizedLabel
, &on
.value
);
2876 CFReleaseSafe(value_string
);
2877 CFReleaseSafe(label
);
2878 CFReleaseSafe(localizedLabel
);
2881 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
, otherNameContent
);
2885 GeneralName ::= CHOICE {
2886 otherName [0] OtherName,
2887 rfc822Name [1] IA5String,
2888 dNSName [2] IA5String,
2889 x400Address [3] ORAddress,
2890 directoryName [4] Name,
2891 ediPartyName [5] EDIPartyName,
2892 uniformResourceIdentifier [6] IA5String,
2893 iPAddress [7] OCTET STRING,
2894 registeredID [8] OBJECT IDENTIFIER}
2896 EDIPartyName ::= SEQUENCE {
2897 nameAssigner [0] DirectoryString OPTIONAL,
2898 partyName [1] DirectoryString }
2900 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2901 DERTag tag
, const DERItem
*generalName
) {
2903 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2904 appendOtherNameContentProperty(properties
, generalName
);
2906 case ASN1_CONTEXT_SPECIFIC
| 1:
2908 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
2909 generalName
, kCFStringEncodingASCII
);
2911 case ASN1_CONTEXT_SPECIFIC
| 2:
2913 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
2914 kCFStringEncodingASCII
);
2916 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2917 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
2920 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2922 CFArrayRef directory_plist
=
2923 createPropertiesForX501Name(CFGetAllocator(properties
),
2925 appendProperty(properties
, kSecPropertyTypeSection
,
2926 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
);
2927 CFRelease(directory_plist
);
2930 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2931 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
2934 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2935 /* Technically I don't think this is valid, but there are certs out
2936 in the wild that use a constructed IA5String. In particular the
2937 VeriSign Time Stamping Authority CA.cer does this. */
2938 appendURLProperty(properties
, SEC_URI_KEY
, generalName
);
2940 case ASN1_CONTEXT_SPECIFIC
| 6:
2941 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
);
2943 case ASN1_CONTEXT_SPECIFIC
| 7:
2944 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
2947 case ASN1_CONTEXT_SPECIFIC
| 8:
2948 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
, generalName
);
2959 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2960 const DERItem
*generalName
) {
2961 DERDecodedInfo generalNameContent
;
2962 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2963 require_noerr_quiet(drtn
, badDER
);
2964 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2965 &generalNameContent
.content
))
2968 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
, generalName
);
2973 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2975 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2976 const DERItem
*generalNamesContent
) {
2978 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2979 require_noerr_quiet(drtn
, badDER
);
2980 DERDecodedInfo generalNameContent
;
2981 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2983 if (!appendGeneralNameContentProperty(properties
,
2984 generalNameContent
.tag
, &generalNameContent
.content
)) {
2988 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2991 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
2992 generalNamesContent
);
2995 static void appendGeneralNames(CFMutableArrayRef properties
,
2996 const DERItem
*generalNames
) {
2997 DERDecodedInfo generalNamesContent
;
2998 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2999 require_noerr_quiet(drtn
, badDER
);
3000 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3002 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
3005 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
, generalNames
);
3009 BasicConstraints ::= SEQUENCE {
3010 cA BOOLEAN DEFAULT FALSE,
3011 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3013 static void appendBasicConstraints(CFMutableArrayRef properties
,
3014 const DERItem
*extnValue
) {
3015 DERBasicConstraints basicConstraints
;
3016 DERReturn drtn
= DERParseSequence(extnValue
,
3017 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3018 &basicConstraints
, sizeof(basicConstraints
));
3019 require_noerr_quiet(drtn
, badDER
);
3021 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3022 &basicConstraints
.cA
, false);
3024 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3025 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3026 &basicConstraints
.pathLenConstraint
);
3030 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
, extnValue
);
3034 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3036 * NameConstraints ::= SEQUENCE {
3037 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3038 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3040 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3042 * GeneralSubtree ::= SEQUENCE {
3044 * minimum [0] BaseDistance DEFAULT 0,
3045 * maximum [1] BaseDistance OPTIONAL }
3047 * BaseDistance ::= INTEGER (0..MAX)
3049 static void appendNameConstraints(CFMutableArrayRef properties
,
3050 const DERItem
*extnValue
) {
3051 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3052 DERNameConstraints nc
;
3054 drtn
= DERParseSequence(extnValue
,
3055 DERNumNameConstraintsItemSpecs
,
3056 DERNameConstraintsItemSpecs
,
3058 require_noerr_quiet(drtn
, badDER
);
3059 if (nc
.permittedSubtrees
.length
) {
3061 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3062 DERDecodedInfo gsContent
;
3063 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3064 DERGeneralSubtree derGS
;
3065 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3066 drtn
= DERParseSequenceContent(&gsContent
.content
,
3067 DERNumGeneralSubtreeItemSpecs
,
3068 DERGeneralSubtreeItemSpecs
,
3069 &derGS
, sizeof(derGS
));
3070 require_noerr_quiet(drtn
, badDER
);
3071 if (derGS
.minimum
.length
) {
3072 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
, &derGS
.minimum
);
3074 if (derGS
.maximum
.length
) {
3075 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
, &derGS
.maximum
);
3077 if (derGS
.generalName
.length
) {
3078 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3079 &kCFTypeArrayCallBacks
);
3080 appendProperty(properties
, kSecPropertyTypeSection
,
3081 SEC_PERMITTED_NAME_KEY
, NULL
, base
);
3082 appendGeneralNameProperty(base
, &derGS
.generalName
);
3087 if (nc
.excludedSubtrees
.length
) {
3089 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3090 DERDecodedInfo gsContent
;
3091 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3092 DERGeneralSubtree derGS
;
3093 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3094 drtn
= DERParseSequenceContent(&gsContent
.content
,
3095 DERNumGeneralSubtreeItemSpecs
,
3096 DERGeneralSubtreeItemSpecs
,
3097 &derGS
, sizeof(derGS
));
3098 require_noerr_quiet(drtn
, badDER
);
3099 if (derGS
.minimum
.length
) {
3100 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
, &derGS
.minimum
);
3102 if (derGS
.maximum
.length
) {
3103 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
, &derGS
.maximum
);
3105 if (derGS
.generalName
.length
) {
3106 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3107 &kCFTypeArrayCallBacks
);
3108 appendProperty(properties
, kSecPropertyTypeSection
,
3109 SEC_EXCLUDED_NAME_KEY
, NULL
, base
);
3110 appendGeneralNameProperty(base
, &derGS
.generalName
);
3118 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
, extnValue
);
3122 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3124 DistributionPoint ::= SEQUENCE {
3125 distributionPoint [0] DistributionPointName OPTIONAL,
3126 reasons [1] ReasonFlags OPTIONAL,
3127 cRLIssuer [2] GeneralNames OPTIONAL }
3129 DistributionPointName ::= CHOICE {
3130 fullName [0] GeneralNames,
3131 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3133 ReasonFlags ::= BIT STRING {
3137 affiliationChanged (3),
3139 cessationOfOperation (5),
3140 certificateHold (6),
3141 privilegeWithdrawn (7),
3144 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3145 const DERItem
*extnValue
) {
3146 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3149 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3150 require_noerr_quiet(drtn
, badDER
);
3151 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3152 DERDecodedInfo dpSeqContent
;
3153 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3154 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3155 DERDistributionPoint dp
;
3156 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3157 DERNumDistributionPointItemSpecs
,
3158 DERDistributionPointItemSpecs
,
3160 require_noerr_quiet(drtn
, badDER
);
3161 if (dp
.distributionPoint
.length
) {
3162 DERDecodedInfo distributionPointName
;
3163 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3164 require_noerr_quiet(drtn
, badDER
);
3165 if (distributionPointName
.tag
==
3166 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3168 appendGeneralNamesContent(properties
,
3169 &distributionPointName
.content
);
3170 } else if (distributionPointName
.tag
==
3171 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3172 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3174 appendProperty(properties
, kSecPropertyTypeSection
,
3175 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
);
3176 CFRelease(rdn_props
);
3181 if (dp
.reasons
.length
) {
3182 static const CFStringRef reasonNames
[] = {
3184 SEC_KEY_COMPROMISE_KEY
,
3185 SEC_CA_COMPROMISE_KEY
,
3186 SEC_AFFILIATION_CHANGED_KEY
,
3188 SEC_CESSATION_OF_OPER_KEY
,
3189 SEC_CERTIFICATE_HOLD_KEY
,
3190 SEC_PRIV_WITHDRAWN_KEY
,
3191 SEC_AA_COMPROMISE_KEY
3193 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3195 reasonNames
, array_size(reasonNames
));
3197 if (dp
.cRLIssuer
.length
) {
3198 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3199 &kCFTypeArrayCallBacks
);
3200 appendProperty(properties
, kSecPropertyTypeSection
,
3201 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
);
3202 CFRelease(crlIssuer
);
3203 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
3206 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3209 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
, extnValue
);
3212 /* Decode a sequence of integers into a comma separated list of ints. */
3213 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3214 CFStringRef label
, const DERItem
*intSequenceContent
) {
3215 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3217 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3218 require_noerr_quiet(drtn
, badDER
);
3219 DERDecodedInfo intContent
;
3220 CFStringRef value
= NULL
;
3221 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3222 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3223 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3224 CFStringRef intDesc
= copyIntegerContentDescription(
3225 allocator
, &intContent
.content
);
3228 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3237 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3239 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
3243 /* DROPTHOUGH if !value. */
3245 appendInvalidProperty(properties
, label
, intSequenceContent
);
3248 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3249 const DERItem
*extnValue
) {
3250 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3253 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3254 require_noerr_quiet(drtn
, badDER
);
3255 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3256 DERDecodedInfo piContent
;
3258 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3259 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3260 DERPolicyInformation pi
;
3261 drtn
= DERParseSequenceContent(&piContent
.content
,
3262 DERNumPolicyInformationItemSpecs
,
3263 DERPolicyInformationItemSpecs
,
3265 require_noerr_quiet(drtn
, badDER
);
3266 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3267 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3268 CFStringRef piFmt
= SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
);
3269 CFStringRef lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3272 appendOIDProperty(properties
, piLabel
, lpiLabel
, &pi
.policyIdentifier
);
3274 CFRelease(lpiLabel
);
3275 if (pi
.policyQualifiers
.length
== 0)
3279 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3280 require_noerr_quiet(drtn
, badDER
);
3281 DERDecodedInfo pqContent
;
3283 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3284 DERPolicyQualifierInfo pqi
;
3285 drtn
= DERParseSequenceContent(&pqContent
.content
,
3286 DERNumPolicyQualifierInfoItemSpecs
,
3287 DERPolicyQualifierInfoItemSpecs
,
3289 require_noerr_quiet(drtn
, badDER
);
3290 DERDecodedInfo qualifierContent
;
3291 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3292 require_noerr_quiet(drtn
, badDER
);
3293 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3294 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3295 CFStringRef pqFmt
= SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
);
3296 CFStringRef lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3299 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3300 &pqi
.policyQualifierID
);
3302 CFRelease(lpqLabel
);
3303 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3304 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3305 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3306 &qualifierContent
.content
);
3307 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3308 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3310 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3311 DERNumUserNoticeItemSpecs
,
3312 DERUserNoticeItemSpecs
,
3314 require_noerr_quiet(drtn
, badDER
);
3315 if (un
.noticeRef
.length
) {
3316 DERNoticeReference nr
;
3317 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3318 DERNumNoticeReferenceItemSpecs
,
3319 DERNoticeReferenceItemSpecs
,
3321 require_noerr_quiet(drtn
, badDER
);
3322 appendDERThingProperty(properties
,
3323 SEC_ORGANIZATION_KEY
, NULL
,
3325 appendIntegerSequenceContent(properties
,
3326 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
);
3328 if (un
.explicitText
.length
) {
3329 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3330 NULL
, &un
.explicitText
);
3333 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3338 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3341 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
, extnValue
);
3344 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3345 const DERItem
*extnValue
) {
3347 DERDecodedInfo keyIdentifier
;
3348 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3349 require_noerr_quiet(drtn
, badDER
);
3350 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3351 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3352 &keyIdentifier
.content
);
3356 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3361 AuthorityKeyIdentifier ::= SEQUENCE {
3362 keyIdentifier [0] KeyIdentifier OPTIONAL,
3363 authorityCertIssuer [1] GeneralNames OPTIONAL,
3364 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3365 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3366 -- be present or both be absent
3368 KeyIdentifier ::= OCTET STRING
3370 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3371 const DERItem
*extnValue
) {
3372 DERAuthorityKeyIdentifier akid
;
3374 drtn
= DERParseSequence(extnValue
,
3375 DERNumAuthorityKeyIdentifierItemSpecs
,
3376 DERAuthorityKeyIdentifierItemSpecs
,
3377 &akid
, sizeof(akid
));
3378 require_noerr_quiet(drtn
, badDER
);
3379 if (akid
.keyIdentifier
.length
) {
3380 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3381 &akid
.keyIdentifier
);
3383 if (akid
.authorityCertIssuer
.length
||
3384 akid
.authorityCertSerialNumber
.length
) {
3385 require_quiet(akid
.authorityCertIssuer
.length
&&
3386 akid
.authorityCertSerialNumber
.length
, badDER
);
3387 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3388 appendGeneralNamesContent(properties
,
3389 &akid
.authorityCertIssuer
);
3390 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3391 &akid
.authorityCertSerialNumber
);
3396 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
, extnValue
);
3400 PolicyConstraints ::= SEQUENCE {
3401 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3402 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3404 SkipCerts ::= INTEGER (0..MAX)
3406 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3407 const DERItem
*extnValue
) {
3408 DERPolicyConstraints pc
;
3410 drtn
= DERParseSequence(extnValue
,
3411 DERNumPolicyConstraintsItemSpecs
,
3412 DERPolicyConstraintsItemSpecs
,
3414 require_noerr_quiet(drtn
, badDER
);
3415 if (pc
.requireExplicitPolicy
.length
) {
3416 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3417 &pc
.requireExplicitPolicy
);
3419 if (pc
.inhibitPolicyMapping
.length
) {
3420 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3421 &pc
.inhibitPolicyMapping
);
3427 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
, extnValue
);
3431 extendedKeyUsage EXTENSION ::= {
3432 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3433 IDENTIFIED BY id-ce-extKeyUsage }
3435 KeyPurposeId ::= OBJECT IDENTIFIER
3437 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3438 const DERItem
*extnValue
) {
3441 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3442 require_noerr_quiet(drtn
, badDER
);
3443 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3444 DERDecodedInfo currDecoded
;
3445 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3446 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3447 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3448 &currDecoded
.content
);
3450 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3453 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
, extnValue
);
3457 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3459 AuthorityInfoAccessSyntax ::=
3460 SEQUENCE SIZE (1..MAX) OF AccessDescription
3462 AccessDescription ::= SEQUENCE {
3463 accessMethod OBJECT IDENTIFIER,
3464 accessLocation GeneralName }
3466 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3468 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3470 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3472 static void appendInfoAccess(CFMutableArrayRef properties
,
3473 const DERItem
*extnValue
) {
3476 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3477 require_noerr_quiet(drtn
, badDER
);
3478 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3479 DERDecodedInfo adContent
;
3480 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3481 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3482 DERAccessDescription ad
;
3483 drtn
= DERParseSequenceContent(&adContent
.content
,
3484 DERNumAccessDescriptionItemSpecs
,
3485 DERAccessDescriptionItemSpecs
,
3487 require_noerr_quiet(drtn
, badDER
);
3488 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3490 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3491 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3493 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3496 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
, extnValue
);
3499 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3500 const DERItem
*extnValue
) {
3501 static const CFStringRef certTypes
[] = {
3505 SEC_OBJECT_SIGNING_KEY
,
3509 SEC_OBJECT_SIGNING_CA_KEY
3511 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3512 certTypes
, array_size(certTypes
));
3516 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3517 const DERItem
*extnValue
) {
3521 * The list of Qualified Cert Statement statementIds we understand, even though
3522 * we don't actually do anything with them; if these are found in a Qualified
3523 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3525 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3527 /* id-qcs := { id-pkix 11 } */
3528 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3529 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3530 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3531 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3532 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3533 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3535 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3537 static void appendQCCertStatements(CFMutableArrayRef properties
,
3538 const DERItem
*extnValue
) {
3543 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3544 CFStringRef label
, const DERItem
*sequence
) {
3547 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3548 require_noerr_quiet(drtn
, badSequence
);
3549 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3550 DERDecodedInfo currDecoded
;
3551 bool appendedSomething
= false;
3552 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3553 switch (currDecoded
.tag
)
3556 case ASN1_SEQUENCE
: // 16
3557 case ASN1_SET
: // 17
3558 // skip constructed object lengths
3561 case ASN1_UTF8_STRING
: // 12
3562 case ASN1_NUMERIC_STRING
: // 18
3563 case ASN1_PRINTABLE_STRING
: // 19
3564 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3565 case ASN1_VIDEOTEX_STRING
: // 21
3566 case ASN1_IA5_STRING
: // 22
3567 case ASN1_GRAPHIC_STRING
: // 25
3568 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3569 case ASN1_GENERAL_STRING
: // 27
3570 case ASN1_UNIVERSAL_STRING
: // 28
3572 CFStringRef string
=
3573 copyDERThingContentDescription(CFGetAllocator(properties
),
3574 currDecoded
.tag
, &currDecoded
.content
, false);
3575 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3577 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3580 appendedSomething
= true;
3587 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3588 return appendedSomething
;
3593 static void appendExtension(CFMutableArrayRef parent
,
3594 const SecCertificateExtension
*extn
) {
3595 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3596 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3597 &kCFTypeArrayCallBacks
);
3599 *extnID
= &extn
->extnID
,
3600 *extnValue
= &extn
->extnValue
;
3601 CFStringRef label
= NULL
;
3602 CFStringRef localizedLabel
= NULL
;
3604 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
);
3605 require_quiet(extnID
, xit
);
3608 bool handled
= true;
3609 /* Extensions that we know how to handle ourselves... */
3610 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3611 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3613 switch (extnID
->data
[extnID
->length
- 1]) {
3614 case 14: /* SubjectKeyIdentifier id-ce 14 */
3615 appendSubjectKeyIdentifier(properties
, extnValue
);
3617 case 15: /* KeyUsage id-ce 15 */
3618 appendKeyUsage(properties
, extnValue
);
3620 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3621 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3623 case 17: /* SubjectAltName id-ce 17 */
3624 case 18: /* IssuerAltName id-ce 18 */
3625 appendGeneralNames(properties
, extnValue
);
3627 case 19: /* BasicConstraints id-ce 19 */
3628 appendBasicConstraints(properties
, extnValue
);
3630 case 30: /* NameConstraints id-ce 30 */
3631 appendNameConstraints(properties
, extnValue
);
3633 case 31: /* CRLDistributionPoints id-ce 31 */
3634 appendCrlDistributionPoints(properties
, extnValue
);
3636 case 32: /* CertificatePolicies id-ce 32 */
3637 appendCertificatePolicies(properties
, extnValue
);
3639 case 33: /* PolicyMappings id-ce 33 */
3642 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3643 appendAuthorityKeyIdentifier(properties
, extnValue
);
3645 case 36: /* PolicyConstraints id-ce 36 */
3646 appendPolicyConstraints(properties
, extnValue
);
3648 case 37: /* ExtKeyUsage id-ce 37 */
3649 appendExtendedKeyUsage(properties
, extnValue
);
3651 case 46: /* FreshestCRL id-ce 46 */
3654 case 54: /* InhibitAnyPolicy id-ce 54 */
3661 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3662 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3664 switch (extnID
->data
[extnID
->length
- 1]) {
3665 case 1: /* AuthorityInfoAccess id-pe 1 */
3666 appendInfoAccess(properties
, extnValue
);
3668 case 3: /* QCStatements id-pe 3 */
3671 case 11: /* SubjectInfoAccess id-pe 11 */
3672 appendInfoAccess(properties
, extnValue
);
3678 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3679 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3680 appendNetscapeCertType(properties
, extnValue
);
3686 /* Try to parse and display printable string(s). */
3687 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3688 /* Nothing to do here appendPrintableDERSequence did the work. */
3690 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3691 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3695 /* Extensions that we know how to handle ourselves... */
3696 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3697 appendSubjectKeyIdentifier(properties
, extnValue
);
3698 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3699 appendKeyUsage(properties
, extnValue
);
3700 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3701 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3702 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3703 appendGeneralNames(properties
, extnValue
);
3704 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3705 appendGeneralNames(properties
, extnValue
);
3706 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3707 appendBasicConstraints(properties
, extnValue
);
3708 } else if (DEROidCompare(extnID
, &oidNameConstraints
)) {
3709 appendNameConstraints(properties
, extnValue
);
3710 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3711 appendCrlDistributionPoints(properties
, extnValue
);
3712 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3713 appendCertificatePolicies(properties
, extnValue
);
3714 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3715 appendAuthorityKeyIdentifier(properties
, extnValue
);
3716 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3717 appendPolicyConstraints(properties
, extnValue
);
3718 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3719 appendExtendedKeyUsage(properties
, extnValue
);
3720 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3721 appendInfoAccess(properties
, extnValue
);
3722 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3723 appendInfoAccess(properties
, extnValue
);
3724 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3725 appendNetscapeCertType(properties
, extnValue
);
3727 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3728 appendEntrustVersInfo(properties
, extnValue
);
3731 /* Try to parse and display printable string(s). */
3732 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3733 /* Nothing to do here appendPrintableDERSequence did the work. */
3735 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3736 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3739 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3740 localizedLabel
= copyLocalizedOidDescription(allocator
, extnID
);
3741 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
, properties
);
3744 CFReleaseSafe(localizedLabel
);
3745 CFReleaseSafe(label
);
3746 CFReleaseSafe(properties
);
3749 /* Different types of summary types from least desired to most desired. */
3752 kSummaryTypePrintable
,
3753 kSummaryTypeOrganizationName
,
3754 kSummaryTypeOrganizationalUnitName
,
3755 kSummaryTypeCommonName
,
3759 enum SummaryType type
;
3760 CFStringRef summary
;
3761 CFStringRef description
;
3764 static OSStatus
obtainSummaryFromX501Name(void *context
,
3765 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3766 struct Summary
*summary
= (struct Summary
*)context
;
3767 enum SummaryType stype
= kSummaryTypeNone
;
3768 CFStringRef string
= NULL
;
3769 if (DEROidCompare(type
, &oidCommonName
)) {
3770 stype
= kSummaryTypeCommonName
;
3771 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3772 stype
= kSummaryTypeOrganizationalUnitName
;
3773 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3774 stype
= kSummaryTypeOrganizationName
;
3775 } else if (DEROidCompare(type
, &oidDescription
)) {
3776 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3778 if (summary
->description
) {
3779 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3780 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->description
);
3782 CFRelease(summary
->description
);
3783 summary
->description
= newDescription
;
3785 summary
->description
= string
;
3788 stype
= kSummaryTypePrintable
;
3791 stype
= kSummaryTypePrintable
;
3794 /* Build a string with all instances of the most desired
3795 component type in reverse order encountered comma separated list,
3796 The order of desirability is defined by enum SummaryType. */
3797 if (summary
->type
<= stype
) {
3799 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3802 if (summary
->type
== stype
) {
3803 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3804 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->summary
);
3807 string
= newSummary
;
3809 summary
->type
= stype
;
3811 CFReleaseSafe(summary
->summary
);
3812 summary
->summary
= string
;
3815 CFReleaseSafe(string
);
3818 return errSecSuccess
;
3821 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3822 struct Summary summary
= {};
3823 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3824 /* If we found a description and a common name we change the summary to
3825 CommonName (Description). */
3826 if (summary
.description
) {
3827 if (summary
.type
== kSummaryTypeCommonName
) {
3828 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3829 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3831 CFRelease(summary
.summary
);
3832 summary
.summary
= newSummary
;
3834 CFRelease(summary
.description
);
3837 if (!summary
.summary
) {
3838 /* If we didn't find a suitable printable string in the subject at all, we try
3839 the first email address in the certificate instead. */
3840 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3842 /* If we didn't find any email addresses in the certificate, we try finding
3843 a DNS name instead. */
3844 names
= SecCertificateCopyDNSNames(certificate
);
3847 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3848 CFRetain(summary
.summary
);
3853 return summary
.summary
;
3856 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
3857 struct Summary summary
= {};
3858 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3859 /* If we found a description and a common name we change the summary to
3860 CommonName (Description). */
3861 if (summary
.description
) {
3862 if (summary
.type
== kSummaryTypeCommonName
) {
3863 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3864 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3866 CFRelease(summary
.summary
);
3867 summary
.summary
= newSummary
;
3869 CFRelease(summary
.description
);
3872 return summary
.summary
;
3875 /* Return the earliest date on which all certificates in this chain are still
3877 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3878 SecCertificateRef certificate
) {
3879 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3881 while (certificate
->_parent
) {
3882 certificate
= certificate
->_parent
;
3883 if (earliest
> certificate
->_notAfter
)
3884 earliest
= certificate
->_notAfter
;
3891 /* Return the latest date on which all certificates in this chain will be
3893 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3894 SecCertificateRef certificate
) {
3895 CFAbsoluteTime latest
= certificate
->_notBefore
;
3897 while (certificate
->_parent
) {
3898 certificate
= certificate
->_parent
;
3899 if (latest
< certificate
->_notBefore
)
3900 latest
= certificate
->_notBefore
;
3907 bool SecCertificateIsValid(SecCertificateRef certificate
,
3908 CFAbsoluteTime verifyTime
) {
3909 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
3910 verifyTime
<= certificate
->_notAfter
;
3913 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
3914 return certificate
->_version
+ 1;
3917 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
3918 return certificate
->_notBefore
;
3921 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
3922 return certificate
->_notAfter
;
3925 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3926 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
3927 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3928 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3929 &kCFTypeArrayCallBacks
);
3931 /* First we put the subject summary name. */
3932 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
3934 appendProperty(summary
, kSecPropertyTypeTitle
,
3935 NULL
, NULL
, ssummary
);
3936 CFRelease(ssummary
);
3939 CFStringRef isummary
= SEC_ISSUER_SUMMARY_KEY
;
3940 appendProperty(summary
, kSecPropertyTypeString
,
3941 SEC_ISSUED_BY_KEY
, isummary
);
3942 CFRelease(isummary
);
3945 /* Let see if this certificate is currently valid. */
3947 CFAbsoluteTime when
;
3948 CFStringRef message
;
3950 if (verifyTime
> certificate
->_notAfter
) {
3951 label
= SEC_EXPIRED_KEY
;
3952 when
= certificate
->_notAfter
;
3953 ptype
= kSecPropertyTypeError
;
3954 message
= SEC_CERT_EXPIRED_KEY
;
3955 } else if (certificate
->_notBefore
> verifyTime
) {
3956 label
= SEC_VALID_FROM_KEY
;
3957 when
= certificate
->_notBefore
;
3958 ptype
= kSecPropertyTypeError
;
3959 message
= SEC_CERT_NOT_YET_VALID_KEY
;
3961 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3962 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3963 if (verifyTime
> last
) {
3964 label
= SEC_EXPIRED_KEY
;
3966 ptype
= kSecPropertyTypeError
;
3967 message
= SEC_ISSUER_EXPIRED_KEY
;
3968 } else if (verifyTime
< first
) {
3969 label
= SEC_VALID_FROM_KEY
;
3971 ptype
= kSecPropertyTypeError
;
3972 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
3974 label
= SEC_EXPIRES_KEY
;
3975 when
= certificate
->_notAfter
;
3976 ptype
= kSecPropertyTypeSuccess
;
3977 message
= SEC_CERT_VALID_KEY
;
3981 appendDateProperty(summary
, label
, when
);
3982 CFStringRef lmessage
= SecCopyCertString(message
);
3983 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
);
3984 CFRelease(lmessage
);
3989 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
3990 if (!certificate
->_properties
) {
3991 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3992 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3993 &kCFTypeArrayCallBacks
);
3995 /* First we put the Subject Name in the property list. */
3996 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3997 &certificate
->_subject
);
3998 appendProperty(properties
, kSecPropertyTypeSection
,
3999 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
);
4000 CFRelease(subject_plist
);
4003 /* Put Normalized subject in for testing. */
4004 if (certificate
->_normalizedSubject
) {
4005 DERItem nsubject
= {
4006 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
4007 CFDataGetLength(certificate
->_normalizedSubject
)
4009 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
4011 appendProperty(properties
, kSecPropertyTypeSection
,
4012 CFSTR("Normalized Subject Name"), nsubject_plist
);
4013 CFRelease(nsubject_plist
);
4017 /* Next we put the Issuer Name in the property list. */
4018 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4019 &certificate
->_issuer
);
4020 appendProperty(properties
, kSecPropertyTypeSection
,
4021 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
);
4022 CFRelease(issuer_plist
);
4025 /* Certificate version/type. */
4026 bool isRoot
= false;
4027 CFStringRef fmt
= SecCopyCertString(SEC_X509_VERSION_KEY
);
4028 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
4029 fmt
, certificate
->_version
+ 1, isRoot
? "root " : "");
4031 appendProperty(properties
, kSecPropertyTypeString
,
4032 SEC_CERTIFICATE_TYPE_KEY
, typeString
);
4033 CFRelease(typeString
);
4037 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4038 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4039 NULL
, fmt
, certificate
->_version
+ 1);
4041 appendProperty(properties
, kSecPropertyTypeString
,
4042 SEC_VERSION_KEY
, NULL
, versionString
);
4043 CFRelease(versionString
);
4046 if (certificate
->_serialNum
.length
) {
4047 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
4048 &certificate
->_serialNum
);
4051 /* Signature algorithm. */
4053 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
4054 &certificate
->_sigAlg
);
4056 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
4057 &certificate
->_tbsSigAlg
);
4060 /* Validity dates. */
4061 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
4062 certificate
->_notBefore
);
4063 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
4064 certificate
->_notAfter
);
4066 if (certificate
->_subjectUniqueID
.length
) {
4067 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4068 &certificate
->_subjectUniqueID
);
4070 if (certificate
->_issuerUniqueID
.length
) {
4071 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4072 &certificate
->_issuerUniqueID
);
4075 /* Public key algorithm. */
4076 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
4077 &certificate
->_algId
);
4079 /* Consider breaking down an RSA public key into modulus and
4081 appendDataProperty(properties
, SEC_PULIC_KEY_DATA_KEY
, NULL
,
4082 &certificate
->_pubKeyDER
);
4083 /* TODO: Add Key Size. */
4084 /* TODO: Add Key Usage. */
4086 appendDataProperty(properties
, SEC_SIGNATURE_KEY
, NULL
,
4087 &certificate
->_signature
);
4090 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4091 appendExtension(properties
, &certificate
->_extensions
[ix
]);
4094 /* TODO: Certificate/Key Fingerprints. */
4096 certificate
->_properties
= properties
;
4099 CFRetain(certificate
->_properties
);
4100 return certificate
->_properties
;
4103 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4104 /* On OS X, the SecCertificateCopySerialNumber API takes two arguments. */
4105 CFDataRef
SecCertificateCopySerialNumber(
4106 SecCertificateRef certificate
,
4107 CFErrorRef
*error
) {
4110 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4114 if (certificate
->_serialNumber
) {
4115 CFRetain(certificate
->_serialNumber
);
4117 return certificate
->_serialNumber
;
4120 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4121 CFDataRef
SecCertificateCopySerialNumber(
4122 SecCertificateRef certificate
) {
4123 if (certificate
->_serialNumber
) {
4124 CFRetain(certificate
->_serialNumber
);
4126 return certificate
->_serialNumber
;
4130 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4131 SecCertificateRef certificate
) {
4132 return certificate
->_normalizedIssuer
;
4135 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4136 SecCertificateRef certificate
) {
4137 return certificate
->_normalizedSubject
;
4140 /* Verify that certificate was signed by issuerKey. */
4141 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4142 SecKeyRef issuerKey
) {
4143 /* Setup algId in SecAsn1AlgId format. */
4145 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4146 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4147 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4148 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4150 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
4151 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4152 certificate
->_signature
.data
, certificate
->_signature
.length
);
4154 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, status
);
4155 return errSecNotSigner
;
4158 return errSecSuccess
;
4162 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRef certificate
,
4163 SecCertificateRef issuer
, bool signatureCheckOnly
) {
4164 if (!signatureCheckOnly
) {
4165 /* It turns out we don't actually need to use normalized subject and
4166 issuer according to rfc2459. */
4168 /* If present we should check issuerID against the issuer subjectID. */
4170 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
4171 then we should look for a SubjectKeyIdentifier in the issuer
4173 If we have a authorityCertSerialNumber we can use that for chaining.
4174 If we have a authorityCertIssuer we can use that? (or not) */
4176 /* Verify that this cert was issued by issuer. Do so by chaining
4177 either issuerID to subjectID or normalized issuer to normalized
4179 CFDataRef normalizedIssuer
=
4180 SecCertificateGetNormalizedIssuerContent(certificate
);
4181 CFDataRef normalizedIssuerSubject
=
4182 SecCertificateGetNormalizedSubjectContent(issuer
);
4183 if (normalizedIssuer
&& normalizedIssuerSubject
&&
4184 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
4185 return errSecIssuerMismatch
;
4188 /* Next verify that this cert was signed by issuer. */
4189 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
4191 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
4192 /* FIXME: We sould cache this (or at least the digest) until we find
4193 a suitable issuer. */
4194 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
4195 CFIndex signedDataLength
;
4196 CertVerifyReturn crtn
;
4197 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
4198 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
4199 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
4200 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
4201 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
4202 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
4203 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
4204 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
4205 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
4207 secdebug("verify", "unsupported algorithm");
4208 return errSecUnsupportedAlgorithm
;
4211 secdebug("verify", "*DigestInfo returned: %d", crtn
);
4212 /* FIXME: Do proper error code translation. */
4213 return errSecUnsupportedAlgorithm
;
4216 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
4217 signedData
, signedDataLength
,
4218 certificate
->_signature
.data
, certificate
->_signature
.length
);
4220 secdebug("verify", "signature verify failed: %d", status
);
4221 return errSecNotSigner
;
4224 return errSecSuccess
;
4227 static OSStatus
_SecCertificateSetParent(SecCertificateRef certificate
,
4228 SecCertificateRef issuer
, bool signatureCheckOnly
) {
4230 if (certificate
->_parent
) {
4231 /* Setting a certificates issuer twice is only allowed if the new
4232 issuer is equal to the current one. */
4233 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
4237 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
4238 signatureCheckOnly
);
4240 OSStatus status
= errSecSuccess
;
4243 if (CFEqual(certificate
, issuer
)) {
4244 /* We don't retain ourselves cause that would be bad mojo,
4245 however we do record that we are properly self signed. */
4246 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
4247 secdebug("cert", "set self as parent");
4248 return errSecSuccess
;
4252 certificate
->_parent
= issuer
;
4253 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
4259 /* Return true iff we were able to set our own parent from one of the
4260 certificates in other_certificates, return false otherwise. If
4261 signatureCheckOnly is true, we can skip the subject == issuer or
4262 authorityKeyIdentifier tests. */
4263 static bool SecCertificateSetParentFrom(SecCertificateRef certificate
,
4264 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
4265 CFIndex count
= CFArrayGetCount(other_certificates
);
4267 for (ix
= 0; ix
< count
; ++ix
) {
4268 SecCertificateRef candidate
= (SecCertificateRef
)
4269 CFArrayGetValueAtIndex(other_certificates
, ix
);
4270 if (_SecCertificateSetParent(certificate
, candidate
,
4271 signatureCheckOnly
))
4277 /* Lookup the parent of certificate in the keychain and set it. */
4278 static bool SecCertificateFindParent(SecCertificateRef certificate
) {
4279 /* FIXME: Search for things other than just subject of our issuer if we
4280 have a subjectID or authorityKeyIdentifier. */
4281 CFDataRef normalizedIssuer
=
4282 SecCertificateGetNormalizedIssuerContent(certificate
);
4283 const void *keys
[] = {
4290 kSecClassCertificate
,
4295 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
4296 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4298 OSStatus status
= SecItemCopyMatching(query
, &results
);
4301 secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
4305 CFArrayRef certs
= (CFArrayRef
)results
;
4306 /* Since we already know the certificates we are providing as candidates
4307 have been checked for subject matching, we can ask
4308 SecCertificateSetParentFrom to skip everything except the signature
4310 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
4315 OSStatus
SecCertificateCompleteChain(SecCertificateRef certificate
,
4316 CFArrayRef other_certificates
) {
4318 if (certificate
->_parent
== NULL
) {
4319 Boolean isSelfSigned
= false;
4320 OSStatus status
= SecCertificateIsSelfSigned(certificate
, &isSelfSigned
);
4321 if (!status
&& isSelfSigned
)
4322 return errSecSuccess
;
4323 if (!other_certificates
||
4324 !SecCertificateSetParentFrom(certificate
, other_certificates
,
4326 if (!SecCertificateFindParent(certificate
))
4327 return errSecIssuerNotFound
;
4330 certificate
= certificate
->_parent
;
4335 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4336 if (!certificate
->_subjectAltName
) {
4339 return &certificate
->_subjectAltName
->extnValue
;
4342 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4343 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4344 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4345 if (gnType
== GNT_IPAddress
) {
4346 CFStringRef string
= copyIPAddressContentDescription(
4347 kCFAllocatorDefault
, generalName
);
4349 CFArrayAppendValue(ipAddresses
, string
);
4352 return errSecInvalidCertificate
;
4355 return errSecSuccess
;
4358 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4359 /* These can only exist in the subject alt name. */
4360 if (!certificate
->_subjectAltName
)
4363 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4364 0, &kCFTypeArrayCallBacks
);
4365 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4366 ipAddresses
, appendIPAddressesFromGeneralNames
);
4367 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4368 CFRelease(ipAddresses
);
4374 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4375 const DERItem
*generalName
) {
4376 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4377 if (gnType
== GNT_DNSName
) {
4378 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4379 generalName
->data
, generalName
->length
,
4380 kCFStringEncodingUTF8
, FALSE
);
4382 CFArrayAppendValue(dnsNames
, string
);
4385 return errSecInvalidCertificate
;
4388 return errSecSuccess
;
4391 /* Return true if the passed in string matches the
4392 Preferred name syntax from sections 2.3.1. in RFC 1035.
4393 With the added check that we disallow empty dns names.
4394 Also in order to support wildcard DNSNames we allow for the '*'
4395 character anywhere in a dns component where we currently allow
4398 <domain> ::= <subdomain> | " "
4400 <subdomain> ::= <label> | <subdomain> "." <label>
4402 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4404 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4406 <let-dig-hyp> ::= <let-dig> | "-"
4408 <let-dig> ::= <letter> | <digit>
4410 <letter> ::= any one of the 52 alphabetic characters A through Z in
4411 upper case and a through z in lower case
4413 <digit> ::= any one of the ten digits 0 through 9
4415 static bool isDNSName(CFStringRef string
) {
4416 CFStringInlineBuffer buf
;
4417 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4418 /* From RFC 1035 2.3.4. Size limits:
4419 labels 63 octets or less
4420 names 255 octets or less */
4421 require_quiet(length
<= 255, notDNS
);
4422 CFRange range
= { 0, length
};
4423 CFStringInitInlineBuffer(string
, &buf
, range
);
4427 kDNSStateAfterAlpha
,
4428 kDNSStateAfterDigit
,
4430 } state
= kDNSStateInital
;
4432 for (ix
= 0; ix
< length
; ++ix
) {
4433 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4436 require_quiet(labelLength
<= 64 &&
4437 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4439 state
= kDNSStateAfterDot
;
4441 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4443 state
= kDNSStateAfterAlpha
;
4444 } else if ('0' <= ch
&& ch
<= '9') {
4446 /* The requirement for labels to start with a letter was
4447 dropped so we don't check this anymore. */
4448 require_quiet(state
== kDNSStateAfterAlpha
||
4449 state
== kDNSStateAfterDigit
||
4450 state
== kDNSStateAfterDash
, notDNS
);
4452 state
= kDNSStateAfterDigit
;
4453 } else if (ch
== '-') {
4454 require_quiet(state
== kDNSStateAfterAlpha
||
4455 state
== kDNSStateAfterDigit
||
4456 state
== kDNSStateAfterDash
, notDNS
);
4457 state
= kDNSStateAfterDash
;
4463 /* We don't allow a dns name to end in a dot or dash. */
4464 require_quiet(labelLength
<= 63 &&
4465 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4473 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4474 const DERItem
*value
, CFIndex rdnIX
) {
4475 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4476 if (DEROidCompare(type
, &oidCommonName
)) {
4477 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4480 if (isDNSName(string
)) {
4481 /* We found a common name that is formatted like a valid
4483 CFArrayAppendValue(dnsNames
, string
);
4487 return errSecInvalidCertificate
;
4490 return errSecSuccess
;
4493 /* Not everything returned by this function is going to be a proper DNS name,
4494 we also return the certificates common name entries from the subject,
4495 assuming they look like dns names as specified in RFC 1035. */
4496 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4497 /* These can exist in the subject alt name or in the subject. */
4498 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4499 0, &kCFTypeArrayCallBacks
);
4500 OSStatus status
= errSecSuccess
;
4501 if (certificate
->_subjectAltName
) {
4502 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4503 dnsNames
, appendDNSNamesFromGeneralNames
);
4505 /* RFC 2818 section 3.1. Server Identity
4507 If a subjectAltName extension of type dNSName is present, that MUST
4508 be used as the identity. Otherwise, the (most specific) Common Name
4509 field in the Subject field of the certificate MUST be used. Although
4510 the use of the Common Name is existing practice, it is deprecated and
4511 Certification Authorities are encouraged to use the dNSName instead.
4514 This implies that if we found 1 or more DNSNames in the
4515 subjectAltName, we should not use the Common Name of the subject as
4518 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4519 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4520 appendDNSNamesFromX501Name
);
4522 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4523 CFRelease(dnsNames
);
4529 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4530 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4531 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4532 if (gnType
== GNT_RFC822Name
) {
4533 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4534 generalName
->data
, generalName
->length
,
4535 kCFStringEncodingASCII
, FALSE
);
4537 CFArrayAppendValue(dnsNames
, string
);
4540 return errSecInvalidCertificate
;
4543 return errSecSuccess
;
4546 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4547 const DERItem
*value
, CFIndex rdnIX
) {
4548 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4549 if (DEROidCompare(type
, &oidEmailAddress
)) {
4550 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4553 CFArrayAppendValue(dnsNames
, string
);
4556 return errSecInvalidCertificate
;
4559 return errSecSuccess
;
4562 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4563 /* These can exist in the subject alt name or in the subject. */
4564 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4565 0, &kCFTypeArrayCallBacks
);
4566 OSStatus status
= errSecSuccess
;
4567 if (certificate
->_subjectAltName
) {
4568 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4569 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4572 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4573 appendRFC822NamesFromX501Name
);
4575 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4576 CFRelease(rfc822Names
);
4582 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4583 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4584 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4585 if (DEROidCompare(type
, &oidCommonName
)) {
4586 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4589 CFArrayAppendValue(commonNames
, string
);
4592 return errSecInvalidCertificate
;
4595 return errSecSuccess
;
4598 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4599 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4600 0, &kCFTypeArrayCallBacks
);
4602 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4603 appendCommonNamesFromX501Name
);
4604 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4605 CFRelease(commonNames
);
4611 static OSStatus
appendOrganizationFromX501Name(void *context
,
4612 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4613 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4614 if (DEROidCompare(type
, &oidOrganizationName
)) {
4615 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4618 CFArrayAppendValue(organization
, string
);
4621 return errSecInvalidCertificate
;
4624 return errSecSuccess
;
4627 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4628 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4629 0, &kCFTypeArrayCallBacks
);
4631 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4632 appendOrganizationFromX501Name
);
4633 if (status
|| CFArrayGetCount(organization
) == 0) {
4634 CFRelease(organization
);
4635 organization
= NULL
;
4637 return organization
;
4640 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4641 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4642 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4643 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4644 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4647 CFArrayAppendValue(organizationalUnit
, string
);
4650 return errSecInvalidCertificate
;
4653 return errSecSuccess
;
4656 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4657 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4658 0, &kCFTypeArrayCallBacks
);
4660 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4661 appendOrganizationalUnitFromX501Name
);
4662 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4663 CFRelease(organizationalUnit
);
4664 organizationalUnit
= NULL
;
4666 return organizationalUnit
;
4669 const SecCEBasicConstraints
*
4670 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4671 if (certificate
->_basicConstraints
.present
)
4672 return &certificate
->_basicConstraints
;
4677 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
4678 return (certificate
->_permittedSubtrees
);
4681 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
4682 return (certificate
->_excludedSubtrees
);
4685 const SecCEPolicyConstraints
*
4686 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4687 if (certificate
->_policyConstraints
.present
)
4688 return &certificate
->_policyConstraints
;
4694 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4695 return certificate
->_policyMappings
;
4698 const SecCECertificatePolicies
*
4699 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4700 if (certificate
->_certificatePolicies
.present
)
4701 return &certificate
->_certificatePolicies
;
4707 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4708 return certificate
->_inhibitAnyPolicySkipCerts
;
4711 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4712 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4713 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4714 if (gnType
== GNT_OtherName
) {
4716 DERReturn drtn
= DERParseSequenceContent(generalName
,
4717 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4719 require_noerr_quiet(drtn
, badDER
);
4720 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4722 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4723 &on
.value
, true), badDER
);
4724 CFArrayAppendValue(ntPrincipalNames
, string
);
4728 return errSecSuccess
;
4731 return errSecInvalidCertificate
;
4735 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
4736 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4737 0, &kCFTypeArrayCallBacks
);
4738 OSStatus status
= errSecSuccess
;
4739 if (certificate
->_subjectAltName
) {
4740 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4741 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4743 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4744 CFRelease(ntPrincipalNames
);
4745 ntPrincipalNames
= NULL
;
4747 return ntPrincipalNames
;
4750 static OSStatus
appendToRFC2253String(void *context
,
4751 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4752 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4756 ST stateOrProvinceName
4758 OU organizationalUnitName
4760 STREET streetAddress
4764 /* Prepend a + if this is not the first RDN in an RDN set.
4765 Otherwise prepend a , if this is not the first RDN. */
4767 CFStringAppend(string
, CFSTR("+"));
4768 else if (CFStringGetLength(string
)) {
4769 CFStringAppend(string
, CFSTR(","));
4772 CFStringRef label
, oid
= NULL
;
4773 /* @@@ Consider changing this to a dictionary lookup keyed by the
4774 decimal representation. */
4775 if (DEROidCompare(type
, &oidCommonName
)) {
4776 label
= CFSTR("CN");
4777 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4779 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4780 label
= CFSTR("ST");
4781 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4783 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4784 label
= CFSTR("OU");
4785 } else if (DEROidCompare(type
, &oidCountryName
)) {
4788 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4789 label
= CFSTR("STREET");
4790 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4791 label
= CFSTR("DC");
4792 } else if (DEROidCompare(type
, &oidUserID
)) {
4793 label
= CFSTR("UID");
4796 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4799 CFStringAppend(string
, label
);
4800 CFStringAppend(string
, CFSTR("="));
4801 CFStringRef raw
= NULL
;
4803 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4806 /* Append raw to string while escaping:
4807 a space or "#" character occurring at the beginning of the string
4808 a space character occurring at the end of the string
4809 one of the characters ",", "+", """, "\", "<", ">" or ";"
4811 CFStringInlineBuffer buffer
;
4812 CFIndex ix
, length
= CFStringGetLength(raw
);
4813 CFRange range
= { 0, length
};
4814 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4815 for (ix
= 0; ix
< length
; ++ix
) {
4816 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4818 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4819 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4820 ch
== '<' || ch
== '>' || ch
== ';' ||
4821 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4822 (ch
== '#' && ix
== 0)) {
4823 UniChar chars
[] = { '\\', ch
};
4824 CFStringAppendCharacters(string
, chars
, 2);
4826 CFStringAppendCharacters(string
, &ch
, 1);
4831 /* Append the value in hex. */
4832 CFStringAppend(string
, CFSTR("#"));
4834 for (ix
= 0; ix
< value
->length
; ++ix
)
4835 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4840 return errSecSuccess
;
4843 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
4844 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4845 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4846 if (status
|| CFStringGetLength(string
) == 0) {
4853 static OSStatus
appendToCompanyNameString(void *context
,
4854 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4855 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4856 if (CFStringGetLength(string
) != 0)
4857 return errSecSuccess
;
4859 if (!DEROidCompare(type
, &oidOrganizationName
))
4860 return errSecSuccess
;
4863 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4865 return errSecSuccess
;
4866 CFStringAppend(string
, raw
);
4869 return errSecSuccess
;
4872 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
4873 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4874 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4875 appendToCompanyNameString
);
4876 if (status
|| CFStringGetLength(string
) == 0) {
4883 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4884 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4885 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4886 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4888 CFDataSetLength(sequence
, sequence_length
);
4889 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4890 *sequence_ptr
++ = 0x30; /* ASN1_CONSTR_SEQUENCE */
4891 require_noerr_quiet(DEREncodeLength(content
->length
,
4892 sequence_ptr
, &seq_len_length
), out
);
4893 sequence_ptr
+= seq_len_length
;
4894 memcpy(sequence_ptr
, content
->data
, content
->length
);
4897 CFReleaseSafe(sequence
);
4901 CFDataRef
SecCertificateCopyIssuerSequence(
4902 SecCertificateRef certificate
) {
4903 return SecDERItemCopySequence(&certificate
->_issuer
);
4906 CFDataRef
SecCertificateCopySubjectSequence(
4907 SecCertificateRef certificate
) {
4908 return SecDERItemCopySequence(&certificate
->_subject
);
4911 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4912 SecCertificateRef certificate
) {
4913 return &certificate
->_algId
;
4916 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
4917 return &certificate
->_pubKeyDER
;
4921 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
4922 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
4924 SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
4926 SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
4929 const DERAlgorithmId
*algId
=
4930 SecCertificateGetPublicKeyAlgorithm(certificate
);
4931 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4932 const DERItem
*params
= NULL
;
4933 if (algId
->params
.length
!= 0) {
4934 params
= &algId
->params
;
4936 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
4937 SecAsn1Item params1
= {
4938 .Data
= params
? params
->data
: NULL
,
4939 .Length
= params
? params
->length
: 0
4941 SecAsn1Item keyData1
= {
4942 .Data
= keyData
? keyData
->data
: NULL
,
4943 .Length
= keyData
? keyData
->length
: 0
4945 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
4949 bool SecCertificateIsWeak(SecCertificateRef certificate
) {
4951 SecKeyRef pubKey
= NULL
;
4953 require_quiet(pubKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
4955 require_quiet(pubKey
= SecCertificateCopyPublicKey(certificate
) ,out
);
4957 size_t size
= SecKeyGetBlockSize(pubKey
);
4958 switch (SecKeyGetAlgorithmIdentifier(pubKey
)) {
4959 case kSecRSAAlgorithmID
:
4960 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
4962 case kSecECDSAAlgorithmID
:
4963 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
4970 CFReleaseSafe(pubKey
);
4974 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
4975 if (!certificate
->_sha1Digest
) {
4976 certificate
->_sha1Digest
=
4977 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4978 certificate
->_der
.data
, certificate
->_der
.length
);
4981 return certificate
->_sha1Digest
;
4984 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
4985 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4986 certificate
->_der
.data
, certificate
->_der
.length
);
4989 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
4990 CFDataRef digest
= NULL
;
4991 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
4993 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4994 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
5000 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
5001 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
5002 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
5005 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
5006 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
5007 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
5010 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
5015 CFRetainSafe(certificate
->_keychain_item
);
5016 return certificate
->_keychain_item
;
5019 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
5020 if (!certificate
->_authorityKeyID
&&
5021 certificate
->_authorityKeyIdentifier
.length
) {
5022 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
5023 certificate
->_authorityKeyIdentifier
.data
,
5024 certificate
->_authorityKeyIdentifier
.length
);
5027 return certificate
->_authorityKeyID
;
5030 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
5031 if (!certificate
->_subjectKeyID
&&
5032 certificate
->_subjectKeyIdentifier
.length
) {
5033 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5034 certificate
->_subjectKeyIdentifier
.data
,
5035 certificate
->_subjectKeyIdentifier
.length
);
5038 return certificate
->_subjectKeyID
;
5041 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5042 return certificate
->_crlDistributionPoints
;
5045 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5046 return certificate
->_ocspResponders
;
5049 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5050 return certificate
->_caIssuers
;
5053 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5054 return certificate
->_subjectAltName
&&
5055 certificate
->_subjectAltName
->critical
;
5058 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5059 /* Since the _subject field is the content of the subject and not the
5060 whole thing, we can simply check for a 0 length subject here. */
5061 return certificate
->_subject
.length
!= 0;
5064 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5065 return certificate
->_foundUnknownCriticalExtension
;
5068 /* Private API functions. */
5069 void SecCertificateShow(SecCertificateRef certificate
) {
5071 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5072 fprintf(stderr
, "\n");
5076 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5077 SecCertificateRef certificate
) {
5078 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5079 CFNumberRef certificateType
, certificateEncoding
;
5080 CFStringRef label
, alias
;
5081 CFDataRef skid
, pubKeyDigest
, certData
;
5082 CFDictionaryRef dict
= NULL
;
5086 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5087 SInt32 ctv
= certificate
->_version
+ 1;
5088 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5089 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5090 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5091 certData
= SecCertificateCopyData(certificate
);
5092 skid
= SecCertificateGetSubjectKeyID(certificate
);
5093 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5094 certificate
->_pubKeyDER
.length
);
5096 /* We still need to figure out how to deal with multi valued attributes. */
5097 alias
= SecCertificateCopyRFC822Names(certificate
);
5098 label
= SecCertificateCopySubjectSummary(certificate
);
5104 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5105 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5106 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5108 DICT_ADDPAIR(kSecAttrLabel
, label
);
5110 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5111 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5112 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5113 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5115 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5116 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5117 DICT_ADDPAIR(kSecValueData
, certData
);
5118 dict
= DICT_CREATE(allocator
);
5120 CFReleaseSafe(label
);
5121 CFReleaseSafe(pubKeyDigest
);
5122 CFReleaseSafe(certData
);
5123 CFReleaseSafe(certificateEncoding
);
5124 CFReleaseSafe(certificateType
);
5129 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5130 CFDictionaryRef refAttributes
) {
5131 /* @@@ Support having an allocator in refAttributes. */
5132 CFAllocatorRef allocator
= NULL
;
5133 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5134 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5138 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5139 if (!certificate
|| (CFGetTypeID(certificate
) != SecCertificateGetTypeID())) {
5140 return errSecInvalidCertificate
;
5142 if (!isSelfSigned
) {
5146 // %%% TBD: IsSelfSigned doesn't require basicConstraints like IsSelfSignedCA,
5147 // which is actually what we want here. Probably need a separate version
5148 // of this function to do the signature comparison, and have the basicConstraints
5149 // check be implemented only in IsSelfSignedCA.
5151 if (certificate
->_isSelfSigned
== 0) {
5152 certificate
->_isSelfSigned
=
5153 (SecCertificateIsIssuedBy(certificate
, certificate
, 0) ?
5156 *isSelfSigned
= (certificate
->_isSelfSigned
== 1);
5158 *isSelfSigned
= SecCertificateIsSelfSignedCA(certificate
);
5160 return errSecSuccess
;
5163 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5164 bool result
= false;
5165 SecKeyRef publicKey
= NULL
;
5167 require(publicKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
5169 require(publicKey
= SecCertificateCopyPublicKey(certificate
), out
);
5171 CFDataRef normalizedIssuer
=
5172 SecCertificateGetNormalizedIssuerContent(certificate
);
5173 CFDataRef normalizedSubject
=
5174 SecCertificateGetNormalizedSubjectContent(certificate
);
5175 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5176 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5178 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5179 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5180 if (authorityKeyID
) {
5181 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5184 if (SecCertificateVersion(certificate
) >= 3) {
5185 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5186 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
5187 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5192 CFReleaseSafe(publicKey
);
5196 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5197 return certificate
->_keyUsage
;
5200 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5202 CFMutableArrayRef extended_key_usage_oids
=
5203 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5204 require_quiet(extended_key_usage_oids
, out
);
5206 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5207 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5208 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5209 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5212 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5213 require_noerr_quiet(drtn
, out
);
5214 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5215 DERDecodedInfo currDecoded
;
5217 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5218 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5219 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5220 currDecoded
.content
.data
, currDecoded
.content
.length
);
5222 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5226 require_quiet(drtn
== DR_EndOfSequence
, out
);
5227 return extended_key_usage_oids
;
5231 CFReleaseSafe(extended_key_usage_oids
);
5235 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5239 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5240 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5241 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5242 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5243 /* Got the SCT oid */
5244 DERDecodedInfo sctList
;
5245 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5246 require_noerr_quiet(drtn
, out
);
5247 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5248 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5256 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5257 if (der
.length
> 1) {
5258 DERDecodedInfo decoded
;
5259 DERDecodeItem(&der
, &decoded
);
5260 switch (decoded
.tag
) {
5263 return decoded
.content
.length
== 0 && expected
== NULL
;
5267 case ASN1_UTF8_STRING
: {
5268 if (isString(expected
)) {
5269 CFStringRef expectedString
= (CFStringRef
) expected
;
5270 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5272 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5273 CFReleaseNull(itemString
);
5279 case ASN1_OCTET_STRING
: {
5280 if (isData(expected
)) {
5281 CFDataRef expectedData
= (CFDataRef
) expected
;
5282 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5284 bool result
= CFEqual(expectedData
, itemData
);
5285 CFReleaseNull(itemData
);
5291 case ASN1_INTEGER
: {
5292 SInt32 expected_value
= 0;
5293 if (isString(expected
))
5295 CFStringRef aStr
= (CFStringRef
)expected
;
5296 expected_value
= CFStringGetIntValue(aStr
);
5298 else if (isNumber(expected
))
5300 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5303 uint32_t num_value
= 0;
5304 if (!DERParseInteger(&decoded
.content
, &num_value
))
5306 return ((uint32_t)expected_value
== num_value
);
5319 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5322 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5323 size_t oid_len
= CFDataGetLength(oid
);
5325 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5326 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5327 if (extn
->extnID
.length
== oid_len
5328 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5330 return matches_expected(extn
->extnValue
, expectedValue
);
5336 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5338 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5341 struct search_context
{
5343 SecCertificateRef certificate
;
5346 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5348 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5349 bool result
= false;
5351 if ( CFStringGetLength(string
) > 0
5352 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5355 *value
= CFStringGetIntValue(string
);
5359 CFReleaseNull(nonDecimalDigit
);
5364 static CFDataRef
CreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
5366 CFMutableDataRef currentResult
= NULL
;
5367 CFDataRef encodedResult
= NULL
;
5369 CFArrayRef parts
= NULL
;
5375 parts
= CFStringCreateArrayBySeparatingStrings(NULL
, string
, CFSTR("."));
5380 count
= CFArrayGetCount(parts
);
5384 // assume no more than 5 bytes needed to represent any part of the oid,
5385 // since we limit parts to 32-bit values,
5386 // but the first two parts only need 1 byte
5387 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
5393 part
= CFArrayGetValueAtIndex(parts
, 0);
5395 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
5402 part
= CFArrayGetValueAtIndex(parts
, 1);
5404 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
5410 CFDataAppendBytes(currentResult
, &firstByte
, 1);
5412 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
5413 uint8_t b
[5] = {0, 0, 0, 0, 0};
5415 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
5416 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
5417 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
5418 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
5420 // Skip the unused extension bytes.
5421 size_t skipBytes
= 0;
5422 while (b
[skipBytes
] == 0x80)
5425 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
5428 encodedResult
= currentResult
;
5429 currentResult
= NULL
;
5432 CFReleaseNull(parts
);
5433 CFReleaseNull(currentResult
);
5435 return encodedResult
;
5438 static void check_for_marker(const void *key
, const void *value
, void *context
)
5440 struct search_context
* search_ctx
= (struct search_context
*) context
;
5441 CFStringRef key_string
= (CFStringRef
) key
;
5442 CFTypeRef value_ref
= (CFTypeRef
) value
;
5444 // If we could have short circuted the iteration
5445 // we would have, but the best we can do
5446 // is not waste time comparing once a match
5448 if (search_ctx
->found
)
5451 if (CFGetTypeID(key_string
) != CFStringGetTypeID())
5454 CFDataRef key_data
= CreateOidDataFromString(NULL
, key_string
);
5456 if (NULL
== key_data
)
5459 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
5460 search_ctx
->found
= true;
5462 CFReleaseNull(key_data
);
5466 // CFType Ref is either:
5468 // CFData - OID to match with no data permitted
5469 // CFDictionary - OID -> Value table for expected values Single Object or Array
5470 // CFArray - Array of the above.
5472 // This returns true if any of the requirements are met.
5473 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
5475 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
5476 CFIndex ix
, length
= CFArrayGetCount(oids
);
5477 for (ix
= 0; ix
< length
; ix
++)
5478 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
5480 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
5481 struct search_context context
= { .found
= false, .certificate
= certificate
};
5482 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
5483 return context
.found
;
5484 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
5485 return cert_contains_marker_extension(certificate
, oids
);
5490 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
5491 CFDataRef pem_certificate
)
5493 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
5494 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
5495 uint8_t *base64_data
= NULL
;
5496 SecCertificateRef cert
= NULL
;
5497 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
5498 //const size_t length = CFDataGetLength(pem_certificate);
5499 char *begin
= strstr((const char *)data
, begin_cert
);
5500 char *end
= strstr((const char *)data
, end_cert
);
5503 begin
+= sizeof(begin_cert
) - 1;
5504 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
5505 if (base64_length
) {
5506 require_quiet(base64_data
= calloc(1, base64_length
), out
);
5507 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
5508 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
5517 // -- MARK -- XPC encoding/decoding
5520 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5522 return true; // NOOP
5524 size_t length
= SecCertificateGetLength(certificate
);
5525 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
5526 #if SECTRUST_VERBOSE_DEBUG
5527 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
5529 if (!length
|| !bytes
) {
5530 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
5532 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
5536 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
5537 SecCertificateRef certificate
= NULL
;
5539 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
5541 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
5544 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
5549 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
5550 xpc_object_t xpc_certificates
;
5551 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
5552 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
5553 CFIndex ix
, count
= CFArrayGetCount(certificates
);
5554 for (ix
= 0; ix
< count
; ++ix
) {
5555 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
5556 #if SECTRUST_VERBOSE_DEBUG
5557 CFIndex length
= SecCertificateGetLength(certificate
);
5558 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
5559 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
);
5561 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
5562 xpc_release(xpc_certificates
);
5563 xpc_certificates
= NULL
;
5569 return xpc_certificates
;
5572 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5573 CFMutableArrayRef certificates
= NULL
;
5574 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
5575 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
5576 size_t count
= xpc_array_get_count(xpc_certificates
);
5577 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
5578 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
5581 for (ix
= 0; ix
< count
; ++ix
) {
5582 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
5584 CFRelease(certificates
);
5587 CFArraySetValueAtIndex(certificates
, ix
, cert
);
5592 return certificates
;
5595 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
5598 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
5600 __block CFArrayRef result
= NULL
;
5602 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
5604 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
5605 ^bool(xpc_object_t message
, CFErrorRef
*error
)
5607 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
5610 ^bool(xpc_object_t response
, CFErrorRef
*error
)
5612 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
5614 if (response
&& (NULL
!= xpc_array
))
5616 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
5620 return SecError(errSecInternal
, error
, CFSTR("Did not get the Escrow certificates"));
5622 return result
!= NULL
;
5627 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
5629 CFArrayRef result
= NULL
;
5631 CFDataRef certData
= NULL
;
5634 // The request is for the base line certificates.
5635 // Use the hard coded data to generate the return array
5636 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
5637 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
)
5639 // Get the hard coded set of roots
5640 numRoots
= (kSecCertificateBaselineEscrowRoot
== escrowRootType
) ?
5641 kNumberOfBaseLineEscrowRoots
:
5642 kNumberOfBaseLinePCSEscrowRoots
;
5643 SecCertificateRef baseLineCerts
[numRoots
];
5644 struct RootRecord
** pEscrowRoots
= kBaseLineEscrowRoots
;
5645 struct RootRecord
* pRootRecord
= NULL
;
5647 if (kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
) {
5648 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
5651 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5653 pRootRecord
= pEscrowRoots
[iCnt
];
5654 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
)
5656 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
5657 if (NULL
!= certData
)
5659 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5660 CFRelease(certData
);
5664 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5665 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5667 if (NULL
!= baseLineCerts
[iCnt
])
5669 CFRelease(baseLineCerts
[iCnt
]);
5673 // The request is for the current certificates.
5676 CFErrorRef error
= NULL
;
5677 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
5678 if (NULL
!= error
|| NULL
== cert_datas
)
5685 if (NULL
!= cert_datas
)
5687 CFRelease(cert_datas
);
5692 numRoots
= (int)(CFArrayGetCount(cert_datas
));
5694 SecCertificateRef assetCerts
[numRoots
];
5695 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5697 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
5698 if (NULL
!= certData
)
5700 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5701 assetCerts
[iCnt
] = aCertRef
;
5705 assetCerts
[iCnt
] = NULL
;
5711 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5712 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5714 if (NULL
!= assetCerts
[iCnt
])
5716 CFRelease(assetCerts
[iCnt
]);
5720 CFReleaseSafe(cert_datas
);
5725 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
5727 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
5728 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
5729 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
5731 if (!algOid
->data
|| !algOid
->length
) {
5734 /* classify the signature algorithm OID into one of our known types */
5735 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
5736 DEROidCompare(algOid
, &oidSha512Rsa
) ||
5737 DEROidCompare(algOid
, &oidSha512
)) {
5738 result
= kSecSignatureHashAlgorithmSHA512
;
5741 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
5742 DEROidCompare(algOid
, &oidSha384Rsa
) ||
5743 DEROidCompare(algOid
, &oidSha384
)) {
5744 result
= kSecSignatureHashAlgorithmSHA384
;
5747 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
5748 DEROidCompare(algOid
, &oidSha256Rsa
) ||
5749 DEROidCompare(algOid
, &oidSha256
)) {
5750 result
= kSecSignatureHashAlgorithmSHA256
;
5753 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
5754 DEROidCompare(algOid
, &oidSha224Rsa
) ||
5755 DEROidCompare(algOid
, &oidSha224
)) {
5756 result
= kSecSignatureHashAlgorithmSHA224
;
5759 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
5760 DEROidCompare(algOid
, &oidSha1Rsa
) ||
5761 DEROidCompare(algOid
, &oidSha1Dsa
) ||
5762 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
5763 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
5764 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
5765 DEROidCompare(algOid
, &oidSha1Fee
) ||
5766 DEROidCompare(algOid
, &oidSha1
)) {
5767 result
= kSecSignatureHashAlgorithmSHA1
;
5770 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
5771 DEROidCompare(algOid
, &oidMd5Fee
) ||
5772 DEROidCompare(algOid
, &oidMd5
)) {
5773 result
= kSecSignatureHashAlgorithmMD5
;
5776 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
5777 DEROidCompare(algOid
, &oidMd4
)) {
5778 result
= kSecSignatureHashAlgorithmMD4
;
5781 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
5782 DEROidCompare(algOid
, &oidMd2
)) {
5783 result
= kSecSignatureHashAlgorithmMD2
;