2 * Copyright (c) 2006-2011 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 /* Allows us to build genanchors against the BaseSDK. */
30 #undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31 #undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
34 #include <Security/SecCertificateInternal.h>
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFBundle.h>
40 #include <CoreFoundation/CFDictionary.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFCalendar.h>
43 #include <CoreFoundation/CFTimeZone.h>
46 #include <AssertMacros.h>
47 #include <libDER/DER_CertCrl.h>
48 #include <libDER/DER_Encode.h>
49 #include <libDER/DER_Keys.h>
50 #include <libDER/asn1Types.h>
51 #include <libDER/oids.h>
52 #include "SecBasePriv.h"
53 #include "SecRSAKey.h"
54 #include "SecFramework.h"
56 #include "SecItemPriv.h"
58 #include <security_utilities/debugging.h>
60 #include <libkern/OSByteOrder.h>
62 #include <Security/SecInternal.h>
63 #include <Security/SecFrameworkStrings.h>
64 #include "SecBase64.h"
66 typedef struct SecCertificateExtension
{
70 } SecCertificateExtension
;
73 typedef struct KnownExtension
{
79 kSecSelfSignedUnknown
= 0,
85 struct __SecCertificate
{
88 DERItem _der
; /* Entire certificate in DER form. */
89 DERItem _tbs
; /* To Be Signed cert DER bytes. */
90 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
91 DERItem _signature
; /* The content of the sig bit string. */
94 DERItem _serialNum
; /* Integer. */
95 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
96 DERItem _issuer
; /* Sequence of RDN. */
97 CFAbsoluteTime _notBefore
;
98 CFAbsoluteTime _notAfter
;
99 DERItem _subject
; /* Sequence of RDN. */
100 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
101 DERItem _pubKeyDER
; /* contents of bit string */
102 DERItem _issuerUniqueID
; /* bit string, optional */
103 DERItem _subjectUniqueID
; /* bit string, optional */
106 /* Known extensions if the certificate contains them,
107 extnValue.length will be > 0. */
108 KnownExtension _authorityKeyID
;
110 /* This extension is used to uniquely identify a certificate from among
111 several that have the same subject name. If the extension is not
112 present, its value is calculated by performing a SHA-1 hash of the
113 certificate's DER encoded subjectPublicKeyInfo, as recommended by
115 KnownExtension _subjectKeyID
;
116 KnownExtension _keyUsage
;
117 KnownExtension _extendedKeyUsage
;
118 KnownExtension _basicConstraints
;
119 KnownExtension _netscapeCertType
;
120 KnownExtension _subjectAltName
;
121 KnownExtension _qualCertStatements
;
124 bool _foundUnknownCriticalExtension
;
126 /* Well known certificate extensions. */
127 SecCEBasicConstraints _basicConstraints
;
128 SecCEPolicyConstraints _policyConstraints
;
129 CFDictionaryRef _policyMappings
;
130 SecCECertificatePolicies _certificatePolicies
;
132 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
133 value of the SkipCerts field of the InhibitAnyPolicy extension
135 uint32_t _inhibitAnyPolicySkipCerts
;
137 /* If KeyUsage extension is not present this is 0, otherwise it's
138 the value of the extension. */
139 SecKeyUsage _keyUsage
;
141 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
142 Length = 0 if not present. */
143 DERItem _subjectKeyIdentifier
;
145 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
146 Length = 0 if not present. */
147 DERItem _authorityKeyIdentifier
;
148 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
149 _authorityKeyIdentifierSerialNumber have non zero length if present.
150 Both are either present or absent together. */
151 DERItem _authorityKeyIdentifierIssuer
;
152 DERItem _authorityKeyIdentifierSerialNumber
;
154 /* Subject alt name extension, if present. Not malloced, it's just a
155 pointer to an element in the _extensions array. */
156 const SecCertificateExtension
*_subjectAltName
;
158 /* Parsed extension values. */
160 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
161 CFMutableArrayRef _crlDistributionPoints
;
163 /* Array of CFURLRefs containing the URI values of accessLocations of each
164 id-ad-ocsp AccessDescription in the Authority Information Access
166 CFMutableArrayRef _ocspResponders
;
168 /* Array of CFURLRefs containing the URI values of accessLocations of each
169 id-ad-caIssuers AccessDescription in the Authority Information Access
171 CFMutableArrayRef _caIssuers
;
173 /* All other (non known) extensions. The _extensions array is malloced. */
174 CFIndex _extensionCount
;
175 SecCertificateExtension
*_extensions
;
177 /* Optional cached fields. */
180 CFArrayRef _properties
;
181 CFDataRef _serialNumber
;
182 CFDataRef _normalizedIssuer
;
183 CFDataRef _normalizedSubject
;
184 CFDataRef _authorityKeyID
;
185 CFDataRef _subjectKeyID
;
187 CFDataRef _sha1Digest
;
188 uint8_t _isSelfSigned
;
192 /* Public Constants for property list keys. */
193 CFStringRef kSecPropertyKeyType
= CFSTR("type");
194 CFStringRef kSecPropertyKeyLabel
= CFSTR("label");
195 CFStringRef kSecPropertyKeyLocalizedLabel
= CFSTR("localized label");
196 CFStringRef kSecPropertyKeyValue
= CFSTR("value");
198 /* Public Constants for property list values. */
199 CFStringRef kSecPropertyTypeWarning
= CFSTR("warning");
200 CFStringRef kSecPropertyTypeError
= CFSTR("error");
201 CFStringRef kSecPropertyTypeSuccess
= CFSTR("success");
202 CFStringRef kSecPropertyTypeTitle
= CFSTR("title");
203 CFStringRef kSecPropertyTypeSection
= CFSTR("section");
204 CFStringRef kSecPropertyTypeData
= CFSTR("data");
205 CFStringRef kSecPropertyTypeString
= CFSTR("string");
206 CFStringRef kSecPropertyTypeURL
= CFSTR("url");
207 CFStringRef kSecPropertyTypeDate
= CFSTR("date");
209 /* Extension parsing routine. */
210 typedef void (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
211 const SecCertificateExtension
*extn
);
213 /* CFRuntime regsitration data. */
214 static pthread_once_t kSecCertificateRegisterClass
= PTHREAD_ONCE_INIT
;
215 static CFTypeID kSecCertificateTypeID
= _kCFRuntimeNotATypeID
;
217 /* Mapping from extension OIDs (as a DERItem *) to
218 SecCertificateExtensionParser extension parsing routines. */
219 static CFDictionaryRef gExtensionParsers
;
221 /* Forward declartions of static functions. */
222 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
);
223 static void SecCertificateDestroy(CFTypeRef cf
);
224 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
225 CFAbsoluteTime
*absTime
);
227 /* Static functions. */
228 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
) {
229 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
230 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
231 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
232 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
233 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
234 CFReleaseSafe(issuer
);
235 CFReleaseSafe(subject
);
239 static void SecCertificateDestroy(CFTypeRef cf
) {
240 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
241 if (certificate
->_certificatePolicies
.policies
)
242 free(certificate
->_certificatePolicies
.policies
);
243 CFReleaseSafe(certificate
->_policyMappings
);
244 CFReleaseSafe(certificate
->_crlDistributionPoints
);
245 CFReleaseSafe(certificate
->_ocspResponders
);
246 CFReleaseSafe(certificate
->_caIssuers
);
247 if (certificate
->_extensions
) {
248 free(certificate
->_extensions
);
250 CFReleaseSafe(certificate
->_pubKey
);
251 CFReleaseSafe(certificate
->_der_data
);
252 CFReleaseSafe(certificate
->_properties
);
253 CFReleaseSafe(certificate
->_serialNumber
);
254 CFReleaseSafe(certificate
->_normalizedIssuer
);
255 CFReleaseSafe(certificate
->_normalizedSubject
);
256 CFReleaseSafe(certificate
->_authorityKeyID
);
257 CFReleaseSafe(certificate
->_subjectKeyID
);
258 CFReleaseSafe(certificate
->_sha1Digest
);
261 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
262 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
263 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
266 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
268 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
271 /* Hash of the certificate is der length + signature length + last 4 bytes
273 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
274 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
275 size_t der_length
= certificate
->_der
.length
;
276 size_t sig_length
= certificate
->_signature
.length
;
277 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
278 CFHashCode hashCode
= 0;
279 for (; ix
< sig_length
; ++ix
)
280 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
282 return (hashCode
+ der_length
+ sig_length
);
287 /************************************************************************/
288 /************************* General Name Parsing *************************/
289 /************************************************************************/
291 typedef OSStatus (*parseGeneralNameCallback
)(void *context
,
292 SecCEGeneralNameType type
, const DERItem
*value
);
296 GeneralName ::= CHOICE {
297 otherName [0] OtherName,
298 rfc822Name [1] IA5String,
299 dNSName [2] IA5String,
300 x400Address [3] ORAddress,
301 directoryName [4] Name,
302 ediPartyName [5] EDIPartyName,
303 uniformResourceIdentifier [6] IA5String,
304 iPAddress [7] OCTET STRING,
305 registeredID [8] OBJECT IDENTIFIER}
307 OtherName ::= SEQUENCE {
308 type-id OBJECT IDENTIFIER,
309 value [0] EXPLICIT ANY DEFINED BY type-id }
311 EDIPartyName ::= SEQUENCE {
312 nameAssigner [0] DirectoryString OPTIONAL,
313 partyName [1] DirectoryString }
315 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
316 const DERItem
*generalNameContent
,
317 void *context
, parseGeneralNameCallback callback
) {
319 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
320 return callback(context
, GNT_OtherName
, generalNameContent
);
321 case ASN1_CONTEXT_SPECIFIC
| 1:
322 return callback(context
, GNT_RFC822Name
, generalNameContent
);
323 case ASN1_CONTEXT_SPECIFIC
| 2:
324 return callback(context
, GNT_DNSName
, generalNameContent
);
325 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
326 return callback(context
, GNT_X400Address
, generalNameContent
);
327 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
328 return callback(context
, GNT_DirectoryName
, generalNameContent
);
329 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
330 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
331 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
333 /* Technically I don't think this is valid, but there are certs out
334 in the wild that use a constructed IA5String. In particular the
335 VeriSign Time Stamping Authority CA.cer does this. */
336 DERDecodedInfo uriContent
;
337 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
338 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
339 return callback(context
, GNT_URI
, &uriContent
.content
);
341 case ASN1_CONTEXT_SPECIFIC
| 6:
342 return callback(context
, GNT_URI
, generalNameContent
);
343 case ASN1_CONTEXT_SPECIFIC
| 7:
344 return callback(context
, GNT_IPAddress
, generalNameContent
);
345 case ASN1_CONTEXT_SPECIFIC
| 8:
346 return callback(context
, GNT_RegisteredID
, generalNameContent
);
351 return errSecInvalidCertificate
;
354 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
355 void *context
, parseGeneralNameCallback callback
) {
357 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
358 require_noerr_quiet(drtn
, badDER
);
359 DERDecodedInfo generalNameContent
;
360 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
362 OSStatus status
= parseGeneralNameContentProperty(
363 generalNameContent
.tag
, &generalNameContent
.content
, context
,
368 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
372 return errSecInvalidCertificate
;
375 static OSStatus
parseGeneralNames(const DERItem
*generalNames
, void *context
,
376 parseGeneralNameCallback callback
) {
377 DERDecodedInfo generalNamesContent
;
378 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
379 require_noerr_quiet(drtn
, badDER
);
380 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
381 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
384 return errSecInvalidCertificate
;
390 GeneralName ::= CHOICE {
391 otherName [0] OtherName,
392 rfc822Name [1] IA5String,
393 dNSName [2] IA5String,
394 x400Address [3] ORAddress,
395 directoryName [4] Name,
396 ediPartyName [5] EDIPartyName,
397 uniformResourceIdentifier [6] IA5String,
398 iPAddress [7] OCTET STRING,
399 registeredID [8] OBJECT IDENTIFIER}
401 EDIPartyName ::= SEQUENCE {
402 nameAssigner [0] DirectoryString OPTIONAL,
403 partyName [1] DirectoryString }
405 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
406 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
408 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
409 generalName
->nameType
= GNT_OtherName
;
410 generalName
->berEncoded
= true;
411 generalName
->name
= *generalNameContent
;
413 case ASN1_CONTEXT_SPECIFIC
| 1:
415 generalName
->nameType
= GNT_RFC822Name
;
416 generalName
->berEncoded
= false;
417 generalName
->name
= *generalNameContent
;
419 case ASN1_CONTEXT_SPECIFIC
| 2:
421 generalName
->nameType
= GNT_DNSName
;
422 generalName
->berEncoded
= false;
423 generalName
->name
= *generalNameContent
;
425 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
426 generalName
->nameType
= GNT_X400Address
;
427 generalName
->berEncoded
= true;
428 generalName
->name
= *generalNameContent
;
430 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
431 generalName
->nameType
= GNT_DirectoryName
;
432 generalName
->berEncoded
= true;
433 generalName
->name
= *generalNameContent
;
435 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
436 generalName
->nameType
= GNT_EdiPartyName
;
437 generalName
->berEncoded
= true;
438 generalName
->name
= *generalNameContent
;
440 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
442 /* Technically I don't think this is valid, but there are certs out
443 in the wild that use a constructed IA5String. In particular the
444 VeriSign Time Stamping Authority CA.cer does this. */
445 DERDecodedInfo decoded
;
446 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
447 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
448 generalName
->nameType
= GNT_URI
;
449 generalName
->berEncoded
= false;
450 generalName
->name
= decoded
.content
;
453 case ASN1_CONTEXT_SPECIFIC
| 6:
454 generalName
->nameType
= GNT_URI
;
455 generalName
->berEncoded
= false;
456 generalName
->name
= *generalNameContent
;
458 case ASN1_CONTEXT_SPECIFIC
| 7:
459 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
460 8 octects, addr/mask for ipv6 it's 32. */
461 generalName
->nameType
= GNT_IPAddress
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= *generalNameContent
;
465 case ASN1_CONTEXT_SPECIFIC
| 8:
466 /* name is the content of an OID. */
467 generalName
->nameType
= GNT_RegisteredID
;
468 generalName
->berEncoded
= false;
469 generalName
->name
= *generalNameContent
;
477 return errSecInvalidCertificate
;
481 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
483 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
484 CFIndex
*count
, SecCEGeneralName
**name
) {
485 SecCEGeneralName
*generalNames
= NULL
;
487 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
488 require_noerr_quiet(drtn
, badDER
);
489 DERDecodedInfo generalNameContent
;
490 CFIndex generalNamesCount
= 0;
491 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
495 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
497 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
499 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
501 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
503 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
504 &generalNameContent
.content
, &generalNames
[ix
])) {
509 *count
= generalNamesCount
;
510 *name
= generalNames
;
516 return errSecInvalidCertificate
;
519 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
520 CFIndex
*count
, SecCEGeneralName
**name
) {
521 DERDecodedInfo generalNamesContent
;
522 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
523 require_noerr_quiet(drtn
, badDER
);
524 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
526 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
529 return errSecInvalidCertificate
;
533 /************************************************************************/
534 /************************** X.509 Name Parsing **************************/
535 /************************************************************************/
537 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
538 const DERItem
*value
, CFIndex rdnIX
);
540 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
541 parseX501NameCallback callback
) {
543 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
544 require_noerr_quiet(drtn
, badDER
);
545 DERDecodedInfo atvContent
;
547 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
548 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
549 DERAttributeTypeAndValue atv
;
550 drtn
= DERParseSequenceContent(&atvContent
.content
,
551 DERNumAttributeTypeAndValueItemSpecs
,
552 DERAttributeTypeAndValueItemSpecs
,
554 require_noerr_quiet(drtn
, badDER
);
555 require_quiet(atv
.type
.length
!= 0, badDER
);
556 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
560 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
564 return errSecInvalidCertificate
;
567 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
568 parseX501NameCallback callback
) {
570 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
571 require_noerr_quiet(drtn
, badDER
);
572 DERDecodedInfo currDecoded
;
573 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
574 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
575 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
580 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
585 return errSecInvalidCertificate
;
588 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
589 parseX501NameCallback callback
) {
590 DERDecodedInfo x501NameContent
;
591 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
592 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
593 return errSecInvalidCertificate
;
595 return parseX501NameContent(&x501NameContent
.content
, context
,
600 /************************************************************************/
601 /********************** Extension Parsing Routines **********************/
602 /************************************************************************/
604 static void SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
605 const SecCertificateExtension
*extn
) {
606 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
607 DERDecodedInfo keyIdentifier
;
608 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
609 require_noerr_quiet(drtn
, badDER
);
610 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
611 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
615 secwarning("Invalid SubjectKeyIdentifier Extension");
618 static void SecCEPKeyUsage(SecCertificateRef certificate
,
619 const SecCertificateExtension
*extn
) {
620 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
621 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
622 DERDecodedInfo bitStringContent
;
623 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
624 require_noerr_quiet(drtn
, badDER
);
625 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
626 DERSize len
= bitStringContent
.content
.length
- 1;
627 require_quiet(len
== 1 || len
== 2, badDER
);
628 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
629 require_quiet(numUnusedBits
< 8, badDER
);
630 /* Flip the bits in the bit string so the first bit in the lsb. */
631 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
632 uint_fast16_t value
= bitStringContent
.content
.data
[1];
635 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
641 for (ix
= 0; ix
< bits
; ++ix
) {
647 certificate
->_keyUsage
= keyUsage
;
650 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
653 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
654 const SecCertificateExtension
*extn
) {
655 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
658 static void SecCEPSubjectAltName(SecCertificateRef certificate
,
659 const SecCertificateExtension
*extn
) {
660 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
661 certificate
->_subjectAltName
= extn
;
664 static void SecCEPIssuerAltName(SecCertificateRef certificate
,
665 const SecCertificateExtension
*extn
) {
666 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
669 static void SecCEPBasicConstraints(SecCertificateRef certificate
,
670 const SecCertificateExtension
*extn
) {
671 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
672 DERBasicConstraints basicConstraints
;
673 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
674 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
675 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
676 require_noerr_quiet(DERParseBoolean(&basicConstraints
.cA
, false,
677 &certificate
->_basicConstraints
.isCA
), badDER
);
678 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
679 require_noerr_quiet(DERParseInteger(
680 &basicConstraints
.pathLenConstraint
,
681 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
682 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
684 certificate
->_basicConstraints
.present
= true;
685 certificate
->_basicConstraints
.critical
= extn
->critical
;
688 certificate
->_basicConstraints
.present
= false;
689 secwarning("Invalid BasicConstraints Extension");
692 static void SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
693 const SecCertificateExtension
*extn
) {
694 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
698 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
700 PolicyInformation ::= SEQUENCE {
701 policyIdentifier CertPolicyId,
702 policyQualifiers SEQUENCE SIZE (1..MAX) OF
703 PolicyQualifierInfo OPTIONAL }
705 CertPolicyId ::= OBJECT IDENTIFIER
707 PolicyQualifierInfo ::= SEQUENCE {
708 policyQualifierId PolicyQualifierId,
709 qualifier ANY DEFINED BY policyQualifierId }
711 static void SecCEPCertificatePolicies(SecCertificateRef certificate
,
712 const SecCertificateExtension
*extn
) {
713 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
716 SecCEPolicyInformation
*policies
= NULL
;
717 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
718 require_noerr_quiet(drtn
, badDER
);
719 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
720 DERDecodedInfo piContent
;
721 DERSize policy_count
= 0;
722 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
723 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
726 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
727 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
729 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
730 DERSize policy_ix
= 0;
731 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
732 DERPolicyInformation pi
;
733 drtn
= DERParseSequenceContent(&piContent
.content
,
734 DERNumPolicyInformationItemSpecs
,
735 DERPolicyInformationItemSpecs
,
737 require_noerr_quiet(drtn
, badDER
);
738 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
739 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
741 certificate
->_certificatePolicies
.present
= true;
742 certificate
->_certificatePolicies
.critical
= extn
->critical
;
743 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
744 certificate
->_certificatePolicies
.policies
= policies
;
749 certificate
->_certificatePolicies
.present
= false;
750 secwarning("Invalid CertificatePolicies Extension");
754 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
756 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
757 issuerDomainPolicy CertPolicyId,
758 subjectDomainPolicy CertPolicyId }
761 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
762 const SecCertificateExtension
*extn
) {
763 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
766 SecCEPolicyMapping
*mappings
= NULL
;
767 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
768 require_noerr_quiet(drtn
, badDER
);
769 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
770 DERDecodedInfo pmContent
;
771 DERSize mapping_count
= 0;
772 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
773 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
776 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
778 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
779 DERSize mapping_ix
= 0;
780 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
782 drtn
= DERParseSequenceContent(&pmContent
.content
,
783 DERNumPolicyMappingItemSpecs
,
784 DERPolicyMappingItemSpecs
,
786 require_noerr_quiet(drtn
, badDER
);
787 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
788 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
790 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
791 certificate
->_policyMappings
.present
= true;
792 certificate
->_policyMappings
.critical
= extn
->critical
;
793 certificate
->_policyMappings
.numMappings
= mapping_count
;
794 certificate
->_policyMappings
.mappings
= mappings
;
799 CFReleaseSafe(mappings
);
800 certificate
->_policyMappings
.present
= false;
801 secwarning("Invalid CertificatePolicies Extension");
804 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
805 const SecCertificateExtension
*extn
) {
806 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
809 CFMutableDictionaryRef mappings
= NULL
;
810 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
811 require_noerr_quiet(drtn
, badDER
);
812 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
813 DERDecodedInfo pmContent
;
814 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
815 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
817 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
818 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
820 drtn
= DERParseSequenceContent(&pmContent
.content
,
821 DERNumPolicyMappingItemSpecs
,
822 DERPolicyMappingItemSpecs
,
824 require_noerr_quiet(drtn
, badDER
);
826 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
827 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
828 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
829 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
830 CFMutableArrayRef sdps
=
831 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
833 CFArrayAppendValue(sdps
, sdp
);
835 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
836 &kCFTypeArrayCallBacks
), badDER
);
837 CFDictionarySetValue(mappings
, idp
, sdps
);
841 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
842 certificate
->_policyMappings
= mappings
;
845 CFReleaseSafe(mappings
);
846 certificate
->_policyMappings
= NULL
;
847 secwarning("Invalid CertificatePolicies Extension");
852 AuthorityKeyIdentifier ::= SEQUENCE {
853 keyIdentifier [0] KeyIdentifier OPTIONAL,
854 authorityCertIssuer [1] GeneralNames OPTIONAL,
855 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
856 -- authorityCertIssuer and authorityCertSerialNumber MUST both
857 -- be present or both be absent
859 KeyIdentifier ::= OCTET STRING
861 static void SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
862 const SecCertificateExtension
*extn
) {
863 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
864 DERAuthorityKeyIdentifier akid
;
866 drtn
= DERParseSequence(&extn
->extnValue
,
867 DERNumAuthorityKeyIdentifierItemSpecs
,
868 DERAuthorityKeyIdentifierItemSpecs
,
869 &akid
, sizeof(akid
));
870 require_noerr_quiet(drtn
, badDER
);
871 if (akid
.keyIdentifier
.length
) {
872 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
874 if (akid
.authorityCertIssuer
.length
||
875 akid
.authorityCertSerialNumber
.length
) {
876 require_quiet(akid
.authorityCertIssuer
.length
&&
877 akid
.authorityCertSerialNumber
.length
, badDER
);
878 /* Perhaps put in a subsection called Authority Certificate Issuer. */
879 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
880 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
885 secwarning("Invalid AuthorityKeyIdentifier Extension");
888 static void SecCEPPolicyConstraints(SecCertificateRef certificate
,
889 const SecCertificateExtension
*extn
) {
890 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
891 DERPolicyConstraints pc
;
893 drtn
= DERParseSequence(&extn
->extnValue
,
894 DERNumPolicyConstraintsItemSpecs
,
895 DERPolicyConstraintsItemSpecs
,
897 require_noerr_quiet(drtn
, badDER
);
898 if (pc
.requireExplicitPolicy
.length
) {
899 require_noerr_quiet(DERParseInteger(
900 &pc
.requireExplicitPolicy
,
901 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
902 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
904 if (pc
.inhibitPolicyMapping
.length
) {
905 require_noerr_quiet(DERParseInteger(
906 &pc
.inhibitPolicyMapping
,
907 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
908 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
911 certificate
->_policyConstraints
.present
= true;
912 certificate
->_policyConstraints
.critical
= extn
->critical
;
916 certificate
->_policyConstraints
.present
= false;
917 secwarning("Invalid PolicyConstraints Extension");
920 static void SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
921 const SecCertificateExtension
*extn
) {
922 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
926 InhibitAnyPolicy ::= SkipCerts
928 SkipCerts ::= INTEGER (0..MAX)
930 static void SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
931 const SecCertificateExtension
*extn
) {
932 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
933 require_noerr_quiet(DERParseInteger(
935 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
938 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
939 secwarning("Invalid InhibitAnyPolicy Extension");
943 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
945 AuthorityInfoAccessSyntax ::=
946 SEQUENCE SIZE (1..MAX) OF AccessDescription
948 AccessDescription ::= SEQUENCE {
949 accessMethod OBJECT IDENTIFIER,
950 accessLocation GeneralName }
952 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
954 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
956 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
958 static void SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
959 const SecCertificateExtension
*extn
) {
960 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
963 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
964 require_noerr_quiet(drtn
, badDER
);
965 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
966 DERDecodedInfo adContent
;
967 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
968 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
969 DERAccessDescription ad
;
970 drtn
= DERParseSequenceContent(&adContent
.content
,
971 DERNumAccessDescriptionItemSpecs
,
972 DERAccessDescriptionItemSpecs
,
974 require_noerr_quiet(drtn
, badDER
);
975 CFMutableArrayRef
*urls
;
976 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
977 urls
= &certificate
->_ocspResponders
;
978 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
979 urls
= &certificate
->_caIssuers
;
983 DERDecodedInfo generalNameContent
;
984 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
985 require_noerr_quiet(drtn
, badDER
);
986 switch (generalNameContent
.tag
) {
988 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
989 /* Technically I don't think this is valid, but there are certs out
990 in the wild that use a constructed IA5String. In particular the
991 VeriSign Time Stamping Authority CA.cer does this. */
993 case ASN1_CONTEXT_SPECIFIC
| 6:
995 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
996 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
997 kCFStringEncodingASCII
, NULL
);
1000 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1001 CFArrayAppendValue(*urls
, url
);
1007 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02x v: %.*s",
1008 generalNameContent
.tag
, generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1013 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1016 secdebug("cert", "failed to parse Authority Information Access extension");
1019 static void SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1020 const SecCertificateExtension
*extn
) {
1021 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1024 static void SecCEPNetscapeCertType(SecCertificateRef certificate
,
1025 const SecCertificateExtension
*extn
) {
1026 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1029 static void SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1030 const SecCertificateExtension
*extn
) {
1031 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1034 /* Dictionary key callback for comparing to DERItems. */
1035 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1036 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1039 /* Dictionary key callback calculating the hash of a DERItem. */
1040 static CFHashCode
SecDERItemHash(const void *value
) {
1041 const DERItem
*derItem
= (const DERItem
*)value
;
1042 CFHashCode hash
= derItem
->length
;
1043 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1044 for (; ix
< derItem
->length
; ++ix
) {
1045 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1051 /* Dictionary key callbacks using the above 2 functions. */
1052 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1056 NULL
, /* copyDescription */
1057 SecDERItemEqual
, /* equal */
1058 SecDERItemHash
/* hash */
1061 static void SecCertificateRegisterClass(void) {
1062 static const CFRuntimeClass kSecCertificateClass
= {
1064 "SecCertificate", /* class name */
1067 SecCertificateDestroy
, /* dealloc */
1068 SecCertificateEqual
, /* equal */
1069 SecCertificateHash
, /* hash */
1070 NULL
, /* copyFormattingDesc */
1071 SecCertificateDescribe
/* copyDebugDesc */
1074 kSecCertificateTypeID
= _CFRuntimeRegisterClass(&kSecCertificateClass
);
1076 /* Build a dictionary that maps from extension OIDs to callback functions
1077 which can parse the extension of the type given. */
1078 static const void *extnOIDs
[] = {
1079 &oidSubjectKeyIdentifier
,
1081 &oidPrivateKeyUsagePeriod
,
1084 &oidBasicConstraints
,
1085 &oidCrlDistributionPoints
,
1086 &oidCertificatePolicies
,
1088 &oidAuthorityKeyIdentifier
,
1089 &oidPolicyConstraints
,
1090 &oidExtendedKeyUsage
,
1091 &oidInhibitAnyPolicy
,
1092 &oidAuthorityInfoAccess
,
1093 &oidSubjectInfoAccess
,
1094 &oidNetscapeCertType
,
1097 static const void *extnParsers
[] = {
1098 SecCEPSubjectKeyIdentifier
,
1100 SecCEPPrivateKeyUsagePeriod
,
1101 SecCEPSubjectAltName
,
1102 SecCEPIssuerAltName
,
1103 SecCEPBasicConstraints
,
1104 SecCEPCrlDistributionPoints
,
1105 SecCEPCertificatePolicies
,
1106 SecCEPPolicyMappings
,
1107 SecCEPAuthorityKeyIdentifier
,
1108 SecCEPPolicyConstraints
,
1109 SecCEPExtendedKeyUsage
,
1110 SecCEPInhibitAnyPolicy
,
1111 SecCEPAuthorityInfoAccess
,
1112 SecCEPSubjectInfoAccess
,
1113 SecCEPNetscapeCertType
,
1114 SecCEPEntrustVersInfo
1116 gExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1117 extnParsers
, sizeof(extnOIDs
) / sizeof(*extnOIDs
),
1118 &SecDERItemKeyCallBacks
, NULL
);
1121 /* Given the contents of an X.501 Name return the contents of a normalized
1123 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1124 const DERItem
*x501name
) {
1125 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1126 CFIndex length
= x501name
->length
;
1127 CFDataSetLength(result
, length
);
1128 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1131 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1133 require_noerr_quiet(drtn
, badDER
);
1136 /* Always points to last rdn tag. */
1137 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1138 /* Offset relative to base of current rdn set tag. */
1139 CFIndex rdnTagLocation
= 0;
1140 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1141 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1142 /* We don't allow empty RDNs. */
1143 require_quiet(rdn
.content
.length
!= 0, badDER
);
1144 /* Length of the tag and length of the current rdn. */
1145 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1146 CFIndex rdnContentLength
= rdn
.content
.length
;
1147 /* Copy the tag and length of the RDN. */
1148 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1151 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1153 /* Always points to tag of current atv sequence. */
1154 const DERByte
*atvTag
= atvSeq
.nextItem
;
1155 /* Offset relative to base of current atv sequence tag. */
1156 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1157 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1158 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1159 /* Length of the tag and length of the current atv. */
1160 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1161 CFIndex atvContentLength
= atv
.content
.length
;
1162 /* Copy the tag and length of the atv and the atv itself. */
1163 memcpy(base
+ atvTagLocation
, atvTag
,
1164 atvTLLength
+ atv
.content
.length
);
1166 /* Now decode the atv sequence. */
1167 DERAttributeTypeAndValue atvPair
;
1168 drtn
= DERParseSequenceContent(&atv
.content
,
1169 DERNumAttributeTypeAndValueItemSpecs
,
1170 DERAttributeTypeAndValueItemSpecs
,
1171 &atvPair
, sizeof(atvPair
));
1172 require_noerr_quiet(drtn
, badDER
);
1173 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1174 DERDecodedInfo value
;
1175 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1176 require_noerr_quiet(drtn
, badDER
);
1178 /* (c) attribute values in PrintableString are not case sensitive
1179 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1181 (d) attribute values in PrintableString are compared after
1182 removing leading and trailing white space and converting internal
1183 substrings of one or more consecutive white space characters to a
1185 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1186 /* Offset relative to base of current value tag. */
1187 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1188 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1189 CFIndex valueContentLength
= value
.content
.length
;
1191 /* Now copy all the bytes, but convert to upper case while
1192 doing so and convert multiple whitespace chars into a
1194 bool lastWasBlank
= false;
1195 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1196 CFIndex valueCurrentLocation
= valueLocation
;
1198 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1199 UInt8 ch
= value
.content
.data
[ix
];
1204 /* Don't insert a space for first character
1206 if (valueCurrentLocation
> valueLocation
) {
1207 base
[valueCurrentLocation
++] = ' ';
1209 lastWasBlank
= true;
1212 lastWasBlank
= false;
1213 if ('a' <= ch
&& ch
<= 'z') {
1214 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1216 base
[valueCurrentLocation
++] = ch
;
1220 /* Finally if lastWasBlank remove the trailing space. */
1221 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1222 valueCurrentLocation
--;
1224 /* Adjust content length to normalized length. */
1225 valueContentLength
= valueCurrentLocation
- valueLocation
;
1227 /* Number of bytes by which the length should be shorted. */
1228 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1229 if (lengthDiff
== 0) {
1230 /* Easy case no need to adjust lengths. */
1232 /* Hard work we need to go back and fix up length fields
1234 1) The value itself.
1235 2) The ATV Sequence containing type/value
1236 3) The RDN Set containing one or more atv pairs.
1240 /* Step 1 fix up length of value. */
1241 /* Length of value tag and length minus the tag. */
1242 DERSize newValueTLLength
= valueTLLength
- 1;
1243 drtn
= DEREncodeLength(valueContentLength
,
1244 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1245 /* Add the length of the tag back in. */
1247 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1249 /* The size of the length field changed, let's slide
1250 the value back by valueLLDiff bytes. */
1251 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1252 base
+ valueTagLocation
+ valueTLLength
,
1253 valueContentLength
);
1254 /* The length diff for the enclosing object. */
1255 lengthDiff
+= valueLLDiff
;
1258 /* Step 2 fix up length of the enclosing ATV Sequence. */
1259 atvContentLength
-= lengthDiff
;
1260 DERSize newATVTLLength
= atvTLLength
- 1;
1261 drtn
= DEREncodeLength(atvContentLength
,
1262 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1263 /* Add the length of the tag back in. */
1265 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1267 /* The size of the length field changed, let's slide
1268 the value back by valueLLDiff bytes. */
1269 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1270 base
+ atvTagLocation
+ atvTLLength
,
1272 /* The length diff for the enclosing object. */
1273 lengthDiff
+= atvLLDiff
;
1274 atvTLLength
= newATVTLLength
;
1277 /* Step 3 fix up length of enclosing RDN Set. */
1278 rdnContentLength
-= lengthDiff
;
1279 DERSize newRDNTLLength
= rdnTLLength
- 1;
1280 drtn
= DEREncodeLength(rdnContentLength
,
1281 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1282 /* Add the length of the tag back in. */
1284 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1286 /* The size of the length field changed, let's slide
1287 the value back by valueLLDiff bytes. */
1288 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1289 base
+ rdnTagLocation
+ rdnTLLength
,
1291 /* The length diff for the enclosing object. */
1292 lengthDiff
+= rdnLLDiff
;
1293 rdnTLLength
= newRDNTLLength
;
1295 /* Adjust the locations that might have changed due to
1297 atvTagLocation
-= rdnLLDiff
;
1301 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1302 atvTag
= atvSeq
.nextItem
;
1304 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1305 rdnTag
= rdnSeq
.nextItem
;
1307 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1308 /* Truncate the result to the proper length. */
1309 CFDataSetLength(result
, rdnTagLocation
);
1318 /* AUDIT[securityd]:
1319 certificate->_der is a caller provided data of any length (might be 0).
1321 Top level certificate decode.
1323 static bool SecCertificateParse(SecCertificateRef certificate
)
1328 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1330 /* top level decode */
1331 DERSignedCertCrl signedCert
;
1332 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1333 DERSignedCertCrlItemSpecs
, &signedCert
,
1334 sizeof(signedCert
));
1335 require_noerr_quiet(drtn
, badCert
);
1336 /* Store tbs since we need to digest it for verification later on. */
1337 certificate
->_tbs
= signedCert
.tbs
;
1339 /* decode the TBSCert - it was saved in full DER form */
1341 drtn
= DERParseSequence(&signedCert
.tbs
,
1342 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1343 &tbsCert
, sizeof(tbsCert
));
1344 require_noerr_quiet(drtn
, badCert
);
1346 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1347 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1348 of the params field. */
1349 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1350 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1351 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1352 require_noerr_quiet(drtn
, badCert
);
1354 /* The contents of signedCert.sig is a bit string whose contents
1355 are the signature itself. */
1356 DERByte numUnusedBits
;
1357 drtn
= DERParseBitString(&signedCert
.sig
,
1358 &certificate
->_signature
, &numUnusedBits
);
1359 require_noerr_quiet(drtn
, badCert
);
1361 /* Now decode the tbsCert. */
1363 /* First we turn the optional version into an int. */
1364 if (tbsCert
.version
.length
) {
1365 DERDecodedInfo decoded
;
1366 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1367 require_noerr_quiet(drtn
, badCert
);
1368 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1369 require_quiet(decoded
.content
.length
== 1, badCert
);
1370 certificate
->_version
= decoded
.content
.data
[0];
1371 require_quiet(certificate
->_version
> 0, badCert
);
1372 require_quiet(certificate
->_version
< 3, badCert
);
1374 certificate
->_version
= 0;
1377 /* The serial number is in the tbsCert.serialNum - it was saved in
1378 INTEGER form without the tag and length. */
1379 certificate
->_serialNum
= tbsCert
.serialNum
;
1380 certificate
->_serialNumber
= CFDataCreate(allocator
,
1381 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1383 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1384 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1385 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1386 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1387 require_noerr_quiet(drtn
, badCert
);
1389 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1390 and length fields. */
1391 certificate
->_issuer
= tbsCert
.issuer
;
1392 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1395 /* sequence we're given: decode the tbsCerts Validity sequence. */
1396 DERValidity validity
;
1397 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1398 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1399 &validity
, sizeof(validity
));
1400 require_noerr_quiet(drtn
, badCert
);
1401 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1402 &certificate
->_notBefore
), badCert
);
1403 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1404 &certificate
->_notAfter
), badCert
);
1406 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1407 and length fields. */
1408 certificate
->_subject
= tbsCert
.subject
;
1409 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1412 /* sequence we're given: encoded DERSubjPubKeyInfo */
1413 DERSubjPubKeyInfo pubKeyInfo
;
1414 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1415 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1416 &pubKeyInfo
, sizeof(pubKeyInfo
));
1417 require_noerr_quiet(drtn
, badCert
);
1419 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1420 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1421 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1422 &certificate
->_algId
, sizeof(certificate
->_algId
));
1423 require_noerr_quiet(drtn
, badCert
);
1425 /* Now we can figure out the key's algorithm id and params based on
1426 certificate->_algId.oid. */
1428 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1429 are a PKCS1 format RSA key. */
1430 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1431 &certificate
->_pubKeyDER
, &numUnusedBits
);
1432 require_noerr_quiet(drtn
, badCert
);
1434 /* The contents of tbsCert.issuerID is a bit string. */
1435 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1437 /* The contents of tbsCert.subjectID is a bit string. */
1438 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1441 if (tbsCert
.extensions
.length
) {
1442 CFIndex extensionCount
= 0;
1445 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1447 require_noerr_quiet(drtn
, badCert
);
1448 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1449 DERDecodedInfo currDecoded
;
1450 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1452 /* ! = MUST recognize ? = SHOULD recognize
1455 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1456 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1457 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1458 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1459 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1460 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1461 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1462 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1464 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1465 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1466 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1467 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1468 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1469 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1470 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1471 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1473 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1474 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1479 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1481 /* Put some upper limit on the number of extentions allowed. */
1482 require_quiet(extensionCount
< 10000, badCert
);
1483 certificate
->_extensionCount
= extensionCount
;
1484 certificate
->_extensions
=
1485 malloc(sizeof(SecCertificateExtension
) * extensionCount
);
1488 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1489 require_noerr_quiet(drtn
, badCert
);
1490 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1491 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1492 require_quiet(drtn
== DR_Success
||
1493 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1494 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1496 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1497 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1498 &extn
, sizeof(extn
));
1499 require_noerr_quiet(drtn
, badCert
);
1500 /* Copy stuff into certificate->extensions[ix]. */
1501 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1502 require_noerr_quiet(drtn
= DERParseBoolean(&extn
.critical
, false,
1503 &certificate
->_extensions
[ix
].critical
), badCert
);
1504 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1506 SecCertificateExtensionParser parser
=
1507 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1508 gExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1510 /* Invoke the parser. */
1511 parser(certificate
, &certificate
->_extensions
[ix
]);
1512 } else if (certificate
->_extensions
[ix
].critical
) {
1513 secdebug("cert", "Found unknown critical extension");
1514 certificate
->_foundUnknownCriticalExtension
= true;
1516 secdebug("cert", "Found unknown non critical extension");
1528 /* Public API functions. */
1529 CFTypeID
SecCertificateGetTypeID(void) {
1530 pthread_once(&kSecCertificateRegisterClass
, SecCertificateRegisterClass
);
1531 return kSecCertificateTypeID
;
1534 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1535 const UInt8
*der_bytes
, CFIndex der_length
) {
1538 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1539 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1540 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1542 memset((char*)result
+ sizeof(result
->_base
), 0,
1543 sizeof(*result
) - sizeof(result
->_base
));
1544 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1545 result
->_der
.length
= der_length
;
1546 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1547 if (!SecCertificateParse(result
)) {
1555 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1556 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1557 const UInt8
*der_bytes
, CFIndex der_length
);
1559 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1560 const UInt8
*der_bytes
, CFIndex der_length
) {
1561 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1563 /* @@@ End of placeholder. */
1565 /* AUDIT[securityd](done):
1566 der_certificate is a caller provided data of any length (might be 0), only
1567 its cf type has been checked.
1569 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1570 CFDataRef der_certificate
) {
1571 check(der_certificate
);
1572 CFIndex size
= sizeof(struct __SecCertificate
);
1573 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1574 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1576 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1577 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1578 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1579 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1580 if (!SecCertificateParse(result
)) {
1588 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1591 if (certificate
->_der_data
) {
1592 CFRetain(certificate
->_der_data
);
1593 result
= certificate
->_der_data
;
1595 result
= CFDataCreate(CFGetAllocator(certificate
),
1596 certificate
->_der
.data
, certificate
->_der
.length
);
1598 /* FIXME: If we wish to cache result we need to lock the certificate.
1599 Also this create 2 copies of the certificate data which is somewhat
1602 certificate
->_der_data
= result
;
1609 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1610 return certificate
->_der
.length
;
1613 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1614 return certificate
->_der
.data
;
1617 /* From rfc3280 - Appendix B. ASN.1 Notes
1619 Object Identifiers (OIDs) are used throughout this specification to
1620 identify certificate policies, public key and signature algorithms,
1621 certificate extensions, etc. There is no maximum size for OIDs.
1622 This specification mandates support for OIDs which have arc elements
1623 with values that are less than 2^28, that is, they MUST be between 0
1624 and 268,435,455, inclusive. This allows each arc element to be
1625 represented within a single 32 bit word. Implementations MUST also
1626 support OIDs where the length of the dotted decimal (see [RFC 2252],
1627 section 4.1) string representation can be up to 100 bytes
1628 (inclusive). Implementations MUST be able to handle OIDs with up to
1629 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1630 contain OIDs that exceed these requirements. Likewise, CRL issuers
1631 SHOULD NOT issue CRLs which contain OIDs that exceed these
1635 /* Oids longer than this are considered invalid. */
1636 #define MAX_OID_SIZE 32
1638 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1639 const DERItem
*oid
) {
1641 if (oid
->length
== 0) {
1642 return SecCopyCertString(SEC_NULL_KEY
);
1644 if (oid
->length
> MAX_OID_SIZE
) {
1645 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
1648 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1650 // The first two levels are encoded into one byte, since the root level
1651 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1652 // y may be > 39, so we have to add special-case handling for this.
1653 uint32_t x
= oid
->data
[0] / 40;
1654 uint32_t y
= oid
->data
[0] % 40;
1657 // Handle special case for large y if x = 2
1661 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1664 for (x
= 1; x
< oid
->length
; ++x
)
1666 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1667 /* @@@ value may not span more than 4 bytes. */
1668 /* A max number of 20 values is allowed. */
1669 if (!(oid
->data
[x
] & 0x80))
1671 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), value
);
1678 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1679 const DERItem
*oid
) {
1680 if (oid
->length
== 0) {
1681 return SecCopyCertString(SEC_NULL_KEY
);
1684 /* Build the key we use to lookup the localized OID description. */
1685 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1686 oid
->length
* 3 + 5);
1687 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02X"), oid
->length
);
1689 for (ix
= 0; ix
< oid
->length
; ++ix
)
1690 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1692 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1693 if (CFEqual(oidKey
, name
)) {
1695 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1702 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1703 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1704 have a length of exactly 4 or 16 octects. */
1705 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1706 const DERItem
*ip
) {
1707 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1708 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1709 16 octects addr, or 32 octects addr/mask. */
1710 CFStringRef value
= NULL
;
1711 if (ip
->length
== 4) {
1712 value
= CFStringCreateWithFormat(allocator
, NULL
,
1713 CFSTR("%u.%u.%u.%u"),
1714 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1715 } else if (ip
->length
== 16) {
1716 value
= CFStringCreateWithFormat(allocator
, NULL
,
1717 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1718 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1719 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
1720 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
1721 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
1722 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
1729 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
1730 const DERItem
*oid
) {
1731 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1732 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
1733 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
1734 CFSTR("%@ (%@)"), name
, decimal
);
1741 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
1742 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
) {
1743 CFDictionaryRef property
;
1746 if (localizedLabel
) {
1749 ll
= localizedLabel
= SecCopyCertString(label
);
1751 const void *all_keys
[4];
1752 all_keys
[0] = kSecPropertyKeyType
;
1753 all_keys
[1] = kSecPropertyKeyLabel
;
1754 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
1755 all_keys
[3] = kSecPropertyKeyValue
;
1756 const void *property_values
[] = {
1762 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1763 all_keys
, property_values
, value
? 4 : 3,
1764 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1767 const void *nolabel_keys
[2];
1768 nolabel_keys
[0] = kSecPropertyKeyType
;
1769 nolabel_keys
[1] = kSecPropertyKeyValue
;
1770 const void *property_values
[] = {
1774 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1775 nolabel_keys
, property_values
, 2,
1776 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1779 CFArrayAppendValue(properties
, property
);
1780 CFRelease(property
);
1784 #define UTC_TIME_NOSEC_ZULU_LEN 11
1786 #define UTC_TIME_ZULU_LEN 13
1787 /* YYMMDDhhmmssThhmm */
1788 #define UTC_TIME_LOCALIZED_LEN 17
1789 /* YYYYMMDDhhmmssZ */
1790 #define GENERALIZED_TIME_ZULU_LEN 15
1791 /* YYYYMMDDhhmmssThhmm */
1792 #define GENERALIZED_TIME_LOCALIZED_LEN 19
1794 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
1796 static inline int parseDecimalPair(const DERByte
**p
) {
1797 const DERByte
*cp
= *p
;
1799 return 10 * (cp
[0] - '0') + cp
[1] - '0';
1802 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1803 true if the date was valid and properly decoded, also return the result in
1804 absTime. Return false otherwise. */
1805 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
1811 bool isUtcLength
= false;
1812 bool isLocalized
= false;
1813 bool noSeconds
= false;
1815 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
1819 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
1822 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
1824 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
1827 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
1830 default: /* unknown format */
1834 /* Make sure the der tag fits the thing inside it. */
1835 if (tag
== ASN1_UTC_TIME
) {
1838 } else if (tag
== ASN1_GENERALIZED_TIME
) {
1845 const DERByte
*cp
= bytes
;
1846 /* Check that all characters are digits, except if localized the timezone
1847 indicator or if not localized the 'Z' at the end. */
1849 for (ix
= 0; ix
< length
; ++ix
) {
1850 if (!(isdigit(cp
[ix
]))) {
1851 if ((isLocalized
&& ix
== length
- 5 &&
1852 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
1853 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
1860 /* Parse the date and time fields. */
1861 int year
, month
, day
, hour
, minute
, second
;
1863 year
= parseDecimalPair(&cp
);
1865 /* 0 <= year < 50 : assume century 21 */
1867 } else if (year
< 70) {
1868 /* 50 <= year < 70 : illegal per PKIX */
1871 /* 70 < year <= 99 : assume century 20 */
1875 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
1877 month
= parseDecimalPair(&cp
);
1878 day
= parseDecimalPair(&cp
);
1879 hour
= parseDecimalPair(&cp
);
1880 minute
= parseDecimalPair(&cp
);
1884 second
= parseDecimalPair(&cp
);
1887 CFTimeInterval timeZoneOffset
;
1889 /* ZONE INDICATOR */
1890 int multiplier
= *cp
++ == '+' ? 60 : -60;
1891 timeZoneOffset
= multiplier
*
1892 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
1897 secdebug("dateparse",
1898 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
1899 length
, bytes
, year
, month
,
1900 day
, hour
, minute
, second
,
1901 timeZoneOffset
/ 60);
1903 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
1904 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
1905 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
1906 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
1907 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
1912 int dy
= year
- 2001;
1917 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
1918 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
1920 day
+= is_leap_year
;
1922 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24 + hour
) * 60 + minute
) * 60 + second
;
1923 return absTime
- timeZoneOffset
;
1926 static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
1927 CFAbsoluteTime
*pabsTime
) {
1928 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
1930 if (absTime
== NULL_TIME
)
1933 *pabsTime
= absTime
;
1937 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1938 true if the date was valid and properly decoded, also return the result in
1939 absTime. Return false otherwise. */
1940 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
1941 CFAbsoluteTime
*absTime
) {
1944 if (dateChoice
->length
== 0)
1947 DERDecodedInfo decoded
;
1948 if (DERDecodeItem(dateChoice
, &decoded
))
1951 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
1955 static void appendDataProperty(CFMutableArrayRef properties
,
1956 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
1957 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
1958 der_data
->data
, der_data
->length
);
1959 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
1964 static void appendRelabeledProperty(CFMutableArrayRef properties
,
1966 CFStringRef localizedLabel
,
1967 const DERItem
*der_data
,
1968 CFStringRef labelFormat
) {
1969 CFStringRef newLabel
=
1970 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
1971 labelFormat
, label
);
1973 if (localizedLabel
) {
1976 ll
= localizedLabel
= SecCopyCertString(label
);
1978 CFStringRef localizedLabelFormat
= SecCopyCertString(labelFormat
);
1979 CFStringRef newLocalizedLabel
=
1980 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
1981 localizedLabelFormat
, localizedLabel
);
1983 CFReleaseSafe(localizedLabelFormat
);
1984 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
);
1985 CFReleaseSafe(newLabel
);
1986 CFReleaseSafe(newLocalizedLabel
);
1990 static void appendUnparsedProperty(CFMutableArrayRef properties
,
1991 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
1992 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
1996 static void appendInvalidProperty(CFMutableArrayRef properties
,
1997 CFStringRef label
, const DERItem
*der_data
) {
1998 appendRelabeledProperty(properties
, label
, NULL
, der_data
, SEC_INVALID_KEY
);
2001 static void appendDateContentProperty(CFMutableArrayRef properties
,
2002 CFStringRef label
, DERTag tag
,
2003 const DERItem
*dateContent
) {
2004 CFAbsoluteTime absTime
;
2005 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2006 /* Date decode failure insert hex bytes instead. */
2007 return appendInvalidProperty(properties
, label
, dateContent
);
2009 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2010 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2014 static void appendDateProperty(CFMutableArrayRef properties
,
2015 CFStringRef label
, CFAbsoluteTime absTime
) {
2016 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2017 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2021 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2022 CFStringRef label
, const DERItem
*ip
) {
2024 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2026 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2029 appendUnparsedProperty(properties
, label
, NULL
, ip
);
2033 static void appendURLContentProperty(CFMutableArrayRef properties
,
2034 CFStringRef label
, const DERItem
*urlContent
) {
2035 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2036 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2038 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
);
2041 appendInvalidProperty(properties
, label
, urlContent
);
2045 static void appendURLProperty(CFMutableArrayRef properties
,
2046 CFStringRef label
, const DERItem
*url
) {
2047 DERDecodedInfo decoded
;
2050 drtn
= DERDecodeItem(url
, &decoded
);
2051 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2052 appendInvalidProperty(properties
, label
, url
);
2054 appendURLContentProperty(properties
, label
, &decoded
.content
);
2058 static void appendOIDProperty(CFMutableArrayRef properties
,
2059 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
) {
2060 CFStringRef oid_string
=
2061 copyLocalizedOidDescription(CFGetAllocator(properties
), oid
);
2062 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2064 CFRelease(oid_string
);
2067 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2068 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2069 CFMutableArrayRef alg_props
=
2070 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2071 &kCFTypeArrayCallBacks
);
2072 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
, &algorithm
->oid
);
2073 if (algorithm
->params
.length
) {
2074 if (algorithm
->params
.length
== 2 &&
2075 algorithm
->params
.data
[0] == ASN1_NULL
&&
2076 algorithm
->params
.data
[1] == 0) {
2077 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2078 appendProperty(alg_props
, kSecPropertyTypeString
,
2079 SEC_PARAMETERS_KEY
, NULL
, value
);
2082 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2083 &algorithm
->params
);
2086 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
, alg_props
);
2087 CFRelease(alg_props
);
2090 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2091 const DERItem
*blob
) {
2092 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2093 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2094 blob
->length
* 3 - 1);
2095 for (ix
= 0; ix
< length
; ++ix
)
2097 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2099 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2104 /* Returns a (localized) blob string. */
2105 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2106 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2107 CFStringRef localizedBlobType
= SecCopyCertString(blobType
);
2108 CFStringRef localizedQuanta
= SecCopyCertString(quanta
);
2109 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2110 "data = 00 00 ...)" */
2111 CFStringRef blobFormat
= SecCopyCertString(SEC_BLOB_KEY
);
2112 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2113 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2114 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2116 CFRelease(blobFormat
);
2117 CFReleaseSafe(localizedQuanta
);
2118 CFReleaseSafe(localizedBlobType
);
2123 /* Return a string verbatim (unlocalized) from a DER field. */
2124 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2125 const DERItem
*string
, CFStringEncoding encoding
,
2126 bool printableOnly
) {
2127 /* Strip potential bogus trailing zero from printable strings. */
2128 DERSize length
= string
->length
;
2129 if (length
&& string
->data
[length
- 1] == 0) {
2130 /* Don't mess with the length of UTF16 strings though. */
2131 if (encoding
!= kCFStringEncodingUTF16
)
2134 /* A zero length string isn't considered printable. */
2135 if (!length
&& printableOnly
)
2138 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2139 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2140 passing false makes it treat it as native endian by default. */
2141 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2142 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2146 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2149 /* From rfc3280 - Appendix B. ASN.1 Notes
2151 CAs MUST force the serialNumber to be a non-negative integer, that
2152 is, the sign bit in the DER encoding of the INTEGER value MUST be
2153 zero - this can be done by adding a leading (leftmost) `00'H octet if
2154 necessary. This removes a potential ambiguity in mapping between a
2155 string of octets and an integer value.
2157 As noted in section 4.1.2.2, serial numbers can be expected to
2158 contain long integers. Certificate users MUST be able to handle
2159 serialNumber values up to 20 octets in length. Conformant CAs MUST
2160 NOT use serialNumber values longer than 20 octets.
2163 /* Return the given numeric data as a string: decimal up to 64 bits,
2165 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2166 const DERItem
*integer
) {
2168 CFIndex ix
, length
= integer
->length
;
2170 if (length
== 0 || length
> 8)
2171 return copyHexDescription(allocator
, integer
);
2173 for(ix
= 0; ix
< length
; ++ix
) {
2175 value
+= integer
->data
[ix
];
2178 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2181 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2182 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2186 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2187 case ASN1_PRINTABLE_STRING
:
2188 case ASN1_IA5_STRING
:
2189 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2190 case ASN1_UTF8_STRING
:
2191 case ASN1_GENERAL_STRING
:
2192 case ASN1_UNIVERSAL_STRING
:
2193 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2194 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2195 case ASN1_VIDEOTEX_STRING
: // 21
2196 case ASN1_VISIBLE_STRING
: // 26
2197 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2198 case ASN1_BMP_STRING
: // 30
2199 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2200 case ASN1_OCTET_STRING
:
2201 return printableOnly
? NULL
:
2202 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2204 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2205 case ASN1_BIT_STRING
:
2206 return printableOnly
? NULL
:
2207 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2209 case ASN1_CONSTR_SEQUENCE
:
2210 return printableOnly
? NULL
:
2211 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2213 case ASN1_CONSTR_SET
:
2214 return printableOnly
? NULL
:
2215 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
, derThing
);
2216 case ASN1_OBJECT_ID
:
2217 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2219 if (printableOnly
) {
2222 CFStringRef fmt
= SecCopyCertString(SEC_NOT_DISPLAYED_KEY
);
2223 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2224 tag
, derThing
->length
);
2231 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2232 const DERItem
*derThing
, bool printableOnly
) {
2233 DERDecodedInfo decoded
;
2236 drtn
= DERDecodeItem(derThing
, &decoded
);
2238 /* TODO: Perhaps put something in the label saying we couldn't parse
2240 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2242 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2243 &decoded
.content
, false);
2247 static void appendDERThingProperty(CFMutableArrayRef properties
,
2248 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*derThing
) {
2249 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2251 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2256 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2257 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2258 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2260 /* If there is more than one value pair we create a subsection for the
2261 second pair, and append things to the subsection for subsequent
2263 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2264 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2266 /* Since this is the second rdn pair for a given rdn, we setup a
2267 new subsection for this rdn. We remove the first property
2268 from the properties array and make it the first element in the
2269 subsection instead. */
2270 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2271 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2272 CFArrayAppendValue(rdn_props
, lastValue
);
2273 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2274 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2276 properties
= rdn_props
;
2278 /* Since this is the third or later rdn pair we have already
2279 created a subsection in the top level properties array. Instead
2280 of appending to that directly we append to the array inside the
2282 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2283 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2287 /* Finally we append the new rdn value to the property array. */
2288 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(
2289 CFGetAllocator(properties
), rdnType
);
2290 CFStringRef localizedLabel
=
2291 copyLocalizedOidDescription(CFGetAllocator(properties
), rdnType
);
2292 appendDERThingProperty(properties
, label
, localizedLabel
, rdnValue
);
2294 CFRelease(localizedLabel
);
2298 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2299 const DERItem
*rdnSetContent
) {
2300 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2301 &kCFTypeArrayCallBacks
);
2302 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2305 CFArrayRemoveAllValues(properties
);
2306 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
);
2313 From rfc3739 - 3.1.2. Subject
2315 When parsing the subject here are some tips for a short name of the cert.
2316 Choice I: commonName
2317 Choice II: givenName
2318 Choice III: pseudonym
2320 The commonName attribute value SHALL, when present, contain a name
2321 of the subject. This MAY be in the subject's preferred
2322 presentation format, or a format preferred by the CA, or some
2323 other format. Pseudonyms, nicknames, and names with spelling
2324 other than defined by the registered name MAY be used. To
2325 understand the nature of the name presented in commonName,
2326 complying applications MAY have to examine present values of the
2327 givenName and surname attributes, or the pseudonym attribute.
2330 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2331 const DERItem
*x501NameContent
) {
2332 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2333 &kCFTypeArrayCallBacks
);
2334 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2337 CFArrayRemoveAllValues(properties
);
2338 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501NameContent
);
2344 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2345 const DERItem
*x501Name
) {
2346 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2347 &kCFTypeArrayCallBacks
);
2348 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2350 CFArrayRemoveAllValues(properties
);
2351 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501Name
);
2357 static void appendIntegerProperty(CFMutableArrayRef properties
,
2358 CFStringRef label
, const DERItem
*integer
) {
2359 CFStringRef string
= copyIntegerContentDescription(
2360 CFGetAllocator(properties
), integer
);
2361 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2365 static void appendBoolProperty(CFMutableArrayRef properties
,
2366 CFStringRef label
, bool boolean
) {
2367 CFStringRef value
= SecCopyCertString(boolean
? SEC_YES_KEY
: SEC_NO_KEY
);
2368 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2372 static void appendBooleanProperty(CFMutableArrayRef properties
,
2373 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2375 DERReturn drtn
= DERParseBoolean(boolean
, defaultValue
, &result
);
2377 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2378 appendInvalidProperty(properties
, label
, boolean
);
2380 appendBoolProperty(properties
, label
, result
);
2384 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2385 CFStringRef label
, const DERItem
*bitStringContent
,
2386 const CFStringRef
*names
, CFIndex namesCount
) {
2387 DERSize len
= bitStringContent
->length
- 1;
2388 require_quiet(len
== 1 || len
== 2, badDER
);
2389 DERByte numUnusedBits
= bitStringContent
->data
[0];
2390 require_quiet(numUnusedBits
< 8, badDER
);
2391 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2392 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2393 uint_fast16_t value
= bitStringContent
->data
[1];
2396 value
= (value
<< 8) + bitStringContent
->data
[2];
2402 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2403 CFStringRef string
= NULL
;
2404 for (ix
= 0; ix
< bits
; ++ix
) {
2408 CFStringCreateWithFormat(CFGetAllocator(properties
),
2409 NULL
, fmt
, string
, names
[ix
]);
2420 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2421 string
? string
: CFSTR(""));
2422 CFReleaseSafe(string
);
2425 appendInvalidProperty(properties
, label
, bitStringContent
);
2428 static void appendBitStringNames(CFMutableArrayRef properties
,
2429 CFStringRef label
, const DERItem
*bitString
,
2430 const CFStringRef
*names
, CFIndex namesCount
) {
2431 DERDecodedInfo bitStringContent
;
2432 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2433 require_noerr_quiet(drtn
, badDER
);
2434 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2435 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2439 appendInvalidProperty(properties
, label
, bitString
);
2443 typedef uint16_t SecKeyUsage
;
2445 #define kSecKeyUsageDigitalSignature 0x8000
2446 #define kSecKeyUsageNonRepudiation 0x4000
2447 #define kSecKeyUsageKeyEncipherment 0x2000
2448 #define kSecKeyUsageDataEncipherment 0x1000
2449 #define kSecKeyUsageKeyAgreement 0x0800
2450 #define kSecKeyUsageKeyCertSign 0x0400
2451 #define kSecKeyUsageCRLSign 0x0200
2452 #define kSecKeyUsageEncipherOnly 0x0100
2453 #define kSecKeyUsageDecipherOnly 0x0080
2456 KeyUsage ::= BIT STRING {
2457 digitalSignature (0),
2459 keyEncipherment (2),
2460 dataEncipherment (3),
2467 static void appendKeyUsage(CFMutableArrayRef properties
,
2468 const DERItem
*extnValue
) {
2469 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2470 extnValue
->data
[0] != ASN1_BIT_STRING
||
2471 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2472 extnValue
->data
[2] > 7) {
2473 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2476 CFMutableStringRef string
=
2477 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2478 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2479 if (extnValue
->length
== 5)
2480 usage
+= extnValue
->data
[4];
2481 secdebug("keyusage", "keyusage: %04X", usage
);
2482 static const CFStringRef usageNames
[] = {
2483 CFSTR("Digital Signature"),
2484 CFSTR("Non-Repudiation"),
2485 CFSTR("Key Encipherment"),
2486 CFSTR("Data Encipherment"),
2487 CFSTR("Key Agreement"),
2493 bool didOne
= false;
2494 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2495 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2496 for (ix
= 0; ix
< bits
; ++ix
) {
2499 CFStringAppend(string
, CFSTR(", "));
2503 /* @@@ Localize usageNames[ix]. */
2504 CFStringAppend(string
, usageNames
[ix
]);
2508 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2514 static void appendKeyUsage(CFMutableArrayRef properties
,
2515 const DERItem
*extnValue
) {
2516 static const CFStringRef usageNames
[] = {
2517 SEC_DIGITAL_SIGNATURE_KEY
,
2518 SEC_NON_REPUDIATION_KEY
,
2519 SEC_KEY_ENCIPHERMENT_KEY
,
2520 SEC_DATA_ENCIPHERMENT_KEY
,
2521 SEC_KEY_AGREEMENT_KEY
,
2524 SEC_ENCIPHER_ONLY_KEY
,
2525 SEC_DECIPHER_ONLY_KEY
2527 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2528 usageNames
, sizeof(usageNames
) / sizeof(*usageNames
));
2532 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2533 const DERItem
*extnValue
) {
2534 DERPrivateKeyUsagePeriod pkup
;
2535 DERReturn drtn
= DERParseSequence(extnValue
,
2536 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2537 &pkup
, sizeof(pkup
));
2538 require_noerr_quiet(drtn
, badDER
);
2539 if (pkup
.notBefore
.length
) {
2540 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2541 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2543 if (pkup
.notAfter
.length
) {
2544 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2545 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2549 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
, extnValue
);
2552 static void appendStringContentProperty(CFMutableArrayRef properties
,
2553 CFStringRef label
, const DERItem
*stringContent
,
2554 CFStringEncoding encoding
) {
2555 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2556 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2558 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2561 appendInvalidProperty(properties
, label
, stringContent
);
2566 OtherName ::= SEQUENCE {
2567 type-id OBJECT IDENTIFIER,
2568 value [0] EXPLICIT ANY DEFINED BY type-id }
2570 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2571 const DERItem
*otherNameContent
) {
2573 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2574 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2576 require_noerr_quiet(drtn
, badDER
);
2577 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2579 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
2580 CFStringRef localizedLabel
=
2581 copyLocalizedOidDescription(allocator
, &on
.typeIdentifier
);
2582 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2584 appendProperty(properties
, kSecPropertyTypeString
, label
,
2585 localizedLabel
, value_string
);
2587 appendUnparsedProperty(properties
, label
, localizedLabel
, &on
.value
);
2589 CFRelease(localizedLabel
);
2592 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
, otherNameContent
);
2596 GeneralName ::= CHOICE {
2597 otherName [0] OtherName,
2598 rfc822Name [1] IA5String,
2599 dNSName [2] IA5String,
2600 x400Address [3] ORAddress,
2601 directoryName [4] Name,
2602 ediPartyName [5] EDIPartyName,
2603 uniformResourceIdentifier [6] IA5String,
2604 iPAddress [7] OCTET STRING,
2605 registeredID [8] OBJECT IDENTIFIER}
2607 EDIPartyName ::= SEQUENCE {
2608 nameAssigner [0] DirectoryString OPTIONAL,
2609 partyName [1] DirectoryString }
2611 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2612 DERTag tag
, const DERItem
*generalName
) {
2614 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2615 appendOtherNameContentProperty(properties
, generalName
);
2617 case ASN1_CONTEXT_SPECIFIC
| 1:
2619 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
2620 generalName
, kCFStringEncodingASCII
);
2622 case ASN1_CONTEXT_SPECIFIC
| 2:
2624 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
2625 kCFStringEncodingASCII
);
2627 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2628 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
2631 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2633 CFArrayRef directory_plist
=
2634 createPropertiesForX501Name(CFGetAllocator(properties
),
2636 appendProperty(properties
, kSecPropertyTypeSection
,
2637 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
);
2638 CFRelease(directory_plist
);
2641 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2642 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
2645 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2646 /* Technically I don't think this is valid, but there are certs out
2647 in the wild that use a constructed IA5String. In particular the
2648 VeriSign Time Stamping Authority CA.cer does this. */
2649 appendURLProperty(properties
, SEC_URI_KEY
, generalName
);
2651 case ASN1_CONTEXT_SPECIFIC
| 6:
2652 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
);
2654 case ASN1_CONTEXT_SPECIFIC
| 7:
2655 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
2658 case ASN1_CONTEXT_SPECIFIC
| 8:
2659 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
, generalName
);
2670 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2671 const DERItem
*generalName
) {
2672 DERDecodedInfo generalNameContent
;
2673 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2674 require_noerr_quiet(drtn
, badDER
);
2675 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2676 &generalNameContent
.content
))
2679 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
, generalName
);
2684 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2686 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2687 const DERItem
*generalNamesContent
) {
2689 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2690 require_noerr_quiet(drtn
, badDER
);
2691 DERDecodedInfo generalNameContent
;
2692 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2694 if (!appendGeneralNameContentProperty(properties
,
2695 generalNameContent
.tag
, &generalNameContent
.content
)) {
2699 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2702 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
2703 generalNamesContent
);
2706 static void appendGeneralNames(CFMutableArrayRef properties
,
2707 const DERItem
*generalNames
) {
2708 DERDecodedInfo generalNamesContent
;
2709 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2710 require_noerr_quiet(drtn
, badDER
);
2711 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2713 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2716 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
, generalNames
);
2720 BasicConstraints ::= SEQUENCE {
2721 cA BOOLEAN DEFAULT FALSE,
2722 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
2724 static void appendBasicConstraints(CFMutableArrayRef properties
,
2725 const DERItem
*extnValue
) {
2726 DERBasicConstraints basicConstraints
;
2727 DERReturn drtn
= DERParseSequence(extnValue
,
2728 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
2729 &basicConstraints
, sizeof(basicConstraints
));
2730 require_noerr_quiet(drtn
, badDER
);
2732 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
2733 &basicConstraints
.cA
, false);
2735 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
2736 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
2737 &basicConstraints
.pathLenConstraint
);
2741 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
, extnValue
);
2745 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
2747 DistributionPoint ::= SEQUENCE {
2748 distributionPoint [0] DistributionPointName OPTIONAL,
2749 reasons [1] ReasonFlags OPTIONAL,
2750 cRLIssuer [2] GeneralNames OPTIONAL }
2752 DistributionPointName ::= CHOICE {
2753 fullName [0] GeneralNames,
2754 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
2756 ReasonFlags ::= BIT STRING {
2760 affiliationChanged (3),
2762 cessationOfOperation (5),
2763 certificateHold (6),
2764 privilegeWithdrawn (7),
2767 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
2768 const DERItem
*extnValue
) {
2769 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2772 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
2773 require_noerr_quiet(drtn
, badDER
);
2774 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2775 DERDecodedInfo dpSeqContent
;
2776 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
2777 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2778 DERDistributionPoint dp
;
2779 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
2780 DERNumDistributionPointItemSpecs
,
2781 DERDistributionPointItemSpecs
,
2783 require_noerr_quiet(drtn
, badDER
);
2784 if (dp
.distributionPoint
.length
) {
2785 DERDecodedInfo distributionPointName
;
2786 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
2787 require_noerr_quiet(drtn
, badDER
);
2788 if (distributionPointName
.tag
==
2789 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
2791 appendGeneralNamesContent(properties
,
2792 &distributionPointName
.content
);
2793 } else if (distributionPointName
.tag
==
2794 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
2795 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
2797 appendProperty(properties
, kSecPropertyTypeSection
,
2798 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
);
2799 CFRelease(rdn_props
);
2804 if (dp
.reasons
.length
) {
2805 static const CFStringRef reasonNames
[] = {
2807 SEC_KEY_COMPROMISE_KEY
,
2808 SEC_CA_COMPROMISE_KEY
,
2809 SEC_AFFILIATION_CHANGED_KEY
,
2811 SEC_CESSATION_OF_OPER_KEY
,
2812 SEC_CERTIFICATE_HOLD_KEY
,
2813 SEC_PRIV_WITHDRAWN_KEY
,
2814 SEC_AA_COMPROMISE_KEY
2816 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
2818 reasonNames
, sizeof(reasonNames
) / sizeof(*reasonNames
));
2820 if (dp
.cRLIssuer
.length
) {
2821 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
2822 &kCFTypeArrayCallBacks
);
2823 appendProperty(properties
, kSecPropertyTypeSection
,
2824 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
);
2825 CFRelease(crlIssuer
);
2826 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
2829 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2832 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
, extnValue
);
2835 /* Decode a sequence of integers into a comma separated list of ints. */
2836 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
2837 CFStringRef label
, const DERItem
*intSequenceContent
) {
2838 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2840 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
2841 require_noerr_quiet(drtn
, badDER
);
2842 DERDecodedInfo intContent
;
2843 CFStringRef value
= NULL
;
2844 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2845 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
2846 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
2847 CFStringRef intDesc
= copyIntegerContentDescription(
2848 allocator
, &intContent
.content
);
2851 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
2860 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2862 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2866 /* DROPTHOUGH if !value. */
2868 appendInvalidProperty(properties
, label
, intSequenceContent
);
2871 static void appendCertificatePolicies(CFMutableArrayRef properties
,
2872 const DERItem
*extnValue
) {
2873 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2876 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
2877 require_noerr_quiet(drtn
, badDER
);
2878 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2879 DERDecodedInfo piContent
;
2881 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
2882 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2883 DERPolicyInformation pi
;
2884 drtn
= DERParseSequenceContent(&piContent
.content
,
2885 DERNumPolicyInformationItemSpecs
,
2886 DERPolicyInformationItemSpecs
,
2888 require_noerr_quiet(drtn
, badDER
);
2889 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2890 SEC_POLICY_IDENTIFIER_KEY
, pin
);
2891 CFStringRef piFmt
= SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
);
2892 CFStringRef lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2895 appendOIDProperty(properties
, piLabel
, lpiLabel
, &pi
.policyIdentifier
);
2897 CFRelease(lpiLabel
);
2898 if (pi
.policyQualifiers
.length
== 0)
2902 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
2903 require_noerr_quiet(drtn
, badDER
);
2904 DERDecodedInfo pqContent
;
2906 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
2907 DERPolicyQualifierInfo pqi
;
2908 drtn
= DERParseSequenceContent(&pqContent
.content
,
2909 DERNumPolicyQualifierInfoItemSpecs
,
2910 DERPolicyQualifierInfoItemSpecs
,
2912 require_noerr_quiet(drtn
, badDER
);
2913 DERDecodedInfo qualifierContent
;
2914 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
2915 require_noerr_quiet(drtn
, badDER
);
2916 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2917 SEC_POLICY_QUALIFIER_KEY
, pqn
);
2918 CFStringRef pqFmt
= SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
);
2919 CFStringRef lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2922 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
2923 &pqi
.policyQualifierID
);
2925 CFRelease(lpqLabel
);
2926 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
2927 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
2928 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
2929 &qualifierContent
.content
);
2930 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
2931 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2933 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
2934 DERNumUserNoticeItemSpecs
,
2935 DERUserNoticeItemSpecs
,
2937 require_noerr_quiet(drtn
, badDER
);
2938 if (un
.noticeRef
.length
) {
2939 DERNoticeReference nr
;
2940 drtn
= DERParseSequenceContent(&un
.noticeRef
,
2941 DERNumNoticeReferenceItemSpecs
,
2942 DERNoticeReferenceItemSpecs
,
2944 require_noerr_quiet(drtn
, badDER
);
2945 appendDERThingProperty(properties
,
2946 SEC_ORGANIZATION_KEY
, NULL
,
2948 appendIntegerSequenceContent(properties
,
2949 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
);
2951 if (un
.explicitText
.length
) {
2952 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
2953 NULL
, &un
.explicitText
);
2956 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
2961 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2964 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
, extnValue
);
2967 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
2968 const DERItem
*extnValue
) {
2970 DERDecodedInfo keyIdentifier
;
2971 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
2972 require_noerr_quiet(drtn
, badDER
);
2973 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
2974 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
2975 &keyIdentifier
.content
);
2979 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
2984 AuthorityKeyIdentifier ::= SEQUENCE {
2985 keyIdentifier [0] KeyIdentifier OPTIONAL,
2986 authorityCertIssuer [1] GeneralNames OPTIONAL,
2987 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
2988 -- authorityCertIssuer and authorityCertSerialNumber MUST both
2989 -- be present or both be absent
2991 KeyIdentifier ::= OCTET STRING
2993 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
2994 const DERItem
*extnValue
) {
2995 DERAuthorityKeyIdentifier akid
;
2997 drtn
= DERParseSequence(extnValue
,
2998 DERNumAuthorityKeyIdentifierItemSpecs
,
2999 DERAuthorityKeyIdentifierItemSpecs
,
3000 &akid
, sizeof(akid
));
3001 require_noerr_quiet(drtn
, badDER
);
3002 if (akid
.keyIdentifier
.length
) {
3003 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3004 &akid
.keyIdentifier
);
3006 if (akid
.authorityCertIssuer
.length
||
3007 akid
.authorityCertSerialNumber
.length
) {
3008 require_quiet(akid
.authorityCertIssuer
.length
&&
3009 akid
.authorityCertSerialNumber
.length
, badDER
);
3010 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3011 appendGeneralNamesContent(properties
,
3012 &akid
.authorityCertIssuer
);
3013 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3014 &akid
.authorityCertSerialNumber
);
3019 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
, extnValue
);
3023 PolicyConstraints ::= SEQUENCE {
3024 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3025 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3027 SkipCerts ::= INTEGER (0..MAX)
3029 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3030 const DERItem
*extnValue
) {
3031 DERPolicyConstraints pc
;
3033 drtn
= DERParseSequence(extnValue
,
3034 DERNumPolicyConstraintsItemSpecs
,
3035 DERPolicyConstraintsItemSpecs
,
3037 require_noerr_quiet(drtn
, badDER
);
3038 if (pc
.requireExplicitPolicy
.length
) {
3039 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3040 &pc
.requireExplicitPolicy
);
3042 if (pc
.inhibitPolicyMapping
.length
) {
3043 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3044 &pc
.inhibitPolicyMapping
);
3050 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
, extnValue
);
3054 extendedKeyUsage EXTENSION ::= {
3055 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3056 IDENTIFIED BY id-ce-extKeyUsage }
3058 KeyPurposeId ::= OBJECT IDENTIFIER
3060 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3061 const DERItem
*extnValue
) {
3064 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3065 require_noerr_quiet(drtn
, badDER
);
3066 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3067 DERDecodedInfo currDecoded
;
3068 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3069 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3070 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3071 &currDecoded
.content
);
3073 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3076 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
, extnValue
);
3080 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3082 AuthorityInfoAccessSyntax ::=
3083 SEQUENCE SIZE (1..MAX) OF AccessDescription
3085 AccessDescription ::= SEQUENCE {
3086 accessMethod OBJECT IDENTIFIER,
3087 accessLocation GeneralName }
3089 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3091 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3093 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3095 static void appendInfoAccess(CFMutableArrayRef properties
,
3096 const DERItem
*extnValue
) {
3099 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3100 require_noerr_quiet(drtn
, badDER
);
3101 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3102 DERDecodedInfo adContent
;
3103 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3104 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3105 DERAccessDescription ad
;
3106 drtn
= DERParseSequenceContent(&adContent
.content
,
3107 DERNumAccessDescriptionItemSpecs
,
3108 DERAccessDescriptionItemSpecs
,
3110 require_noerr_quiet(drtn
, badDER
);
3111 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3113 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3114 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3116 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3119 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
, extnValue
);
3122 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3123 const DERItem
*extnValue
) {
3124 static const CFStringRef certTypes
[] = {
3128 SEC_OBJECT_SIGNING_KEY
,
3132 SEC_OBJECT_SIGNING_CA_KEY
3134 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3135 certTypes
, sizeof(certTypes
) / sizeof(*certTypes
));
3139 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3140 const DERItem
*extnValue
) {
3144 * The list of Qualified Cert Statement statementIds we understand, even though
3145 * we don't actually do anything with them; if these are found in a Qualified
3146 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3148 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3150 /* id-qcs := { id-pkix 11 } */
3151 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3152 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3153 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3154 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3155 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3156 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3158 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3160 static void appendQCCertStatements(CFMutableArrayRef properties
,
3161 const DERItem
*extnValue
) {
3166 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3167 CFStringRef label
, const DERItem
*sequence
) {
3170 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3171 require_noerr_quiet(drtn
, badSequence
);
3172 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3173 DERDecodedInfo currDecoded
;
3174 bool appendedSomething
= false;
3175 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3176 switch (currDecoded
.tag
)
3179 case ASN1_SEQUENCE
: // 16
3180 case ASN1_SET
: // 17
3181 // skip constructed object lengths
3184 case ASN1_UTF8_STRING
: // 12
3185 case ASN1_NUMERIC_STRING
: // 18
3186 case ASN1_PRINTABLE_STRING
: // 19
3187 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3188 case ASN1_VIDEOTEX_STRING
: // 21
3189 case ASN1_IA5_STRING
: // 22
3190 case ASN1_GRAPHIC_STRING
: // 25
3191 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3192 case ASN1_GENERAL_STRING
: // 27
3193 case ASN1_UNIVERSAL_STRING
: // 28
3195 CFStringRef string
=
3196 copyDERThingContentDescription(CFGetAllocator(properties
),
3197 currDecoded
.tag
, &currDecoded
.content
, false);
3198 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3200 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3203 appendedSomething
= true;
3210 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3211 return appendedSomething
;
3216 static void appendExtension(CFMutableArrayRef parent
,
3217 const SecCertificateExtension
*extn
) {
3218 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3219 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3220 &kCFTypeArrayCallBacks
);
3222 *extnID
= &extn
->extnID
,
3223 *extnValue
= &extn
->extnValue
;
3225 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
);
3228 bool handeled
= true;
3229 /* Extensions that we know how to handle ourselves... */
3230 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3231 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3233 switch (extnID
->data
[extnID
->length
- 1]) {
3234 case 14: /* SubjectKeyIdentifier id-ce 14 */
3235 appendSubjectKeyIdentifier(properties
, extnValue
);
3237 case 15: /* KeyUsage id-ce 15 */
3238 appendKeyUsage(properties
, extnValue
);
3240 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3241 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3243 case 17: /* SubjectAltName id-ce 17 */
3244 case 18: /* IssuerAltName id-ce 18 */
3245 appendGeneralNames(properties
, extnValue
);
3247 case 19: /* BasicConstraints id-ce 19 */
3248 appendBasicConstraints(properties
, extnValue
);
3250 case 30: /* NameConstraints id-ce 30 */
3253 case 31: /* CRLDistributionPoints id-ce 31 */
3254 appendCrlDistributionPoints(properties
, extnValue
);
3256 case 32: /* CertificatePolicies id-ce 32 */
3257 appendCertificatePolicies(properties
, extnValue
);
3259 case 33: /* PolicyMappings id-ce 33 */
3262 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3263 appendAuthorityKeyIdentifier(properties
, extnValue
);
3265 case 36: /* PolicyConstraints id-ce 36 */
3266 appendPolicyConstraints(properties
, extnValue
);
3268 case 37: /* ExtKeyUsage id-ce 37 */
3269 appendExtendedKeyUsage(properties
, extnValue
);
3271 case 46: /* FreshestCRL id-ce 46 */
3274 case 54: /* InhibitAnyPolicy id-ce 54 */
3281 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3282 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3284 switch (extnID
->data
[extnID
->length
- 1]) {
3285 case 1: /* AuthorityInfoAccess id-pe 1 */
3286 appendInfoAccess(properties
, extnValue
);
3288 case 3: /* QCStatements id-pe 3 */
3291 case 11: /* SubjectInfoAccess id-pe 11 */
3292 appendInfoAccess(properties
, extnValue
);
3298 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3299 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3300 appendNetscapeCertType(properties
, extnValue
);
3306 /* Try to parse and display printable string(s). */
3307 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3308 /* Nothing to do here appendPrintableDERSequence did the work. */
3310 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3311 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3315 /* Extensions that we know how to handle ourselves... */
3316 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3317 appendSubjectKeyIdentifier(properties
, extnValue
);
3318 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3319 appendKeyUsage(properties
, extnValue
);
3320 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3321 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3322 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3323 appendGeneralNames(properties
, extnValue
);
3324 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3325 appendGeneralNames(properties
, extnValue
);
3326 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3327 appendBasicConstraints(properties
, extnValue
);
3328 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3329 appendCrlDistributionPoints(properties
, extnValue
);
3330 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3331 appendCertificatePolicies(properties
, extnValue
);
3332 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3333 appendAuthorityKeyIdentifier(properties
, extnValue
);
3334 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3335 appendPolicyConstraints(properties
, extnValue
);
3336 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3337 appendExtendedKeyUsage(properties
, extnValue
);
3338 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3339 appendInfoAccess(properties
, extnValue
);
3340 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3341 appendInfoAccess(properties
, extnValue
);
3342 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3343 appendNetscapeCertType(properties
, extnValue
);
3345 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3346 appendEntrustVersInfo(properties
, extnValue
);
3349 /* Try to parse and display printable string(s). */
3350 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3351 /* Nothing to do here appendPrintableDERSequence did the work. */
3353 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3354 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3357 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(allocator
,
3359 CFStringRef localizedLabel
= copyLocalizedOidDescription(allocator
,
3361 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
,
3363 CFRelease(localizedLabel
);
3365 CFRelease(properties
);
3368 /* Different types of summary types from least desired to most desired. */
3371 kSummaryTypePrintable
,
3372 kSummaryTypeOrganizationName
,
3373 kSummaryTypeOrganizationalUnitName
,
3374 kSummaryTypeCommonName
,
3378 enum SummaryType type
;
3379 CFStringRef summary
;
3380 CFStringRef description
;
3383 static OSStatus
obtainSummaryFromX501Name(void *context
,
3384 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3385 struct Summary
*summary
= (struct Summary
*)context
;
3386 enum SummaryType stype
= kSummaryTypeNone
;
3387 CFStringRef string
= NULL
;
3388 if (DEROidCompare(type
, &oidCommonName
)) {
3389 stype
= kSummaryTypeCommonName
;
3390 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3391 stype
= kSummaryTypeOrganizationalUnitName
;
3392 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3393 stype
= kSummaryTypeOrganizationName
;
3394 } else if (DEROidCompare(type
, &oidDescription
)) {
3395 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3397 if (summary
->description
) {
3398 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3399 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->description
);
3401 CFRelease(summary
->description
);
3402 summary
->description
= newDescription
;
3404 summary
->description
= string
;
3407 stype
= kSummaryTypePrintable
;
3410 stype
= kSummaryTypePrintable
;
3413 /* Build a string with all instances of the most desired
3414 component type in reverse order encountered comma separated list,
3415 The order of desirability is defined by enum SummaryType. */
3416 if (summary
->type
<= stype
) {
3418 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3421 if (summary
->type
== stype
) {
3422 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3423 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->summary
);
3426 string
= newSummary
;
3428 summary
->type
= stype
;
3430 CFReleaseSafe(summary
->summary
);
3431 summary
->summary
= string
;
3434 CFReleaseSafe(string
);
3440 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3441 struct Summary summary
= {};
3442 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3443 /* If we found a description and a common name we change the summary to
3444 CommonName (Description). */
3445 if (summary
.description
) {
3446 if (summary
.type
== kSummaryTypeCommonName
) {
3447 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3448 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3450 CFRelease(summary
.summary
);
3451 summary
.summary
= newSummary
;
3453 CFRelease(summary
.description
);
3456 if (!summary
.summary
) {
3457 /* If we didn't find a suitable printable string in the subject at all, we try
3458 the first email address in the certificate instead. */
3459 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3461 /* If we didn't find any email addresses in the certificate, we try finding
3462 a DNS name instead. */
3463 names
= SecCertificateCopyDNSNames(certificate
);
3466 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3467 CFRetain(summary
.summary
);
3472 return summary
.summary
;
3475 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
3476 struct Summary summary
= {};
3477 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3478 /* If we found a description and a common name we change the summary to
3479 CommonName (Description). */
3480 if (summary
.description
) {
3481 if (summary
.type
== kSummaryTypeCommonName
) {
3482 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3483 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3485 CFRelease(summary
.summary
);
3486 summary
.summary
= newSummary
;
3488 CFRelease(summary
.description
);
3491 return summary
.summary
;
3494 /* Return the earliest date on which all certificates in this chain are still
3496 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3497 SecCertificateRef certificate
) {
3498 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3500 while (certificate
->_parent
) {
3501 certificate
= certificate
->_parent
;
3502 if (earliest
> certificate
->_notAfter
)
3503 earliest
= certificate
->_notAfter
;
3510 /* Return the latest date on which all certificates in this chain will be
3512 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3513 SecCertificateRef certificate
) {
3514 CFAbsoluteTime latest
= certificate
->_notBefore
;
3516 while (certificate
->_parent
) {
3517 certificate
= certificate
->_parent
;
3518 if (latest
< certificate
->_notBefore
)
3519 latest
= certificate
->_notBefore
;
3526 bool SecCertificateIsValid(SecCertificateRef certificate
,
3527 CFAbsoluteTime verifyTime
) {
3529 return certificate
->_notBefore
<= verifyTime
&&
3530 verifyTime
<= certificate
->_notAfter
;
3533 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
3534 return certificate
->_version
+ 1;
3537 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
3538 return certificate
->_notBefore
;
3541 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
3542 return certificate
->_notAfter
;
3545 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3546 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
3547 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3548 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3549 &kCFTypeArrayCallBacks
);
3551 /* First we put the subject summary name. */
3552 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
3554 appendProperty(summary
, kSecPropertyTypeTitle
,
3555 NULL
, NULL
, ssummary
);
3556 CFRelease(ssummary
);
3559 CFStringRef isummary
= SEC_ISSUER_SUMMARY_KEY
;
3560 appendProperty(summary
, kSecPropertyTypeString
,
3561 SEC_ISSUED_BY_KEY
, isummary
);
3562 CFRelease(isummary
);
3565 /* Let see if this certificate is currently valid. */
3567 CFAbsoluteTime when
;
3568 CFStringRef message
;
3570 if (verifyTime
> certificate
->_notAfter
) {
3571 label
= SEC_EXPIRED_KEY
;
3572 when
= certificate
->_notAfter
;
3573 ptype
= kSecPropertyTypeError
;
3574 message
= SEC_CERT_EXPIRED_KEY
;
3575 } else if (certificate
->_notBefore
> verifyTime
) {
3576 label
= SEC_VALID_FROM_KEY
;
3577 when
= certificate
->_notBefore
;
3578 ptype
= kSecPropertyTypeError
;
3579 message
= SEC_CERT_NOT_YET_VALID_KEY
;
3581 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3582 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3583 if (verifyTime
> last
) {
3584 label
= SEC_EXPIRED_KEY
;
3586 ptype
= kSecPropertyTypeError
;
3587 message
= SEC_ISSUER_EXPIRED_KEY
;
3588 } else if (verifyTime
< first
) {
3589 label
= SEC_VALID_FROM_KEY
;
3591 ptype
= kSecPropertyTypeError
;
3592 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
3594 label
= SEC_EXPIRES_KEY
;
3595 when
= certificate
->_notAfter
;
3596 ptype
= kSecPropertyTypeSuccess
;
3597 message
= SEC_CERT_VALID_KEY
;
3601 appendDateProperty(summary
, label
, when
);
3602 CFStringRef lmessage
= SecCopyCertString(message
);
3603 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
);
3604 CFRelease(lmessage
);
3609 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
3610 if (!certificate
->_properties
) {
3611 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3612 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3613 &kCFTypeArrayCallBacks
);
3615 /* First we put the Subject Name in the property list. */
3616 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3617 &certificate
->_subject
);
3618 appendProperty(properties
, kSecPropertyTypeSection
,
3619 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
);
3620 CFRelease(subject_plist
);
3623 /* Put Normalized subject in for testing. */
3624 if (certificate
->_normalizedSubject
) {
3625 DERItem nsubject
= {
3626 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
3627 CFDataGetLength(certificate
->_normalizedSubject
)
3629 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
3631 appendProperty(properties
, kSecPropertyTypeSection
,
3632 CFSTR("Normalized Subject Name"), nsubject_plist
);
3633 CFRelease(nsubject_plist
);
3637 /* Next we put the Issuer Name in the property list. */
3638 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3639 &certificate
->_issuer
);
3640 appendProperty(properties
, kSecPropertyTypeSection
,
3641 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
);
3642 CFRelease(issuer_plist
);
3645 /* Certificate version/type. */
3646 bool isRoot
= false;
3647 CFStringRef fmt
= SecCopyCertString(SEC_X509_VERSION_KEY
);
3648 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
3649 fmt
, certificate
->_version
+ 1, isRoot
? "root " : "");
3651 appendProperty(properties
, kSecPropertyTypeString
,
3652 SEC_CERTIFICATE_TYPE_KEY
, typeString
);
3653 CFRelease(typeString
);
3657 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
3658 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
3659 NULL
, fmt
, certificate
->_version
+ 1);
3661 appendProperty(properties
, kSecPropertyTypeString
,
3662 SEC_VERSION_KEY
, NULL
, versionString
);
3663 CFRelease(versionString
);
3666 if (certificate
->_serialNum
.length
) {
3667 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
3668 &certificate
->_serialNum
);
3671 /* Signature algorithm. */
3673 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
3674 &certificate
->_sigAlg
);
3676 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
3677 &certificate
->_tbsSigAlg
);
3680 /* Validity dates. */
3681 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
3682 certificate
->_notBefore
);
3683 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
3684 certificate
->_notAfter
);
3686 if (certificate
->_subjectUniqueID
.length
) {
3687 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
3688 &certificate
->_subjectUniqueID
);
3690 if (certificate
->_issuerUniqueID
.length
) {
3691 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
3692 &certificate
->_issuerUniqueID
);
3695 /* Public key algorithm. */
3696 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
3697 &certificate
->_algId
);
3699 /* Consider breaking down an RSA public key into modulus and
3701 appendDataProperty(properties
, SEC_PULIC_KEY_DATA_KEY
, NULL
,
3702 &certificate
->_pubKeyDER
);
3703 /* TODO: Add Key Size. */
3704 /* TODO: Add Key Usage. */
3706 appendDataProperty(properties
, SEC_SIGNATURE_KEY
, NULL
,
3707 &certificate
->_signature
);
3710 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
3711 appendExtension(properties
, &certificate
->_extensions
[ix
]);
3714 /* TODO: Certificate/Key Fingerprints. */
3716 certificate
->_properties
= properties
;
3719 CFRetain(certificate
->_properties
);
3720 return certificate
->_properties
;
3723 CFDataRef
SecCertificateCopySerialNumber(
3724 SecCertificateRef certificate
) {
3725 if (certificate
->_serialNumber
) {
3726 CFRetain(certificate
->_serialNumber
);
3728 return certificate
->_serialNumber
;
3731 CFDataRef
SecCertificateGetNormalizedIssuerContent(
3732 SecCertificateRef certificate
) {
3733 return certificate
->_normalizedIssuer
;
3736 CFDataRef
SecCertificateGetNormalizedSubjectContent(
3737 SecCertificateRef certificate
) {
3738 return certificate
->_normalizedSubject
;
3741 /* Verify that certificate was signed by issuerKey. */
3742 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
3743 SecKeyRef issuerKey
) {
3744 /* Setup algId in SecAsn1AlgId format. */
3746 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3747 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3748 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3749 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3751 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3752 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3753 certificate
->_signature
.data
, certificate
->_signature
.length
);
3755 secdebug("verify", "signature verify failed: %d", status
);
3756 return errSecNotSigner
;
3763 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRef certificate
,
3764 SecCertificateRef issuer
, bool signatureCheckOnly
) {
3765 if (!signatureCheckOnly
) {
3766 /* It turns out we don't actually need to use normalized subject and
3767 issuer according to rfc2459. */
3769 /* If present we should check issuerID against the issuer subjectID. */
3771 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3772 then we should look for a SubjectKeyIdentifier in the issuer
3774 If we have a authorityCertSerialNumber we can use that for chaining.
3775 If we have a authorityCertIssuer we can use that? (or not) */
3777 /* Verify that this cert was issued by issuer. Do so by chaining
3778 either issuerID to subjectID or normalized issuer to normalized
3780 CFDataRef normalizedIssuer
=
3781 SecCertificateGetNormalizedIssuerContent(certificate
);
3782 CFDataRef normalizedIssuerSubject
=
3783 SecCertificateGetNormalizedSubjectContent(issuer
);
3784 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3785 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3786 return errSecIssuerMismatch
;
3789 /* Next verify that this cert was signed by issuer. */
3790 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3792 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3793 /* FIXME: We sould cache this (or at least the digest) until we find
3794 a suitable issuer. */
3795 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3796 CFIndex signedDataLength
;
3797 CertVerifyReturn crtn
;
3798 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3799 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3800 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3801 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3802 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3803 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3804 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3805 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3806 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3808 secdebug("verify", "unsupported algorithm");
3809 return errSecUnsupportedAlgorithm
;
3812 secdebug("verify", "*DigestInfo returned: %d", crtn
);
3813 /* FIXME: Do proper error code translation. */
3814 return errSecUnsupportedAlgorithm
;
3817 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3818 signedData
, signedDataLength
,
3819 certificate
->_signature
.data
, certificate
->_signature
.length
);
3821 secdebug("verify", "signature verify failed: %d", status
);
3822 return errSecNotSigner
;
3828 static OSStatus
_SecCertificateSetParent(SecCertificateRef certificate
,
3829 SecCertificateRef issuer
, bool signatureCheckOnly
) {
3831 if (certificate
->_parent
) {
3832 /* Setting a certificates issuer twice is only allowed if the new
3833 issuer is equal to the current one. */
3834 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3838 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3839 signatureCheckOnly
);
3841 OSStatus status
= noErr
;
3844 if (CFEqual(certificate
, issuer
)) {
3845 /* We don't retain ourselves cause that would be bad mojo,
3846 however we do record that we are properly self signed. */
3847 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3848 secdebug("cert", "set self as parent");
3853 certificate
->_parent
= issuer
;
3854 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3860 static bool SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
3861 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3862 certificate
->_isSelfSigned
=
3863 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3864 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3867 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3870 /* Return true iff we were able to set our own parent from one of the
3871 certificates in other_certificates, return false otherwise. If
3872 signatureCheckOnly is true, we can skip the subject == issuer or
3873 authorityKeyIdentifier tests. */
3874 static bool SecCertificateSetParentFrom(SecCertificateRef certificate
,
3875 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3876 CFIndex count
= CFArrayGetCount(other_certificates
);
3878 for (ix
= 0; ix
< count
; ++ix
) {
3879 SecCertificateRef candidate
= (SecCertificateRef
)
3880 CFArrayGetValueAtIndex(other_certificates
, ix
);
3881 if (_SecCertificateSetParent(certificate
, candidate
,
3882 signatureCheckOnly
))
3888 /* Lookup the parent of certificate in the keychain and set it. */
3889 static bool SecCertificateFindParent(SecCertificateRef certificate
) {
3890 /* FIXME: Search for things other than just subject of our issuer if we
3891 have a subjectID or authorityKeyIdentifier. */
3892 CFDataRef normalizedIssuer
=
3893 SecCertificateGetNormalizedIssuerContent(certificate
);
3894 const void *keys
[] = {
3901 kSecClassCertificate
,
3906 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3907 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3909 OSStatus status
= SecItemCopyMatching(query
, &results
);
3912 secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3916 CFArrayRef certs
= (CFArrayRef
)results
;
3917 /* Since we already know the certificates we are providing as candidates
3918 have been checked for subject matching, we can ask
3919 SecCertificateSetParentFrom to skip everything except the signature
3921 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3926 OSStatus
SecCertificateCompleteChain(SecCertificateRef certificate
,
3927 CFArrayRef other_certificates
) {
3929 if (certificate
->_parent
== NULL
) {
3930 if (SecCertificateIsSelfSigned(certificate
))
3932 if (!other_certificates
||
3933 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
3935 if (!SecCertificateFindParent(certificate
))
3936 return errSecIssuerNotFound
;
3939 certificate
= certificate
->_parent
;
3944 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
3945 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
3946 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
3947 if (gnType
== GNT_IPAddress
) {
3948 CFStringRef string
= copyIPAddressContentDescription(
3949 kCFAllocatorDefault
, generalName
);
3951 CFArrayAppendValue(ipAddresses
, string
);
3954 return errSecInvalidCertificate
;
3960 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
3961 /* These can only exist in the subject alt name. */
3962 if (!certificate
->_subjectAltName
)
3965 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
3966 0, &kCFTypeArrayCallBacks
);
3967 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
3968 ipAddresses
, appendIPAddressesFromGeneralNames
);
3969 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
3970 CFRelease(ipAddresses
);
3976 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
3977 const DERItem
*generalName
) {
3978 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
3979 if (gnType
== GNT_DNSName
) {
3980 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
3981 generalName
->data
, generalName
->length
,
3982 kCFStringEncodingUTF8
, FALSE
);
3984 CFArrayAppendValue(dnsNames
, string
);
3987 return errSecInvalidCertificate
;
3993 /* Return true if the passed in string matches the
3994 Preferred name syntax from sections 2.3.1. in RFC 1035.
3995 With the added check that we disallow empty dns names.
3996 Also in order to support wildcard DNSNames we allow for the '*'
3997 character anywhere in a dns component where we currently allow
4000 <domain> ::= <subdomain> | " "
4002 <subdomain> ::= <label> | <subdomain> "." <label>
4004 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4006 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4008 <let-dig-hyp> ::= <let-dig> | "-"
4010 <let-dig> ::= <letter> | <digit>
4012 <letter> ::= any one of the 52 alphabetic characters A through Z in
4013 upper case and a through z in lower case
4015 <digit> ::= any one of the ten digits 0 through 9
4017 static bool isDNSName(CFStringRef string
) {
4018 CFStringInlineBuffer buf
;
4019 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4020 /* From RFC 1035 2.3.4. Size limits:
4021 labels 63 octets or less
4022 names 255 octets or less */
4023 require_quiet(length
<= 255, notDNS
);
4024 CFRange range
= { 0, length
};
4025 CFStringInitInlineBuffer(string
, &buf
, range
);
4029 kDNSStateAfterAlpha
,
4030 kDNSStateAfterDigit
,
4032 } state
= kDNSStateInital
;
4034 for (ix
= 0; ix
< length
; ++ix
) {
4035 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4038 require_quiet(labelLength
<= 64 &&
4039 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4041 state
= kDNSStateAfterDot
;
4043 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4045 state
= kDNSStateAfterAlpha
;
4046 } else if ('0' <= ch
&& ch
<= '9') {
4048 /* The requirement for labels to start with a letter was
4049 dropped so we don't check this anymore. */
4050 require_quiet(state
== kDNSStateAfterAlpha
||
4051 state
== kDNSStateAfterDigit
||
4052 state
== kDNSStateAfterDash
, notDNS
);
4054 state
= kDNSStateAfterDigit
;
4055 } else if (ch
== '-') {
4056 require_quiet(state
== kDNSStateAfterAlpha
||
4057 state
== kDNSStateAfterDigit
||
4058 state
== kDNSStateAfterDash
, notDNS
);
4059 state
= kDNSStateAfterDash
;
4065 /* We don't allow a dns name to end in a dot or dash. */
4066 require_quiet(labelLength
<= 63 &&
4067 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4075 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4076 const DERItem
*value
, CFIndex rdnIX
) {
4077 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4078 if (DEROidCompare(type
, &oidCommonName
)) {
4079 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4082 if (isDNSName(string
)) {
4083 /* We found a common name that is formatted like a valid
4085 CFArrayAppendValue(dnsNames
, string
);
4089 return errSecInvalidCertificate
;
4095 /* Not everything returned by this function is going to be a proper DNS name,
4096 we also return the certificates common name entries from the subject,
4097 assuming they look like dns names as specified in RFC 1035. */
4098 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4099 /* These can exist in the subject alt name or in the subject. */
4100 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4101 0, &kCFTypeArrayCallBacks
);
4102 OSStatus status
= noErr
;
4103 if (certificate
->_subjectAltName
) {
4104 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4105 dnsNames
, appendDNSNamesFromGeneralNames
);
4107 /* RFC 2818 section 3.1. Server Identity
4109 If a subjectAltName extension of type dNSName is present, that MUST
4110 be used as the identity. Otherwise, the (most specific) Common Name
4111 field in the Subject field of the certificate MUST be used. Although
4112 the use of the Common Name is existing practice, it is deprecated and
4113 Certification Authorities are encouraged to use the dNSName instead.
4116 This implies that if we found 1 or more DNSNames in the
4117 subjectAltName, we should not use the Common Name of the subject as
4120 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4121 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4122 appendDNSNamesFromX501Name
);
4124 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4125 CFRelease(dnsNames
);
4131 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4132 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4133 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4134 if (gnType
== GNT_RFC822Name
) {
4135 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4136 generalName
->data
, generalName
->length
,
4137 kCFStringEncodingASCII
, FALSE
);
4139 CFArrayAppendValue(dnsNames
, string
);
4142 return errSecInvalidCertificate
;
4148 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4149 const DERItem
*value
, CFIndex rdnIX
) {
4150 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4151 if (DEROidCompare(type
, &oidEmailAddress
)) {
4152 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4155 CFArrayAppendValue(dnsNames
, string
);
4158 return errSecInvalidCertificate
;
4164 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4165 /* These can exist in the subject alt name or in the subject. */
4166 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4167 0, &kCFTypeArrayCallBacks
);
4168 OSStatus status
= noErr
;
4169 if (certificate
->_subjectAltName
) {
4170 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4171 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4174 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4175 appendRFC822NamesFromX501Name
);
4177 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4178 CFRelease(rfc822Names
);
4184 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4185 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4186 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4187 if (DEROidCompare(type
, &oidCommonName
)) {
4188 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4191 CFArrayAppendValue(commonNames
, string
);
4194 return errSecInvalidCertificate
;
4200 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4201 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4202 0, &kCFTypeArrayCallBacks
);
4204 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4205 appendCommonNamesFromX501Name
);
4206 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4207 CFRelease(commonNames
);
4213 static OSStatus
appendOrganizationFromX501Name(void *context
,
4214 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4215 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4216 if (DEROidCompare(type
, &oidOrganizationName
)) {
4217 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4220 CFArrayAppendValue(organization
, string
);
4223 return errSecInvalidCertificate
;
4229 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4230 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4231 0, &kCFTypeArrayCallBacks
);
4233 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4234 appendOrganizationFromX501Name
);
4235 if (status
|| CFArrayGetCount(organization
) == 0) {
4236 CFRelease(organization
);
4237 organization
= NULL
;
4239 return organization
;
4242 const SecCEBasicConstraints
*
4243 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4244 if (certificate
->_basicConstraints
.present
)
4245 return &certificate
->_basicConstraints
;
4250 const SecCEPolicyConstraints
*
4251 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4252 if (certificate
->_policyConstraints
.present
)
4253 return &certificate
->_policyConstraints
;
4259 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4260 return certificate
->_policyMappings
;
4263 const SecCECertificatePolicies
*
4264 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4265 if (certificate
->_certificatePolicies
.present
)
4266 return &certificate
->_certificatePolicies
;
4272 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4273 return certificate
->_inhibitAnyPolicySkipCerts
;
4276 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4277 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4278 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4279 if (gnType
== GNT_OtherName
) {
4281 DERReturn drtn
= DERParseSequenceContent(generalName
,
4282 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4284 require_noerr_quiet(drtn
, badDER
);
4285 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4287 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4288 &on
.value
, true), badDER
);
4289 CFArrayAppendValue(ntPrincipalNames
, string
);
4296 return errSecInvalidCertificate
;
4300 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
4301 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4302 0, &kCFTypeArrayCallBacks
);
4303 OSStatus status
= noErr
;
4304 if (certificate
->_subjectAltName
) {
4305 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4306 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4308 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4309 CFRelease(ntPrincipalNames
);
4310 ntPrincipalNames
= NULL
;
4312 return ntPrincipalNames
;
4315 static OSStatus
appendToRFC2253String(void *context
,
4316 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4317 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4321 ST stateOrProvinceName
4323 OU organizationalUnitName
4325 STREET streetAddress
4329 /* Prepend a + if this is not the first RDN in an RDN set.
4330 Otherwise prepend a , if this is not the first RDN. */
4332 CFStringAppend(string
, CFSTR("+"));
4333 else if (CFStringGetLength(string
)) {
4334 CFStringAppend(string
, CFSTR(","));
4337 CFStringRef label
, oid
= NULL
;
4338 /* @@@ Consider changing this to a dictionary lookup keyed by the
4339 decimal representation. */
4340 if (DEROidCompare(type
, &oidCommonName
)) {
4341 label
= CFSTR("CN");
4342 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4344 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4345 label
= CFSTR("ST");
4346 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4348 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4349 label
= CFSTR("OU");
4350 } else if (DEROidCompare(type
, &oidCountryName
)) {
4353 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4354 label
= CFSTR("STREET");
4355 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4356 label
= CFSTR("DC");
4357 } else if (DEROidCompare(type
, &oidUserID
)) {
4358 label
= CFSTR("UID");
4361 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4364 CFStringAppend(string
, label
);
4365 CFStringAppend(string
, CFSTR("="));
4366 CFStringRef raw
= NULL
;
4368 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4371 /* Append raw to string while escaping:
4372 a space or "#" character occurring at the beginning of the string
4373 a space character occurring at the end of the string
4374 one of the characters ",", "+", """, "\", "<", ">" or ";"
4376 CFStringInlineBuffer buffer
;
4377 CFIndex ix
, length
= CFStringGetLength(raw
);
4378 CFRange range
= { 0, length
};
4379 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4380 for (ix
= 0; ix
< length
; ++ix
) {
4381 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4383 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4384 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4385 ch
== '<' || ch
== '>' || ch
== ';' ||
4386 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4387 (ch
== '#' && ix
== 0)) {
4388 UniChar chars
[] = { '\\', ch
};
4389 CFStringAppendCharacters(string
, chars
, 2);
4391 CFStringAppendCharacters(string
, &ch
, 1);
4396 /* Append the value in hex. */
4397 CFStringAppend(string
, CFSTR("#"));
4399 for (ix
= 0; ix
< value
->length
; ++ix
)
4400 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4408 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
4409 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4410 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4411 if (status
|| CFStringGetLength(string
) == 0) {
4418 static OSStatus
appendToCompanyNameString(void *context
,
4419 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4420 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4421 if (CFStringGetLength(string
) != 0)
4424 if (!DEROidCompare(type
, &oidOrganizationName
))
4428 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4431 CFStringAppend(string
, raw
);
4437 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
4438 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4439 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4440 appendToCompanyNameString
);
4441 if (status
|| CFStringGetLength(string
) == 0) {
4448 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4449 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4450 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4451 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4453 CFDataSetLength(sequence
, sequence_length
);
4454 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4455 *sequence_ptr
++ = 0x30; /* ASN1_CONSTR_SEQUENCE */
4456 require_noerr_quiet(DEREncodeLength(content
->length
,
4457 sequence_ptr
, &seq_len_length
), out
);
4458 sequence_ptr
+= seq_len_length
;
4459 memcpy(sequence_ptr
, content
->data
, content
->length
);
4462 CFReleaseSafe(sequence
);
4466 CFDataRef
SecCertificateCopyIssuerSequence(
4467 SecCertificateRef certificate
) {
4468 return SecDERItemCopySequence(&certificate
->_issuer
);
4471 CFDataRef
SecCertificateCopySubjectSequence(
4472 SecCertificateRef certificate
) {
4473 return SecDERItemCopySequence(&certificate
->_subject
);
4476 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4477 SecCertificateRef certificate
) {
4478 return &certificate
->_algId
;
4481 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
4482 return &certificate
->_pubKeyDER
;
4485 SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
) {
4486 const DERAlgorithmId
*algId
=
4487 SecCertificateGetPublicKeyAlgorithm(certificate
);
4488 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4489 const DERItem
*params
= NULL
;
4490 if (algId
->params
.length
!= 0) {
4491 params
= &algId
->params
;
4493 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
4494 SecAsn1Item params1
= {
4495 .Data
= params
? params
->data
: NULL
,
4496 .Length
= params
? params
->length
: 0
4498 SecAsn1Item keyData1
= {
4499 .Data
= keyData
? keyData
->data
: NULL
,
4500 .Length
= keyData
? keyData
->length
: 0
4502 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
4506 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
4507 if (!certificate
->_sha1Digest
) {
4508 certificate
->_sha1Digest
=
4509 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4510 certificate
->_der
.data
, certificate
->_der
.length
);
4513 return certificate
->_sha1Digest
;
4516 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
4517 CFDataRef digest
= NULL
;
4518 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
4520 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4521 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4527 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
4528 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4529 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4532 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
4533 if (!certificate
->_authorityKeyID
&&
4534 certificate
->_authorityKeyIdentifier
.length
) {
4535 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4536 certificate
->_authorityKeyIdentifier
.data
,
4537 certificate
->_authorityKeyIdentifier
.length
);
4540 return certificate
->_authorityKeyID
;
4543 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
4544 if (!certificate
->_subjectKeyID
&&
4545 certificate
->_subjectKeyIdentifier
.length
) {
4546 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4547 certificate
->_subjectKeyIdentifier
.data
,
4548 certificate
->_subjectKeyIdentifier
.length
);
4551 return certificate
->_subjectKeyID
;
4554 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
4555 return certificate
->_crlDistributionPoints
;
4558 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
4559 return certificate
->_ocspResponders
;
4562 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
4563 return certificate
->_caIssuers
;
4566 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
4567 return certificate
->_subjectAltName
&&
4568 certificate
->_subjectAltName
->critical
;
4571 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
4572 /* Since the _subject field is the content of the subject and not the
4573 whole thing, we can simply check for a 0 length subject here. */
4574 return certificate
->_subject
.length
!= 0;
4577 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
4578 return certificate
->_foundUnknownCriticalExtension
;
4581 /* Private API functions. */
4582 void SecCertificateShow(SecCertificateRef certificate
) {
4584 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4585 fprintf(stderr
, "\n");
4589 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
4590 SecCertificateRef certificate
) {
4591 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4592 CFNumberRef certificateType
, certificateEncoding
;
4593 CFStringRef label
, alias
;
4594 CFDataRef skid
, pubKeyDigest
, certData
;
4595 CFDictionaryRef dict
= NULL
;
4599 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4600 SInt32 ctv
= certificate
->_version
+ 1;
4601 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4602 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4603 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4604 certData
= SecCertificateCopyData(certificate
);
4605 skid
= SecCertificateGetSubjectKeyID(certificate
);
4606 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4607 certificate
->_pubKeyDER
.length
);
4609 /* We still need to figure out how to deal with multi valued attributes. */
4610 alias
= SecCertificateCopyRFC822Names(certificate
);
4611 label
= SecCertificateCopySubjectSummary(certificate
);
4617 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4618 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4619 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4621 DICT_ADDPAIR(kSecAttrLabel
, label
);
4623 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4624 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4625 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4626 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4628 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4629 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4630 DICT_ADDPAIR(kSecValueData
, certData
);
4631 dict
= DICT_CREATE(allocator
);
4633 CFReleaseSafe(label
);
4634 CFReleaseSafe(pubKeyDigest
);
4635 CFReleaseSafe(certData
);
4636 CFReleaseSafe(certificateEncoding
);
4637 CFReleaseSafe(certificateType
);
4642 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
4643 CFDictionaryRef refAttributes
) {
4644 /* @@@ Support having an allocator in refAttributes. */
4645 CFAllocatorRef allocator
= NULL
;
4646 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4647 return SecCertificateCreateWithData(allocator
, data
);
4651 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
4652 bool result
= false;
4653 SecKeyRef publicKey
;
4654 require(publicKey
= SecCertificateCopyPublicKey(certificate
), out
);
4655 CFDataRef normalizedIssuer
=
4656 SecCertificateGetNormalizedIssuerContent(certificate
);
4657 CFDataRef normalizedSubject
=
4658 SecCertificateGetNormalizedSubjectContent(certificate
);
4659 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4660 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4662 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
4663 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
4664 if (authorityKeyID
) {
4665 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4668 if (SecCertificateVersion(certificate
) >= 3) {
4669 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
4670 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4671 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
4676 CFReleaseSafe(publicKey
);
4680 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
4681 return certificate
->_keyUsage
;
4684 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
4686 CFMutableArrayRef extended_key_usage_oids
=
4687 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4688 require_quiet(extended_key_usage_oids
, out
);
4690 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4691 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4692 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4693 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4696 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4697 require_noerr_quiet(drtn
, out
);
4698 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4699 DERDecodedInfo currDecoded
;
4701 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4702 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4703 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4704 currDecoded
.content
.data
, currDecoded
.content
.length
);
4706 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4710 require_quiet(drtn
== DR_EndOfSequence
, out
);
4711 return extended_key_usage_oids
;
4715 CFReleaseSafe(extended_key_usage_oids
);
4719 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFDataRef oid
)
4722 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
4723 size_t oid_len
= CFDataGetLength(oid
);
4725 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4726 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4727 if (extn
->extnID
.length
== oid_len
4728 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
4730 if ((extn
->extnValue
.length
== 2) && (*extn
->extnValue
.data
== ASN1_NULL
))
4737 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
4739 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
4740 CFIndex ix
, length
= CFArrayGetCount(oids
);
4741 for (ix
= 0; ix
< length
; ix
++)
4742 if (cert_contains_marker_extension(certificate
, (CFDataRef
)CFArrayGetValueAtIndex(oids
, ix
)))
4744 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
4745 return cert_contains_marker_extension(certificate
, (CFDataRef
)oids
);
4750 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
4751 CFDataRef pem_certificate
)
4753 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
4754 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
4755 uint8_t *base64_data
= NULL
;
4756 SecCertificateRef cert
= NULL
;
4757 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
4758 //const size_t length = CFDataGetLength(pem_certificate);
4759 char *begin
= strstr((const char *)data
, begin_cert
);
4760 char *end
= strstr((const char *)data
, end_cert
);
4763 begin
+= sizeof(begin_cert
) - 1;
4764 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
4765 if (base64_length
) {
4766 require_quiet(base64_data
= calloc(1, base64_length
), out
);
4767 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
4768 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
4775 static void convertCertificateToCFData(const void *value
, void *context
) {
4776 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4777 SecCertificateRef certificate
= (SecCertificateRef
)value
;
4778 CFDataRef data
= SecCertificateCopyData(certificate
);
4779 CFArrayAppendValue(result
, data
);
4783 /* Return an array of CFDataRefs from an array of SecCertificateRefs. */
4784 CFArrayRef
SecCertificateArrayCopyDataArray(CFArrayRef certificates
) {
4785 CFIndex count
= CFArrayGetCount(certificates
);
4786 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
4787 CFRange all_certs
= { 0, count
};
4788 CFArrayApplyFunction(certificates
, all_certs
, convertCertificateToCFData
, result
);
4792 /* AUDIT[securityd](done):
4793 value (ok) is an element in a caller provided array.
4795 static void convertCFDataToCertificate(const void *value
, void *context
) {
4796 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4797 CFDataRef data
= (CFDataRef
)value
;
4798 if (data
&& CFGetTypeID(data
) == CFDataGetTypeID()) {
4799 SecCertificateRef certificate
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
4801 CFArrayAppendValue(result
, certificate
);
4802 CFRelease(certificate
);
4807 /* AUDIT[securityd](done):
4808 certificates (ok) is a caller provided array, only its cf type has
4811 CFArrayRef
SecCertificateDataArrayCopyArray(CFArrayRef certificates
) {
4812 CFIndex count
= CFArrayGetCount(certificates
);
4813 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
4814 CFRange all_certs
= { 0, count
};
4815 CFArrayApplyFunction(certificates
, all_certs
, convertCFDataToCertificate
, result
);