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 #include "SecCertificateInternalP.h"
31 #include <CommonCrypto/CommonDigest.h>
32 #include <CoreFoundation/CFRuntime.h>
33 #include <CoreFoundation/CFString.h>
34 #include <CoreFoundation/CFBundle.h>
35 #include <CoreFoundation/CFDictionary.h>
36 #include <CoreFoundation/CFNumber.h>
37 #include <CoreFoundation/CFTimeZone.h>
40 #include <AssertMacros.h>
41 #include <libDER/libDER.h>
42 #include <libDER/DER_CertCrl.h>
43 #include <libDER/DER_Encode.h>
44 #include <libDER/DER_Keys.h>
45 #include <libDER/asn1Types.h>
46 #include <libDER/oidsPriv.h>
48 #include "SecBasePriv.h"
50 #include "SecRSAKeyP.h"
51 #include "SecFrameworkP.h"
53 #include "SecItemPriv.h"
56 #include <libkern/OSByteOrder.h>
58 #include "SecInternal.h"
59 #include "SecBase64P.h"
61 #include <security_utilities/debugging.h>
63 typedef struct SecCertificateExtension
{
67 } SecCertificateExtension
;
70 typedef struct KnownExtension
{
76 kSecSelfSignedUnknown
= 0,
82 struct __SecCertificate
{
85 DERItem _der
; /* Entire certificate in DER form. */
86 DERItem _tbs
; /* To Be Signed cert DER bytes. */
87 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
88 DERItem _signature
; /* The content of the sig bit string. */
91 DERItem _serialNum
; /* Integer. */
92 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
93 DERItem _issuer
; /* Sequence of RDN. */
94 CFAbsoluteTime _notBefore
;
95 CFAbsoluteTime _notAfter
;
96 DERItem _subject
; /* Sequence of RDN. */
97 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
98 DERItem _pubKeyDER
; /* contents of bit string */
99 DERItem _issuerUniqueID
; /* bit string, optional */
100 DERItem _subjectUniqueID
; /* bit string, optional */
103 /* Known extensions if the certificate contains them,
104 extnValue.length will be > 0. */
105 KnownExtension _authorityKeyID
;
107 /* This extension is used to uniquely identify a certificate from among
108 several that have the same subject name. If the extension is not
109 present, its value is calculated by performing a SHA-1 hash of the
110 certificate's DER encoded subjectPublicKeyInfo, as recommended by
112 KnownExtension _subjectKeyID
;
113 KnownExtension _keyUsage
;
114 KnownExtension _extendedKeyUsage
;
115 KnownExtension _basicConstraints
;
116 KnownExtension _netscapeCertType
;
117 KnownExtension _subjectAltName
;
118 KnownExtension _qualCertStatements
;
121 bool _foundUnknownCriticalExtension
;
123 /* Well known certificate extensions. */
124 SecCEBasicConstraints _basicConstraints
;
125 SecCEPolicyConstraints _policyConstraints
;
126 CFDictionaryRef _policyMappings
;
127 SecCECertificatePolicies _certificatePolicies
;
129 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
130 value of the SkipCerts field of the InhibitAnyPolicy extension
132 uint32_t _inhibitAnyPolicySkipCerts
;
134 /* If KeyUsage extension is not present this is 0, otherwise it's
135 the value of the extension. */
136 SecKeyUsage _keyUsage
;
138 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
139 Length = 0 if not present. */
140 DERItem _subjectKeyIdentifier
;
142 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
143 Length = 0 if not present. */
144 DERItem _authorityKeyIdentifier
;
145 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
146 _authorityKeyIdentifierSerialNumber have non zero length if present.
147 Both are either present or absent together. */
148 DERItem _authorityKeyIdentifierIssuer
;
149 DERItem _authorityKeyIdentifierSerialNumber
;
151 /* Subject alt name extension, if present. Not malloced, it's just a
152 pointer to an element in the _extensions array. */
153 const SecCertificateExtension
*_subjectAltName
;
155 /* Parsed extension values. */
157 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
158 CFMutableArrayRef _crlDistributionPoints
;
160 /* Array of CFURLRefs containing the URI values of accessLocations of each
161 id-ad-ocsp AccessDescription in the Authority Information Access
163 CFMutableArrayRef _ocspResponders
;
165 /* Array of CFURLRefs containing the URI values of accessLocations of each
166 id-ad-caIssuers AccessDescription in the Authority Information Access
168 CFMutableArrayRef _caIssuers
;
170 /* All other (non known) extensions. The _extensions array is malloced. */
171 CFIndex _extensionCount
;
172 SecCertificateExtension
*_extensions
;
174 /* Optional cached fields. */
177 CFArrayRef _properties
;
178 CFDataRef _serialNumber
;
179 CFDataRef _normalizedIssuer
;
180 CFDataRef _normalizedSubject
;
181 CFDataRef _authorityKeyID
;
182 CFDataRef _subjectKeyID
;
184 CFDataRef _sha1Digest
;
185 uint8_t _isSelfSigned
;
189 /* Public Constants for property list keys. */
190 const CFStringRef kSecPropertyKeyType
= CFSTR("type");
191 const CFStringRef kSecPropertyKeyLabel
= CFSTR("label");
192 const CFStringRef kSecPropertyKeyLocalizedLabel
= CFSTR("localized label");
193 const CFStringRef kSecPropertyKeyValue
= CFSTR("value");
195 /* Public Constants for property list values. */
196 const CFStringRef kSecPropertyTypeWarning
= CFSTR("warning");
197 const CFStringRef kSecPropertyTypeError
= CFSTR("error");
198 const CFStringRef kSecPropertyTypeSuccess
= CFSTR("success");
199 const CFStringRef kSecPropertyTypeTitle
= CFSTR("title");
200 const CFStringRef kSecPropertyTypeSection
= CFSTR("section");
201 const CFStringRef kSecPropertyTypeData
= CFSTR("data");
202 const CFStringRef kSecPropertyTypeString
= CFSTR("string");
203 const CFStringRef kSecPropertyTypeURL
= CFSTR("url");
204 const CFStringRef kSecPropertyTypeDate
= CFSTR("date");
206 /* Extension parsing routine. */
207 typedef void (*SecCertificateExtensionParser
)(SecCertificateRefP certificate
,
208 const SecCertificateExtension
*extn
);
210 /* CFRuntime regsitration data. */
211 static pthread_once_t kSecCertificateRegisterClass
= PTHREAD_ONCE_INIT
;
212 static CFTypeID kSecCertificateTypeID
= _kCFRuntimeNotATypeID
;
214 /* Mapping from extension OIDs (as a DERItem *) to
215 SecCertificateExtensionParser extension parsing routines. */
216 static CFDictionaryRef gExtensionParsers
;
218 /* Forward declartions of static functions. */
219 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
);
220 static void SecCertificateDestroy(CFTypeRef cf
);
221 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
222 CFAbsoluteTime
*absTime
);
224 /* Static functions. */
225 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
) {
226 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
227 CFStringRef ret
= NULL
;
228 CFStringRef subjectSummary
= SecCertificateCopySubjectSummaryP(certificate
);
229 CFStringRef issuerSummary
= SecCertificateCopyIssuerSummaryP(certificate
);
231 ret
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
232 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
,
236 CFReleaseNull(subjectSummary
);
237 CFReleaseNull(issuerSummary
);
241 static void SecCertificateDestroy(CFTypeRef cf
) {
242 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
243 if (certificate
->_certificatePolicies
.policies
) {
244 free(certificate
->_certificatePolicies
.policies
);
245 certificate
->_certificatePolicies
.policies
= NULL
;
247 CFReleaseNull(certificate
->_policyMappings
);
248 CFReleaseNull(certificate
->_crlDistributionPoints
);
249 CFReleaseNull(certificate
->_ocspResponders
);
250 CFReleaseNull(certificate
->_caIssuers
);
251 if (certificate
->_extensions
) {
252 free(certificate
->_extensions
);
253 certificate
->_extensions
= NULL
;
255 CFReleaseNull(certificate
->_pubKey
);
256 CFReleaseNull(certificate
->_der_data
);
257 CFReleaseNull(certificate
->_properties
);
258 CFReleaseNull(certificate
->_serialNumber
);
259 CFReleaseNull(certificate
->_normalizedIssuer
);
260 CFReleaseNull(certificate
->_normalizedSubject
);
261 CFReleaseNull(certificate
->_authorityKeyID
);
262 CFReleaseNull(certificate
->_subjectKeyID
);
263 CFReleaseNull(certificate
->_sha1Digest
);
266 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
267 SecCertificateRefP cert1
= (SecCertificateRefP
)cf1
;
268 SecCertificateRefP cert2
= (SecCertificateRefP
)cf2
;
271 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
273 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
276 /* Hash of the certificate is der length + signature length + last 4 bytes
278 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
279 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
280 DERSize der_length
= certificate
->_der
.length
;
281 DERSize sig_length
= certificate
->_signature
.length
;
282 DERSize ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
283 CFHashCode hashCode
= 0;
284 for (; ix
< sig_length
; ++ix
)
285 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
287 return (hashCode
+ der_length
+ sig_length
);
292 /************************************************************************/
293 /************************* General Name Parsing *************************/
294 /************************************************************************/
296 typedef OSStatus (*parseGeneralNameCallback
)(void *context
,
297 SecCEGeneralNameType type
, const DERItem
*value
);
301 GeneralName ::= CHOICE {
302 otherName [0] OtherName,
303 rfc822Name [1] IA5String,
304 dNSName [2] IA5String,
305 x400Address [3] ORAddress,
306 directoryName [4] Name,
307 ediPartyName [5] EDIPartyName,
308 uniformResourceIdentifier [6] IA5String,
309 iPAddress [7] OCTET STRING,
310 registeredID [8] OBJECT IDENTIFIER}
312 OtherName ::= SEQUENCE {
313 type-id OBJECT IDENTIFIER,
314 value [0] EXPLICIT ANY DEFINED BY type-id }
316 EDIPartyName ::= SEQUENCE {
317 nameAssigner [0] DirectoryString OPTIONAL,
318 partyName [1] DirectoryString }
320 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
321 const DERItem
*generalNameContent
,
322 void *context
, parseGeneralNameCallback callback
) {
324 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
325 return callback(context
, GNT_OtherName
, generalNameContent
);
326 case ASN1_CONTEXT_SPECIFIC
| 1:
327 return callback(context
, GNT_RFC822Name
, generalNameContent
);
328 case ASN1_CONTEXT_SPECIFIC
| 2:
329 return callback(context
, GNT_DNSName
, generalNameContent
);
330 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
331 return callback(context
, GNT_X400Address
, generalNameContent
);
332 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
333 return callback(context
, GNT_DirectoryName
, generalNameContent
);
334 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
335 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
336 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
338 /* Technically I don't think this is valid, but there are certs out
339 in the wild that use a constructed IA5String. In particular the
340 VeriSign Time Stamping Authority CA.cer does this. */
341 DERDecodedInfo uriContent
;
342 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
343 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
344 return callback(context
, GNT_URI
, &uriContent
.content
);
346 case ASN1_CONTEXT_SPECIFIC
| 6:
347 return callback(context
, GNT_URI
, generalNameContent
);
348 case ASN1_CONTEXT_SPECIFIC
| 7:
349 return callback(context
, GNT_IPAddress
, generalNameContent
);
350 case ASN1_CONTEXT_SPECIFIC
| 8:
351 return callback(context
, GNT_RegisteredID
, generalNameContent
);
356 return errSecInvalidCertificate
;
359 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
360 void *context
, parseGeneralNameCallback callback
) {
362 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
363 require_noerr_quiet(drtn
, badDER
);
364 DERDecodedInfo generalNameContent
;
365 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
367 OSStatus status
= parseGeneralNameContentProperty(
368 generalNameContent
.tag
, &generalNameContent
.content
, context
,
373 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
374 return errSecSuccess
;
377 return errSecInvalidCertificate
;
380 static OSStatus
parseGeneralNames(const DERItem
*generalNames
, void *context
,
381 parseGeneralNameCallback callback
) {
382 DERDecodedInfo generalNamesContent
;
383 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
384 require_noerr_quiet(drtn
, badDER
);
385 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
386 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
389 return errSecInvalidCertificate
;
395 GeneralName ::= CHOICE {
396 otherName [0] OtherName,
397 rfc822Name [1] IA5String,
398 dNSName [2] IA5String,
399 x400Address [3] ORAddress,
400 directoryName [4] Name,
401 ediPartyName [5] EDIPartyName,
402 uniformResourceIdentifier [6] IA5String,
403 iPAddress [7] OCTET STRING,
404 registeredID [8] OBJECT IDENTIFIER}
406 EDIPartyName ::= SEQUENCE {
407 nameAssigner [0] DirectoryString OPTIONAL,
408 partyName [1] DirectoryString }
410 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
411 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
413 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
414 generalName
->nameType
= GNT_OtherName
;
415 generalName
->berEncoded
= true;
416 generalName
->name
= *generalNameContent
;
418 case ASN1_CONTEXT_SPECIFIC
| 1:
420 generalName
->nameType
= GNT_RFC822Name
;
421 generalName
->berEncoded
= false;
422 generalName
->name
= *generalNameContent
;
424 case ASN1_CONTEXT_SPECIFIC
| 2:
426 generalName
->nameType
= GNT_DNSName
;
427 generalName
->berEncoded
= false;
428 generalName
->name
= *generalNameContent
;
430 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
431 generalName
->nameType
= GNT_X400Address
;
432 generalName
->berEncoded
= true;
433 generalName
->name
= *generalNameContent
;
435 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
436 generalName
->nameType
= GNT_DirectoryName
;
437 generalName
->berEncoded
= true;
438 generalName
->name
= *generalNameContent
;
440 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
441 generalName
->nameType
= GNT_EdiPartyName
;
442 generalName
->berEncoded
= true;
443 generalName
->name
= *generalNameContent
;
445 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
447 /* Technically I don't think this is valid, but there are certs out
448 in the wild that use a constructed IA5String. In particular the
449 VeriSign Time Stamping Authority CA.cer does this. */
450 DERDecodedInfo decoded
;
451 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
452 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
453 generalName
->nameType
= GNT_URI
;
454 generalName
->berEncoded
= false;
455 generalName
->name
= decoded
.content
;
458 case ASN1_CONTEXT_SPECIFIC
| 6:
459 generalName
->nameType
= GNT_URI
;
460 generalName
->berEncoded
= false;
461 generalName
->name
= *generalNameContent
;
463 case ASN1_CONTEXT_SPECIFIC
| 7:
464 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
465 8 octects, addr/mask for ipv6 it's 32. */
466 generalName
->nameType
= GNT_IPAddress
;
467 generalName
->berEncoded
= false;
468 generalName
->name
= *generalNameContent
;
470 case ASN1_CONTEXT_SPECIFIC
| 8:
471 /* name is the content of an OID. */
472 generalName
->nameType
= GNT_RegisteredID
;
473 generalName
->berEncoded
= false;
474 generalName
->name
= *generalNameContent
;
480 return errSecSuccess
;
482 return errSecInvalidCertificate
;
486 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
488 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
489 CFIndex
*count
, SecCEGeneralName
**name
) {
490 SecCEGeneralName
*generalNames
= NULL
;
492 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
493 require_noerr_quiet(drtn
, badDER
);
494 DERDecodedInfo generalNameContent
;
495 CFIndex generalNamesCount
= 0;
496 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
500 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
502 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
504 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
506 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
508 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
509 &generalNameContent
.content
, &generalNames
[ix
])) {
514 *count
= generalNamesCount
;
515 *name
= generalNames
;
516 return errSecSuccess
;
521 return errSecInvalidCertificate
;
524 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
525 CFIndex
*count
, SecCEGeneralName
**name
) {
526 DERDecodedInfo generalNamesContent
;
527 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
528 require_noerr_quiet(drtn
, badDER
);
529 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
531 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
532 return errSecSuccess
;
534 return errSecInvalidCertificate
;
538 /************************************************************************/
539 /************************** X.509 Name Parsing **************************/
540 /************************************************************************/
542 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
543 const DERItem
*value
, CFIndex rdnIX
);
545 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
546 parseX501NameCallback callback
) {
548 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
549 require_noerr_quiet(drtn
, badDER
);
550 DERDecodedInfo atvContent
;
552 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
553 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
554 DERAttributeTypeAndValue atv
;
555 drtn
= DERParseSequenceContent(&atvContent
.content
,
556 DERNumAttributeTypeAndValueItemSpecs
,
557 DERAttributeTypeAndValueItemSpecs
,
559 require_noerr_quiet(drtn
, badDER
);
560 require_quiet(atv
.type
.length
!= 0, badDER
);
561 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
565 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
567 return errSecSuccess
;
569 return errSecInvalidCertificate
;
572 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
573 parseX501NameCallback callback
) {
575 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
576 require_noerr_quiet(drtn
, badDER
);
577 DERDecodedInfo currDecoded
;
578 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
579 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
580 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
585 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
587 return errSecSuccess
;
590 return errSecInvalidCertificate
;
593 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
594 parseX501NameCallback callback
) {
595 DERDecodedInfo x501NameContent
;
596 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
597 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
598 return errSecInvalidCertificate
;
600 return parseX501NameContent(&x501NameContent
.content
, context
,
605 /************************************************************************/
606 /********************** Extension Parsing Routines **********************/
607 /************************************************************************/
609 static void SecCEPSubjectKeyIdentifier(SecCertificateRefP certificate
,
610 const SecCertificateExtension
*extn
) {
611 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
612 DERDecodedInfo keyIdentifier
;
613 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
614 require_noerr_quiet(drtn
, badDER
);
615 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
616 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
620 secinfo("cert", "Invalid SubjectKeyIdentifier Extension");
623 static void SecCEPKeyUsage(SecCertificateRefP certificate
,
624 const SecCertificateExtension
*extn
) {
625 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
626 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
627 DERDecodedInfo bitStringContent
;
628 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
629 require_noerr_quiet(drtn
, badDER
);
630 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
631 DERSize len
= bitStringContent
.content
.length
- 1;
632 require_quiet(len
== 1 || len
== 2, badDER
);
633 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
634 require_quiet(numUnusedBits
< 8, badDER
);
635 /* Flip the bits in the bit string so the first bit in the lsb. */
636 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
637 uint_fast16_t value
= bitStringContent
.content
.data
[1];
640 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
646 for (ix
= 0; ix
< bits
; ++ix
) {
652 certificate
->_keyUsage
= keyUsage
;
655 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
658 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRefP certificate
,
659 const SecCertificateExtension
*extn
) {
660 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
663 static void SecCEPSubjectAltName(SecCertificateRefP certificate
,
664 const SecCertificateExtension
*extn
) {
665 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
666 certificate
->_subjectAltName
= extn
;
669 static void SecCEPIssuerAltName(SecCertificateRefP certificate
,
670 const SecCertificateExtension
*extn
) {
671 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
674 static void SecCEPBasicConstraints(SecCertificateRefP certificate
,
675 const SecCertificateExtension
*extn
) {
676 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
677 DERBasicConstraints basicConstraints
;
678 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
679 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
680 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
681 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
682 &certificate
->_basicConstraints
.isCA
), badDER
);
683 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
684 require_noerr_quiet(DERParseInteger(
685 &basicConstraints
.pathLenConstraint
,
686 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
687 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
689 certificate
->_basicConstraints
.present
= true;
690 certificate
->_basicConstraints
.critical
= extn
->critical
;
693 certificate
->_basicConstraints
.present
= false;
694 secinfo("cert", "Invalid BasicConstraints Extension");
697 static void SecCEPCrlDistributionPoints(SecCertificateRefP certificate
,
698 const SecCertificateExtension
*extn
) {
699 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
703 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
705 PolicyInformation ::= SEQUENCE {
706 policyIdentifier CertPolicyId,
707 policyQualifiers SEQUENCE SIZE (1..MAX) OF
708 PolicyQualifierInfo OPTIONAL }
710 CertPolicyId ::= OBJECT IDENTIFIER
712 PolicyQualifierInfo ::= SEQUENCE {
713 policyQualifierId PolicyQualifierId,
714 qualifier ANY DEFINED BY policyQualifierId }
716 /* maximum number of policies of 8192 seems more than adequate */
717 #define MAX_CERTIFICATE_POLICIES 8192
718 static void SecCEPCertificatePolicies(SecCertificateRefP certificate
,
719 const SecCertificateExtension
*extn
) {
720 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
723 SecCEPolicyInformation
*policies
= NULL
;
724 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
725 require_noerr_quiet(drtn
, badDER
);
726 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
727 DERDecodedInfo piContent
;
728 DERSize policy_count
= 0;
729 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
730 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
731 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
734 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
735 DERSize malloc_policies
= policy_count
> 0 ? policy_count
: 1;
736 require_quiet(policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
737 * malloc_policies
), badDER
);
738 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
739 require_noerr_quiet(drtn
, badDER
);
740 DERSize policy_ix
= 0;
741 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
742 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
743 DERPolicyInformation pi
;
744 drtn
= DERParseSequenceContent(&piContent
.content
,
745 DERNumPolicyInformationItemSpecs
,
746 DERPolicyInformationItemSpecs
,
748 require_noerr_quiet(drtn
, badDER
);
749 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
750 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
752 certificate
->_certificatePolicies
.present
= true;
753 certificate
->_certificatePolicies
.critical
= extn
->critical
;
754 certificate
->_certificatePolicies
.numPolicies
= (uint32_t)policy_count
;
755 certificate
->_certificatePolicies
.policies
= policies
;
760 certificate
->_certificatePolicies
.present
= false;
761 secinfo("cert", "Invalid CertificatePolicies Extension");
765 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
767 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
768 issuerDomainPolicy CertPolicyId,
769 subjectDomainPolicy CertPolicyId }
772 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
773 const SecCertificateExtension
*extn
) {
774 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
777 SecCEPolicyMapping
*mappings
= NULL
;
778 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
779 require_noerr_quiet(drtn
, badDER
);
780 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
781 DERDecodedInfo pmContent
;
782 DERSize mapping_count
= 0;
783 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
784 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
787 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
789 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
790 require_noerr_quiet(drtn
, badDER
);
791 DERSize mapping_ix
= 0;
792 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
794 drtn
= DERParseSequenceContent(&pmContent
.content
,
795 DERNumPolicyMappingItemSpecs
,
796 DERPolicyMappingItemSpecs
,
798 require_noerr_quiet(drtn
, badDER
);
799 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
800 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
802 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
803 certificate
->_policyMappings
.present
= true;
804 certificate
->_policyMappings
.critical
= extn
->critical
;
805 certificate
->_policyMappings
.numMappings
= mapping_count
;
806 certificate
->_policyMappings
.mappings
= mappings
;
811 CFReleaseSafe(mappings
);
812 certificate
->_policyMappings
.present
= false;
813 secinfo("cert", "Invalid CertificatePolicies Extension");
816 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
817 const SecCertificateExtension
*extn
) {
818 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
821 CFMutableDictionaryRef mappings
= NULL
;
822 CFDataRef idp
= NULL
, sdp
= NULL
;
823 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
824 require_noerr_quiet(drtn
, badDER
);
825 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
826 DERDecodedInfo pmContent
;
827 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
828 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
830 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
831 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
833 drtn
= DERParseSequenceContent(&pmContent
.content
,
834 DERNumPolicyMappingItemSpecs
,
835 DERPolicyMappingItemSpecs
,
837 require_noerr_quiet(drtn
, badDER
);
839 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
840 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
841 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
842 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
843 CFMutableArrayRef sdps
=
844 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
846 CFArrayAppendValue(sdps
, sdp
);
848 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
849 &kCFTypeArrayCallBacks
), badDER
);
850 CFDictionarySetValue(mappings
, idp
, sdps
);
856 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
857 certificate
->_policyMappings
= mappings
;
862 CFReleaseSafe(mappings
);
863 certificate
->_policyMappings
= NULL
;
864 secinfo("cert", "Invalid CertificatePolicies Extension");
869 AuthorityKeyIdentifier ::= SEQUENCE {
870 keyIdentifier [0] KeyIdentifier OPTIONAL,
871 authorityCertIssuer [1] GeneralNames OPTIONAL,
872 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
873 -- authorityCertIssuer and authorityCertSerialNumber MUST both
874 -- be present or both be absent
876 KeyIdentifier ::= OCTET STRING
878 static void SecCEPAuthorityKeyIdentifier(SecCertificateRefP certificate
,
879 const SecCertificateExtension
*extn
) {
880 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
881 DERAuthorityKeyIdentifier akid
;
883 drtn
= DERParseSequence(&extn
->extnValue
,
884 DERNumAuthorityKeyIdentifierItemSpecs
,
885 DERAuthorityKeyIdentifierItemSpecs
,
886 &akid
, sizeof(akid
));
887 require_noerr_quiet(drtn
, badDER
);
888 if (akid
.keyIdentifier
.length
) {
889 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
891 if (akid
.authorityCertIssuer
.length
||
892 akid
.authorityCertSerialNumber
.length
) {
893 require_quiet(akid
.authorityCertIssuer
.length
&&
894 akid
.authorityCertSerialNumber
.length
, badDER
);
895 /* Perhaps put in a subsection called Authority Certificate Issuer. */
896 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
897 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
902 secinfo("cert", "Invalid AuthorityKeyIdentifier Extension");
905 static void SecCEPPolicyConstraints(SecCertificateRefP certificate
,
906 const SecCertificateExtension
*extn
) {
907 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
908 DERPolicyConstraints pc
;
910 drtn
= DERParseSequence(&extn
->extnValue
,
911 DERNumPolicyConstraintsItemSpecs
,
912 DERPolicyConstraintsItemSpecs
,
914 require_noerr_quiet(drtn
, badDER
);
915 if (pc
.requireExplicitPolicy
.length
) {
916 require_noerr_quiet(DERParseInteger(
917 &pc
.requireExplicitPolicy
,
918 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
919 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
921 if (pc
.inhibitPolicyMapping
.length
) {
922 require_noerr_quiet(DERParseInteger(
923 &pc
.inhibitPolicyMapping
,
924 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
925 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
928 certificate
->_policyConstraints
.present
= true;
929 certificate
->_policyConstraints
.critical
= extn
->critical
;
933 certificate
->_policyConstraints
.present
= false;
934 secinfo("cert", "Invalid PolicyConstraints Extension");
937 static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate
,
938 const SecCertificateExtension
*extn
) {
939 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
943 InhibitAnyPolicy ::= SkipCerts
945 SkipCerts ::= INTEGER (0..MAX)
947 static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate
,
948 const SecCertificateExtension
*extn
) {
949 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
950 require_noerr_quiet(DERParseInteger(
952 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
955 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
956 secinfo("cert", "Invalid InhibitAnyPolicy Extension");
960 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
962 AuthorityInfoAccessSyntax ::=
963 SEQUENCE SIZE (1..MAX) OF AccessDescription
965 AccessDescription ::= SEQUENCE {
966 accessMethod OBJECT IDENTIFIER,
967 accessLocation GeneralName }
969 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
971 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
973 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
975 static void SecCEPAuthorityInfoAccess(SecCertificateRefP certificate
,
976 const SecCertificateExtension
*extn
) {
977 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
980 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
981 require_noerr_quiet(drtn
, badDER
);
982 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
983 DERDecodedInfo adContent
;
984 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
985 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
986 DERAccessDescription ad
;
987 drtn
= DERParseSequenceContent(&adContent
.content
,
988 DERNumAccessDescriptionItemSpecs
,
989 DERAccessDescriptionItemSpecs
,
991 require_noerr_quiet(drtn
, badDER
);
992 CFMutableArrayRef
*urls
;
993 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
994 urls
= &certificate
->_ocspResponders
;
995 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
996 urls
= &certificate
->_caIssuers
;
1000 DERDecodedInfo generalNameContent
;
1001 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1002 require_noerr_quiet(drtn
, badDER
);
1003 switch (generalNameContent
.tag
) {
1005 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1006 /* Technically I don't think this is valid, but there are certs out
1007 in the wild that use a constructed IA5String. In particular the
1008 VeriSign Time Stamping Authority CA.cer does this. */
1010 case ASN1_CONTEXT_SPECIFIC
| 6:
1012 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1013 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1014 kCFStringEncodingASCII
, NULL
);
1017 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1018 CFArrayAppendValue(*urls
, url
);
1024 secinfo("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1025 generalNameContent
.tag
, (int)generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1029 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1032 secinfo("cert", "failed to parse Authority Information Access extension");
1035 static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate
,
1036 const SecCertificateExtension
*extn
) {
1037 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1040 static void SecCEPNetscapeCertType(SecCertificateRefP certificate
,
1041 const SecCertificateExtension
*extn
) {
1042 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1045 static void SecCEPEntrustVersInfo(SecCertificateRefP certificate
,
1046 const SecCertificateExtension
*extn
) {
1047 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1050 /* Dictionary key callback for comparing to DERItems. */
1051 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1052 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1055 /* Dictionary key callback calculating the hash of a DERItem. */
1056 static CFHashCode
SecDERItemHash(const void *value
) {
1057 const DERItem
*derItem
= (const DERItem
*)value
;
1058 CFHashCode hash
= derItem
->length
;
1059 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1060 for (; ix
< derItem
->length
; ++ix
) {
1061 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1067 /* Dictionary key callbacks using the above 2 functions. */
1068 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1072 NULL
, /* copyDescription */
1073 SecDERItemEqual
, /* equal */
1074 SecDERItemHash
/* hash */
1077 static void SecCertificateRegisterClass(void) {
1078 static const CFRuntimeClass kSecCertificateClass
= {
1080 "SecCertificate", /* class name */
1083 SecCertificateDestroy
, /* dealloc */
1084 SecCertificateEqual
, /* equal */
1085 SecCertificateHash
, /* hash */
1086 NULL
, /* copyFormattingDesc */
1087 SecCertificateCopyDescription
/* copyDebugDesc */
1090 kSecCertificateTypeID
= _CFRuntimeRegisterClass(&kSecCertificateClass
);
1092 /* Build a dictionary that maps from extension OIDs to callback functions
1093 which can parse the extension of the type given. */
1094 static const void *extnOIDs
[] = {
1095 &oidSubjectKeyIdentifier
,
1097 &oidPrivateKeyUsagePeriod
,
1100 &oidBasicConstraints
,
1101 &oidCrlDistributionPoints
,
1102 &oidCertificatePolicies
,
1104 &oidAuthorityKeyIdentifier
,
1105 &oidPolicyConstraints
,
1106 &oidExtendedKeyUsage
,
1107 &oidInhibitAnyPolicy
,
1108 &oidAuthorityInfoAccess
,
1109 &oidSubjectInfoAccess
,
1110 &oidNetscapeCertType
,
1113 static const void *extnParsers
[] = {
1114 SecCEPSubjectKeyIdentifier
,
1116 SecCEPPrivateKeyUsagePeriod
,
1117 SecCEPSubjectAltName
,
1118 SecCEPIssuerAltName
,
1119 SecCEPBasicConstraints
,
1120 SecCEPCrlDistributionPoints
,
1121 SecCEPCertificatePolicies
,
1122 SecCEPPolicyMappings
,
1123 SecCEPAuthorityKeyIdentifier
,
1124 SecCEPPolicyConstraints
,
1125 SecCEPExtendedKeyUsage
,
1126 SecCEPInhibitAnyPolicy
,
1127 SecCEPAuthorityInfoAccess
,
1128 SecCEPSubjectInfoAccess
,
1129 SecCEPNetscapeCertType
,
1130 SecCEPEntrustVersInfo
1132 gExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1133 extnParsers
, sizeof(extnOIDs
) / sizeof(*extnOIDs
),
1134 &SecDERItemKeyCallBacks
, NULL
);
1137 /* Given the contents of an X.501 Name return the contents of a normalized
1139 static CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1140 const DERItem
*x501name
) {
1141 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1142 CFIndex length
= x501name
->length
;
1143 CFDataSetLength(result
, length
);
1144 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1147 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1149 require_noerr_quiet(drtn
, badDER
);
1152 /* Always points to last rdn tag. */
1153 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1154 /* Offset relative to base of current rdn set tag. */
1155 CFIndex rdnTagLocation
= 0;
1156 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1157 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1158 /* We don't allow empty RDNs. */
1159 require_quiet(rdn
.content
.length
!= 0, badDER
);
1160 /* Length of the tag and length of the current rdn. */
1161 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1162 CFIndex rdnContentLength
= rdn
.content
.length
;
1163 /* Copy the tag and length of the RDN. */
1164 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1167 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1169 /* Always points to tag of current atv sequence. */
1170 const DERByte
*atvTag
= atvSeq
.nextItem
;
1171 /* Offset relative to base of current atv sequence tag. */
1172 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1173 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1174 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1175 /* Length of the tag and length of the current atv. */
1176 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1177 CFIndex atvContentLength
= atv
.content
.length
;
1178 /* Copy the tag and length of the atv and the atv itself. */
1179 memcpy(base
+ atvTagLocation
, atvTag
,
1180 atvTLLength
+ atv
.content
.length
);
1182 /* Now decode the atv sequence. */
1183 DERAttributeTypeAndValue atvPair
;
1184 drtn
= DERParseSequenceContent(&atv
.content
,
1185 DERNumAttributeTypeAndValueItemSpecs
,
1186 DERAttributeTypeAndValueItemSpecs
,
1187 &atvPair
, sizeof(atvPair
));
1188 require_noerr_quiet(drtn
, badDER
);
1189 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1190 DERDecodedInfo value
;
1191 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1192 require_noerr_quiet(drtn
, badDER
);
1194 /* (c) attribute values in PrintableString are not case sensitive
1195 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1197 (d) attribute values in PrintableString are compared after
1198 removing leading and trailing white space and converting internal
1199 substrings of one or more consecutive white space characters to a
1201 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1202 /* Offset relative to base of current value tag. */
1203 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1204 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1205 CFIndex valueContentLength
= value
.content
.length
;
1207 /* Now copy all the bytes, but convert to upper case while
1208 doing so and convert multiple whitespace chars into a
1210 bool lastWasBlank
= false;
1211 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1212 CFIndex valueCurrentLocation
= valueLocation
;
1214 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1215 UInt8 ch
= value
.content
.data
[ix
];
1220 /* Don't insert a space for first character
1222 if (valueCurrentLocation
> valueLocation
) {
1223 base
[valueCurrentLocation
++] = ' ';
1225 lastWasBlank
= true;
1228 lastWasBlank
= false;
1229 if ('a' <= ch
&& ch
<= 'z') {
1230 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1232 base
[valueCurrentLocation
++] = ch
;
1236 /* Finally if lastWasBlank remove the trailing space. */
1237 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1238 valueCurrentLocation
--;
1240 /* Adjust content length to normalized length. */
1241 valueContentLength
= valueCurrentLocation
- valueLocation
;
1243 /* Number of bytes by which the length should be shorted. */
1244 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1245 if (lengthDiff
== 0) {
1246 /* Easy case no need to adjust lengths. */
1248 /* Hard work we need to go back and fix up length fields
1250 1) The value itself.
1251 2) The ATV Sequence containing type/value
1252 3) The RDN Set containing one or more atv pairs.
1256 /* Step 1 fix up length of value. */
1257 /* Length of value tag and length minus the tag. */
1258 DERSize newValueTLLength
= valueTLLength
- 1;
1259 drtn
= DEREncodeLength(valueContentLength
,
1260 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1261 /* Add the length of the tag back in. */
1263 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1265 /* The size of the length field changed, let's slide
1266 the value back by valueLLDiff bytes. */
1267 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1268 base
+ valueTagLocation
+ valueTLLength
,
1269 valueContentLength
);
1270 /* The length diff for the enclosing object. */
1271 lengthDiff
+= valueLLDiff
;
1274 /* Step 2 fix up length of the enclosing ATV Sequence. */
1275 atvContentLength
-= lengthDiff
;
1276 DERSize newATVTLLength
= atvTLLength
- 1;
1277 drtn
= DEREncodeLength(atvContentLength
,
1278 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1279 /* Add the length of the tag back in. */
1281 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1283 /* The size of the length field changed, let's slide
1284 the value back by valueLLDiff bytes. */
1285 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1286 base
+ atvTagLocation
+ atvTLLength
,
1288 /* The length diff for the enclosing object. */
1289 lengthDiff
+= atvLLDiff
;
1290 atvTLLength
= newATVTLLength
;
1293 /* Step 3 fix up length of enclosing RDN Set. */
1294 rdnContentLength
-= lengthDiff
;
1295 DERSize newRDNTLLength
= rdnTLLength
- 1;
1296 drtn
= DEREncodeLength(rdnContentLength
,
1297 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1298 /* Add the length of the tag back in. */
1300 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1302 /* The size of the length field changed, let's slide
1303 the value back by valueLLDiff bytes. */
1304 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1305 base
+ rdnTagLocation
+ rdnTLLength
,
1307 /* The length diff for the enclosing object. */
1308 lengthDiff
+= rdnLLDiff
;
1309 rdnTLLength
= newRDNTLLength
;
1311 /* Adjust the locations that might have changed due to
1313 atvTagLocation
-= rdnLLDiff
;
1317 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1318 atvTag
= atvSeq
.nextItem
;
1320 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1321 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1322 rdnTag
= rdnSeq
.nextItem
;
1324 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1325 /* Truncate the result to the proper length. */
1326 CFDataSetLength(result
, rdnTagLocation
);
1335 /* AUDIT[securityd]:
1336 certificate->_der is a caller provided data of any length (might be 0).
1338 Top level certificate decode.
1340 static bool SecCertificateParse(SecCertificateRefP certificate
)
1345 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1347 /* top level decode */
1348 DERSignedCertCrl signedCert
;
1349 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1350 DERSignedCertCrlItemSpecs
, &signedCert
,
1351 sizeof(signedCert
));
1352 require_noerr_quiet(drtn
, badCert
);
1353 /* Store tbs since we need to digest it for verification later on. */
1354 certificate
->_tbs
= signedCert
.tbs
;
1356 /* decode the TBSCert - it was saved in full DER form */
1358 drtn
= DERParseSequence(&signedCert
.tbs
,
1359 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1360 &tbsCert
, sizeof(tbsCert
));
1361 require_noerr_quiet(drtn
, badCert
);
1363 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1364 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1365 of the params field. */
1366 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1367 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1368 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1369 require_noerr_quiet(drtn
, badCert
);
1371 /* The contents of signedCert.sig is a bit string whose contents
1372 are the signature itself. */
1373 DERByte numUnusedBits
;
1374 drtn
= DERParseBitString(&signedCert
.sig
,
1375 &certificate
->_signature
, &numUnusedBits
);
1376 require_noerr_quiet(drtn
, badCert
);
1378 /* Now decode the tbsCert. */
1380 /* First we turn the optional version into an int. */
1381 if (tbsCert
.version
.length
) {
1382 DERDecodedInfo decoded
;
1383 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1384 require_noerr_quiet(drtn
, badCert
);
1385 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1386 require_quiet(decoded
.content
.length
== 1, badCert
);
1387 certificate
->_version
= decoded
.content
.data
[0];
1388 require_quiet(certificate
->_version
> 0, badCert
);
1389 require_quiet(certificate
->_version
< 3, badCert
);
1391 certificate
->_version
= 0;
1394 /* The serial number is in the tbsCert.serialNum - it was saved in
1395 INTEGER form without the tag and length. */
1396 certificate
->_serialNum
= tbsCert
.serialNum
;
1397 certificate
->_serialNumber
= CFDataCreate(allocator
,
1398 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1400 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1401 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1402 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1403 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1404 require_noerr_quiet(drtn
, badCert
);
1406 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1407 and length fields. */
1408 certificate
->_issuer
= tbsCert
.issuer
;
1409 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1412 /* sequence we're given: decode the tbsCerts Validity sequence. */
1413 DERValidity validity
;
1414 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1415 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1416 &validity
, sizeof(validity
));
1417 require_noerr_quiet(drtn
, badCert
);
1418 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1419 &certificate
->_notBefore
), badCert
);
1420 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1421 &certificate
->_notAfter
), badCert
);
1423 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1424 and length fields. */
1425 certificate
->_subject
= tbsCert
.subject
;
1426 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1429 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1430 DERSubjPubKeyInfo pubKeyInfo
;
1431 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1432 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1433 &pubKeyInfo
, sizeof(pubKeyInfo
));
1434 require_noerr_quiet(drtn
, badCert
);
1436 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1437 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1438 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1439 &certificate
->_algId
, sizeof(certificate
->_algId
));
1440 require_noerr_quiet(drtn
, badCert
);
1442 /* Now we can figure out the key's algorithm id and params based on
1443 certificate->_algId.oid. */
1445 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1446 are a PKCS1 format RSA key. */
1447 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1448 &certificate
->_pubKeyDER
, &numUnusedBits
);
1449 require_noerr_quiet(drtn
, badCert
);
1451 /* The contents of tbsCert.issuerID is a bit string. */
1452 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1454 /* The contents of tbsCert.subjectID is a bit string. */
1455 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1458 if (tbsCert
.extensions
.length
) {
1459 CFIndex extensionCount
= 0;
1462 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1464 require_noerr_quiet(drtn
, badCert
);
1465 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1466 DERDecodedInfo currDecoded
;
1467 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1469 /* ! = MUST recognize ? = SHOULD recognize
1472 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1473 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1474 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1475 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1476 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1477 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1478 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1479 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1481 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1482 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1483 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1484 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1485 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1486 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1487 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1488 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1490 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1491 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1496 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1498 /* Put some upper limit on the number of extentions allowed. */
1499 require_quiet(extensionCount
< 10000, badCert
);
1500 certificate
->_extensionCount
= extensionCount
;
1501 CFIndex mallocCount
= extensionCount
> 0 ? extensionCount
: 1;
1502 certificate
->_extensions
=
1503 malloc(sizeof(SecCertificateExtension
) * mallocCount
);
1504 require_quiet(certificate
->_extensions
, badCert
);
1507 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1508 require_noerr_quiet(drtn
, badCert
);
1509 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1510 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1511 require_quiet(drtn
== DR_Success
||
1512 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1513 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1515 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1516 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1517 &extn
, sizeof(extn
));
1518 require_noerr_quiet(drtn
, badCert
);
1519 /* Copy stuff into certificate->extensions[ix]. */
1520 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1521 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1522 &certificate
->_extensions
[ix
].critical
), badCert
);
1523 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1525 SecCertificateExtensionParser parser
=
1526 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1527 gExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1529 /* Invoke the parser. */
1530 parser(certificate
, &certificate
->_extensions
[ix
]);
1531 } else if (certificate
->_extensions
[ix
].critical
) {
1532 secinfo("cert", "Found unknown critical extension");
1533 certificate
->_foundUnknownCriticalExtension
= true;
1535 secinfo("cert", "Found unknown non critical extension");
1547 /* Public API functions. */
1548 CFTypeID
SecCertificateGetTypeIDP(void) {
1549 pthread_once(&kSecCertificateRegisterClass
, SecCertificateRegisterClass
);
1550 return kSecCertificateTypeID
;
1553 SecCertificateRefP
SecCertificateCreateWithBytesP(CFAllocatorRef allocator
,
1554 const UInt8
*der_bytes
, CFIndex der_length
) {
1557 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1558 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1559 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1561 memset((char*)result
+ sizeof(result
->_base
), 0,
1562 sizeof(*result
) - sizeof(result
->_base
));
1563 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1564 result
->_der
.length
= der_length
;
1566 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1568 if (!SecCertificateParse(result
)) {
1576 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1577 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1578 const UInt8
*der_bytes
, CFIndex der_length
);
1580 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1581 const UInt8
*der_bytes
, CFIndex der_length
) {
1582 return SecCertificateCreateWithBytesP(allocator
, der_bytes
, der_length
);
1584 /* @@@ End of placeholder. */
1586 /* AUDIT[securityd](done):
1587 der_certificate is a caller provided data of any length (might be 0), only
1588 its cf type has been checked.
1590 SecCertificateRefP
SecCertificateCreateWithDataP(CFAllocatorRef allocator
,
1591 CFDataRef der_certificate
) {
1592 check(der_certificate
);
1593 CFIndex size
= sizeof(struct __SecCertificate
);
1594 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1595 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1597 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1598 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1599 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1600 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1601 if (!SecCertificateParse(result
)) {
1609 CFDataRef
SecCertificateCopyDataP(SecCertificateRefP certificate
) {
1612 if (certificate
->_der_data
) {
1613 CFRetain(certificate
->_der_data
);
1614 result
= certificate
->_der_data
;
1616 result
= CFDataCreate(CFGetAllocator(certificate
),
1617 certificate
->_der
.data
, certificate
->_der
.length
);
1619 /* FIXME: If we wish to cache result we need to lock the certificate.
1620 Also this create 2 copies of the certificate data which is somewhat
1623 certificate
->_der_data
= result
;
1630 CFIndex
SecCertificateGetLengthP(SecCertificateRefP certificate
) {
1631 return certificate
->_der
.length
;
1634 const UInt8
*SecCertificateGetBytePtrP(SecCertificateRefP certificate
) {
1635 return certificate
->_der
.data
;
1638 /* From rfc3280 - Appendix B. ASN.1 Notes
1640 Object Identifiers (OIDs) are used throughout this specification to
1641 identify certificate policies, public key and signature algorithms,
1642 certificate extensions, etc. There is no maximum size for OIDs.
1643 This specification mandates support for OIDs which have arc elements
1644 with values that are less than 2^28, that is, they MUST be between 0
1645 and 268,435,455, inclusive. This allows each arc element to be
1646 represented within a single 32 bit word. Implementations MUST also
1647 support OIDs where the length of the dotted decimal (see [RFC 2252],
1648 section 4.1) string representation can be up to 100 bytes
1649 (inclusive). Implementations MUST be able to handle OIDs with up to
1650 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1651 contain OIDs that exceed these requirements. Likewise, CRL issuers
1652 SHOULD NOT issue CRLs which contain OIDs that exceed these
1656 /* Oids longer than this are considered invalid. */
1657 #define MAX_OID_SIZE 32
1659 static CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1660 const DERItem
*oid
) {
1662 if (oid
->length
== 0) {
1663 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1664 CFSTR("SecCertificate"));
1666 if (oid
->length
> MAX_OID_SIZE
) {
1667 return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"),
1668 CFSTR("SecCertificate"));
1671 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1673 // The first two levels are encoded into one byte, since the root level
1674 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1675 // y may be > 39, so we have to add special-case handling for this.
1676 uint32_t x
= oid
->data
[0] / 40;
1677 uint32_t y
= oid
->data
[0] % 40;
1680 // Handle special case for large y if x = 2
1684 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1687 for (x
= 1; x
< oid
->length
; ++x
)
1689 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1690 /* @@@ value may not span more than 4 bytes. */
1691 /* A max number of 20 values is allowed. */
1692 if (!(oid
->data
[x
] & 0x80))
1694 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), (unsigned long)value
);
1701 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1702 const DERItem
*oid
) {
1703 if (oid
->length
== 0) {
1704 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1705 CFSTR("SecCertificate"));
1708 /* Build the key we use to lookup the localized OID description. */
1709 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1710 oid
->length
* 3 + 5);
1711 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), (unsigned long)oid
->length
);
1713 for (ix
= 0; ix
< oid
->length
; ++ix
)
1714 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1716 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1717 if (CFEqual(oidKey
, name
)) {
1719 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1726 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1727 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1728 have a length of exactly 4 or 16 octects. */
1729 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1730 const DERItem
*ip
) {
1731 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1732 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1733 16 octects addr, or 32 octects addr/mask. */
1734 CFStringRef value
= NULL
;
1735 if (ip
->length
== 4) {
1736 value
= CFStringCreateWithFormat(allocator
, NULL
,
1737 CFSTR("%u.%u.%u.%u"),
1738 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1739 } else if (ip
->length
== 16) {
1740 value
= CFStringCreateWithFormat(allocator
, NULL
,
1741 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1742 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1743 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
1744 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
1745 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
1746 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
1753 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
1754 const DERItem
*oid
) {
1755 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1756 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
1757 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
1758 CFSTR("%@ (%@)"), name
, decimal
);
1765 void appendPropertyP(CFMutableArrayRef properties
,
1766 CFStringRef propertyType
, CFStringRef label
, CFTypeRef value
) {
1767 CFDictionaryRef property
;
1769 CFStringRef localizedLabel
= SecFrameworkCopyLocalizedString(label
,
1770 CFSTR("SecCertificate"));
1771 const void *all_keys
[4];
1772 all_keys
[0] = kSecPropertyKeyType
;
1773 all_keys
[1] = kSecPropertyKeyLabel
;
1774 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
1775 all_keys
[3] = kSecPropertyKeyValue
;
1776 const void *property_values
[] = {
1782 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1783 all_keys
, property_values
, value
? 4 : 3,
1784 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1785 CFRelease(localizedLabel
);
1787 const void *nolabel_keys
[2];
1788 nolabel_keys
[0] = kSecPropertyKeyType
;
1789 nolabel_keys
[1] = kSecPropertyKeyValue
;
1790 const void *property_values
[] = {
1794 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1795 nolabel_keys
, property_values
, 2,
1796 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1799 CFArrayAppendValue(properties
, property
);
1800 CFRelease(property
);
1804 #define UTC_TIME_NOSEC_ZULU_LEN 11
1806 #define UTC_TIME_ZULU_LEN 13
1807 /* YYMMDDhhmmssThhmm */
1808 #define UTC_TIME_LOCALIZED_LEN 17
1809 /* YYYYMMDDhhmmssZ */
1810 #define GENERALIZED_TIME_ZULU_LEN 15
1811 /* YYYYMMDDhhmmssThhmm */
1812 #define GENERALIZED_TIME_LOCALIZED_LEN 19
1814 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
1816 static inline SInt32
parseDecimalPair(const DERByte
**p
) {
1817 const DERByte
*cp
= *p
;
1819 return 10 * (cp
[0] - '0') + cp
[1] - '0';
1822 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1823 true if the date was valid and properly decoded, also return the result in
1824 absTime. Return false otherwise. */
1825 static CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
1831 bool isUtcLength
= false;
1832 bool isLocalized
= false;
1833 bool noSeconds
= false;
1835 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
1839 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
1842 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
1844 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
1847 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
1850 default: /* unknown format */
1854 /* Make sure the der tag fits the thing inside it. */
1855 if (tag
== ASN1_UTC_TIME
) {
1858 } else if (tag
== ASN1_GENERALIZED_TIME
) {
1865 const DERByte
*cp
= bytes
;
1866 /* Check that all characters are digits, except if localized the timezone
1867 indicator or if not localized the 'Z' at the end. */
1869 for (ix
= 0; ix
< length
; ++ix
) {
1870 if (!(isdigit(cp
[ix
]))) {
1871 if ((isLocalized
&& ix
== length
- 5 &&
1872 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
1873 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
1880 /* Initialize the fields in a gregorian date struct. */
1881 CFGregorianDate gdate
;
1883 SInt32 year
= parseDecimalPair(&cp
);
1885 /* 0 <= year < 50 : assume century 21 */
1886 gdate
.year
= 2000 + year
;
1887 } else if (year
< 70) {
1888 /* 50 <= year < 70 : illegal per PKIX */
1891 /* 70 < year <= 99 : assume century 20 */
1892 gdate
.year
= 1900 + year
;
1895 gdate
.year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
1897 gdate
.month
= parseDecimalPair(&cp
);
1898 gdate
.day
= parseDecimalPair(&cp
);
1899 gdate
.hour
= parseDecimalPair(&cp
);
1900 gdate
.minute
= parseDecimalPair(&cp
);
1904 gdate
.second
= parseDecimalPair(&cp
);
1907 CFTimeInterval timeZoneOffset
= 0;
1909 /* ZONE INDICATOR */
1910 SInt32 multiplier
= *cp
++ == '+' ? 60 : -60;
1911 timeZoneOffset
= multiplier
*
1912 (parseDecimalPair(&cp
) + 60 * parseDecimalPair(&cp
));
1917 secinfo("dateparse",
1918 "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f",
1919 (int)length
, bytes
, (int)gdate
.year
, gdate
.month
,
1920 gdate
.day
, gdate
.hour
, gdate
.minute
, gdate
.second
,
1921 timeZoneOffset
/ 60);
1923 if (!CFGregorianDateIsValid(gdate
, kCFGregorianAllUnits
))
1925 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
,
1929 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(gdate
, timeZone
);
1930 CFRelease(timeZone
);
1934 static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
1935 CFAbsoluteTime
*pabsTime
) {
1936 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
1938 if (absTime
== NULL_TIME
)
1941 *pabsTime
= absTime
;
1945 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1946 true if the date was valid and properly decoded, also return the result in
1947 absTime. Return false otherwise. */
1948 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
1949 CFAbsoluteTime
*absTime
) {
1952 if (dateChoice
->length
== 0)
1955 DERDecodedInfo decoded
;
1956 if (DERDecodeItem(dateChoice
, &decoded
))
1959 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
1963 static void appendDataProperty(CFMutableArrayRef properties
,
1964 CFStringRef label
, const DERItem
*der_data
) {
1965 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
1966 der_data
->data
, der_data
->length
);
1967 appendPropertyP(properties
, kSecPropertyTypeData
, label
, data
);
1971 static void appendUnparsedProperty(CFMutableArrayRef properties
,
1972 CFStringRef label
, const DERItem
*der_data
) {
1973 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1974 NULL
, CFSTR("Unparsed %@"), label
);
1975 appendDataProperty(properties
, newLabel
, der_data
);
1976 CFRelease(newLabel
);
1979 static void appendInvalidProperty(CFMutableArrayRef properties
,
1980 CFStringRef label
, const DERItem
*der_data
) {
1981 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1982 NULL
, CFSTR("Invalid %@"), label
);
1983 appendDataProperty(properties
, newLabel
, der_data
);
1984 CFRelease(newLabel
);
1987 static void appendDateContentProperty(CFMutableArrayRef properties
,
1988 CFStringRef label
, DERTag tag
, const DERItem
*dateContent
) {
1989 CFAbsoluteTime absTime
;
1990 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
1991 /* Date decode failure insert hex bytes instead. */
1992 return appendInvalidProperty(properties
, label
, dateContent
);
1994 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1995 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1999 static void appendDateProperty(CFMutableArrayRef properties
,
2000 CFStringRef label
, CFAbsoluteTime absTime
) {
2001 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2002 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
2006 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2007 CFStringRef label
, const DERItem
*ip
) {
2009 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2011 appendPropertyP(properties
, kSecPropertyTypeString
, label
, value
);
2014 appendUnparsedProperty(properties
, label
, ip
);
2018 static void appendURLContentProperty(CFMutableArrayRef properties
,
2019 CFStringRef label
, const DERItem
*urlContent
) {
2020 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2021 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2023 appendPropertyP(properties
, kSecPropertyTypeURL
, label
, url
);
2026 appendInvalidProperty(properties
, label
, urlContent
);
2030 static void appendURLProperty(CFMutableArrayRef properties
,
2031 CFStringRef label
, const DERItem
*url
) {
2032 DERDecodedInfo decoded
;
2035 drtn
= DERDecodeItem(url
, &decoded
);
2036 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2037 appendInvalidProperty(properties
, label
, url
);
2039 appendURLContentProperty(properties
, label
, &decoded
.content
);
2043 static void appendOIDProperty(CFMutableArrayRef properties
,
2044 CFStringRef label
, const DERItem
*oid
) {
2045 CFStringRef oid_string
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2047 appendPropertyP(properties
, kSecPropertyTypeString
, label
, oid_string
);
2048 CFRelease(oid_string
);
2051 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2052 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2053 CFMutableArrayRef alg_props
=
2054 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2055 &kCFTypeArrayCallBacks
);
2056 appendOIDProperty(alg_props
, CFSTR("Algorithm"), &algorithm
->oid
);
2057 if (algorithm
->params
.length
) {
2058 if (algorithm
->params
.length
== 2 &&
2059 algorithm
->params
.data
[0] == ASN1_NULL
&&
2060 algorithm
->params
.data
[1] == 0) {
2061 /* @@@ Localize <NULL> or perhaps skip it? */
2062 appendPropertyP(alg_props
, kSecPropertyTypeString
,
2063 CFSTR("Parameters"), CFSTR("none"));
2065 appendUnparsedProperty(alg_props
, CFSTR("Parameters"),
2066 &algorithm
->params
);
2069 appendPropertyP(properties
, kSecPropertyTypeSection
, label
, alg_props
);
2070 CFRelease(alg_props
);
2073 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2074 const DERItem
*blob
) {
2075 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2076 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2077 blob
->length
* 3 - 1);
2078 for (ix
= 0; ix
< length
; ++ix
)
2080 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2082 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2087 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2088 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2089 CFStringRef blobFormat
= SecFrameworkCopyLocalizedString(
2090 CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate")
2091 /*, "format string for encoded field data (e.g. Sequence; 128 bytes; "
2092 "data = 00 00 ...)" */);
2093 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2094 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2095 blobFormat
, blobType
, blob
->length
, quanta
, hex
);
2097 CFRelease(blobFormat
);
2102 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2103 const DERItem
*string
, CFStringEncoding encoding
,
2104 bool printableOnly
) {
2105 /* Strip potential bogus trailing zero from printable strings. */
2106 DERSize length
= string
->length
;
2107 if (length
&& string
->data
[length
- 1] == 0) {
2108 /* Don't mess with the length of UTF16 strings though. */
2109 if (encoding
!= kCFStringEncodingUTF16
)
2112 /* A zero length string isn't considered printable. */
2113 if (!length
&& printableOnly
)
2116 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2117 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2118 passing false makes it treat it as native endian by default. */
2119 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2120 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2124 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2127 /* From rfc3280 - Appendix B. ASN.1 Notes
2129 CAs MUST force the serialNumber to be a non-negative integer, that
2130 is, the sign bit in the DER encoding of the INTEGER value MUST be
2131 zero - this can be done by adding a leading (leftmost) `00'H octet if
2132 necessary. This removes a potential ambiguity in mapping between a
2133 string of octets and an integer value.
2135 As noted in section 4.1.2.2, serial numbers can be expected to
2136 contain long integers. Certificate users MUST be able to handle
2137 serialNumber values up to 20 octets in length. Conformant CAs MUST
2138 NOT use serialNumber values longer than 20 octets.
2141 /* Return the given numeric data as a string: decimal up to 64 bits,
2143 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2144 const DERItem
*integer
) {
2146 CFIndex ix
, length
= integer
->length
;
2148 if (length
== 0 || length
> 8)
2149 return copyHexDescription(allocator
, integer
);
2151 for(ix
= 0; ix
< length
; ++ix
) {
2153 value
+= integer
->data
[ix
];
2156 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2159 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2160 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2164 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2165 case ASN1_PRINTABLE_STRING
:
2166 case ASN1_IA5_STRING
:
2167 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2168 case ASN1_UTF8_STRING
:
2169 case ASN1_GENERAL_STRING
:
2170 case ASN1_UNIVERSAL_STRING
:
2171 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2172 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2173 case ASN1_VIDEOTEX_STRING
: // 21
2174 case ASN1_VISIBLE_STRING
: // 26
2175 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2176 case ASN1_BMP_STRING
: // 30
2177 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2178 case ASN1_OCTET_STRING
:
2179 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Byte string"), CFSTR("bytes"),
2181 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2182 case ASN1_BIT_STRING
:
2183 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Bit string"), CFSTR("bits"),
2185 case (DERByte
)ASN1_CONSTR_SEQUENCE
:
2186 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Sequence"), CFSTR("bytes"),
2188 case (DERByte
)ASN1_CONSTR_SET
:
2189 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Set"), CFSTR("bytes"),
2191 case ASN1_OBJECT_ID
:
2192 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2195 /* "format string for undisplayed field data with a given DER tag" */
2196 return printableOnly
? NULL
: CFStringCreateWithFormat(allocator
, NULL
,
2197 CFSTR("not displayed (tag = %llu; length %d)"),
2198 tag
, (int)derThing
->length
);
2202 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2203 const DERItem
*derThing
, bool printableOnly
) {
2204 DERDecodedInfo decoded
;
2207 drtn
= DERDecodeItem(derThing
, &decoded
);
2209 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2211 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2212 &decoded
.content
, false);
2216 static void appendDERThingProperty(CFMutableArrayRef properties
,
2217 CFStringRef label
, const DERItem
*derThing
) {
2218 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2220 appendPropertyP(properties
, kSecPropertyTypeString
, label
, value
);
2221 CFReleaseSafe(value
);
2224 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2225 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2226 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2228 /* If there is more than one value pair we create a subsection for the
2229 second pair, and append things to the subsection for subsequent
2231 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2232 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2234 /* Since this is the second rdn pair for a given rdn, we setup a
2235 new subsection for this rdn. We remove the first property
2236 from the properties array and make it the first element in the
2237 subsection instead. */
2238 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2239 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2240 CFArrayAppendValue(rdn_props
, lastValue
);
2241 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2242 appendPropertyP(properties
, kSecPropertyTypeSection
, NULL
, rdn_props
);
2243 properties
= rdn_props
;
2245 /* Since this is the third or later rdn pair we have already
2246 created a subsection in the top level properties array. Instead
2247 of appending to that directly we append to the array inside the
2249 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2250 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2254 /* Finally we append the new rdn value to the property array. */
2255 CFStringRef label
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2258 appendDERThingProperty(properties
, label
, rdnValue
);
2260 return errSecSuccess
;
2262 return errSecInvalidCertificate
;
2266 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2267 const DERItem
*rdnSetContent
) {
2268 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2269 &kCFTypeArrayCallBacks
);
2270 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2273 CFArrayRemoveAllValues(properties
);
2274 appendInvalidProperty(properties
, CFSTR("RDN"), rdnSetContent
);
2281 From rfc3739 - 3.1.2. Subject
2283 When parsing the subject here are some tips for a short name of the cert.
2284 Choice I: commonName
2285 Choice II: givenName
2286 Choice III: pseudonym
2288 The commonName attribute value SHALL, when present, contain a name
2289 of the subject. This MAY be in the subject's preferred
2290 presentation format, or a format preferred by the CA, or some
2291 other format. Pseudonyms, nicknames, and names with spelling
2292 other than defined by the registered name MAY be used. To
2293 understand the nature of the name presented in commonName,
2294 complying applications MAY have to examine present values of the
2295 givenName and surname attributes, or the pseudonym attribute.
2298 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2299 const DERItem
*x501NameContent
) {
2300 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2301 &kCFTypeArrayCallBacks
);
2302 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2305 CFArrayRemoveAllValues(properties
);
2306 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501NameContent
);
2312 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2313 const DERItem
*x501Name
) {
2314 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2315 &kCFTypeArrayCallBacks
);
2316 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2318 CFArrayRemoveAllValues(properties
);
2319 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501Name
);
2325 static void appendIntegerProperty(CFMutableArrayRef properties
,
2326 CFStringRef label
, const DERItem
*integer
) {
2327 CFStringRef string
= copyIntegerContentDescription(
2328 CFGetAllocator(properties
), integer
);
2329 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2333 static void appendBoolProperty(CFMutableArrayRef properties
,
2334 CFStringRef label
, bool boolean
) {
2335 appendPropertyP(properties
, kSecPropertyTypeString
,
2336 label
, boolean
? CFSTR("Yes") : CFSTR("No"));
2339 static void appendBooleanProperty(CFMutableArrayRef properties
,
2340 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2342 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2344 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2345 appendInvalidProperty(properties
, label
, boolean
);
2347 appendBoolProperty(properties
, label
, result
);
2351 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2352 CFStringRef label
, const DERItem
*bitStringContent
,
2353 const CFStringRef
*names
, CFIndex namesCount
) {
2354 DERSize len
= bitStringContent
->length
- 1;
2355 require_quiet(len
== 1 || len
== 2, badDER
);
2356 DERByte numUnusedBits
= bitStringContent
->data
[0];
2357 require_quiet(numUnusedBits
< 8, badDER
);
2358 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2359 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2360 uint_fast16_t value
= bitStringContent
->data
[1];
2363 value
= (value
<< 8) + bitStringContent
->data
[2];
2369 bool didOne
= false;
2370 CFMutableStringRef string
=
2371 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2372 for (ix
= 0; ix
< bits
; ++ix
) {
2375 CFStringAppend(string
, CFSTR(", "));
2379 CFStringAppend(string
, names
[ix
]);
2383 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2387 appendInvalidProperty(properties
, label
, bitStringContent
);
2390 static void appendBitStringNames(CFMutableArrayRef properties
,
2391 CFStringRef label
, const DERItem
*bitString
,
2392 const CFStringRef
*names
, CFIndex namesCount
) {
2393 DERDecodedInfo bitStringContent
;
2394 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2395 require_noerr_quiet(drtn
, badDER
);
2396 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2397 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2401 appendInvalidProperty(properties
, label
, bitString
);
2405 typedef uint16_t SecKeyUsage
;
2407 #define kSecKeyUsageDigitalSignature 0x8000
2408 #define kSecKeyUsageNonRepudiation 0x4000
2409 #define kSecKeyUsageKeyEncipherment 0x2000
2410 #define kSecKeyUsageDataEncipherment 0x1000
2411 #define kSecKeyUsageKeyAgreement 0x0800
2412 #define kSecKeyUsageKeyCertSign 0x0400
2413 #define kSecKeyUsageCRLSign 0x0200
2414 #define kSecKeyUsageEncipherOnly 0x0100
2415 #define kSecKeyUsageDecipherOnly 0x0080
2418 KeyUsage ::= BIT STRING {
2419 digitalSignature (0),
2421 keyEncipherment (2),
2422 dataEncipherment (3),
2429 static void appendKeyUsage(CFMutableArrayRef properties
,
2430 const DERItem
*extnValue
) {
2431 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2432 extnValue
->data
[0] != ASN1_BIT_STRING
||
2433 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2434 extnValue
->data
[2] > 7) {
2435 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2438 CFMutableStringRef string
=
2439 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2440 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2441 if (extnValue
->length
== 5)
2442 usage
+= extnValue
->data
[4];
2443 secinfo("keyusage", "keyusage: %04X", usage
);
2444 static const CFStringRef usageNames
[] = {
2445 CFSTR("Digital Signature"),
2446 CFSTR("Non-Repudiation"),
2447 CFSTR("Key Encipherment"),
2448 CFSTR("Data Encipherment"),
2449 CFSTR("Key Agreement"),
2455 bool didOne
= false;
2456 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2457 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2458 for (ix
= 0; ix
< bits
; ++ix
) {
2461 CFStringAppend(string
, CFSTR(", "));
2465 /* @@@ Localize usageNames[ix]. */
2466 CFStringAppend(string
, usageNames
[ix
]);
2470 appendPropertyP(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2476 static void appendKeyUsage(CFMutableArrayRef properties
,
2477 const DERItem
*extnValue
) {
2478 static const CFStringRef usageNames
[] = {
2479 CFSTR("Digital Signature"),
2480 CFSTR("Non-Repudiation"),
2481 CFSTR("Key Encipherment"),
2482 CFSTR("Data Encipherment"),
2483 CFSTR("Key Agreement"),
2486 CFSTR("Encipher Only"),
2487 CFSTR("Decipher Only")
2489 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
2490 usageNames
, sizeof(usageNames
) / sizeof(*usageNames
));
2494 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2495 const DERItem
*extnValue
) {
2496 DERPrivateKeyUsagePeriod pkup
;
2497 DERReturn drtn
= DERParseSequence(extnValue
,
2498 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2499 &pkup
, sizeof(pkup
));
2500 require_noerr_quiet(drtn
, badDER
);
2501 if (pkup
.notBefore
.length
) {
2502 appendDateContentProperty(properties
, CFSTR("Not Valid Before"),
2503 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2505 if (pkup
.notAfter
.length
) {
2506 appendDateContentProperty(properties
, CFSTR("Not Valid After"),
2507 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2511 appendInvalidProperty(properties
, CFSTR("Private Key Usage Period"),
2515 static void appendStringContentProperty(CFMutableArrayRef properties
,
2516 CFStringRef label
, const DERItem
*stringContent
,
2517 CFStringEncoding encoding
) {
2518 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2519 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2521 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2524 appendInvalidProperty(properties
, label
, stringContent
);
2529 OtherName ::= SEQUENCE {
2530 type-id OBJECT IDENTIFIER,
2531 value [0] EXPLICIT ANY DEFINED BY type-id }
2533 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2534 const DERItem
*otherNameContent
) {
2536 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2537 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2539 require_noerr_quiet(drtn
, badDER
);
2540 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2541 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
,
2542 &on
.typeIdentifier
);
2543 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2545 appendPropertyP(properties
, kSecPropertyTypeString
, oid_string
,
2548 appendUnparsedProperty(properties
, oid_string
, &on
.value
);
2550 CFReleaseNull(value_string
);
2551 CFReleaseNull(oid_string
);
2554 appendInvalidProperty(properties
, CFSTR("Other Name"), otherNameContent
);
2558 GeneralName ::= CHOICE {
2559 otherName [0] OtherName,
2560 rfc822Name [1] IA5String,
2561 dNSName [2] IA5String,
2562 x400Address [3] ORAddress,
2563 directoryName [4] Name,
2564 ediPartyName [5] EDIPartyName,
2565 uniformResourceIdentifier [6] IA5String,
2566 iPAddress [7] OCTET STRING,
2567 registeredID [8] OBJECT IDENTIFIER}
2569 EDIPartyName ::= SEQUENCE {
2570 nameAssigner [0] DirectoryString OPTIONAL,
2571 partyName [1] DirectoryString }
2573 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2574 DERTag tag
, const DERItem
*generalName
) {
2576 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2577 appendOtherNameContentProperty(properties
, generalName
);
2579 case ASN1_CONTEXT_SPECIFIC
| 1:
2581 appendStringContentProperty(properties
, CFSTR("Email Address"),
2582 generalName
, kCFStringEncodingASCII
);
2584 case ASN1_CONTEXT_SPECIFIC
| 2:
2586 appendStringContentProperty(properties
, CFSTR("DNS Name"), generalName
,
2587 kCFStringEncodingASCII
);
2589 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2590 appendUnparsedProperty(properties
, CFSTR("X.400 Address"),
2593 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2595 CFArrayRef directory_plist
=
2596 createPropertiesForX501Name(CFGetAllocator(properties
),
2598 appendPropertyP(properties
, kSecPropertyTypeSection
,
2599 CFSTR("Directory Name"), directory_plist
);
2600 CFRelease(directory_plist
);
2603 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2604 appendUnparsedProperty(properties
, CFSTR("EDI Party Name"),
2607 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2608 /* Technically I don't think this is valid, but there are certs out
2609 in the wild that use a constructed IA5String. In particular the
2610 VeriSign Time Stamping Authority CA.cer does this. */
2611 appendURLProperty(properties
, CFSTR("URI"), generalName
);
2613 case ASN1_CONTEXT_SPECIFIC
| 6:
2614 appendURLContentProperty(properties
, CFSTR("URI"), generalName
);
2616 case ASN1_CONTEXT_SPECIFIC
| 7:
2617 appendIPAddressContentProperty(properties
, CFSTR("IP Address"),
2620 case ASN1_CONTEXT_SPECIFIC
| 8:
2621 appendOIDProperty(properties
, CFSTR("Registered ID"), generalName
);
2631 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2632 const DERItem
*generalName
) {
2633 DERDecodedInfo generalNameContent
;
2634 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2635 require_noerr_quiet(drtn
, badDER
);
2636 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2637 &generalNameContent
.content
))
2640 appendInvalidProperty(properties
, CFSTR("General Name"), generalName
);
2645 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2647 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2648 const DERItem
*generalNamesContent
) {
2650 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2651 require_noerr_quiet(drtn
, badDER
);
2652 DERDecodedInfo generalNameContent
;
2653 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2655 if (!appendGeneralNameContentProperty(properties
,
2656 generalNameContent
.tag
, &generalNameContent
.content
)) {
2660 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2663 appendInvalidProperty(properties
, CFSTR("General Names"),
2664 generalNamesContent
);
2667 static void appendGeneralNames(CFMutableArrayRef properties
,
2668 const DERItem
*generalNames
) {
2669 DERDecodedInfo generalNamesContent
;
2670 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2671 require_noerr_quiet(drtn
, badDER
);
2672 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2674 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2677 appendInvalidProperty(properties
, CFSTR("General Names"), generalNames
);
2681 BasicConstraints ::= SEQUENCE {
2682 cA BOOLEAN DEFAULT FALSE,
2683 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
2685 static void appendBasicConstraints(CFMutableArrayRef properties
,
2686 const DERItem
*extnValue
) {
2687 DERBasicConstraints basicConstraints
;
2688 DERReturn drtn
= DERParseSequence(extnValue
,
2689 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
2690 &basicConstraints
, sizeof(basicConstraints
));
2691 require_noerr_quiet(drtn
, badDER
);
2693 appendBooleanProperty(properties
, CFSTR("Certificate Authority"),
2694 &basicConstraints
.cA
, false);
2696 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
2697 appendIntegerProperty(properties
, CFSTR("Path Length Constraint"),
2698 &basicConstraints
.pathLenConstraint
);
2702 appendInvalidProperty(properties
, CFSTR("Basic Constraints"), extnValue
);
2706 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
2708 DistributionPoint ::= SEQUENCE {
2709 distributionPoint [0] DistributionPointName OPTIONAL,
2710 reasons [1] ReasonFlags OPTIONAL,
2711 cRLIssuer [2] GeneralNames OPTIONAL }
2713 DistributionPointName ::= CHOICE {
2714 fullName [0] GeneralNames,
2715 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
2717 ReasonFlags ::= BIT STRING {
2721 affiliationChanged (3),
2723 cessationOfOperation (5),
2724 certificateHold (6),
2725 privilegeWithdrawn (7),
2728 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
2729 const DERItem
*extnValue
) {
2730 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2733 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
2734 require_noerr_quiet(drtn
, badDER
);
2735 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2736 DERDecodedInfo dpSeqContent
;
2737 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
2738 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2739 DERDistributionPoint dp
;
2740 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
2741 DERNumDistributionPointItemSpecs
,
2742 DERDistributionPointItemSpecs
,
2744 require_noerr_quiet(drtn
, badDER
);
2745 if (dp
.distributionPoint
.length
) {
2746 DERDecodedInfo distributionPointName
;
2747 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
2748 require_noerr_quiet(drtn
, badDER
);
2749 if (distributionPointName
.tag
==
2750 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
2752 appendGeneralNamesContent(properties
,
2753 &distributionPointName
.content
);
2754 } else if (distributionPointName
.tag
==
2755 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
2756 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
2758 appendPropertyP(properties
, kSecPropertyTypeSection
,
2759 CFSTR("Name Relative To CRL Issuer"), rdn_props
);
2760 CFRelease(rdn_props
);
2765 if (dp
.reasons
.length
) {
2766 static const CFStringRef reasonNames
[] = {
2768 CFSTR("Key Compromise"),
2769 CFSTR("CA Compromise"),
2770 CFSTR("Affiliation Changed"),
2771 CFSTR("Superseded"),
2772 CFSTR("Cessation Of Operation"),
2773 CFSTR("Certificate Hold"),
2774 CFSTR("Priviledge Withdrawn"),
2775 CFSTR("AA Compromise")
2777 appendBitStringContentNames(properties
, CFSTR("Reasons"),
2779 reasonNames
, sizeof(reasonNames
) / sizeof(*reasonNames
));
2781 if (dp
.cRLIssuer
.length
) {
2782 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
2783 &kCFTypeArrayCallBacks
);
2784 appendPropertyP(properties
, kSecPropertyTypeSection
,
2785 CFSTR("CRL Issuer"), crlIssuer
);
2786 CFRelease(crlIssuer
);
2787 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
2790 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2793 appendInvalidProperty(properties
, CFSTR("Crl Distribution Points"),
2797 /* Decode a sequence of integers into a comma separated list of ints. */
2798 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
2799 CFStringRef label
, const DERItem
*intSequenceContent
) {
2800 CFMutableStringRef value
= NULL
;
2801 CFStringRef intDesc
= NULL
;
2802 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2804 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
2805 require_noerr_quiet(drtn
, badDER
);
2806 DERDecodedInfo intContent
;
2808 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
))
2810 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
2811 intDesc
= copyIntegerContentDescription(
2812 allocator
, &intContent
.content
);
2813 require_quiet(intDesc
, badDER
);
2815 CFStringAppendFormat(value
, NULL
, CFSTR(", %@"), intDesc
);
2817 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
2818 require_quiet(value
, badDER
);
2820 CFReleaseNull(intDesc
);
2822 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2824 appendPropertyP(properties
, kSecPropertyTypeString
,
2825 CFSTR("Notice Numbers"), value
);
2829 /* DROPTHOUGH if !value. */
2831 CFReleaseNull(value
);
2832 CFReleaseNull(intDesc
);
2833 appendInvalidProperty(properties
, label
, intSequenceContent
);
2836 static void appendCertificatePolicies(CFMutableArrayRef properties
,
2837 const DERItem
*extnValue
) {
2838 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2839 CFStringRef piLabel
= NULL
, pqLabel
= NULL
;
2842 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
2843 require_noerr_quiet(drtn
, badDER
);
2844 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2845 DERDecodedInfo piContent
;
2847 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
2848 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2849 DERPolicyInformation pi
;
2850 drtn
= DERParseSequenceContent(&piContent
.content
,
2851 DERNumPolicyInformationItemSpecs
,
2852 DERPolicyInformationItemSpecs
,
2854 require_noerr_quiet(drtn
, badDER
);
2855 require_quiet(piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2856 CFSTR("Policy Identifier #%d"), pin
++), badDER
);
2857 appendOIDProperty(properties
, piLabel
, &pi
.policyIdentifier
);
2858 CFReleaseNull(piLabel
);
2859 if (pi
.policyQualifiers
.length
== 0)
2863 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
2864 require_noerr_quiet(drtn
, badDER
);
2865 DERDecodedInfo pqContent
;
2867 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
2868 DERPolicyQualifierInfo pqi
;
2869 drtn
= DERParseSequenceContent(&pqContent
.content
,
2870 DERNumPolicyQualifierInfoItemSpecs
,
2871 DERPolicyQualifierInfoItemSpecs
,
2873 require_noerr_quiet(drtn
, badDER
);
2874 DERDecodedInfo qualifierContent
;
2875 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
2876 require_noerr_quiet(drtn
, badDER
);
2877 require_quiet(pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2878 CFSTR("Policy Qualifier #%d"), pqn
++), badDER
);
2879 appendOIDProperty(properties
, pqLabel
, &pqi
.policyQualifierID
);
2880 CFReleaseNull(pqLabel
);
2881 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
2882 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
2883 appendURLContentProperty(properties
,
2885 &qualifierContent
.content
);
2886 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
2887 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2889 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
2890 DERNumUserNoticeItemSpecs
,
2891 DERUserNoticeItemSpecs
,
2893 require_noerr_quiet(drtn
, badDER
);
2894 if (un
.noticeRef
.length
) {
2895 DERNoticeReference nr
;
2896 drtn
= DERParseSequenceContent(&un
.noticeRef
,
2897 DERNumNoticeReferenceItemSpecs
,
2898 DERNoticeReferenceItemSpecs
,
2900 require_noerr_quiet(drtn
, badDER
);
2901 appendDERThingProperty(properties
,
2902 CFSTR("Organization"),
2904 appendIntegerSequenceContent(properties
,
2905 CFSTR("Notice Numbers"), &nr
.noticeNumbers
);
2907 if (un
.explicitText
.length
) {
2908 appendDERThingProperty(properties
, CFSTR("Explicit Text"),
2912 appendUnparsedProperty(properties
, CFSTR("Qualifier"),
2916 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2918 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2921 CFReleaseNull(piLabel
);
2922 CFReleaseNull(pqLabel
);
2923 appendInvalidProperty(properties
, CFSTR("Certificate Policies"),
2927 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
2928 const DERItem
*extnValue
) {
2930 DERDecodedInfo keyIdentifier
;
2931 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
2932 require_noerr_quiet(drtn
, badDER
);
2933 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
2934 appendDataProperty(properties
, CFSTR("Key Identifier"),
2935 &keyIdentifier
.content
);
2939 appendInvalidProperty(properties
, CFSTR("Invalid Subject Key Identifier"),
2944 AuthorityKeyIdentifier ::= SEQUENCE {
2945 keyIdentifier [0] KeyIdentifier OPTIONAL,
2946 authorityCertIssuer [1] GeneralNames OPTIONAL,
2947 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
2948 -- authorityCertIssuer and authorityCertSerialNumber MUST both
2949 -- be present or both be absent
2951 KeyIdentifier ::= OCTET STRING
2953 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
2954 const DERItem
*extnValue
) {
2955 DERAuthorityKeyIdentifier akid
;
2957 drtn
= DERParseSequence(extnValue
,
2958 DERNumAuthorityKeyIdentifierItemSpecs
,
2959 DERAuthorityKeyIdentifierItemSpecs
,
2960 &akid
, sizeof(akid
));
2961 require_noerr_quiet(drtn
, badDER
);
2962 if (akid
.keyIdentifier
.length
) {
2963 appendDataProperty(properties
, CFSTR("Key Identifier"),
2964 &akid
.keyIdentifier
);
2966 if (akid
.authorityCertIssuer
.length
||
2967 akid
.authorityCertSerialNumber
.length
) {
2968 require_quiet(akid
.authorityCertIssuer
.length
&&
2969 akid
.authorityCertSerialNumber
.length
, badDER
);
2970 /* Perhaps put in a subsection called Authority Certificate Issuer. */
2971 appendGeneralNamesContent(properties
,
2972 &akid
.authorityCertIssuer
);
2973 appendIntegerProperty(properties
,
2974 CFSTR("Authority Certificate Serial Number"),
2975 &akid
.authorityCertSerialNumber
);
2980 appendInvalidProperty(properties
, CFSTR("Authority Key Identifier"),
2985 PolicyConstraints ::= SEQUENCE {
2986 requireExplicitPolicy [0] SkipCerts OPTIONAL,
2987 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
2989 SkipCerts ::= INTEGER (0..MAX)
2991 static void appendPolicyConstraints(CFMutableArrayRef properties
,
2992 const DERItem
*extnValue
) {
2993 DERPolicyConstraints pc
;
2995 drtn
= DERParseSequence(extnValue
,
2996 DERNumPolicyConstraintsItemSpecs
,
2997 DERPolicyConstraintsItemSpecs
,
2999 require_noerr_quiet(drtn
, badDER
);
3000 if (pc
.requireExplicitPolicy
.length
) {
3001 appendIntegerProperty(properties
,
3002 CFSTR("Require Explicit Policy"), &pc
.requireExplicitPolicy
);
3004 if (pc
.inhibitPolicyMapping
.length
) {
3005 appendIntegerProperty(properties
,
3006 CFSTR("Inhibit Policy Mapping"), &pc
.inhibitPolicyMapping
);
3012 appendInvalidProperty(properties
, CFSTR("Policy Constraints"), extnValue
);
3016 extendedKeyUsage EXTENSION ::= {
3017 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3018 IDENTIFIED BY id-ce-extKeyUsage }
3020 KeyPurposeId ::= OBJECT IDENTIFIER
3022 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3023 const DERItem
*extnValue
) {
3026 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3027 require_noerr_quiet(drtn
, badDER
);
3028 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3029 DERDecodedInfo currDecoded
;
3030 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3031 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3032 appendOIDProperty(properties
, CFSTR("Purpose"),
3033 &currDecoded
.content
);
3035 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3038 appendInvalidProperty(properties
, CFSTR("Extended Key Usage"), extnValue
);
3042 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3044 AuthorityInfoAccessSyntax ::=
3045 SEQUENCE SIZE (1..MAX) OF AccessDescription
3047 AccessDescription ::= SEQUENCE {
3048 accessMethod OBJECT IDENTIFIER,
3049 accessLocation GeneralName }
3051 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3053 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3055 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3057 static void appendInfoAccess(CFMutableArrayRef properties
,
3058 const DERItem
*extnValue
) {
3061 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3062 require_noerr_quiet(drtn
, badDER
);
3063 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3064 DERDecodedInfo adContent
;
3065 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3066 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3067 DERAccessDescription ad
;
3068 drtn
= DERParseSequenceContent(&adContent
.content
,
3069 DERNumAccessDescriptionItemSpecs
,
3070 DERAccessDescriptionItemSpecs
,
3072 require_noerr_quiet(drtn
, badDER
);
3073 appendOIDProperty(properties
, CFSTR("Access Method"),
3075 //CFSTR("Access Location");
3076 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3078 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3081 appendInvalidProperty(properties
, CFSTR("Authority Information Access"),
3085 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3086 const DERItem
*extnValue
) {
3087 static const CFStringRef certTypes
[] = {
3088 CFSTR("SSL client"),
3089 CFSTR("SSL server"),
3091 CFSTR("Object Signing"),
3095 CFSTR("Object Signing CA")
3097 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
3098 certTypes
, sizeof(certTypes
) / sizeof(*certTypes
));
3102 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3103 const DERItem
*extnValue
) {
3107 * The list of Qualified Cert Statement statementIds we understand, even though
3108 * we don't actually do anything with them; if these are found in a Qualified
3109 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3111 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3113 /* id-qcs := { id-pkix 11 } */
3114 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3115 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3116 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3117 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3118 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3119 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3121 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3123 static void appendQCCertStatements(CFMutableArrayRef properties
,
3124 const DERItem
*extnValue
) {
3129 static bool appendPrintableDERSequenceP(CFMutableArrayRef properties
,
3130 CFStringRef label
, const DERItem
*sequence
) {
3133 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3134 require_noerr_quiet(drtn
, badSequence
);
3135 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3136 DERDecodedInfo currDecoded
;
3137 bool appendedSomething
= false;
3138 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3139 switch (currDecoded
.tag
)
3142 case ASN1_SEQUENCE
: // 16
3143 case ASN1_SET
: // 17
3144 // skip constructed object lengths
3147 case ASN1_UTF8_STRING
: // 12
3148 case ASN1_NUMERIC_STRING
: // 18
3149 case ASN1_PRINTABLE_STRING
: // 19
3150 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3151 case ASN1_VIDEOTEX_STRING
: // 21
3152 case ASN1_IA5_STRING
: // 22
3153 case ASN1_GRAPHIC_STRING
: // 25
3154 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3155 case ASN1_GENERAL_STRING
: // 27
3156 case ASN1_UNIVERSAL_STRING
: // 28
3158 CFStringRef string
=
3159 copyDERThingContentDescription(CFGetAllocator(properties
),
3160 currDecoded
.tag
, &currDecoded
.content
, false);
3161 require_quiet(string
, badSequence
);
3163 appendPropertyP(properties
, kSecPropertyTypeString
, label
,
3165 CFReleaseNull(string
);
3166 appendedSomething
= true;
3173 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3174 return appendedSomething
;
3179 static void appendExtension(CFMutableArrayRef parent
,
3180 const SecCertificateExtension
*extn
) {
3181 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3182 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3183 &kCFTypeArrayCallBacks
);
3185 *extnID
= &extn
->extnID
,
3186 *extnValue
= &extn
->extnValue
;
3188 appendBoolProperty(properties
, CFSTR("Critical"), extn
->critical
);
3191 bool handled
= true;
3192 /* Extensions that we know how to handle ourselves... */
3193 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3194 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3196 switch (extnID
->data
[extnID
->length
- 1]) {
3197 case 14: /* SubjectKeyIdentifier id-ce 14 */
3198 appendSubjectKeyIdentifier(properties
, extnValue
);
3200 case 15: /* KeyUsage id-ce 15 */
3201 appendKeyUsage(properties
, extnValue
);
3203 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3204 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3206 case 17: /* SubjectAltName id-ce 17 */
3207 case 18: /* IssuerAltName id-ce 18 */
3208 appendGeneralNames(properties
, extnValue
);
3210 case 19: /* BasicConstraints id-ce 19 */
3211 appendBasicConstraints(properties
, extnValue
);
3213 case 30: /* NameConstraints id-ce 30 */
3216 case 31: /* CRLDistributionPoints id-ce 31 */
3217 appendCrlDistributionPoints(properties
, extnValue
);
3219 case 32: /* CertificatePolicies id-ce 32 */
3220 appendCertificatePolicies(properties
, extnValue
);
3222 case 33: /* PolicyMappings id-ce 33 */
3225 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3226 appendAuthorityKeyIdentifier(properties
, extnValue
);
3228 case 36: /* PolicyConstraints id-ce 36 */
3229 appendPolicyConstraints(properties
, extnValue
);
3231 case 37: /* ExtKeyUsage id-ce 37 */
3232 appendExtendedKeyUsage(properties
, extnValue
);
3234 case 46: /* FreshestCRL id-ce 46 */
3237 case 54: /* InhibitAnyPolicy id-ce 54 */
3244 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3245 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3247 switch (extnID
->data
[extnID
->length
- 1]) {
3248 case 1: /* AuthorityInfoAccess id-pe 1 */
3249 appendInfoAccess(properties
, extnValue
);
3251 case 3: /* QCStatements id-pe 3 */
3254 case 11: /* SubjectInfoAccess id-pe 11 */
3255 appendInfoAccess(properties
, extnValue
);
3261 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3262 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3263 appendNetscapeCertType(properties
, extnValue
);
3269 /* Try to parse and display printable string(s). */
3270 if (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3271 /* Nothing to do here appendPrintableDERSequenceP did the work. */
3273 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3274 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3278 /* Extensions that we know how to handle ourselves... */
3279 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3280 appendSubjectKeyIdentifier(properties
, extnValue
);
3281 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3282 appendKeyUsage(properties
, extnValue
);
3283 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3284 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3285 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3286 appendGeneralNames(properties
, extnValue
);
3287 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3288 appendGeneralNames(properties
, extnValue
);
3289 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3290 appendBasicConstraints(properties
, extnValue
);
3291 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3292 appendCrlDistributionPoints(properties
, extnValue
);
3293 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3294 appendCertificatePolicies(properties
, extnValue
);
3295 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3296 appendAuthorityKeyIdentifier(properties
, extnValue
);
3297 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3298 appendPolicyConstraints(properties
, extnValue
);
3299 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3300 appendExtendedKeyUsage(properties
, extnValue
);
3301 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3302 appendInfoAccess(properties
, extnValue
);
3303 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3304 appendInfoAccess(properties
, extnValue
);
3305 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3306 appendNetscapeCertType(properties
, extnValue
);
3308 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3309 appendEntrustVersInfo(properties
, extnValue
);
3312 /* Try to parse and display printable string(s). */
3313 if (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3314 /* Nothing to do here appendPrintableDERSequenceP did the work. */
3316 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3317 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3320 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
, extnID
);
3321 appendPropertyP(parent
, kSecPropertyTypeSection
, oid_string
, properties
);
3322 CFRelease(oid_string
);
3323 CFRelease(properties
);
3326 /* Different types of summary types from least desired to most desired. */
3329 kSummaryTypePrintable
,
3330 kSummaryTypeOrganizationName
,
3331 kSummaryTypeOrganizationalUnitName
,
3332 kSummaryTypeCommonName
,
3336 enum SummaryType type
;
3337 CFStringRef summary
;
3338 CFStringRef description
;
3341 static OSStatus
obtainSummaryFromX501Name(void *context
,
3342 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3343 struct Summary
*summary
= (struct Summary
*)context
;
3344 enum SummaryType stype
= kSummaryTypeNone
;
3345 CFStringRef string
= NULL
;
3346 if (DEROidCompare(type
, &oidCommonName
)) {
3347 /* We skip Common Names that have generic values. */
3348 const char tfm
[] = "Thawte Freemail Member";
3349 if ((value
->length
== sizeof(tfm
) + 1) &&
3350 !memcmp(value
->data
+ 2, tfm
, sizeof(tfm
) - 1)) {
3351 return errSecSuccess
;
3353 stype
= kSummaryTypeCommonName
;
3354 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3355 stype
= kSummaryTypeOrganizationalUnitName
;
3356 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3357 stype
= kSummaryTypeOrganizationName
;
3358 } else if (DEROidCompare(type
, &oidDescription
)) {
3359 if (!summary
->description
) {
3360 summary
->description
= string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3363 stype
= kSummaryTypePrintable
;
3365 stype
= kSummaryTypePrintable
;
3368 /* Use the first field we encounter of the highest priority type. */
3369 if (summary
->type
< stype
) {
3371 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3375 CFReleaseSafe(summary
->summary
);
3376 summary
->summary
= string
;
3377 summary
->type
= stype
;
3380 CFReleaseSafe(string
);
3383 return errSecSuccess
;
3386 CFStringRef
SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate
) {
3387 struct Summary summary
= {};
3388 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3389 /* If we found a description and a common name we change the summary to
3390 CommonName (Description). */
3391 if (summary
.description
) {
3392 if (summary
.type
== kSummaryTypeCommonName
) {
3393 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3394 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3395 CFRelease(summary
.summary
);
3396 summary
.summary
= newSummary
;
3398 CFRelease(summary
.description
);
3401 if (!summary
.summary
) {
3402 /* If we didn't find a suitable printable string in the subject at all, we try
3403 the first email address in the certificate instead. */
3404 CFArrayRef names
= SecCertificateCopyRFC822NamesP(certificate
);
3406 /* If we didn't find any email addresses in the certificate, we try finding
3407 a DNS name instead. */
3408 names
= SecCertificateCopyDNSNamesP(certificate
);
3411 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3412 CFRetain(summary
.summary
);
3417 return summary
.summary
;
3420 CFStringRef
SecCertificateCopyIssuerSummaryP(SecCertificateRefP certificate
) {
3421 struct Summary summary
= {};
3422 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3423 /* If we found a description and a common name we change the summary to
3424 CommonName (Description). */
3425 if (summary
.description
) {
3426 if (summary
.type
== kSummaryTypeCommonName
) {
3427 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3428 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3429 CFRelease(summary
.summary
);
3430 summary
.summary
= newSummary
;
3432 CFRelease(summary
.description
);
3435 return summary
.summary
;
3438 /* Return the earliest date on which all certificates in this chain are still
3440 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3441 SecCertificateRefP certificate
) {
3442 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3444 while (certificate
->_parent
) {
3445 certificate
= certificate
->_parent
;
3446 if (earliest
> certificate
->_notAfter
)
3447 earliest
= certificate
->_notAfter
;
3454 /* Return the latest date on which all certificates in this chain will be
3456 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3457 SecCertificateRefP certificate
) {
3458 CFAbsoluteTime latest
= certificate
->_notBefore
;
3460 while (certificate
->_parent
) {
3461 certificate
= certificate
->_parent
;
3462 if (latest
< certificate
->_notBefore
)
3463 latest
= certificate
->_notBefore
;
3470 bool SecCertificateIsValidP(SecCertificateRefP certificate
,
3471 CFAbsoluteTime verifyTime
) {
3473 return certificate
->_notBefore
<= verifyTime
&&
3474 verifyTime
<= certificate
->_notAfter
;
3477 CFIndex
SecCertificateVersionP(SecCertificateRefP certificate
) {
3478 return certificate
->_version
+ 1;
3481 CFAbsoluteTime
SecCertificateNotValidBeforeP(SecCertificateRefP certificate
) {
3482 return certificate
->_notBefore
;
3485 CFAbsoluteTime
SecCertificateNotValidAfterP(SecCertificateRefP certificate
) {
3486 return certificate
->_notAfter
;
3489 CFMutableArrayRef
SecCertificateCopySummaryPropertiesP(
3490 SecCertificateRefP certificate
, CFAbsoluteTime verifyTime
) {
3491 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3492 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3493 &kCFTypeArrayCallBacks
);
3495 /* First we put the subject summary name. */
3496 CFStringRef ssummary
= SecCertificateCopySubjectSummaryP(certificate
);
3498 appendPropertyP(summary
, kSecPropertyTypeTitle
,
3500 CFRelease(ssummary
);
3503 CFStringRef isummary
= CFSTR("Issuer Summary");
3504 appendPropertyP(summary
, kSecPropertyTypeString
,
3505 CFSTR("Issued By"), isummary
);
3506 CFRelease(isummary
);
3509 /* Let see if this certificate is currently valid. */
3511 CFAbsoluteTime when
;
3512 CFStringRef message
;
3514 if (verifyTime
> certificate
->_notAfter
) {
3515 label
= CFSTR("Expired");
3516 when
= certificate
->_notAfter
;
3517 ptype
= kSecPropertyTypeError
;
3518 message
= CFSTR("This certificate has expired");
3519 } else if (certificate
->_notBefore
> verifyTime
) {
3520 label
= CFSTR("Valid from");
3521 when
= certificate
->_notBefore
;
3522 ptype
= kSecPropertyTypeError
;
3523 message
= CFSTR("This certificate is not yet valid");
3525 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3526 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3527 if (verifyTime
> last
) {
3528 label
= CFSTR("Expired");
3530 ptype
= kSecPropertyTypeError
;
3531 message
= CFSTR("This certificate has an issuer that has expired");
3532 } else if (verifyTime
< first
) {
3533 label
= CFSTR("Valid from");
3535 ptype
= kSecPropertyTypeError
;
3536 message
= CFSTR("This certificate has an issuer that is not yet valid");
3538 label
= CFSTR("Expires");
3539 when
= certificate
->_notAfter
;
3540 ptype
= kSecPropertyTypeSuccess
;
3541 message
= CFSTR("This certificate is valid");
3545 appendDateProperty(summary
, label
, when
);
3546 appendPropertyP(summary
, ptype
, NULL
, message
);
3551 CFArrayRef
SecCertificateCopyPropertiesP(SecCertificateRefP certificate
) {
3552 if (!certificate
->_properties
) {
3553 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3554 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3555 &kCFTypeArrayCallBacks
);
3556 require_quiet(properties
, out
);
3558 /* First we put the Subject Name in the property list. */
3559 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3560 &certificate
->_subject
);
3561 if (subject_plist
) {
3562 appendPropertyP(properties
, kSecPropertyTypeSection
,
3563 CFSTR("Subject Name"), subject_plist
);
3565 CFReleaseNull(subject_plist
);
3567 /* Next we put the Issuer Name in the property list. */
3568 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3569 &certificate
->_issuer
);
3571 appendPropertyP(properties
, kSecPropertyTypeSection
,
3572 CFSTR("Issuer Name"), issuer_plist
);
3574 CFReleaseNull(issuer_plist
);
3577 CFStringRef versionString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"),
3578 certificate
->_version
+ 1);
3579 if (versionString
) {
3580 appendPropertyP(properties
, kSecPropertyTypeString
,
3581 CFSTR("Version"), versionString
);
3583 CFReleaseNull(versionString
);
3586 if (certificate
->_serialNum
.length
) {
3587 appendIntegerProperty(properties
, CFSTR("Serial Number"),
3588 &certificate
->_serialNum
);
3591 /* Signature algorithm. */
3592 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3593 &certificate
->_tbsSigAlg
);
3596 /* Validity dates. */
3597 appendDateProperty(properties
, CFSTR("Not Valid Before"),
3598 certificate
->_notBefore
);
3599 appendDateProperty(properties
, CFSTR("Not Valid After"),
3600 certificate
->_notAfter
);
3602 if (certificate
->_subjectUniqueID
.length
) {
3603 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
3604 &certificate
->_subjectUniqueID
);
3606 if (certificate
->_issuerUniqueID
.length
) {
3607 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
3608 &certificate
->_issuerUniqueID
);
3611 /* Public key algorithm. */
3612 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
3613 &certificate
->_algId
);
3615 /* Consider breaking down an RSA public key into modulus and
3617 appendDataProperty(properties
, CFSTR("Public Key Data"),
3618 &certificate
->_pubKeyDER
);
3620 /* @@@ Key Usage. */
3622 appendDataProperty(properties
, CFSTR("Signature"),
3623 &certificate
->_signature
);
3626 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
3627 appendExtension(properties
, &certificate
->_extensions
[ix
]);
3630 /* @@@ Key Fingerprints. */
3632 certificate
->_properties
= properties
;
3636 CFRetainSafe(certificate
->_properties
);
3637 return certificate
->_properties
;
3640 CFDataRef
SecCertificateCopySerialNumberP(
3641 SecCertificateRefP certificate
) {
3642 if (certificate
->_serialNumber
) {
3643 CFRetain(certificate
->_serialNumber
);
3645 return certificate
->_serialNumber
;
3649 * Accessor for normalized issuer content
3651 CFDataRef
SecCertificateGetNormalizedIssuerContentP(
3652 SecCertificateRefP certificate
) {
3653 return certificate
->_normalizedIssuer
;
3657 * Accessor for normalized subject content
3659 CFDataRef
SecCertificateGetNormalizedSubjectContentP(
3660 SecCertificateRefP certificate
) {
3661 return certificate
->_normalizedSubject
;
3665 * Returns DER-encoded normalized issuer sequence
3666 * for use with SecItemCopyMatching; caller must release
3668 CFDataRef
SecCertificateCopyNormalizedIssuerSequenceP(
3669 SecCertificateRefP certificate
) {
3670 if (!certificate
|| !certificate
->_normalizedIssuer
) {
3674 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
3675 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
3677 return SecDERItemCopySequenceP(&tmpdi
);
3681 * Returns DER-encoded normalized subject sequence
3682 * for use with SecItemCopyMatching; caller must release
3684 CFDataRef
SecCertificateCopyNormalizedSubjectSequenceP(
3685 SecCertificateRefP certificate
) {
3686 if (!certificate
|| !certificate
->_normalizedSubject
) {
3690 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
);
3691 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
3693 return SecDERItemCopySequenceP(&tmpdi
);
3696 /* Verify that certificate was signed by issuerKey. */
3697 OSStatus
SecCertificateIsSignedByP(SecCertificateRefP certificate
,
3698 SecKeyRefP issuerKey
) {
3699 /* Setup algId in SecAsn1AlgId format. */
3701 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3702 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3703 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3704 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3707 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3708 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3709 certificate
->_signature
.data
, certificate
->_signature
.length
);
3711 secinfo("verify", "signature verify failed: %d", status
);
3712 return errSecNotSigner
;
3716 return errSecSuccess
;
3720 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRefP certificate
,
3721 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3722 if (!signatureCheckOnly
) {
3723 /* It turns out we don't actually need to use normalized subject and
3724 issuer according to rfc2459. */
3726 /* If present we should check issuerID against the issuer subjectID. */
3728 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3729 then we should look for a SubjectKeyIdentifier in the issuer
3731 If we have a authorityCertSerialNumber we can use that for chaining.
3732 If we have a authorityCertIssuer we can use that? (or not) */
3734 /* Verify that this cert was issued by issuer. Do so by chaining
3735 either issuerID to subjectID or normalized issuer to normalized
3737 CFDataRef normalizedIssuer
=
3738 SecCertificateGetNormalizedIssuerContentP(certificate
);
3739 CFDataRef normalizedIssuerSubject
=
3740 SecCertificateGetNormalizedSubjectContentP(issuer
);
3741 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3742 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3743 return errSecIssuerMismatch
;
3746 /* Next verify that this cert was signed by issuer. */
3747 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3749 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3750 /* FIXME: We sould cache this (or at least the digest) until we find
3751 a suitable issuer. */
3752 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3753 CFIndex signedDataLength
;
3754 CertVerifyReturn crtn
;
3755 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3756 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3757 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3758 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3759 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3760 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3761 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3762 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3763 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3765 secinfo("verify", "unsupported algorithm");
3766 return errSecUnsupportedAlgorithm
;
3769 secinfo("verify", "*DigestInfo returned: %d", crtn
);
3770 /* FIXME: Do proper error code translation. */
3771 return errSecUnsupportedAlgorithm
;
3774 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3775 signedData
, signedDataLength
,
3776 certificate
->_signature
.data
, certificate
->_signature
.length
);
3778 secinfo("verify", "signature verify failed: %d", status
);
3779 return errSecNotSigner
;
3782 return errSecSuccess
;
3785 static OSStatus
_SecCertificateSetParent(SecCertificateRefP certificate
,
3786 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3788 if (certificate
->_parent
) {
3789 /* Setting a certificates issuer twice is only allowed if the new
3790 issuer is equal to the current one. */
3791 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3795 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3796 signatureCheckOnly
);
3798 OSStatus status
= errSecSuccess
;
3801 if (CFEqual(certificate
, issuer
)) {
3802 /* We don't retain ourselves cause that would be bad mojo,
3803 however we do record that we are properly self signed. */
3804 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3805 secinfo("cert", "set self as parent");
3806 return errSecSuccess
;
3810 certificate
->_parent
= issuer
;
3811 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3817 static bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate
) {
3818 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3819 certificate
->_isSelfSigned
=
3820 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3821 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3824 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3827 /* Return true iff we were able to set our own parent from one of the
3828 certificates in other_certificates, return false otherwise. If
3829 signatureCheckOnly is true, we can skip the subject == issuer or
3830 authorityKeyIdentifier tests. */
3831 static bool SecCertificateSetParentFrom(SecCertificateRefP certificate
,
3832 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3833 CFIndex count
= CFArrayGetCount(other_certificates
);
3835 for (ix
= 0; ix
< count
; ++ix
) {
3836 SecCertificateRefP candidate
= (SecCertificateRefP
)
3837 CFArrayGetValueAtIndex(other_certificates
, ix
);
3838 if (_SecCertificateSetParent(certificate
, candidate
,
3839 signatureCheckOnly
))
3845 /* Lookup the parent of certificate in the keychain and set it. */
3846 static bool SecCertificateFindParent(SecCertificateRefP certificate
) {
3847 /* FIXME: Search for things other than just subject of our issuer if we
3848 have a subjectID or authorityKeyIdentifier. */
3849 CFDataRef normalizedIssuer
=
3850 SecCertificateGetNormalizedIssuerContentP(certificate
);
3851 const void *keys
[] = {
3858 kSecClassCertificate
,
3863 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3864 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3866 OSStatus status
= SecItemCopyMatching(query
, &results
);
3869 secinfo("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3873 CFArrayRef certs
= (CFArrayRef
)results
;
3874 /* Since we already know the certificates we are providing as candidates
3875 have been checked for subject matching, we can ask
3876 SecCertificateSetParentFrom to skip everything except the signature
3878 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3883 OSStatus
SecCertificateCompleteChainP(SecCertificateRefP certificate
,
3884 CFArrayRef other_certificates
) {
3886 if (certificate
->_parent
== NULL
) {
3887 if (SecCertificateIsSelfSignedP(certificate
))
3888 return errSecSuccess
;
3889 if (!other_certificates
||
3890 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
3892 if (!SecCertificateFindParent(certificate
))
3893 return errSecIssuerNotFound
;
3896 certificate
= certificate
->_parent
;
3901 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
3902 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
3903 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
3904 if (gnType
== GNT_IPAddress
) {
3905 CFStringRef string
= copyIPAddressContentDescription(
3906 kCFAllocatorDefault
, generalName
);
3908 CFArrayAppendValue(ipAddresses
, string
);
3911 return errSecInvalidCertificate
;
3914 return errSecSuccess
;
3917 CFArrayRef
SecCertificateCopyIPAddressesP(SecCertificateRefP certificate
) {
3918 /* These can only exist in the subject alt name. */
3919 if (!certificate
->_subjectAltName
)
3922 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
3923 0, &kCFTypeArrayCallBacks
);
3924 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
3925 ipAddresses
, appendIPAddressesFromGeneralNames
);
3926 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
3927 CFRelease(ipAddresses
);
3933 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
3934 const DERItem
*generalName
) {
3935 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
3936 if (gnType
== GNT_DNSName
) {
3937 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
3938 generalName
->data
, generalName
->length
,
3939 kCFStringEncodingUTF8
, FALSE
);
3941 CFArrayAppendValue(dnsNames
, string
);
3944 return errSecInvalidCertificate
;
3947 return errSecSuccess
;
3950 /* Return true if the passed in string matches the
3951 Preferred name syntax from sections 2.3.1. in RFC 1035.
3952 With the added check that we disallow empty dns names.
3953 Also in order to support wildcard DNSNames we allow for the '*'
3954 character anywhere in a dns component where we currently allow
3957 <domain> ::= <subdomain> | " "
3959 <subdomain> ::= <label> | <subdomain> "." <label>
3961 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
3963 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
3965 <let-dig-hyp> ::= <let-dig> | "-"
3967 <let-dig> ::= <letter> | <digit>
3969 <letter> ::= any one of the 52 alphabetic characters A through Z in
3970 upper case and a through z in lower case
3972 <digit> ::= any one of the ten digits 0 through 9
3974 static bool isDNSName(CFStringRef string
) {
3975 CFStringInlineBuffer buf
;
3976 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
3977 /* From RFC 1035 2.3.4. Size limits:
3978 labels 63 octets or less
3979 names 255 octets or less */
3980 require_quiet(length
<= 255, notDNS
);
3981 CFRange range
= { 0, length
};
3982 CFStringInitInlineBuffer(string
, &buf
, range
);
3986 kDNSStateAfterAlpha
,
3987 kDNSStateAfterDigit
,
3989 } state
= kDNSStateInital
;
3991 bool nonAlpha
= false;
3992 for (ix
= 0; ix
< length
; ++ix
) {
3993 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
3996 require_quiet(labelLength
<= 64 &&
3997 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
3999 state
= kDNSStateAfterDot
;
4002 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4004 state
= kDNSStateAfterAlpha
;
4005 } else if ('0' <= ch
&& ch
<= '9') {
4007 /* The requirement for labels to start with a letter was
4008 dropped so we don't check this anymore. */
4009 require_quiet(state
== kDNSStateAfterAlpha
||
4010 state
== kDNSStateAfterDigit
||
4011 state
== kDNSStateAfterDash
, notDNS
);
4013 state
= kDNSStateAfterDigit
;
4015 } else if (ch
== '-') {
4016 require_quiet(state
== kDNSStateAfterAlpha
||
4017 state
== kDNSStateAfterDigit
||
4018 state
== kDNSStateAfterDash
, notDNS
);
4019 state
= kDNSStateAfterDash
;
4026 /* We don't allow a dns name to end in a dot, and we require the
4027 final name component to only have alphanumeric chars. */
4028 require_quiet(!nonAlpha
&& labelLength
<= 63 &&
4029 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4037 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4038 const DERItem
*value
, CFIndex rdnIX
) {
4039 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4040 if (DEROidCompare(type
, &oidCommonName
)) {
4041 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4044 if (isDNSName(string
)) {
4045 /* We found a common name that is formatted like a valid
4047 CFArrayAppendValue(dnsNames
, string
);
4051 return errSecInvalidCertificate
;
4054 return errSecSuccess
;
4057 /* Not everything returned by this function is going to be a proper DNS name,
4058 we also return the certificates common name entries from the subject,
4059 assuming they look like dns names as specified in RFC 1035. */
4060 CFArrayRef
SecCertificateCopyDNSNamesP(SecCertificateRefP certificate
) {
4061 /* These can exist in the subject alt name or in the subject. */
4062 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4063 0, &kCFTypeArrayCallBacks
);
4064 OSStatus status
= errSecSuccess
;
4065 if (certificate
->_subjectAltName
) {
4066 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4067 dnsNames
, appendDNSNamesFromGeneralNames
);
4069 /* RFC 2818 section 3.1. Server Identity
4071 If a subjectAltName extension of type dNSName is present, that MUST
4072 be used as the identity. Otherwise, the (most specific) Common Name
4073 field in the Subject field of the certificate MUST be used. Although
4074 the use of the Common Name is existing practice, it is deprecated and
4075 Certification Authorities are encouraged to use the dNSName instead.
4078 This implies that if we found 1 or more DNSNames in the
4079 subjectAltName, we should not use the Common Name of the subject as
4082 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4083 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4084 appendDNSNamesFromX501Name
);
4086 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4087 CFRelease(dnsNames
);
4093 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4094 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4095 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4096 if (gnType
== GNT_RFC822Name
) {
4097 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4098 generalName
->data
, generalName
->length
,
4099 kCFStringEncodingASCII
, FALSE
);
4101 CFArrayAppendValue(dnsNames
, string
);
4104 return errSecInvalidCertificate
;
4107 return errSecSuccess
;
4110 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4111 const DERItem
*value
, CFIndex rdnIX
) {
4112 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4113 if (DEROidCompare(type
, &oidEmailAddress
)) {
4114 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4117 CFArrayAppendValue(dnsNames
, string
);
4120 return errSecInvalidCertificate
;
4123 return errSecSuccess
;
4126 CFArrayRef
SecCertificateCopyRFC822NamesP(SecCertificateRefP certificate
) {
4127 /* These can exist in the subject alt name or in the subject. */
4128 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4129 0, &kCFTypeArrayCallBacks
);
4130 OSStatus status
= errSecSuccess
;
4131 if (certificate
->_subjectAltName
) {
4132 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4133 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4136 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4137 appendRFC822NamesFromX501Name
);
4139 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4140 CFRelease(rfc822Names
);
4146 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4147 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4148 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4149 if (DEROidCompare(type
, &oidCommonName
)) {
4150 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4153 CFArrayAppendValue(commonNames
, string
);
4156 return errSecInvalidCertificate
;
4159 return errSecSuccess
;
4162 CFArrayRef
SecCertificateCopyCommonNamesP(SecCertificateRefP certificate
) {
4163 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4164 0, &kCFTypeArrayCallBacks
);
4166 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4167 appendCommonNamesFromX501Name
);
4168 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4169 CFRelease(commonNames
);
4175 static OSStatus
appendOrganizationFromX501Name(void *context
,
4176 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4177 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4178 if (DEROidCompare(type
, &oidOrganizationName
)) {
4179 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4182 CFArrayAppendValue(organization
, string
);
4185 return errSecInvalidCertificate
;
4188 return errSecSuccess
;
4191 CFArrayRef
SecCertificateCopyOrganizationP(SecCertificateRefP certificate
) {
4192 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4193 0, &kCFTypeArrayCallBacks
);
4195 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4196 appendOrganizationFromX501Name
);
4197 if (status
|| CFArrayGetCount(organization
) == 0) {
4198 CFRelease(organization
);
4199 organization
= NULL
;
4201 return organization
;
4204 const SecCEBasicConstraints
*
4205 SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate
) {
4206 if (certificate
->_basicConstraints
.present
)
4207 return &certificate
->_basicConstraints
;
4212 const SecCEPolicyConstraints
*
4213 SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate
) {
4214 if (certificate
->_policyConstraints
.present
)
4215 return &certificate
->_policyConstraints
;
4221 SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate
) {
4222 return certificate
->_policyMappings
;
4225 const SecCECertificatePolicies
*
4226 SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate
) {
4227 if (certificate
->_certificatePolicies
.present
)
4228 return &certificate
->_certificatePolicies
;
4234 SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate
) {
4235 return certificate
->_inhibitAnyPolicySkipCerts
;
4238 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4239 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4240 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4241 if (gnType
== GNT_OtherName
) {
4243 DERReturn drtn
= DERParseSequenceContent(generalName
,
4244 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4246 require_noerr_quiet(drtn
, badDER
);
4247 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4249 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4250 &on
.value
, true), badDER
);
4251 CFArrayAppendValue(ntPrincipalNames
, string
);
4255 return errSecSuccess
;
4258 return errSecInvalidCertificate
;
4262 CFArrayRef
SecCertificateCopyNTPrincipalNamesP(SecCertificateRefP certificate
) {
4263 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4264 0, &kCFTypeArrayCallBacks
);
4265 OSStatus status
= errSecSuccess
;
4266 if (certificate
->_subjectAltName
) {
4267 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4268 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4270 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4271 CFRelease(ntPrincipalNames
);
4272 ntPrincipalNames
= NULL
;
4274 return ntPrincipalNames
;
4277 static OSStatus
appendToRFC2253String(void *context
,
4278 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4279 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4283 ST stateOrProvinceName
4285 OU organizationalUnitName
4287 STREET streetAddress
4291 /* Prepend a + if this is not the first RDN in an RDN set.
4292 Otherwise prepend a , if this is not the first RDN. */
4294 CFStringAppend(string
, CFSTR("+"));
4295 else if (CFStringGetLength(string
)) {
4296 CFStringAppend(string
, CFSTR(","));
4299 CFStringRef label
, oid
= NULL
;
4300 /* @@@ Consider changing this to a dictionary lookup keyed by the
4301 decimal representation. */
4302 #if 0 // represent all labels as oids
4303 if (DEROidCompare(type
, &oidCommonName
)) {
4304 label
= CFSTR("CN");
4305 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4307 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4308 label
= CFSTR("ST");
4309 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4311 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4312 label
= CFSTR("OU");
4313 } else if (DEROidCompare(type
, &oidCountryName
)) {
4316 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4317 label
= CFSTR("STREET");
4318 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4319 label
= CFSTR("DC");
4320 } else if (DEROidCompare(type
, &oidUserID
)) {
4321 label
= CFSTR("UID");
4326 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4329 CFStringAppend(string
, label
);
4330 CFStringAppend(string
, CFSTR("="));
4331 CFStringRef raw
= NULL
;
4333 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4336 /* Append raw to string while escaping:
4337 a space or "#" character occurring at the beginning of the string
4338 a space character occurring at the end of the string
4339 one of the characters ",", "+", """, "\", "<", ">" or ";"
4341 CFStringInlineBuffer buffer
;
4342 CFIndex ix
, length
= CFStringGetLength(raw
);
4343 CFRange range
= { 0, length
};
4344 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4345 for (ix
= 0; ix
< length
; ++ix
) {
4346 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4348 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4349 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4350 ch
== '<' || ch
== '>' || ch
== ';' ||
4351 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4352 (ch
== '#' && ix
== 0)) {
4353 UniChar chars
[] = { '\\', ch
};
4354 CFStringAppendCharacters(string
, chars
, 2);
4356 CFStringAppendCharacters(string
, &ch
, 1);
4361 /* Append the value in hex. */
4362 CFStringAppend(string
, CFSTR("#"));
4364 for (ix
= 0; ix
< value
->length
; ++ix
)
4365 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4370 return errSecSuccess
;
4373 CFStringRef
SecCertificateCopySubjectStringP(SecCertificateRefP certificate
) {
4374 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4375 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4376 if (status
|| CFStringGetLength(string
) == 0) {
4383 static OSStatus
appendToCompanyNameString(void *context
,
4384 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4385 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4386 if (CFStringGetLength(string
) != 0)
4387 return errSecSuccess
;
4389 if (!DEROidCompare(type
, &oidOrganizationName
))
4390 return errSecSuccess
;
4393 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4395 return errSecSuccess
;
4396 CFStringAppend(string
, raw
);
4399 return errSecSuccess
;
4402 CFStringRef
SecCertificateCopyCompanyNameP(SecCertificateRefP certificate
) {
4403 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4404 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4405 appendToCompanyNameString
);
4406 if (status
|| CFStringGetLength(string
) == 0) {
4413 CFDataRef
SecDERItemCopySequenceP(DERItem
*content
) {
4414 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4415 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4416 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4418 CFDataSetLength(sequence
, sequence_length
);
4419 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4420 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
4421 require_noerr_quiet(DEREncodeLength(content
->length
,
4422 sequence_ptr
, &seq_len_length
), out
);
4423 sequence_ptr
+= seq_len_length
;
4424 memcpy(sequence_ptr
, content
->data
, content
->length
);
4427 CFReleaseSafe(sequence
);
4431 CFDataRef
SecCertificateCopyIssuerSequenceP(
4432 SecCertificateRefP certificate
) {
4433 return SecDERItemCopySequenceP(&certificate
->_issuer
);
4436 CFDataRef
SecCertificateCopySubjectSequenceP(
4437 SecCertificateRefP certificate
) {
4438 return SecDERItemCopySequenceP(&certificate
->_subject
);
4441 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithmP(
4442 SecCertificateRefP certificate
) {
4443 return &certificate
->_algId
;
4446 const DERItem
*SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate
) {
4447 return &certificate
->_pubKeyDER
;
4450 SecKeyRefP
SecCertificateCopyPublicKeyP(SecCertificateRefP certificate
) {
4451 SecKeyRefP publicKey
= NULL
;
4453 const DERAlgorithmId
*algId
=
4454 SecCertificateGetPublicKeyAlgorithmP(certificate
);
4455 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4456 if (DEROidCompare(&algId
->oid
, &oidRsa
)) {
4457 publicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
4458 keyData
->data
, keyData
->length
, kSecKeyEncodingPkcs1
);
4460 secinfo("cert", "Unsupported algorithm oid");
4467 CFDataRef
SecCertificateGetSHA1DigestP(SecCertificateRefP certificate
) {
4468 if (!certificate
->_sha1Digest
) {
4469 certificate
->_sha1Digest
=
4470 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4471 certificate
->_der
.data
, certificate
->_der
.length
);
4474 return certificate
->_sha1Digest
;
4477 CFDataRef
SecCertificateCopyIssuerSHA1DigestP(SecCertificateRefP certificate
) {
4478 CFDataRef digest
= NULL
;
4479 CFDataRef issuer
= SecCertificateCopyIssuerSequenceP(certificate
);
4481 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4482 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4488 CFDataRef
SecCertificateCopyPublicKeySHA1DigestP(SecCertificateRefP certificate
) {
4489 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4490 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4493 /* note: this function is exported with a non-P-suffix name.
4494 * since it doesn't accept or return a SecCertificateRefP type, this is OK for now.
4496 CFDataRef
SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator
,
4497 CFDataRef der_certificate
)
4499 CFDataRef result
= NULL
;
4500 SecCertificateRefP iosCertRef
= SecCertificateCreateWithDataP(allocator
, der_certificate
);
4501 if (NULL
== iosCertRef
)
4506 result
= SecCertificateCopyPublicKeySHA1DigestP(iosCertRef
);
4507 CFRelease(iosCertRef
);
4511 CFDataRef
SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate
) {
4512 if (!certificate
->_authorityKeyID
&&
4513 certificate
->_authorityKeyIdentifier
.length
) {
4514 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4515 certificate
->_authorityKeyIdentifier
.data
,
4516 certificate
->_authorityKeyIdentifier
.length
);
4519 return certificate
->_authorityKeyID
;
4522 CFDataRef
SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate
) {
4523 if (!certificate
->_subjectKeyID
&&
4524 certificate
->_subjectKeyIdentifier
.length
) {
4525 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4526 certificate
->_subjectKeyIdentifier
.data
,
4527 certificate
->_subjectKeyIdentifier
.length
);
4530 return certificate
->_subjectKeyID
;
4533 CFArrayRef
SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate
) {
4534 return certificate
->_crlDistributionPoints
;
4537 CFArrayRef
SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate
) {
4538 return certificate
->_ocspResponders
;
4541 CFArrayRef
SecCertificateGetCAIssuersP(SecCertificateRefP certificate
) {
4542 return certificate
->_caIssuers
;
4545 bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate
) {
4546 return certificate
->_subjectAltName
&&
4547 certificate
->_subjectAltName
->critical
;
4550 bool SecCertificateHasSubjectP(SecCertificateRefP certificate
) {
4551 /* Since the _subject field is the content of the subject and not the
4552 whole thing, we can simply check for a 0 length subject here. */
4553 return certificate
->_subject
.length
!= 0;
4556 bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate
) {
4557 return certificate
->_foundUnknownCriticalExtension
;
4560 /* Private API functions. */
4561 void SecCertificateShowP(SecCertificateRefP certificate
) {
4563 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4564 fprintf(stderr
, "\n");
4567 CFDictionaryRef
SecCertificateCopyAttributeDictionaryP(
4568 SecCertificateRefP certificate
) {
4569 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4570 CFNumberRef certificateType
, certificateEncoding
;
4571 CFStringRef label
, alias
;
4572 CFDataRef skid
, pubKeyDigest
, certData
;
4573 CFDictionaryRef dict
= NULL
;
4577 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4578 SInt32 ctv
= certificate
->_version
+ 1;
4579 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4580 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4581 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4582 certData
= SecCertificateCopyDataP(certificate
);
4583 skid
= SecCertificateGetSubjectKeyIDP(certificate
);
4584 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4585 certificate
->_pubKeyDER
.length
);
4587 /* We still need to figure out how to deal with multi valued attributes. */
4588 alias
= SecCertificateCopyRFC822NamesP(certificate
);
4589 label
= SecCertificateCopySubjectSummary(certificate
);
4595 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4596 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4597 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4599 DICT_ADDPAIR(kSecAttrLabel
, label
);
4601 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4602 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4603 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4604 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4606 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4607 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4608 DICT_ADDPAIR(kSecValueData
, certData
);
4609 dict
= DICT_CREATE(allocator
);
4611 CFReleaseSafe(label
);
4612 CFReleaseSafe(pubKeyDigest
);
4613 CFReleaseSafe(certData
);
4614 CFReleaseSafe(certificateEncoding
);
4615 CFReleaseSafe(certificateType
);
4620 SecCertificateRefP
SecCertificateCreateFromAttributeDictionaryP(
4621 CFDictionaryRef refAttributes
) {
4622 /* @@@ Support having an allocator in refAttributes. */
4623 CFAllocatorRef allocator
= NULL
;
4624 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4625 return SecCertificateCreateWithDataP(allocator
, data
);
4628 bool SecCertificateIsSelfSignedCAP(SecCertificateRefP certificate
) {
4629 bool result
= false;
4630 SecKeyRefP publicKey
;
4631 require(publicKey
= SecCertificateCopyPublicKeyP(certificate
), out
);
4632 CFDataRef normalizedIssuer
=
4633 SecCertificateGetNormalizedIssuerContentP(certificate
);
4634 CFDataRef normalizedSubject
=
4635 SecCertificateGetNormalizedSubjectContentP(certificate
);
4636 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4637 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4639 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyIDP(certificate
);
4640 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyIDP(certificate
);
4641 if (authorityKeyID
) {
4642 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4645 if (SecCertificateVersionP(certificate
) >= 3) {
4646 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraintsP(certificate
);
4647 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4648 require_noerr_quiet(SecCertificateIsSignedByP(certificate
, publicKey
), out
);
4653 CFReleaseSafe(publicKey
);
4657 SecKeyUsage
SecCertificateGetKeyUsageP(SecCertificateRefP certificate
) {
4658 return certificate
->_keyUsage
;
4661 CFArrayRef
SecCertificateCopyExtendedKeyUsageP(SecCertificateRefP certificate
)
4663 CFMutableArrayRef extended_key_usage_oids
=
4664 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4665 require_quiet(extended_key_usage_oids
, out
);
4667 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4668 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4669 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4670 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4673 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4674 require_noerr_quiet(drtn
, out
);
4675 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4676 DERDecodedInfo currDecoded
;
4678 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4679 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4680 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4681 currDecoded
.content
.data
, currDecoded
.content
.length
);
4682 require_quiet(oid
, out
);
4683 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4686 require_quiet(drtn
== DR_EndOfSequence
, out
);
4687 return extended_key_usage_oids
;
4691 CFReleaseSafe(extended_key_usage_oids
);
4695 SecCertificateRefP
SecCertificateCreateWithPEMP(CFAllocatorRef allocator
,
4696 CFDataRef pem_certificate
)
4698 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
4699 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
4700 uint8_t *base64_data
= NULL
;
4701 SecCertificateRefP cert
= NULL
;
4702 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
4703 //const size_t length = CFDataGetLength(pem_certificate);
4704 char *begin
= strstr((const char *)data
, begin_cert
);
4705 char *end
= strstr((const char *)data
, end_cert
);
4708 begin
+= sizeof(begin_cert
) - 1;
4709 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
4710 if (base64_length
) {
4711 require_quiet(base64_data
= calloc(1, base64_length
), out
);
4712 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
4713 cert
= SecCertificateCreateWithBytesP(kCFAllocatorDefault
, base64_data
, base64_length
);
4720 static void convertCertificateToCFData(const void *value
, void *context
) {
4721 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4722 SecCertificateRefP certificate
= (SecCertificateRefP
)value
;
4723 CFDataRef data
= SecCertificateCopyDataP(certificate
);
4724 CFArrayAppendValue(result
, data
);
4728 /* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
4729 CFArrayRef
SecCertificateArrayCopyDataArrayP(CFArrayRef certificates
) {
4730 CFIndex count
= CFArrayGetCount(certificates
);
4731 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
4732 CFRange all_certs
= { 0, count
};
4733 CFArrayApplyFunction(certificates
, all_certs
, convertCertificateToCFData
, result
);
4737 /* AUDIT[securityd](done):
4738 value (ok) is an element in a caller provided array.
4740 static void convertCFDataToCertificate(const void *value
, void *context
) {
4741 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4742 CFDataRef data
= (CFDataRef
)value
;
4743 if (data
&& CFGetTypeID(data
) == CFDataGetTypeID()) {
4744 SecCertificateRefP certificate
= SecCertificateCreateWithDataP(kCFAllocatorDefault
, data
);
4746 CFArrayAppendValue(result
, certificate
);
4747 CFRelease(certificate
);
4752 /* AUDIT[securityd](done):
4753 certificates (ok) is a caller provided array, only its cf type has
4756 CFArrayRef
SecCertificateDataArrayCopyArrayP(CFArrayRef certificates
) {
4757 CFIndex count
= CFArrayGetCount(certificates
);
4758 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
4759 CFRange all_certs
= { 0, count
};
4760 CFArrayApplyFunction(certificates
, all_certs
, convertCFDataToCertificate
, result
);