2 * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 /* Allows us to build genanchors against the BaseSDK. */
30 #undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31 #undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
34 #include <Security/SecCertificateInternal.h>
35 #include <utilities/SecIOFormat.h>
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFBundle.h>
40 #include <CoreFoundation/CFDictionary.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFCalendar.h>
43 #include <CoreFoundation/CFTimeZone.h>
44 #include <CoreFoundation/CFXPCBridge.h>
47 #include <AssertMacros.h>
48 #include <libDER/DER_CertCrl.h>
49 #include <libDER/DER_Encode.h>
50 #include <libDER/DER_Keys.h>
51 #include <libDER/asn1Types.h>
52 #include <libDER/oids.h>
53 #include "SecBasePriv.h"
54 #include "SecRSAKey.h"
55 #include "SecFramework.h"
57 #include "SecItemPriv.h"
59 #include <utilities/debugging.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <utilities/SecCFError.h>
62 #include <utilities/array_size.h>
64 #include <libkern/OSByteOrder.h>
66 #include <Security/SecInternal.h>
67 #include <Security/SecFrameworkStrings.h>
68 #include "SecBase64.h"
69 #include "AppleBaselineEscrowCertificates.h"
70 #include "securityd_client.h"
72 typedef struct SecCertificateExtension
{
76 } SecCertificateExtension
;
79 typedef struct KnownExtension
{
85 kSecSelfSignedUnknown
= 0,
91 struct __SecCertificate
{
94 DERItem _der
; /* Entire certificate in DER form. */
95 DERItem _tbs
; /* To Be Signed cert DER bytes. */
96 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
97 DERItem _signature
; /* The content of the sig bit string. */
100 DERItem _serialNum
; /* Integer. */
101 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
102 DERItem _issuer
; /* Sequence of RDN. */
103 CFAbsoluteTime _notBefore
;
104 CFAbsoluteTime _notAfter
;
105 DERItem _subject
; /* Sequence of RDN. */
106 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
107 DERItem _pubKeyDER
; /* contents of bit string */
108 DERItem _issuerUniqueID
; /* bit string, optional */
109 DERItem _subjectUniqueID
; /* bit string, optional */
112 /* Known extensions if the certificate contains them,
113 extnValue.length will be > 0. */
114 KnownExtension _authorityKeyID
;
116 /* This extension is used to uniquely identify a certificate from among
117 several that have the same subject name. If the extension is not
118 present, its value is calculated by performing a SHA-1 hash of the
119 certificate's DER encoded subjectPublicKeyInfo, as recommended by
121 KnownExtension _subjectKeyID
;
122 KnownExtension _keyUsage
;
123 KnownExtension _extendedKeyUsage
;
124 KnownExtension _basicConstraints
;
125 KnownExtension _netscapeCertType
;
126 KnownExtension _subjectAltName
;
127 KnownExtension _qualCertStatements
;
130 bool _foundUnknownCriticalExtension
;
132 /* Well known certificate extensions. */
133 SecCEBasicConstraints _basicConstraints
;
134 SecCEPolicyConstraints _policyConstraints
;
135 CFDictionaryRef _policyMappings
;
136 SecCECertificatePolicies _certificatePolicies
;
138 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
139 value of the SkipCerts field of the InhibitAnyPolicy extension
141 uint32_t _inhibitAnyPolicySkipCerts
;
143 /* If KeyUsage extension is not present this is 0, otherwise it's
144 the value of the extension. */
145 SecKeyUsage _keyUsage
;
147 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
148 Length = 0 if not present. */
149 DERItem _subjectKeyIdentifier
;
151 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
152 Length = 0 if not present. */
153 DERItem _authorityKeyIdentifier
;
154 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
155 _authorityKeyIdentifierSerialNumber have non zero length if present.
156 Both are either present or absent together. */
157 DERItem _authorityKeyIdentifierIssuer
;
158 DERItem _authorityKeyIdentifierSerialNumber
;
160 /* Subject alt name extension, if present. Not malloced, it's just a
161 pointer to an element in the _extensions array. */
162 const SecCertificateExtension
*_subjectAltName
;
164 /* Parsed extension values. */
166 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
167 CFMutableArrayRef _crlDistributionPoints
;
169 /* Array of CFURLRefs containing the URI values of accessLocations of each
170 id-ad-ocsp AccessDescription in the Authority Information Access
172 CFMutableArrayRef _ocspResponders
;
174 /* Array of CFURLRefs containing the URI values of accessLocations of each
175 id-ad-caIssuers AccessDescription in the Authority Information Access
177 CFMutableArrayRef _caIssuers
;
179 /* All other (non known) extensions. The _extensions array is malloced. */
180 CFIndex _extensionCount
;
181 SecCertificateExtension
*_extensions
;
183 /* Optional cached fields. */
186 CFArrayRef _properties
;
187 CFDataRef _serialNumber
;
188 CFDataRef _normalizedIssuer
;
189 CFDataRef _normalizedSubject
;
190 CFDataRef _authorityKeyID
;
191 CFDataRef _subjectKeyID
;
193 CFDataRef _sha1Digest
;
194 uint8_t _isSelfSigned
;
198 #define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
200 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
201 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
203 /* Public Constants for property list keys. */
204 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
205 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
206 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
207 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
209 /* Public Constants for property list values. */
210 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
211 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
212 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
213 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
214 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
215 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
216 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
217 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
218 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
220 /* Extension parsing routine. */
221 typedef void (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
222 const SecCertificateExtension
*extn
);
224 /* CFRuntime registration data. */
225 static pthread_once_t kSecCertificateRegisterClass
= PTHREAD_ONCE_INIT
;
226 static CFTypeID kSecCertificateTypeID
= _kCFRuntimeNotATypeID
;
228 /* Mapping from extension OIDs (as a DERItem *) to
229 SecCertificateExtensionParser extension parsing routines. */
230 static CFDictionaryRef gExtensionParsers
;
232 /* Forward declarations of static functions. */
233 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
);
234 static void SecCertificateDestroy(CFTypeRef cf
);
235 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
236 CFAbsoluteTime
*absTime
);
238 /* Static functions. */
239 static CF_RETURNS_RETAINED CFStringRef
SecCertificateDescribe(CFTypeRef cf
) {
240 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
241 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
242 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
243 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
244 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
245 CFReleaseSafe(issuer
);
246 CFReleaseSafe(subject
);
250 static void SecCertificateDestroy(CFTypeRef cf
) {
251 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
252 if (certificate
->_certificatePolicies
.policies
)
253 free(certificate
->_certificatePolicies
.policies
);
254 CFReleaseSafe(certificate
->_policyMappings
);
255 CFReleaseSafe(certificate
->_crlDistributionPoints
);
256 CFReleaseSafe(certificate
->_ocspResponders
);
257 CFReleaseSafe(certificate
->_caIssuers
);
258 if (certificate
->_extensions
) {
259 free(certificate
->_extensions
);
261 CFReleaseSafe(certificate
->_pubKey
);
262 CFReleaseSafe(certificate
->_der_data
);
263 CFReleaseSafe(certificate
->_properties
);
264 CFReleaseSafe(certificate
->_serialNumber
);
265 CFReleaseSafe(certificate
->_normalizedIssuer
);
266 CFReleaseSafe(certificate
->_normalizedSubject
);
267 CFReleaseSafe(certificate
->_authorityKeyID
);
268 CFReleaseSafe(certificate
->_subjectKeyID
);
269 CFReleaseSafe(certificate
->_sha1Digest
);
272 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
273 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
274 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
277 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
279 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
282 /* Hash of the certificate is der length + signature length + last 4 bytes
284 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
285 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
286 size_t der_length
= certificate
->_der
.length
;
287 size_t sig_length
= certificate
->_signature
.length
;
288 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
289 CFHashCode hashCode
= 0;
290 for (; ix
< sig_length
; ++ix
)
291 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
293 return (hashCode
+ der_length
+ sig_length
);
298 /************************************************************************/
299 /************************* General Name Parsing *************************/
300 /************************************************************************/
302 typedef OSStatus (*parseGeneralNameCallback
)(void *context
,
303 SecCEGeneralNameType type
, const DERItem
*value
);
307 GeneralName ::= CHOICE {
308 otherName [0] OtherName,
309 rfc822Name [1] IA5String,
310 dNSName [2] IA5String,
311 x400Address [3] ORAddress,
312 directoryName [4] Name,
313 ediPartyName [5] EDIPartyName,
314 uniformResourceIdentifier [6] IA5String,
315 iPAddress [7] OCTET STRING,
316 registeredID [8] OBJECT IDENTIFIER}
318 OtherName ::= SEQUENCE {
319 type-id OBJECT IDENTIFIER,
320 value [0] EXPLICIT ANY DEFINED BY type-id }
322 EDIPartyName ::= SEQUENCE {
323 nameAssigner [0] DirectoryString OPTIONAL,
324 partyName [1] DirectoryString }
326 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
327 const DERItem
*generalNameContent
,
328 void *context
, parseGeneralNameCallback callback
) {
330 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
331 return callback(context
, GNT_OtherName
, generalNameContent
);
332 case ASN1_CONTEXT_SPECIFIC
| 1:
333 return callback(context
, GNT_RFC822Name
, generalNameContent
);
334 case ASN1_CONTEXT_SPECIFIC
| 2:
335 return callback(context
, GNT_DNSName
, generalNameContent
);
336 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
337 return callback(context
, GNT_X400Address
, generalNameContent
);
338 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
339 return callback(context
, GNT_DirectoryName
, generalNameContent
);
340 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
341 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
342 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
344 /* Technically I don't think this is valid, but there are certs out
345 in the wild that use a constructed IA5String. In particular the
346 VeriSign Time Stamping Authority CA.cer does this. */
347 DERDecodedInfo uriContent
;
348 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
349 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
350 return callback(context
, GNT_URI
, &uriContent
.content
);
352 case ASN1_CONTEXT_SPECIFIC
| 6:
353 return callback(context
, GNT_URI
, generalNameContent
);
354 case ASN1_CONTEXT_SPECIFIC
| 7:
355 return callback(context
, GNT_IPAddress
, generalNameContent
);
356 case ASN1_CONTEXT_SPECIFIC
| 8:
357 return callback(context
, GNT_RegisteredID
, generalNameContent
);
362 return errSecInvalidCertificate
;
365 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
366 void *context
, parseGeneralNameCallback callback
) {
368 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
369 require_noerr_quiet(drtn
, badDER
);
370 DERDecodedInfo generalNameContent
;
371 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
373 OSStatus status
= parseGeneralNameContentProperty(
374 generalNameContent
.tag
, &generalNameContent
.content
, context
,
379 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
380 return errSecSuccess
;
383 return errSecInvalidCertificate
;
386 static OSStatus
parseGeneralNames(const DERItem
*generalNames
, void *context
,
387 parseGeneralNameCallback callback
) {
388 DERDecodedInfo generalNamesContent
;
389 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
390 require_noerr_quiet(drtn
, badDER
);
391 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
392 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
395 return errSecInvalidCertificate
;
401 GeneralName ::= CHOICE {
402 otherName [0] OtherName,
403 rfc822Name [1] IA5String,
404 dNSName [2] IA5String,
405 x400Address [3] ORAddress,
406 directoryName [4] Name,
407 ediPartyName [5] EDIPartyName,
408 uniformResourceIdentifier [6] IA5String,
409 iPAddress [7] OCTET STRING,
410 registeredID [8] OBJECT IDENTIFIER}
412 EDIPartyName ::= SEQUENCE {
413 nameAssigner [0] DirectoryString OPTIONAL,
414 partyName [1] DirectoryString }
416 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
417 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
419 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
420 generalName
->nameType
= GNT_OtherName
;
421 generalName
->berEncoded
= true;
422 generalName
->name
= *generalNameContent
;
424 case ASN1_CONTEXT_SPECIFIC
| 1:
426 generalName
->nameType
= GNT_RFC822Name
;
427 generalName
->berEncoded
= false;
428 generalName
->name
= *generalNameContent
;
430 case ASN1_CONTEXT_SPECIFIC
| 2:
432 generalName
->nameType
= GNT_DNSName
;
433 generalName
->berEncoded
= false;
434 generalName
->name
= *generalNameContent
;
436 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
437 generalName
->nameType
= GNT_X400Address
;
438 generalName
->berEncoded
= true;
439 generalName
->name
= *generalNameContent
;
441 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
442 generalName
->nameType
= GNT_DirectoryName
;
443 generalName
->berEncoded
= true;
444 generalName
->name
= *generalNameContent
;
446 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
447 generalName
->nameType
= GNT_EdiPartyName
;
448 generalName
->berEncoded
= true;
449 generalName
->name
= *generalNameContent
;
451 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
453 /* Technically I don't think this is valid, but there are certs out
454 in the wild that use a constructed IA5String. In particular the
455 VeriSign Time Stamping Authority CA.cer does this. */
456 DERDecodedInfo decoded
;
457 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
458 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
459 generalName
->nameType
= GNT_URI
;
460 generalName
->berEncoded
= false;
461 generalName
->name
= decoded
.content
;
464 case ASN1_CONTEXT_SPECIFIC
| 6:
465 generalName
->nameType
= GNT_URI
;
466 generalName
->berEncoded
= false;
467 generalName
->name
= *generalNameContent
;
469 case ASN1_CONTEXT_SPECIFIC
| 7:
470 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
471 8 octects, addr/mask for ipv6 it's 32. */
472 generalName
->nameType
= GNT_IPAddress
;
473 generalName
->berEncoded
= false;
474 generalName
->name
= *generalNameContent
;
476 case ASN1_CONTEXT_SPECIFIC
| 8:
477 /* name is the content of an OID. */
478 generalName
->nameType
= GNT_RegisteredID
;
479 generalName
->berEncoded
= false;
480 generalName
->name
= *generalNameContent
;
486 return errSecSuccess
;
488 return errSecInvalidCertificate
;
492 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
494 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
495 CFIndex
*count
, SecCEGeneralName
**name
) {
496 SecCEGeneralName
*generalNames
= NULL
;
498 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
499 require_noerr_quiet(drtn
, badDER
);
500 DERDecodedInfo generalNameContent
;
501 CFIndex generalNamesCount
= 0;
502 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
506 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
508 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
510 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
512 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
514 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
515 &generalNameContent
.content
, &generalNames
[ix
])) {
520 *count
= generalNamesCount
;
521 *name
= generalNames
;
522 return errSecSuccess
;
527 return errSecInvalidCertificate
;
530 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
531 CFIndex
*count
, SecCEGeneralName
**name
) {
532 DERDecodedInfo generalNamesContent
;
533 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
534 require_noerr_quiet(drtn
, badDER
);
535 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
537 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
538 return errSecSuccess
;
540 return errSecInvalidCertificate
;
544 /************************************************************************/
545 /************************** X.509 Name Parsing **************************/
546 /************************************************************************/
548 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
549 const DERItem
*value
, CFIndex rdnIX
);
551 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
552 parseX501NameCallback callback
) {
554 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
555 require_noerr_quiet(drtn
, badDER
);
556 DERDecodedInfo atvContent
;
558 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
559 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
560 DERAttributeTypeAndValue atv
;
561 drtn
= DERParseSequenceContent(&atvContent
.content
,
562 DERNumAttributeTypeAndValueItemSpecs
,
563 DERAttributeTypeAndValueItemSpecs
,
565 require_noerr_quiet(drtn
, badDER
);
566 require_quiet(atv
.type
.length
!= 0, badDER
);
567 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
571 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
573 return errSecSuccess
;
575 return errSecInvalidCertificate
;
578 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
579 parseX501NameCallback callback
) {
581 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
582 require_noerr_quiet(drtn
, badDER
);
583 DERDecodedInfo currDecoded
;
584 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
585 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
586 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
591 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
593 return errSecSuccess
;
596 return errSecInvalidCertificate
;
599 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
600 parseX501NameCallback callback
) {
601 DERDecodedInfo x501NameContent
;
602 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
603 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
604 return errSecInvalidCertificate
;
606 return parseX501NameContent(&x501NameContent
.content
, context
,
611 /************************************************************************/
612 /********************** Extension Parsing Routines **********************/
613 /************************************************************************/
615 static void SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
616 const SecCertificateExtension
*extn
) {
617 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
618 DERDecodedInfo keyIdentifier
;
619 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
620 require_noerr_quiet(drtn
, badDER
);
621 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
622 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
626 secwarning("Invalid SubjectKeyIdentifier Extension");
629 static void SecCEPKeyUsage(SecCertificateRef certificate
,
630 const SecCertificateExtension
*extn
) {
631 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
632 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
633 DERDecodedInfo bitStringContent
;
634 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
635 require_noerr_quiet(drtn
, badDER
);
636 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
637 DERSize len
= bitStringContent
.content
.length
- 1;
638 require_quiet(len
== 1 || len
== 2, badDER
);
639 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
640 require_quiet(numUnusedBits
< 8, badDER
);
641 /* Flip the bits in the bit string so the first bit in the lsb. */
642 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
643 uint_fast16_t value
= bitStringContent
.content
.data
[1];
646 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
652 for (ix
= 0; ix
< bits
; ++ix
) {
658 certificate
->_keyUsage
= keyUsage
;
661 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
664 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
665 const SecCertificateExtension
*extn
) {
666 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
669 static void SecCEPSubjectAltName(SecCertificateRef certificate
,
670 const SecCertificateExtension
*extn
) {
671 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
672 certificate
->_subjectAltName
= extn
;
675 static void SecCEPIssuerAltName(SecCertificateRef certificate
,
676 const SecCertificateExtension
*extn
) {
677 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
680 static void SecCEPBasicConstraints(SecCertificateRef certificate
,
681 const SecCertificateExtension
*extn
) {
682 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
683 DERBasicConstraints basicConstraints
;
684 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
685 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
686 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
687 require_noerr_quiet(DERParseBoolean(&basicConstraints
.cA
, false,
688 &certificate
->_basicConstraints
.isCA
), badDER
);
689 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
690 require_noerr_quiet(DERParseInteger(
691 &basicConstraints
.pathLenConstraint
,
692 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
693 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
695 certificate
->_basicConstraints
.present
= true;
696 certificate
->_basicConstraints
.critical
= extn
->critical
;
699 certificate
->_basicConstraints
.present
= false;
700 secwarning("Invalid BasicConstraints Extension");
703 static void SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
704 const SecCertificateExtension
*extn
) {
705 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
709 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
711 PolicyInformation ::= SEQUENCE {
712 policyIdentifier CertPolicyId,
713 policyQualifiers SEQUENCE SIZE (1..MAX) OF
714 PolicyQualifierInfo OPTIONAL }
716 CertPolicyId ::= OBJECT IDENTIFIER
718 PolicyQualifierInfo ::= SEQUENCE {
719 policyQualifierId PolicyQualifierId,
720 qualifier ANY DEFINED BY policyQualifierId }
722 static void SecCEPCertificatePolicies(SecCertificateRef certificate
,
723 const SecCertificateExtension
*extn
) {
724 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
727 SecCEPolicyInformation
*policies
= NULL
;
728 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
729 require_noerr_quiet(drtn
, badDER
);
730 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
731 DERDecodedInfo piContent
;
732 DERSize policy_count
= 0;
733 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
734 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
737 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
738 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
740 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
741 DERSize policy_ix
= 0;
742 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
743 DERPolicyInformation pi
;
744 drtn
= DERParseSequenceContent(&piContent
.content
,
745 DERNumPolicyInformationItemSpecs
,
746 DERPolicyInformationItemSpecs
,
748 require_noerr_quiet(drtn
, badDER
);
749 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
750 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
752 certificate
->_certificatePolicies
.present
= true;
753 certificate
->_certificatePolicies
.critical
= extn
->critical
;
754 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
755 certificate
->_certificatePolicies
.policies
= policies
;
760 certificate
->_certificatePolicies
.present
= false;
761 secwarning("Invalid CertificatePolicies Extension");
765 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
767 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
768 issuerDomainPolicy CertPolicyId,
769 subjectDomainPolicy CertPolicyId }
772 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
773 const SecCertificateExtension
*extn
) {
774 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
777 SecCEPolicyMapping
*mappings
= NULL
;
778 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
779 require_noerr_quiet(drtn
, badDER
);
780 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
781 DERDecodedInfo pmContent
;
782 DERSize mapping_count
= 0;
783 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
784 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
787 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
789 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
790 DERSize mapping_ix
= 0;
791 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
793 drtn
= DERParseSequenceContent(&pmContent
.content
,
794 DERNumPolicyMappingItemSpecs
,
795 DERPolicyMappingItemSpecs
,
797 require_noerr_quiet(drtn
, badDER
);
798 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
799 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
801 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
802 certificate
->_policyMappings
.present
= true;
803 certificate
->_policyMappings
.critical
= extn
->critical
;
804 certificate
->_policyMappings
.numMappings
= mapping_count
;
805 certificate
->_policyMappings
.mappings
= mappings
;
810 CFReleaseSafe(mappings
);
811 certificate
->_policyMappings
.present
= false;
812 secwarning("Invalid CertificatePolicies Extension");
815 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
816 const SecCertificateExtension
*extn
) {
817 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
820 CFMutableDictionaryRef mappings
= NULL
;
821 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
822 require_noerr_quiet(drtn
, badDER
);
823 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
824 DERDecodedInfo pmContent
;
825 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
826 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
828 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
829 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
831 drtn
= DERParseSequenceContent(&pmContent
.content
,
832 DERNumPolicyMappingItemSpecs
,
833 DERPolicyMappingItemSpecs
,
835 require_noerr_quiet(drtn
, badDER
);
837 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
838 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
839 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
840 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
841 CFMutableArrayRef sdps
=
842 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
844 CFArrayAppendValue(sdps
, sdp
);
846 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
847 &kCFTypeArrayCallBacks
), badDER
);
848 CFDictionarySetValue(mappings
, idp
, sdps
);
852 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
853 certificate
->_policyMappings
= mappings
;
856 CFReleaseSafe(mappings
);
857 certificate
->_policyMappings
= NULL
;
858 secwarning("Invalid CertificatePolicies Extension");
863 AuthorityKeyIdentifier ::= SEQUENCE {
864 keyIdentifier [0] KeyIdentifier OPTIONAL,
865 authorityCertIssuer [1] GeneralNames OPTIONAL,
866 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
867 -- authorityCertIssuer and authorityCertSerialNumber MUST both
868 -- be present or both be absent
870 KeyIdentifier ::= OCTET STRING
872 static void SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
873 const SecCertificateExtension
*extn
) {
874 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
875 DERAuthorityKeyIdentifier akid
;
877 drtn
= DERParseSequence(&extn
->extnValue
,
878 DERNumAuthorityKeyIdentifierItemSpecs
,
879 DERAuthorityKeyIdentifierItemSpecs
,
880 &akid
, sizeof(akid
));
881 require_noerr_quiet(drtn
, badDER
);
882 if (akid
.keyIdentifier
.length
) {
883 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
885 if (akid
.authorityCertIssuer
.length
||
886 akid
.authorityCertSerialNumber
.length
) {
887 require_quiet(akid
.authorityCertIssuer
.length
&&
888 akid
.authorityCertSerialNumber
.length
, badDER
);
889 /* Perhaps put in a subsection called Authority Certificate Issuer. */
890 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
891 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
896 secwarning("Invalid AuthorityKeyIdentifier Extension");
899 static void SecCEPPolicyConstraints(SecCertificateRef certificate
,
900 const SecCertificateExtension
*extn
) {
901 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
902 DERPolicyConstraints pc
;
904 drtn
= DERParseSequence(&extn
->extnValue
,
905 DERNumPolicyConstraintsItemSpecs
,
906 DERPolicyConstraintsItemSpecs
,
908 require_noerr_quiet(drtn
, badDER
);
909 if (pc
.requireExplicitPolicy
.length
) {
910 require_noerr_quiet(DERParseInteger(
911 &pc
.requireExplicitPolicy
,
912 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
913 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
915 if (pc
.inhibitPolicyMapping
.length
) {
916 require_noerr_quiet(DERParseInteger(
917 &pc
.inhibitPolicyMapping
,
918 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
919 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
922 certificate
->_policyConstraints
.present
= true;
923 certificate
->_policyConstraints
.critical
= extn
->critical
;
927 certificate
->_policyConstraints
.present
= false;
928 secwarning("Invalid PolicyConstraints Extension");
931 static void SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
932 const SecCertificateExtension
*extn
) {
933 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
937 InhibitAnyPolicy ::= SkipCerts
939 SkipCerts ::= INTEGER (0..MAX)
941 static void SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
942 const SecCertificateExtension
*extn
) {
943 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
944 require_noerr_quiet(DERParseInteger(
946 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
949 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
950 secwarning("Invalid InhibitAnyPolicy Extension");
954 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
956 AuthorityInfoAccessSyntax ::=
957 SEQUENCE SIZE (1..MAX) OF AccessDescription
959 AccessDescription ::= SEQUENCE {
960 accessMethod OBJECT IDENTIFIER,
961 accessLocation GeneralName }
963 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
965 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
967 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
969 static void SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
970 const SecCertificateExtension
*extn
) {
971 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
974 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
975 require_noerr_quiet(drtn
, badDER
);
976 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
977 DERDecodedInfo adContent
;
978 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
979 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
980 DERAccessDescription ad
;
981 drtn
= DERParseSequenceContent(&adContent
.content
,
982 DERNumAccessDescriptionItemSpecs
,
983 DERAccessDescriptionItemSpecs
,
985 require_noerr_quiet(drtn
, badDER
);
986 CFMutableArrayRef
*urls
;
987 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
988 urls
= &certificate
->_ocspResponders
;
989 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
990 urls
= &certificate
->_caIssuers
;
994 DERDecodedInfo generalNameContent
;
995 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
996 require_noerr_quiet(drtn
, badDER
);
997 switch (generalNameContent
.tag
) {
999 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1000 /* Technically I don't think this is valid, but there are certs out
1001 in the wild that use a constructed IA5String. In particular the
1002 VeriSign Time Stamping Authority CA.cer does this. */
1004 case ASN1_CONTEXT_SPECIFIC
| 6:
1006 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1007 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1008 kCFStringEncodingASCII
, NULL
);
1011 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1012 CFArrayAppendValue(*urls
, url
);
1018 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02x v: %.*s",
1019 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1024 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1027 secdebug("cert", "failed to parse Authority Information Access extension");
1030 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1031 * This is a DER sequence with the leading tag and length bytes removed,
1032 * to match what tbsCert.issuer contains.
1034 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1035 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1036 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1037 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1038 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1039 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1040 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1041 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1042 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1043 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1044 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1047 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1049 certificate
->_crlDistributionPoints
||
1050 certificate
->_ocspResponders
) {
1051 /* We already have an OCSP or CRL URI (or no cert) */
1054 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1055 CFURLRef url
= NULL
;
1056 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1057 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1058 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1059 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1060 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1061 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1062 kCFStringEncodingASCII
, NULL
);
1065 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1066 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1067 CFArrayAppendValue(*urls
, url
);
1072 static void SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1073 const SecCertificateExtension
*extn
) {
1074 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1077 static void SecCEPNetscapeCertType(SecCertificateRef certificate
,
1078 const SecCertificateExtension
*extn
) {
1079 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1082 static void SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1083 const SecCertificateExtension
*extn
) {
1084 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1087 static void SecCEPEscrowMarker(SecCertificateRef certificate
,
1088 const SecCertificateExtension
*extn
) {
1089 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1093 /* Dictionary key callback for comparing to DERItems. */
1094 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1095 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1098 /* Dictionary key callback calculating the hash of a DERItem. */
1099 static CFHashCode
SecDERItemHash(const void *value
) {
1100 const DERItem
*derItem
= (const DERItem
*)value
;
1101 CFHashCode hash
= derItem
->length
;
1102 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1103 for (; ix
< derItem
->length
; ++ix
) {
1104 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1110 /* Dictionary key callbacks using the above 2 functions. */
1111 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1115 NULL
, /* copyDescription */
1116 SecDERItemEqual
, /* equal */
1117 SecDERItemHash
/* hash */
1120 static void SecCertificateRegisterClass(void) {
1121 static const CFRuntimeClass kSecCertificateClass
= {
1123 "SecCertificate", /* class name */
1126 SecCertificateDestroy
, /* dealloc */
1127 SecCertificateEqual
, /* equal */
1128 SecCertificateHash
, /* hash */
1129 NULL
, /* copyFormattingDesc */
1130 SecCertificateDescribe
/* copyDebugDesc */
1133 kSecCertificateTypeID
= _CFRuntimeRegisterClass(&kSecCertificateClass
);
1135 /* Build a dictionary that maps from extension OIDs to callback functions
1136 which can parse the extension of the type given. */
1137 static const void *extnOIDs
[] = {
1138 &oidSubjectKeyIdentifier
,
1140 &oidPrivateKeyUsagePeriod
,
1143 &oidBasicConstraints
,
1144 &oidCrlDistributionPoints
,
1145 &oidCertificatePolicies
,
1147 &oidAuthorityKeyIdentifier
,
1148 &oidPolicyConstraints
,
1149 &oidExtendedKeyUsage
,
1150 &oidInhibitAnyPolicy
,
1151 &oidAuthorityInfoAccess
,
1152 &oidSubjectInfoAccess
,
1153 &oidNetscapeCertType
,
1154 &oidEntrustVersInfo
,
1155 &oidApplePolicyEscrowService
1157 static const void *extnParsers
[] = {
1158 SecCEPSubjectKeyIdentifier
,
1160 SecCEPPrivateKeyUsagePeriod
,
1161 SecCEPSubjectAltName
,
1162 SecCEPIssuerAltName
,
1163 SecCEPBasicConstraints
,
1164 SecCEPCrlDistributionPoints
,
1165 SecCEPCertificatePolicies
,
1166 SecCEPPolicyMappings
,
1167 SecCEPAuthorityKeyIdentifier
,
1168 SecCEPPolicyConstraints
,
1169 SecCEPExtendedKeyUsage
,
1170 SecCEPInhibitAnyPolicy
,
1171 SecCEPAuthorityInfoAccess
,
1172 SecCEPSubjectInfoAccess
,
1173 SecCEPNetscapeCertType
,
1174 SecCEPEntrustVersInfo
,
1177 gExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1178 extnParsers
, array_size(extnOIDs
),
1179 &SecDERItemKeyCallBacks
, NULL
);
1182 /* Given the contents of an X.501 Name return the contents of a normalized
1184 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1185 const DERItem
*x501name
) {
1186 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1187 CFIndex length
= x501name
->length
;
1188 CFDataSetLength(result
, length
);
1189 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1192 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1194 require_noerr_quiet(drtn
, badDER
);
1197 /* Always points to last rdn tag. */
1198 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1199 /* Offset relative to base of current rdn set tag. */
1200 CFIndex rdnTagLocation
= 0;
1201 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1202 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1203 /* We don't allow empty RDNs. */
1204 require_quiet(rdn
.content
.length
!= 0, badDER
);
1205 /* Length of the tag and length of the current rdn. */
1206 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1207 CFIndex rdnContentLength
= rdn
.content
.length
;
1208 /* Copy the tag and length of the RDN. */
1209 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1212 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1214 /* Always points to tag of current atv sequence. */
1215 const DERByte
*atvTag
= atvSeq
.nextItem
;
1216 /* Offset relative to base of current atv sequence tag. */
1217 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1218 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1219 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1220 /* Length of the tag and length of the current atv. */
1221 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1222 CFIndex atvContentLength
= atv
.content
.length
;
1223 /* Copy the tag and length of the atv and the atv itself. */
1224 memcpy(base
+ atvTagLocation
, atvTag
,
1225 atvTLLength
+ atv
.content
.length
);
1227 /* Now decode the atv sequence. */
1228 DERAttributeTypeAndValue atvPair
;
1229 drtn
= DERParseSequenceContent(&atv
.content
,
1230 DERNumAttributeTypeAndValueItemSpecs
,
1231 DERAttributeTypeAndValueItemSpecs
,
1232 &atvPair
, sizeof(atvPair
));
1233 require_noerr_quiet(drtn
, badDER
);
1234 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1235 DERDecodedInfo value
;
1236 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1237 require_noerr_quiet(drtn
, badDER
);
1239 /* (c) attribute values in PrintableString are not case sensitive
1240 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1242 (d) attribute values in PrintableString are compared after
1243 removing leading and trailing white space and converting internal
1244 substrings of one or more consecutive white space characters to a
1246 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1247 /* Offset relative to base of current value tag. */
1248 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1249 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1250 CFIndex valueContentLength
= value
.content
.length
;
1252 /* Now copy all the bytes, but convert to upper case while
1253 doing so and convert multiple whitespace chars into a
1255 bool lastWasBlank
= false;
1256 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1257 CFIndex valueCurrentLocation
= valueLocation
;
1259 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1260 UInt8 ch
= value
.content
.data
[ix
];
1265 /* Don't insert a space for first character
1267 if (valueCurrentLocation
> valueLocation
) {
1268 base
[valueCurrentLocation
++] = ' ';
1270 lastWasBlank
= true;
1273 lastWasBlank
= false;
1274 if ('a' <= ch
&& ch
<= 'z') {
1275 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1277 base
[valueCurrentLocation
++] = ch
;
1281 /* Finally if lastWasBlank remove the trailing space. */
1282 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1283 valueCurrentLocation
--;
1285 /* Adjust content length to normalized length. */
1286 valueContentLength
= valueCurrentLocation
- valueLocation
;
1288 /* Number of bytes by which the length should be shorted. */
1289 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1290 if (lengthDiff
== 0) {
1291 /* Easy case no need to adjust lengths. */
1293 /* Hard work we need to go back and fix up length fields
1295 1) The value itself.
1296 2) The ATV Sequence containing type/value
1297 3) The RDN Set containing one or more atv pairs.
1301 /* Step 1 fix up length of value. */
1302 /* Length of value tag and length minus the tag. */
1303 DERSize newValueTLLength
= valueTLLength
- 1;
1304 drtn
= DEREncodeLength(valueContentLength
,
1305 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1306 /* Add the length of the tag back in. */
1308 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1310 /* The size of the length field changed, let's slide
1311 the value back by valueLLDiff bytes. */
1312 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1313 base
+ valueTagLocation
+ valueTLLength
,
1314 valueContentLength
);
1315 /* The length diff for the enclosing object. */
1316 lengthDiff
+= valueLLDiff
;
1319 /* Step 2 fix up length of the enclosing ATV Sequence. */
1320 atvContentLength
-= lengthDiff
;
1321 DERSize newATVTLLength
= atvTLLength
- 1;
1322 drtn
= DEREncodeLength(atvContentLength
,
1323 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1324 /* Add the length of the tag back in. */
1326 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1328 /* The size of the length field changed, let's slide
1329 the value back by valueLLDiff bytes. */
1330 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1331 base
+ atvTagLocation
+ atvTLLength
,
1333 /* The length diff for the enclosing object. */
1334 lengthDiff
+= atvLLDiff
;
1335 atvTLLength
= newATVTLLength
;
1338 /* Step 3 fix up length of enclosing RDN Set. */
1339 rdnContentLength
-= lengthDiff
;
1340 DERSize newRDNTLLength
= rdnTLLength
- 1;
1341 drtn
= DEREncodeLength(rdnContentLength
,
1342 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1343 /* Add the length of the tag back in. */
1345 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1347 /* The size of the length field changed, let's slide
1348 the value back by valueLLDiff bytes. */
1349 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1350 base
+ rdnTagLocation
+ rdnTLLength
,
1352 /* The length diff for the enclosing object. */
1353 lengthDiff
+= rdnLLDiff
;
1354 rdnTLLength
= newRDNTLLength
;
1356 /* Adjust the locations that might have changed due to
1358 atvTagLocation
-= rdnLLDiff
;
1362 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1363 atvTag
= atvSeq
.nextItem
;
1365 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1366 rdnTag
= rdnSeq
.nextItem
;
1368 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1369 /* Truncate the result to the proper length. */
1370 CFDataSetLength(result
, rdnTagLocation
);
1379 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1381 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1382 DERDecodedInfo content
;
1383 /* Decode top level sequence into DERItem */
1384 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1385 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1389 /* AUDIT[securityd]:
1390 certificate->_der is a caller provided data of any length (might be 0).
1392 Top level certificate decode.
1394 static bool SecCertificateParse(SecCertificateRef certificate
)
1399 require_quiet(certificate
, badCert
);
1400 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1402 /* top level decode */
1403 DERSignedCertCrl signedCert
;
1404 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1405 DERSignedCertCrlItemSpecs
, &signedCert
,
1406 sizeof(signedCert
));
1407 require_noerr_quiet(drtn
, badCert
);
1408 /* Store tbs since we need to digest it for verification later on. */
1409 certificate
->_tbs
= signedCert
.tbs
;
1411 /* decode the TBSCert - it was saved in full DER form */
1413 drtn
= DERParseSequence(&signedCert
.tbs
,
1414 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1415 &tbsCert
, sizeof(tbsCert
));
1416 require_noerr_quiet(drtn
, badCert
);
1418 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1419 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1420 of the params field. */
1421 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1422 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1423 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1424 require_noerr_quiet(drtn
, badCert
);
1426 /* The contents of signedCert.sig is a bit string whose contents
1427 are the signature itself. */
1428 DERByte numUnusedBits
;
1429 drtn
= DERParseBitString(&signedCert
.sig
,
1430 &certificate
->_signature
, &numUnusedBits
);
1431 require_noerr_quiet(drtn
, badCert
);
1433 /* Now decode the tbsCert. */
1435 /* First we turn the optional version into an int. */
1436 if (tbsCert
.version
.length
) {
1437 DERDecodedInfo decoded
;
1438 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1439 require_noerr_quiet(drtn
, badCert
);
1440 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1441 require_quiet(decoded
.content
.length
== 1, badCert
);
1442 certificate
->_version
= decoded
.content
.data
[0];
1443 require_quiet(certificate
->_version
> 0, badCert
);
1444 require_quiet(certificate
->_version
< 3, badCert
);
1446 certificate
->_version
= 0;
1449 /* The serial number is in the tbsCert.serialNum - it was saved in
1450 INTEGER form without the tag and length. */
1451 certificate
->_serialNum
= tbsCert
.serialNum
;
1452 certificate
->_serialNumber
= CFDataCreate(allocator
,
1453 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1455 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1456 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1457 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1458 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1459 require_noerr_quiet(drtn
, badCert
);
1461 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1462 and length fields. */
1463 certificate
->_issuer
= tbsCert
.issuer
;
1464 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1467 /* sequence we're given: decode the tbsCerts Validity sequence. */
1468 DERValidity validity
;
1469 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1470 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1471 &validity
, sizeof(validity
));
1472 require_noerr_quiet(drtn
, badCert
);
1473 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1474 &certificate
->_notBefore
), badCert
);
1475 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1476 &certificate
->_notAfter
), badCert
);
1478 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1479 and length fields. */
1480 certificate
->_subject
= tbsCert
.subject
;
1481 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1484 /* sequence we're given: encoded DERSubjPubKeyInfo */
1485 DERSubjPubKeyInfo pubKeyInfo
;
1486 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1487 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1488 &pubKeyInfo
, sizeof(pubKeyInfo
));
1489 require_noerr_quiet(drtn
, badCert
);
1491 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1492 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1493 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1494 &certificate
->_algId
, sizeof(certificate
->_algId
));
1495 require_noerr_quiet(drtn
, badCert
);
1497 /* Now we can figure out the key's algorithm id and params based on
1498 certificate->_algId.oid. */
1500 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1501 are a PKCS1 format RSA key. */
1502 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1503 &certificate
->_pubKeyDER
, &numUnusedBits
);
1504 require_noerr_quiet(drtn
, badCert
);
1506 /* The contents of tbsCert.issuerID is a bit string. */
1507 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1509 /* The contents of tbsCert.subjectID is a bit string. */
1510 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1513 if (tbsCert
.extensions
.length
) {
1514 CFIndex extensionCount
= 0;
1517 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1519 require_noerr_quiet(drtn
, badCert
);
1520 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1521 DERDecodedInfo currDecoded
;
1522 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1524 /* ! = MUST recognize ? = SHOULD recognize
1527 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1528 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1529 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1530 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1531 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1532 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1533 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1534 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1536 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1537 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1538 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1539 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1540 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1541 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1542 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1543 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1545 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1546 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1551 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1553 /* Put some upper limit on the number of extensions allowed. */
1554 require_quiet(extensionCount
< 10000, badCert
);
1555 certificate
->_extensionCount
= extensionCount
;
1556 certificate
->_extensions
=
1557 malloc(sizeof(SecCertificateExtension
) * extensionCount
);
1560 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1561 require_noerr_quiet(drtn
, badCert
);
1562 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1563 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1564 require_quiet(drtn
== DR_Success
||
1565 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1566 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1568 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1569 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1570 &extn
, sizeof(extn
));
1571 require_noerr_quiet(drtn
, badCert
);
1572 /* Copy stuff into certificate->extensions[ix]. */
1573 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1574 require_noerr_quiet(drtn
= DERParseBoolean(&extn
.critical
, false,
1575 &certificate
->_extensions
[ix
].critical
), badCert
);
1576 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1578 SecCertificateExtensionParser parser
=
1579 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1580 gExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1582 /* Invoke the parser. */
1583 parser(certificate
, &certificate
->_extensions
[ix
]);
1584 } else if (certificate
->_extensions
[ix
].critical
) {
1585 secdebug("cert", "Found unknown critical extension");
1586 certificate
->_foundUnknownCriticalExtension
= true;
1588 secdebug("cert", "Found unknown non critical extension");
1592 checkForMissingRevocationInfo(certificate
);
1601 /* Public API functions. */
1602 CFTypeID
SecCertificateGetTypeID(void) {
1603 pthread_once(&kSecCertificateRegisterClass
, SecCertificateRegisterClass
);
1604 return kSecCertificateTypeID
;
1607 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1608 const UInt8
*der_bytes
, CFIndex der_length
) {
1611 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1612 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1613 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1615 memset((char*)result
+ sizeof(result
->_base
), 0,
1616 sizeof(*result
) - sizeof(result
->_base
));
1617 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1618 result
->_der
.length
= der_length
;
1619 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1620 if (!SecCertificateParse(result
)) {
1628 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1629 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1630 const UInt8
*der_bytes
, CFIndex der_length
);
1632 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1633 const UInt8
*der_bytes
, CFIndex der_length
) {
1634 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1636 /* @@@ End of placeholder. */
1638 /* AUDIT[securityd](done):
1639 der_certificate is a caller provided data of any length (might be 0), only
1640 its cf type has been checked.
1642 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1643 CFDataRef der_certificate
) {
1644 check(der_certificate
);
1645 CFIndex size
= sizeof(struct __SecCertificate
);
1646 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1647 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1649 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1650 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1651 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1652 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1653 if (!SecCertificateParse(result
)) {
1661 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1664 if (certificate
->_der_data
) {
1665 CFRetain(certificate
->_der_data
);
1666 result
= certificate
->_der_data
;
1668 result
= CFDataCreate(CFGetAllocator(certificate
),
1669 certificate
->_der
.data
, certificate
->_der
.length
);
1671 /* FIXME: If we wish to cache result we need to lock the certificate.
1672 Also this create 2 copies of the certificate data which is somewhat
1675 certificate
->_der_data
= result
;
1682 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1683 return certificate
->_der
.length
;
1686 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1687 return certificate
->_der
.data
;
1690 /* From rfc3280 - Appendix B. ASN.1 Notes
1692 Object Identifiers (OIDs) are used throughout this specification to
1693 identify certificate policies, public key and signature algorithms,
1694 certificate extensions, etc. There is no maximum size for OIDs.
1695 This specification mandates support for OIDs which have arc elements
1696 with values that are less than 2^28, that is, they MUST be between 0
1697 and 268,435,455, inclusive. This allows each arc element to be
1698 represented within a single 32 bit word. Implementations MUST also
1699 support OIDs where the length of the dotted decimal (see [RFC 2252],
1700 section 4.1) string representation can be up to 100 bytes
1701 (inclusive). Implementations MUST be able to handle OIDs with up to
1702 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1703 contain OIDs that exceed these requirements. Likewise, CRL issuers
1704 SHOULD NOT issue CRLs which contain OIDs that exceed these
1708 /* Oids longer than this are considered invalid. */
1709 #define MAX_OID_SIZE 32
1711 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1712 const DERItem
*oid
) {
1714 if (oid
->length
== 0) {
1715 return SecCopyCertString(SEC_NULL_KEY
);
1717 if (oid
->length
> MAX_OID_SIZE
) {
1718 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
1721 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1723 // The first two levels are encoded into one byte, since the root level
1724 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1725 // y may be > 39, so we have to add special-case handling for this.
1726 uint32_t x
= oid
->data
[0] / 40;
1727 uint32_t y
= oid
->data
[0] % 40;
1730 // Handle special case for large y if x = 2
1734 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1737 for (x
= 1; x
< oid
->length
; ++x
)
1739 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1740 /* @@@ value may not span more than 4 bytes. */
1741 /* A max number of 20 values is allowed. */
1742 if (!(oid
->data
[x
] & 0x80))
1744 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
1751 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1752 const DERItem
*oid
) {
1753 if (oid
->length
== 0) {
1754 return SecCopyCertString(SEC_NULL_KEY
);
1757 /* Build the key we use to lookup the localized OID description. */
1758 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1759 oid
->length
* 3 + 5);
1760 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
1762 for (ix
= 0; ix
< oid
->length
; ++ix
)
1763 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1765 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1766 if (CFEqual(oidKey
, name
)) {
1768 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1775 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1776 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1777 have a length of exactly 4 or 16 octects. */
1778 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1779 const DERItem
*ip
) {
1780 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1781 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1782 16 octects addr, or 32 octects addr/mask. */
1783 CFStringRef value
= NULL
;
1784 if (ip
->length
== 4) {
1785 value
= CFStringCreateWithFormat(allocator
, NULL
,
1786 CFSTR("%u.%u.%u.%u"),
1787 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1788 } else if (ip
->length
== 16) {
1789 value
= CFStringCreateWithFormat(allocator
, NULL
,
1790 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1791 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1792 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
1793 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
1794 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
1795 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
1802 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
1803 const DERItem
*oid
) {
1804 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1805 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
1806 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
1807 CFSTR("%@ (%@)"), name
, decimal
);
1814 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
1815 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
) {
1816 CFDictionaryRef property
;
1819 if (localizedLabel
) {
1822 ll
= localizedLabel
= SecCopyCertString(label
);
1824 const void *all_keys
[4];
1825 all_keys
[0] = kSecPropertyKeyType
;
1826 all_keys
[1] = kSecPropertyKeyLabel
;
1827 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
1828 all_keys
[3] = kSecPropertyKeyValue
;
1829 const void *property_values
[] = {
1835 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1836 all_keys
, property_values
, value
? 4 : 3,
1837 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1840 const void *nolabel_keys
[2];
1841 nolabel_keys
[0] = kSecPropertyKeyType
;
1842 nolabel_keys
[1] = kSecPropertyKeyValue
;
1843 const void *property_values
[] = {
1847 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1848 nolabel_keys
, property_values
, 2,
1849 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1852 CFArrayAppendValue(properties
, property
);
1853 CFRelease(property
);
1857 #define UTC_TIME_NOSEC_ZULU_LEN 11
1859 #define UTC_TIME_ZULU_LEN 13
1860 /* YYMMDDhhmmssThhmm */
1861 #define UTC_TIME_LOCALIZED_LEN 17
1862 /* YYYYMMDDhhmmssZ */
1863 #define GENERALIZED_TIME_ZULU_LEN 15
1864 /* YYYYMMDDhhmmssThhmm */
1865 #define GENERALIZED_TIME_LOCALIZED_LEN 19
1867 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
1869 static inline int parseDecimalPair(const DERByte
**p
) {
1870 const DERByte
*cp
= *p
;
1872 return 10 * (cp
[0] - '0') + cp
[1] - '0';
1875 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1876 true if the date was valid and properly decoded, also return the result in
1877 absTime. Return false otherwise. */
1878 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
1884 bool isUtcLength
= false;
1885 bool isLocalized
= false;
1886 bool noSeconds
= false;
1888 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
1892 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
1895 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
1897 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
1900 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
1903 default: /* unknown format */
1907 /* Make sure the der tag fits the thing inside it. */
1908 if (tag
== ASN1_UTC_TIME
) {
1911 } else if (tag
== ASN1_GENERALIZED_TIME
) {
1918 const DERByte
*cp
= bytes
;
1919 /* Check that all characters are digits, except if localized the timezone
1920 indicator or if not localized the 'Z' at the end. */
1922 for (ix
= 0; ix
< length
; ++ix
) {
1923 if (!(isdigit(cp
[ix
]))) {
1924 if ((isLocalized
&& ix
== length
- 5 &&
1925 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
1926 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
1933 /* Parse the date and time fields. */
1934 int year
, month
, day
, hour
, minute
, second
;
1936 year
= parseDecimalPair(&cp
);
1938 /* 0 <= year < 50 : assume century 21 */
1940 } else if (year
< 70) {
1941 /* 50 <= year < 70 : illegal per PKIX */
1944 /* 70 < year <= 99 : assume century 20 */
1948 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
1950 month
= parseDecimalPair(&cp
);
1951 day
= parseDecimalPair(&cp
);
1952 hour
= parseDecimalPair(&cp
);
1953 minute
= parseDecimalPair(&cp
);
1957 second
= parseDecimalPair(&cp
);
1960 CFTimeInterval timeZoneOffset
;
1962 /* ZONE INDICATOR */
1963 int multiplier
= *cp
++ == '+' ? 60 : -60;
1964 timeZoneOffset
= multiplier
*
1965 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
1970 secdebug("dateparse",
1971 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
1972 (int) length
, bytes
, year
, month
,
1973 day
, hour
, minute
, second
,
1974 timeZoneOffset
/ 60);
1976 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
1977 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
1978 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
1979 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
1980 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
1985 int dy
= year
- 2001;
1990 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
1991 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
1993 day
+= is_leap_year
;
1995 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24 + hour
) * 60 + minute
) * 60 + second
;
1996 return absTime
- timeZoneOffset
;
1999 static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2000 CFAbsoluteTime
*pabsTime
) {
2001 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
2003 if (absTime
== NULL_TIME
)
2006 *pabsTime
= absTime
;
2010 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2011 true if the date was valid and properly decoded, also return the result in
2012 absTime. Return false otherwise. */
2013 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2014 CFAbsoluteTime
*absTime
) {
2017 if (dateChoice
->length
== 0)
2020 DERDecodedInfo decoded
;
2021 if (DERDecodeItem(dateChoice
, &decoded
))
2024 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2028 static void appendDataProperty(CFMutableArrayRef properties
,
2029 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2030 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2031 der_data
->data
, der_data
->length
);
2032 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2037 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2039 CFStringRef localizedLabel
,
2040 const DERItem
*der_data
,
2041 CFStringRef labelFormat
) {
2042 CFStringRef newLabel
=
2043 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2044 labelFormat
, label
);
2046 if (localizedLabel
) {
2049 ll
= localizedLabel
= SecCopyCertString(label
);
2051 CFStringRef localizedLabelFormat
= SecCopyCertString(labelFormat
);
2052 CFStringRef newLocalizedLabel
=
2053 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2054 localizedLabelFormat
, localizedLabel
);
2056 CFReleaseSafe(localizedLabelFormat
);
2057 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
);
2058 CFReleaseSafe(newLabel
);
2059 CFReleaseSafe(newLocalizedLabel
);
2063 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2064 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2065 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2069 static void appendInvalidProperty(CFMutableArrayRef properties
,
2070 CFStringRef label
, const DERItem
*der_data
) {
2071 appendRelabeledProperty(properties
, label
, NULL
, der_data
, SEC_INVALID_KEY
);
2074 static void appendDateContentProperty(CFMutableArrayRef properties
,
2075 CFStringRef label
, DERTag tag
,
2076 const DERItem
*dateContent
) {
2077 CFAbsoluteTime absTime
;
2078 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2079 /* Date decode failure insert hex bytes instead. */
2080 return appendInvalidProperty(properties
, label
, dateContent
);
2082 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2083 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2087 static void appendDateProperty(CFMutableArrayRef properties
,
2088 CFStringRef label
, CFAbsoluteTime absTime
) {
2089 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2090 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2094 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2095 CFStringRef label
, const DERItem
*ip
) {
2097 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2099 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2102 appendUnparsedProperty(properties
, label
, NULL
, ip
);
2106 static void appendURLContentProperty(CFMutableArrayRef properties
,
2107 CFStringRef label
, const DERItem
*urlContent
) {
2108 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2109 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2111 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
);
2114 appendInvalidProperty(properties
, label
, urlContent
);
2118 static void appendURLProperty(CFMutableArrayRef properties
,
2119 CFStringRef label
, const DERItem
*url
) {
2120 DERDecodedInfo decoded
;
2123 drtn
= DERDecodeItem(url
, &decoded
);
2124 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2125 appendInvalidProperty(properties
, label
, url
);
2127 appendURLContentProperty(properties
, label
, &decoded
.content
);
2131 static void appendOIDProperty(CFMutableArrayRef properties
,
2132 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
) {
2133 CFStringRef oid_string
=
2134 copyLocalizedOidDescription(CFGetAllocator(properties
), oid
);
2135 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2137 CFRelease(oid_string
);
2140 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2141 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2142 CFMutableArrayRef alg_props
=
2143 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2144 &kCFTypeArrayCallBacks
);
2145 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
, &algorithm
->oid
);
2146 if (algorithm
->params
.length
) {
2147 if (algorithm
->params
.length
== 2 &&
2148 algorithm
->params
.data
[0] == ASN1_NULL
&&
2149 algorithm
->params
.data
[1] == 0) {
2150 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2151 appendProperty(alg_props
, kSecPropertyTypeString
,
2152 SEC_PARAMETERS_KEY
, NULL
, value
);
2155 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2156 &algorithm
->params
);
2159 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
, alg_props
);
2160 CFRelease(alg_props
);
2163 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2164 const DERItem
*blob
) {
2165 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2166 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2167 blob
->length
* 3 - 1);
2168 for (ix
= 0; ix
< length
; ++ix
)
2170 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2172 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2177 /* Returns a (localized) blob string. */
2178 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2179 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2180 CFStringRef localizedBlobType
= SecCopyCertString(blobType
);
2181 CFStringRef localizedQuanta
= SecCopyCertString(quanta
);
2182 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2183 "data = 00 00 ...)" */
2184 CFStringRef blobFormat
= SecCopyCertString(SEC_BLOB_KEY
);
2185 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2186 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2187 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2189 CFRelease(blobFormat
);
2190 CFReleaseSafe(localizedQuanta
);
2191 CFReleaseSafe(localizedBlobType
);
2196 /* Return a string verbatim (unlocalized) from a DER field. */
2197 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2198 const DERItem
*string
, CFStringEncoding encoding
,
2199 bool printableOnly
) {
2200 /* Strip potential bogus trailing zero from printable strings. */
2201 DERSize length
= string
->length
;
2202 if (length
&& string
->data
[length
- 1] == 0) {
2203 /* Don't mess with the length of UTF16 strings though. */
2204 if (encoding
!= kCFStringEncodingUTF16
)
2207 /* A zero length string isn't considered printable. */
2208 if (!length
&& printableOnly
)
2211 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2212 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2213 passing false makes it treat it as native endian by default. */
2214 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2215 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2219 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2222 /* From rfc3280 - Appendix B. ASN.1 Notes
2224 CAs MUST force the serialNumber to be a non-negative integer, that
2225 is, the sign bit in the DER encoding of the INTEGER value MUST be
2226 zero - this can be done by adding a leading (leftmost) `00'H octet if
2227 necessary. This removes a potential ambiguity in mapping between a
2228 string of octets and an integer value.
2230 As noted in section 4.1.2.2, serial numbers can be expected to
2231 contain long integers. Certificate users MUST be able to handle
2232 serialNumber values up to 20 octets in length. Conformant CAs MUST
2233 NOT use serialNumber values longer than 20 octets.
2236 /* Return the given numeric data as a string: decimal up to 64 bits,
2238 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2239 const DERItem
*integer
) {
2241 CFIndex ix
, length
= integer
->length
;
2243 if (length
== 0 || length
> 8)
2244 return copyHexDescription(allocator
, integer
);
2246 for(ix
= 0; ix
< length
; ++ix
) {
2248 value
+= integer
->data
[ix
];
2251 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2254 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2255 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2259 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2260 case ASN1_PRINTABLE_STRING
:
2261 case ASN1_IA5_STRING
:
2262 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2263 case ASN1_UTF8_STRING
:
2264 case ASN1_GENERAL_STRING
:
2265 case ASN1_UNIVERSAL_STRING
:
2266 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2267 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2268 case ASN1_VIDEOTEX_STRING
: // 21
2269 case ASN1_VISIBLE_STRING
: // 26
2270 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2271 case ASN1_BMP_STRING
: // 30
2272 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2273 case ASN1_OCTET_STRING
:
2274 return printableOnly
? NULL
:
2275 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2277 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2278 case ASN1_BIT_STRING
:
2279 return printableOnly
? NULL
:
2280 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2282 case ASN1_CONSTR_SEQUENCE
:
2283 return printableOnly
? NULL
:
2284 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2286 case ASN1_CONSTR_SET
:
2287 return printableOnly
? NULL
:
2288 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
, derThing
);
2289 case ASN1_OBJECT_ID
:
2290 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2292 if (printableOnly
) {
2295 CFStringRef fmt
= SecCopyCertString(SEC_NOT_DISPLAYED_KEY
);
2296 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2297 tag
, derThing
->length
);
2304 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2305 const DERItem
*derThing
, bool printableOnly
) {
2306 DERDecodedInfo decoded
;
2309 drtn
= DERDecodeItem(derThing
, &decoded
);
2311 /* TODO: Perhaps put something in the label saying we couldn't parse
2313 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2315 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2316 &decoded
.content
, false);
2320 static void appendDERThingProperty(CFMutableArrayRef properties
,
2321 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*derThing
) {
2322 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2324 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2329 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2330 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2331 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2333 /* If there is more than one value pair we create a subsection for the
2334 second pair, and append things to the subsection for subsequent
2336 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2337 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2339 /* Since this is the second rdn pair for a given rdn, we setup a
2340 new subsection for this rdn. We remove the first property
2341 from the properties array and make it the first element in the
2342 subsection instead. */
2343 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2344 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2345 CFArrayAppendValue(rdn_props
, lastValue
);
2346 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2347 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2349 properties
= rdn_props
;
2351 /* Since this is the third or later rdn pair we have already
2352 created a subsection in the top level properties array. Instead
2353 of appending to that directly we append to the array inside the
2355 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2356 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2360 /* Finally we append the new rdn value to the property array. */
2361 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(
2362 CFGetAllocator(properties
), rdnType
);
2363 CFStringRef localizedLabel
=
2364 copyLocalizedOidDescription(CFGetAllocator(properties
), rdnType
);
2365 appendDERThingProperty(properties
, label
, localizedLabel
, rdnValue
);
2367 CFRelease(localizedLabel
);
2368 return errSecSuccess
;
2371 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2372 const DERItem
*rdnSetContent
) {
2373 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2374 &kCFTypeArrayCallBacks
);
2375 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2378 CFArrayRemoveAllValues(properties
);
2379 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
);
2386 From rfc3739 - 3.1.2. Subject
2388 When parsing the subject here are some tips for a short name of the cert.
2389 Choice I: commonName
2390 Choice II: givenName
2391 Choice III: pseudonym
2393 The commonName attribute value SHALL, when present, contain a name
2394 of the subject. This MAY be in the subject's preferred
2395 presentation format, or a format preferred by the CA, or some
2396 other format. Pseudonyms, nicknames, and names with spelling
2397 other than defined by the registered name MAY be used. To
2398 understand the nature of the name presented in commonName,
2399 complying applications MAY have to examine present values of the
2400 givenName and surname attributes, or the pseudonym attribute.
2403 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2404 const DERItem
*x501NameContent
) {
2405 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2406 &kCFTypeArrayCallBacks
);
2407 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2410 CFArrayRemoveAllValues(properties
);
2411 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501NameContent
);
2417 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2418 const DERItem
*x501Name
) {
2419 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2420 &kCFTypeArrayCallBacks
);
2421 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2423 CFArrayRemoveAllValues(properties
);
2424 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501Name
);
2430 static void appendIntegerProperty(CFMutableArrayRef properties
,
2431 CFStringRef label
, const DERItem
*integer
) {
2432 CFStringRef string
= copyIntegerContentDescription(
2433 CFGetAllocator(properties
), integer
);
2434 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2438 static void appendBoolProperty(CFMutableArrayRef properties
,
2439 CFStringRef label
, bool boolean
) {
2440 CFStringRef value
= SecCopyCertString(boolean
? SEC_YES_KEY
: SEC_NO_KEY
);
2441 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2445 static void appendBooleanProperty(CFMutableArrayRef properties
,
2446 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2448 DERReturn drtn
= DERParseBoolean(boolean
, defaultValue
, &result
);
2450 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2451 appendInvalidProperty(properties
, label
, boolean
);
2453 appendBoolProperty(properties
, label
, result
);
2457 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2458 CFStringRef label
, const DERItem
*bitStringContent
,
2459 const CFStringRef
*names
, CFIndex namesCount
) {
2460 DERSize len
= bitStringContent
->length
- 1;
2461 require_quiet(len
== 1 || len
== 2, badDER
);
2462 DERByte numUnusedBits
= bitStringContent
->data
[0];
2463 require_quiet(numUnusedBits
< 8, badDER
);
2464 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2465 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2466 uint_fast16_t value
= bitStringContent
->data
[1];
2469 value
= (value
<< 8) + bitStringContent
->data
[2];
2475 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2476 CFStringRef string
= NULL
;
2477 for (ix
= 0; ix
< bits
; ++ix
) {
2481 CFStringCreateWithFormat(CFGetAllocator(properties
),
2482 NULL
, fmt
, string
, names
[ix
]);
2493 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2494 string
? string
: CFSTR(""));
2495 CFReleaseSafe(string
);
2498 appendInvalidProperty(properties
, label
, bitStringContent
);
2501 static void appendBitStringNames(CFMutableArrayRef properties
,
2502 CFStringRef label
, const DERItem
*bitString
,
2503 const CFStringRef
*names
, CFIndex namesCount
) {
2504 DERDecodedInfo bitStringContent
;
2505 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2506 require_noerr_quiet(drtn
, badDER
);
2507 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2508 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2512 appendInvalidProperty(properties
, label
, bitString
);
2516 typedef uint16_t SecKeyUsage
;
2518 #define kSecKeyUsageDigitalSignature 0x8000
2519 #define kSecKeyUsageNonRepudiation 0x4000
2520 #define kSecKeyUsageKeyEncipherment 0x2000
2521 #define kSecKeyUsageDataEncipherment 0x1000
2522 #define kSecKeyUsageKeyAgreement 0x0800
2523 #define kSecKeyUsageKeyCertSign 0x0400
2524 #define kSecKeyUsageCRLSign 0x0200
2525 #define kSecKeyUsageEncipherOnly 0x0100
2526 #define kSecKeyUsageDecipherOnly 0x0080
2529 KeyUsage ::= BIT STRING {
2530 digitalSignature (0),
2532 keyEncipherment (2),
2533 dataEncipherment (3),
2540 static void appendKeyUsage(CFMutableArrayRef properties
,
2541 const DERItem
*extnValue
) {
2542 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2543 extnValue
->data
[0] != ASN1_BIT_STRING
||
2544 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2545 extnValue
->data
[2] > 7) {
2546 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2549 CFMutableStringRef string
=
2550 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2551 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2552 if (extnValue
->length
== 5)
2553 usage
+= extnValue
->data
[4];
2554 secdebug("keyusage", "keyusage: %04X", usage
);
2555 static const CFStringRef usageNames
[] = {
2556 CFSTR("Digital Signature"),
2557 CFSTR("Non-Repudiation"),
2558 CFSTR("Key Encipherment"),
2559 CFSTR("Data Encipherment"),
2560 CFSTR("Key Agreement"),
2566 bool didOne
= false;
2567 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2568 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2569 for (ix
= 0; ix
< bits
; ++ix
) {
2572 CFStringAppend(string
, CFSTR(", "));
2576 /* @@@ Localize usageNames[ix]. */
2577 CFStringAppend(string
, usageNames
[ix
]);
2581 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2587 static void appendKeyUsage(CFMutableArrayRef properties
,
2588 const DERItem
*extnValue
) {
2589 static const CFStringRef usageNames
[] = {
2590 SEC_DIGITAL_SIGNATURE_KEY
,
2591 SEC_NON_REPUDIATION_KEY
,
2592 SEC_KEY_ENCIPHERMENT_KEY
,
2593 SEC_DATA_ENCIPHERMENT_KEY
,
2594 SEC_KEY_AGREEMENT_KEY
,
2597 SEC_ENCIPHER_ONLY_KEY
,
2598 SEC_DECIPHER_ONLY_KEY
2600 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2601 usageNames
, array_size(usageNames
));
2605 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2606 const DERItem
*extnValue
) {
2607 DERPrivateKeyUsagePeriod pkup
;
2608 DERReturn drtn
= DERParseSequence(extnValue
,
2609 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2610 &pkup
, sizeof(pkup
));
2611 require_noerr_quiet(drtn
, badDER
);
2612 if (pkup
.notBefore
.length
) {
2613 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2614 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2616 if (pkup
.notAfter
.length
) {
2617 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2618 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2622 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
, extnValue
);
2625 static void appendStringContentProperty(CFMutableArrayRef properties
,
2626 CFStringRef label
, const DERItem
*stringContent
,
2627 CFStringEncoding encoding
) {
2628 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2629 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2631 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2634 appendInvalidProperty(properties
, label
, stringContent
);
2639 OtherName ::= SEQUENCE {
2640 type-id OBJECT IDENTIFIER,
2641 value [0] EXPLICIT ANY DEFINED BY type-id }
2643 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2644 const DERItem
*otherNameContent
) {
2646 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2647 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2649 require_noerr_quiet(drtn
, badDER
);
2650 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2652 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
2653 CFStringRef localizedLabel
=
2654 copyLocalizedOidDescription(allocator
, &on
.typeIdentifier
);
2655 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2657 appendProperty(properties
, kSecPropertyTypeString
, label
,
2658 localizedLabel
, value_string
);
2660 appendUnparsedProperty(properties
, label
, localizedLabel
, &on
.value
);
2662 CFRelease(localizedLabel
);
2665 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
, otherNameContent
);
2669 GeneralName ::= CHOICE {
2670 otherName [0] OtherName,
2671 rfc822Name [1] IA5String,
2672 dNSName [2] IA5String,
2673 x400Address [3] ORAddress,
2674 directoryName [4] Name,
2675 ediPartyName [5] EDIPartyName,
2676 uniformResourceIdentifier [6] IA5String,
2677 iPAddress [7] OCTET STRING,
2678 registeredID [8] OBJECT IDENTIFIER}
2680 EDIPartyName ::= SEQUENCE {
2681 nameAssigner [0] DirectoryString OPTIONAL,
2682 partyName [1] DirectoryString }
2684 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2685 DERTag tag
, const DERItem
*generalName
) {
2687 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2688 appendOtherNameContentProperty(properties
, generalName
);
2690 case ASN1_CONTEXT_SPECIFIC
| 1:
2692 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
2693 generalName
, kCFStringEncodingASCII
);
2695 case ASN1_CONTEXT_SPECIFIC
| 2:
2697 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
2698 kCFStringEncodingASCII
);
2700 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2701 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
2704 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2706 CFArrayRef directory_plist
=
2707 createPropertiesForX501Name(CFGetAllocator(properties
),
2709 appendProperty(properties
, kSecPropertyTypeSection
,
2710 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
);
2711 CFRelease(directory_plist
);
2714 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2715 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
2718 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2719 /* Technically I don't think this is valid, but there are certs out
2720 in the wild that use a constructed IA5String. In particular the
2721 VeriSign Time Stamping Authority CA.cer does this. */
2722 appendURLProperty(properties
, SEC_URI_KEY
, generalName
);
2724 case ASN1_CONTEXT_SPECIFIC
| 6:
2725 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
);
2727 case ASN1_CONTEXT_SPECIFIC
| 7:
2728 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
2731 case ASN1_CONTEXT_SPECIFIC
| 8:
2732 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
, generalName
);
2743 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2744 const DERItem
*generalName
) {
2745 DERDecodedInfo generalNameContent
;
2746 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2747 require_noerr_quiet(drtn
, badDER
);
2748 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2749 &generalNameContent
.content
))
2752 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
, generalName
);
2757 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2759 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2760 const DERItem
*generalNamesContent
) {
2762 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2763 require_noerr_quiet(drtn
, badDER
);
2764 DERDecodedInfo generalNameContent
;
2765 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2767 if (!appendGeneralNameContentProperty(properties
,
2768 generalNameContent
.tag
, &generalNameContent
.content
)) {
2772 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2775 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
2776 generalNamesContent
);
2779 static void appendGeneralNames(CFMutableArrayRef properties
,
2780 const DERItem
*generalNames
) {
2781 DERDecodedInfo generalNamesContent
;
2782 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2783 require_noerr_quiet(drtn
, badDER
);
2784 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2786 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2789 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
, generalNames
);
2793 BasicConstraints ::= SEQUENCE {
2794 cA BOOLEAN DEFAULT FALSE,
2795 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
2797 static void appendBasicConstraints(CFMutableArrayRef properties
,
2798 const DERItem
*extnValue
) {
2799 DERBasicConstraints basicConstraints
;
2800 DERReturn drtn
= DERParseSequence(extnValue
,
2801 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
2802 &basicConstraints
, sizeof(basicConstraints
));
2803 require_noerr_quiet(drtn
, badDER
);
2805 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
2806 &basicConstraints
.cA
, false);
2808 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
2809 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
2810 &basicConstraints
.pathLenConstraint
);
2814 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
, extnValue
);
2818 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
2820 DistributionPoint ::= SEQUENCE {
2821 distributionPoint [0] DistributionPointName OPTIONAL,
2822 reasons [1] ReasonFlags OPTIONAL,
2823 cRLIssuer [2] GeneralNames OPTIONAL }
2825 DistributionPointName ::= CHOICE {
2826 fullName [0] GeneralNames,
2827 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
2829 ReasonFlags ::= BIT STRING {
2833 affiliationChanged (3),
2835 cessationOfOperation (5),
2836 certificateHold (6),
2837 privilegeWithdrawn (7),
2840 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
2841 const DERItem
*extnValue
) {
2842 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2845 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
2846 require_noerr_quiet(drtn
, badDER
);
2847 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2848 DERDecodedInfo dpSeqContent
;
2849 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
2850 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2851 DERDistributionPoint dp
;
2852 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
2853 DERNumDistributionPointItemSpecs
,
2854 DERDistributionPointItemSpecs
,
2856 require_noerr_quiet(drtn
, badDER
);
2857 if (dp
.distributionPoint
.length
) {
2858 DERDecodedInfo distributionPointName
;
2859 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
2860 require_noerr_quiet(drtn
, badDER
);
2861 if (distributionPointName
.tag
==
2862 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
2864 appendGeneralNamesContent(properties
,
2865 &distributionPointName
.content
);
2866 } else if (distributionPointName
.tag
==
2867 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
2868 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
2870 appendProperty(properties
, kSecPropertyTypeSection
,
2871 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
);
2872 CFRelease(rdn_props
);
2877 if (dp
.reasons
.length
) {
2878 static const CFStringRef reasonNames
[] = {
2880 SEC_KEY_COMPROMISE_KEY
,
2881 SEC_CA_COMPROMISE_KEY
,
2882 SEC_AFFILIATION_CHANGED_KEY
,
2884 SEC_CESSATION_OF_OPER_KEY
,
2885 SEC_CERTIFICATE_HOLD_KEY
,
2886 SEC_PRIV_WITHDRAWN_KEY
,
2887 SEC_AA_COMPROMISE_KEY
2889 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
2891 reasonNames
, array_size(reasonNames
));
2893 if (dp
.cRLIssuer
.length
) {
2894 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
2895 &kCFTypeArrayCallBacks
);
2896 appendProperty(properties
, kSecPropertyTypeSection
,
2897 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
);
2898 CFRelease(crlIssuer
);
2899 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
2902 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2905 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
, extnValue
);
2908 /* Decode a sequence of integers into a comma separated list of ints. */
2909 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
2910 CFStringRef label
, const DERItem
*intSequenceContent
) {
2911 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2913 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
2914 require_noerr_quiet(drtn
, badDER
);
2915 DERDecodedInfo intContent
;
2916 CFStringRef value
= NULL
;
2917 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2918 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
2919 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
2920 CFStringRef intDesc
= copyIntegerContentDescription(
2921 allocator
, &intContent
.content
);
2924 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
2933 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2935 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2939 /* DROPTHOUGH if !value. */
2941 appendInvalidProperty(properties
, label
, intSequenceContent
);
2944 static void appendCertificatePolicies(CFMutableArrayRef properties
,
2945 const DERItem
*extnValue
) {
2946 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2949 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
2950 require_noerr_quiet(drtn
, badDER
);
2951 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2952 DERDecodedInfo piContent
;
2954 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
2955 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2956 DERPolicyInformation pi
;
2957 drtn
= DERParseSequenceContent(&piContent
.content
,
2958 DERNumPolicyInformationItemSpecs
,
2959 DERPolicyInformationItemSpecs
,
2961 require_noerr_quiet(drtn
, badDER
);
2962 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2963 SEC_POLICY_IDENTIFIER_KEY
, pin
);
2964 CFStringRef piFmt
= SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
);
2965 CFStringRef lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2968 appendOIDProperty(properties
, piLabel
, lpiLabel
, &pi
.policyIdentifier
);
2970 CFRelease(lpiLabel
);
2971 if (pi
.policyQualifiers
.length
== 0)
2975 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
2976 require_noerr_quiet(drtn
, badDER
);
2977 DERDecodedInfo pqContent
;
2979 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
2980 DERPolicyQualifierInfo pqi
;
2981 drtn
= DERParseSequenceContent(&pqContent
.content
,
2982 DERNumPolicyQualifierInfoItemSpecs
,
2983 DERPolicyQualifierInfoItemSpecs
,
2985 require_noerr_quiet(drtn
, badDER
);
2986 DERDecodedInfo qualifierContent
;
2987 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
2988 require_noerr_quiet(drtn
, badDER
);
2989 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2990 SEC_POLICY_QUALIFIER_KEY
, pqn
);
2991 CFStringRef pqFmt
= SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
);
2992 CFStringRef lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2995 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
2996 &pqi
.policyQualifierID
);
2998 CFRelease(lpqLabel
);
2999 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3000 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3001 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3002 &qualifierContent
.content
);
3003 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3004 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3006 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3007 DERNumUserNoticeItemSpecs
,
3008 DERUserNoticeItemSpecs
,
3010 require_noerr_quiet(drtn
, badDER
);
3011 if (un
.noticeRef
.length
) {
3012 DERNoticeReference nr
;
3013 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3014 DERNumNoticeReferenceItemSpecs
,
3015 DERNoticeReferenceItemSpecs
,
3017 require_noerr_quiet(drtn
, badDER
);
3018 appendDERThingProperty(properties
,
3019 SEC_ORGANIZATION_KEY
, NULL
,
3021 appendIntegerSequenceContent(properties
,
3022 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
);
3024 if (un
.explicitText
.length
) {
3025 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3026 NULL
, &un
.explicitText
);
3029 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3034 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3037 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
, extnValue
);
3040 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3041 const DERItem
*extnValue
) {
3043 DERDecodedInfo keyIdentifier
;
3044 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3045 require_noerr_quiet(drtn
, badDER
);
3046 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3047 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3048 &keyIdentifier
.content
);
3052 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3057 AuthorityKeyIdentifier ::= SEQUENCE {
3058 keyIdentifier [0] KeyIdentifier OPTIONAL,
3059 authorityCertIssuer [1] GeneralNames OPTIONAL,
3060 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3061 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3062 -- be present or both be absent
3064 KeyIdentifier ::= OCTET STRING
3066 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3067 const DERItem
*extnValue
) {
3068 DERAuthorityKeyIdentifier akid
;
3070 drtn
= DERParseSequence(extnValue
,
3071 DERNumAuthorityKeyIdentifierItemSpecs
,
3072 DERAuthorityKeyIdentifierItemSpecs
,
3073 &akid
, sizeof(akid
));
3074 require_noerr_quiet(drtn
, badDER
);
3075 if (akid
.keyIdentifier
.length
) {
3076 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3077 &akid
.keyIdentifier
);
3079 if (akid
.authorityCertIssuer
.length
||
3080 akid
.authorityCertSerialNumber
.length
) {
3081 require_quiet(akid
.authorityCertIssuer
.length
&&
3082 akid
.authorityCertSerialNumber
.length
, badDER
);
3083 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3084 appendGeneralNamesContent(properties
,
3085 &akid
.authorityCertIssuer
);
3086 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3087 &akid
.authorityCertSerialNumber
);
3092 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
, extnValue
);
3096 PolicyConstraints ::= SEQUENCE {
3097 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3098 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3100 SkipCerts ::= INTEGER (0..MAX)
3102 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3103 const DERItem
*extnValue
) {
3104 DERPolicyConstraints pc
;
3106 drtn
= DERParseSequence(extnValue
,
3107 DERNumPolicyConstraintsItemSpecs
,
3108 DERPolicyConstraintsItemSpecs
,
3110 require_noerr_quiet(drtn
, badDER
);
3111 if (pc
.requireExplicitPolicy
.length
) {
3112 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3113 &pc
.requireExplicitPolicy
);
3115 if (pc
.inhibitPolicyMapping
.length
) {
3116 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3117 &pc
.inhibitPolicyMapping
);
3123 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
, extnValue
);
3127 extendedKeyUsage EXTENSION ::= {
3128 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3129 IDENTIFIED BY id-ce-extKeyUsage }
3131 KeyPurposeId ::= OBJECT IDENTIFIER
3133 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3134 const DERItem
*extnValue
) {
3137 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3138 require_noerr_quiet(drtn
, badDER
);
3139 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3140 DERDecodedInfo currDecoded
;
3141 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3142 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3143 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3144 &currDecoded
.content
);
3146 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3149 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
, extnValue
);
3153 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3155 AuthorityInfoAccessSyntax ::=
3156 SEQUENCE SIZE (1..MAX) OF AccessDescription
3158 AccessDescription ::= SEQUENCE {
3159 accessMethod OBJECT IDENTIFIER,
3160 accessLocation GeneralName }
3162 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3164 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3166 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3168 static void appendInfoAccess(CFMutableArrayRef properties
,
3169 const DERItem
*extnValue
) {
3172 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3173 require_noerr_quiet(drtn
, badDER
);
3174 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3175 DERDecodedInfo adContent
;
3176 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3177 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3178 DERAccessDescription ad
;
3179 drtn
= DERParseSequenceContent(&adContent
.content
,
3180 DERNumAccessDescriptionItemSpecs
,
3181 DERAccessDescriptionItemSpecs
,
3183 require_noerr_quiet(drtn
, badDER
);
3184 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3186 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3187 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3189 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3192 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
, extnValue
);
3195 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3196 const DERItem
*extnValue
) {
3197 static const CFStringRef certTypes
[] = {
3201 SEC_OBJECT_SIGNING_KEY
,
3205 SEC_OBJECT_SIGNING_CA_KEY
3207 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3208 certTypes
, array_size(certTypes
));
3212 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3213 const DERItem
*extnValue
) {
3217 * The list of Qualified Cert Statement statementIds we understand, even though
3218 * we don't actually do anything with them; if these are found in a Qualified
3219 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3221 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3223 /* id-qcs := { id-pkix 11 } */
3224 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3225 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3226 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3227 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3228 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3229 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3231 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3233 static void appendQCCertStatements(CFMutableArrayRef properties
,
3234 const DERItem
*extnValue
) {
3239 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3240 CFStringRef label
, const DERItem
*sequence
) {
3243 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3244 require_noerr_quiet(drtn
, badSequence
);
3245 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3246 DERDecodedInfo currDecoded
;
3247 bool appendedSomething
= false;
3248 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3249 switch (currDecoded
.tag
)
3252 case ASN1_SEQUENCE
: // 16
3253 case ASN1_SET
: // 17
3254 // skip constructed object lengths
3257 case ASN1_UTF8_STRING
: // 12
3258 case ASN1_NUMERIC_STRING
: // 18
3259 case ASN1_PRINTABLE_STRING
: // 19
3260 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3261 case ASN1_VIDEOTEX_STRING
: // 21
3262 case ASN1_IA5_STRING
: // 22
3263 case ASN1_GRAPHIC_STRING
: // 25
3264 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3265 case ASN1_GENERAL_STRING
: // 27
3266 case ASN1_UNIVERSAL_STRING
: // 28
3268 CFStringRef string
=
3269 copyDERThingContentDescription(CFGetAllocator(properties
),
3270 currDecoded
.tag
, &currDecoded
.content
, false);
3271 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3273 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3276 appendedSomething
= true;
3283 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3284 return appendedSomething
;
3289 static void appendExtension(CFMutableArrayRef parent
,
3290 const SecCertificateExtension
*extn
) {
3291 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3292 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3293 &kCFTypeArrayCallBacks
);
3295 *extnID
= &extn
->extnID
,
3296 *extnValue
= &extn
->extnValue
;
3298 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
);
3301 bool handeled
= true;
3302 /* Extensions that we know how to handle ourselves... */
3303 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3304 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3306 switch (extnID
->data
[extnID
->length
- 1]) {
3307 case 14: /* SubjectKeyIdentifier id-ce 14 */
3308 appendSubjectKeyIdentifier(properties
, extnValue
);
3310 case 15: /* KeyUsage id-ce 15 */
3311 appendKeyUsage(properties
, extnValue
);
3313 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3314 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3316 case 17: /* SubjectAltName id-ce 17 */
3317 case 18: /* IssuerAltName id-ce 18 */
3318 appendGeneralNames(properties
, extnValue
);
3320 case 19: /* BasicConstraints id-ce 19 */
3321 appendBasicConstraints(properties
, extnValue
);
3323 case 30: /* NameConstraints id-ce 30 */
3326 case 31: /* CRLDistributionPoints id-ce 31 */
3327 appendCrlDistributionPoints(properties
, extnValue
);
3329 case 32: /* CertificatePolicies id-ce 32 */
3330 appendCertificatePolicies(properties
, extnValue
);
3332 case 33: /* PolicyMappings id-ce 33 */
3335 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3336 appendAuthorityKeyIdentifier(properties
, extnValue
);
3338 case 36: /* PolicyConstraints id-ce 36 */
3339 appendPolicyConstraints(properties
, extnValue
);
3341 case 37: /* ExtKeyUsage id-ce 37 */
3342 appendExtendedKeyUsage(properties
, extnValue
);
3344 case 46: /* FreshestCRL id-ce 46 */
3347 case 54: /* InhibitAnyPolicy id-ce 54 */
3354 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3355 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3357 switch (extnID
->data
[extnID
->length
- 1]) {
3358 case 1: /* AuthorityInfoAccess id-pe 1 */
3359 appendInfoAccess(properties
, extnValue
);
3361 case 3: /* QCStatements id-pe 3 */
3364 case 11: /* SubjectInfoAccess id-pe 11 */
3365 appendInfoAccess(properties
, extnValue
);
3371 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3372 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3373 appendNetscapeCertType(properties
, extnValue
);
3379 /* Try to parse and display printable string(s). */
3380 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3381 /* Nothing to do here appendPrintableDERSequence did the work. */
3383 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3384 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3388 /* Extensions that we know how to handle ourselves... */
3389 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3390 appendSubjectKeyIdentifier(properties
, extnValue
);
3391 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3392 appendKeyUsage(properties
, extnValue
);
3393 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3394 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3395 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3396 appendGeneralNames(properties
, extnValue
);
3397 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3398 appendGeneralNames(properties
, extnValue
);
3399 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3400 appendBasicConstraints(properties
, extnValue
);
3401 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3402 appendCrlDistributionPoints(properties
, extnValue
);
3403 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3404 appendCertificatePolicies(properties
, extnValue
);
3405 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3406 appendAuthorityKeyIdentifier(properties
, extnValue
);
3407 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3408 appendPolicyConstraints(properties
, extnValue
);
3409 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3410 appendExtendedKeyUsage(properties
, extnValue
);
3411 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3412 appendInfoAccess(properties
, extnValue
);
3413 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3414 appendInfoAccess(properties
, extnValue
);
3415 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3416 appendNetscapeCertType(properties
, extnValue
);
3418 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3419 appendEntrustVersInfo(properties
, extnValue
);
3422 /* Try to parse and display printable string(s). */
3423 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3424 /* Nothing to do here appendPrintableDERSequence did the work. */
3426 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3427 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3430 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(allocator
,
3432 CFStringRef localizedLabel
= copyLocalizedOidDescription(allocator
,
3434 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
,
3436 CFRelease(localizedLabel
);
3438 CFRelease(properties
);
3441 /* Different types of summary types from least desired to most desired. */
3444 kSummaryTypePrintable
,
3445 kSummaryTypeOrganizationName
,
3446 kSummaryTypeOrganizationalUnitName
,
3447 kSummaryTypeCommonName
,
3451 enum SummaryType type
;
3452 CFStringRef summary
;
3453 CFStringRef description
;
3456 static OSStatus
obtainSummaryFromX501Name(void *context
,
3457 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3458 struct Summary
*summary
= (struct Summary
*)context
;
3459 enum SummaryType stype
= kSummaryTypeNone
;
3460 CFStringRef string
= NULL
;
3461 if (DEROidCompare(type
, &oidCommonName
)) {
3462 stype
= kSummaryTypeCommonName
;
3463 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3464 stype
= kSummaryTypeOrganizationalUnitName
;
3465 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3466 stype
= kSummaryTypeOrganizationName
;
3467 } else if (DEROidCompare(type
, &oidDescription
)) {
3468 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3470 if (summary
->description
) {
3471 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3472 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->description
);
3474 CFRelease(summary
->description
);
3475 summary
->description
= newDescription
;
3477 summary
->description
= string
;
3480 stype
= kSummaryTypePrintable
;
3483 stype
= kSummaryTypePrintable
;
3486 /* Build a string with all instances of the most desired
3487 component type in reverse order encountered comma separated list,
3488 The order of desirability is defined by enum SummaryType. */
3489 if (summary
->type
<= stype
) {
3491 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3494 if (summary
->type
== stype
) {
3495 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3496 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->summary
);
3499 string
= newSummary
;
3501 summary
->type
= stype
;
3503 CFReleaseSafe(summary
->summary
);
3504 summary
->summary
= string
;
3507 CFReleaseSafe(string
);
3510 return errSecSuccess
;
3513 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3514 struct Summary summary
= {};
3515 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3516 /* If we found a description and a common name we change the summary to
3517 CommonName (Description). */
3518 if (summary
.description
) {
3519 if (summary
.type
== kSummaryTypeCommonName
) {
3520 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3521 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3523 CFRelease(summary
.summary
);
3524 summary
.summary
= newSummary
;
3526 CFRelease(summary
.description
);
3529 if (!summary
.summary
) {
3530 /* If we didn't find a suitable printable string in the subject at all, we try
3531 the first email address in the certificate instead. */
3532 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3534 /* If we didn't find any email addresses in the certificate, we try finding
3535 a DNS name instead. */
3536 names
= SecCertificateCopyDNSNames(certificate
);
3539 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3540 CFRetain(summary
.summary
);
3545 return summary
.summary
;
3548 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
3549 struct Summary summary
= {};
3550 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3551 /* If we found a description and a common name we change the summary to
3552 CommonName (Description). */
3553 if (summary
.description
) {
3554 if (summary
.type
== kSummaryTypeCommonName
) {
3555 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3556 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3558 CFRelease(summary
.summary
);
3559 summary
.summary
= newSummary
;
3561 CFRelease(summary
.description
);
3564 return summary
.summary
;
3567 /* Return the earliest date on which all certificates in this chain are still
3569 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3570 SecCertificateRef certificate
) {
3571 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3573 while (certificate
->_parent
) {
3574 certificate
= certificate
->_parent
;
3575 if (earliest
> certificate
->_notAfter
)
3576 earliest
= certificate
->_notAfter
;
3583 /* Return the latest date on which all certificates in this chain will be
3585 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3586 SecCertificateRef certificate
) {
3587 CFAbsoluteTime latest
= certificate
->_notBefore
;
3589 while (certificate
->_parent
) {
3590 certificate
= certificate
->_parent
;
3591 if (latest
< certificate
->_notBefore
)
3592 latest
= certificate
->_notBefore
;
3599 bool SecCertificateIsValid(SecCertificateRef certificate
,
3600 CFAbsoluteTime verifyTime
) {
3602 return certificate
->_notBefore
<= verifyTime
&&
3603 verifyTime
<= certificate
->_notAfter
;
3606 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
3607 return certificate
->_version
+ 1;
3610 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
3611 return certificate
->_notBefore
;
3614 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
3615 return certificate
->_notAfter
;
3618 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3619 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
3620 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3621 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3622 &kCFTypeArrayCallBacks
);
3624 /* First we put the subject summary name. */
3625 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
3627 appendProperty(summary
, kSecPropertyTypeTitle
,
3628 NULL
, NULL
, ssummary
);
3629 CFRelease(ssummary
);
3632 CFStringRef isummary
= SEC_ISSUER_SUMMARY_KEY
;
3633 appendProperty(summary
, kSecPropertyTypeString
,
3634 SEC_ISSUED_BY_KEY
, isummary
);
3635 CFRelease(isummary
);
3638 /* Let see if this certificate is currently valid. */
3640 CFAbsoluteTime when
;
3641 CFStringRef message
;
3643 if (verifyTime
> certificate
->_notAfter
) {
3644 label
= SEC_EXPIRED_KEY
;
3645 when
= certificate
->_notAfter
;
3646 ptype
= kSecPropertyTypeError
;
3647 message
= SEC_CERT_EXPIRED_KEY
;
3648 } else if (certificate
->_notBefore
> verifyTime
) {
3649 label
= SEC_VALID_FROM_KEY
;
3650 when
= certificate
->_notBefore
;
3651 ptype
= kSecPropertyTypeError
;
3652 message
= SEC_CERT_NOT_YET_VALID_KEY
;
3654 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3655 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3656 if (verifyTime
> last
) {
3657 label
= SEC_EXPIRED_KEY
;
3659 ptype
= kSecPropertyTypeError
;
3660 message
= SEC_ISSUER_EXPIRED_KEY
;
3661 } else if (verifyTime
< first
) {
3662 label
= SEC_VALID_FROM_KEY
;
3664 ptype
= kSecPropertyTypeError
;
3665 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
3667 label
= SEC_EXPIRES_KEY
;
3668 when
= certificate
->_notAfter
;
3669 ptype
= kSecPropertyTypeSuccess
;
3670 message
= SEC_CERT_VALID_KEY
;
3674 appendDateProperty(summary
, label
, when
);
3675 CFStringRef lmessage
= SecCopyCertString(message
);
3676 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
);
3677 CFRelease(lmessage
);
3682 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
3683 if (!certificate
->_properties
) {
3684 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3685 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3686 &kCFTypeArrayCallBacks
);
3688 /* First we put the Subject Name in the property list. */
3689 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3690 &certificate
->_subject
);
3691 appendProperty(properties
, kSecPropertyTypeSection
,
3692 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
);
3693 CFRelease(subject_plist
);
3696 /* Put Normalized subject in for testing. */
3697 if (certificate
->_normalizedSubject
) {
3698 DERItem nsubject
= {
3699 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
3700 CFDataGetLength(certificate
->_normalizedSubject
)
3702 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
3704 appendProperty(properties
, kSecPropertyTypeSection
,
3705 CFSTR("Normalized Subject Name"), nsubject_plist
);
3706 CFRelease(nsubject_plist
);
3710 /* Next we put the Issuer Name in the property list. */
3711 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3712 &certificate
->_issuer
);
3713 appendProperty(properties
, kSecPropertyTypeSection
,
3714 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
);
3715 CFRelease(issuer_plist
);
3718 /* Certificate version/type. */
3719 bool isRoot
= false;
3720 CFStringRef fmt
= SecCopyCertString(SEC_X509_VERSION_KEY
);
3721 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
3722 fmt
, certificate
->_version
+ 1, isRoot
? "root " : "");
3724 appendProperty(properties
, kSecPropertyTypeString
,
3725 SEC_CERTIFICATE_TYPE_KEY
, typeString
);
3726 CFRelease(typeString
);
3730 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
3731 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
3732 NULL
, fmt
, certificate
->_version
+ 1);
3734 appendProperty(properties
, kSecPropertyTypeString
,
3735 SEC_VERSION_KEY
, NULL
, versionString
);
3736 CFRelease(versionString
);
3739 if (certificate
->_serialNum
.length
) {
3740 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
3741 &certificate
->_serialNum
);
3744 /* Signature algorithm. */
3746 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
3747 &certificate
->_sigAlg
);
3749 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
3750 &certificate
->_tbsSigAlg
);
3753 /* Validity dates. */
3754 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
3755 certificate
->_notBefore
);
3756 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
3757 certificate
->_notAfter
);
3759 if (certificate
->_subjectUniqueID
.length
) {
3760 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
3761 &certificate
->_subjectUniqueID
);
3763 if (certificate
->_issuerUniqueID
.length
) {
3764 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
3765 &certificate
->_issuerUniqueID
);
3768 /* Public key algorithm. */
3769 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
3770 &certificate
->_algId
);
3772 /* Consider breaking down an RSA public key into modulus and
3774 appendDataProperty(properties
, SEC_PULIC_KEY_DATA_KEY
, NULL
,
3775 &certificate
->_pubKeyDER
);
3776 /* TODO: Add Key Size. */
3777 /* TODO: Add Key Usage. */
3779 appendDataProperty(properties
, SEC_SIGNATURE_KEY
, NULL
,
3780 &certificate
->_signature
);
3783 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
3784 appendExtension(properties
, &certificate
->_extensions
[ix
]);
3787 /* TODO: Certificate/Key Fingerprints. */
3789 certificate
->_properties
= properties
;
3792 CFRetain(certificate
->_properties
);
3793 return certificate
->_properties
;
3796 CFDataRef
SecCertificateCopySerialNumber(
3797 SecCertificateRef certificate
) {
3798 if (certificate
->_serialNumber
) {
3799 CFRetain(certificate
->_serialNumber
);
3801 return certificate
->_serialNumber
;
3804 CFDataRef
SecCertificateGetNormalizedIssuerContent(
3805 SecCertificateRef certificate
) {
3806 return certificate
->_normalizedIssuer
;
3809 CFDataRef
SecCertificateGetNormalizedSubjectContent(
3810 SecCertificateRef certificate
) {
3811 return certificate
->_normalizedSubject
;
3814 /* Verify that certificate was signed by issuerKey. */
3815 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
3816 SecKeyRef issuerKey
) {
3817 /* Setup algId in SecAsn1AlgId format. */
3819 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3820 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3821 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3822 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3824 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3825 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3826 certificate
->_signature
.data
, certificate
->_signature
.length
);
3828 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, status
);
3829 return errSecNotSigner
;
3832 return errSecSuccess
;
3836 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRef certificate
,
3837 SecCertificateRef issuer
, bool signatureCheckOnly
) {
3838 if (!signatureCheckOnly
) {
3839 /* It turns out we don't actually need to use normalized subject and
3840 issuer according to rfc2459. */
3842 /* If present we should check issuerID against the issuer subjectID. */
3844 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3845 then we should look for a SubjectKeyIdentifier in the issuer
3847 If we have a authorityCertSerialNumber we can use that for chaining.
3848 If we have a authorityCertIssuer we can use that? (or not) */
3850 /* Verify that this cert was issued by issuer. Do so by chaining
3851 either issuerID to subjectID or normalized issuer to normalized
3853 CFDataRef normalizedIssuer
=
3854 SecCertificateGetNormalizedIssuerContent(certificate
);
3855 CFDataRef normalizedIssuerSubject
=
3856 SecCertificateGetNormalizedSubjectContent(issuer
);
3857 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3858 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3859 return errSecIssuerMismatch
;
3862 /* Next verify that this cert was signed by issuer. */
3863 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3865 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3866 /* FIXME: We sould cache this (or at least the digest) until we find
3867 a suitable issuer. */
3868 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3869 CFIndex signedDataLength
;
3870 CertVerifyReturn crtn
;
3871 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3872 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3873 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3874 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3875 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3876 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3877 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3878 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3879 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3881 secdebug("verify", "unsupported algorithm");
3882 return errSecUnsupportedAlgorithm
;
3885 secdebug("verify", "*DigestInfo returned: %d", crtn
);
3886 /* FIXME: Do proper error code translation. */
3887 return errSecUnsupportedAlgorithm
;
3890 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3891 signedData
, signedDataLength
,
3892 certificate
->_signature
.data
, certificate
->_signature
.length
);
3894 secdebug("verify", "signature verify failed: %d", status
);
3895 return errSecNotSigner
;
3898 return errSecSuccess
;
3901 static OSStatus
_SecCertificateSetParent(SecCertificateRef certificate
,
3902 SecCertificateRef issuer
, bool signatureCheckOnly
) {
3904 if (certificate
->_parent
) {
3905 /* Setting a certificates issuer twice is only allowed if the new
3906 issuer is equal to the current one. */
3907 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3911 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3912 signatureCheckOnly
);
3914 OSStatus status
= errSecSuccess
;
3917 if (CFEqual(certificate
, issuer
)) {
3918 /* We don't retain ourselves cause that would be bad mojo,
3919 however we do record that we are properly self signed. */
3920 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3921 secdebug("cert", "set self as parent");
3922 return errSecSuccess
;
3926 certificate
->_parent
= issuer
;
3927 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3933 static bool SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
3934 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3935 certificate
->_isSelfSigned
=
3936 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3937 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3940 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3943 /* Return true iff we were able to set our own parent from one of the
3944 certificates in other_certificates, return false otherwise. If
3945 signatureCheckOnly is true, we can skip the subject == issuer or
3946 authorityKeyIdentifier tests. */
3947 static bool SecCertificateSetParentFrom(SecCertificateRef certificate
,
3948 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3949 CFIndex count
= CFArrayGetCount(other_certificates
);
3951 for (ix
= 0; ix
< count
; ++ix
) {
3952 SecCertificateRef candidate
= (SecCertificateRef
)
3953 CFArrayGetValueAtIndex(other_certificates
, ix
);
3954 if (_SecCertificateSetParent(certificate
, candidate
,
3955 signatureCheckOnly
))
3961 /* Lookup the parent of certificate in the keychain and set it. */
3962 static bool SecCertificateFindParent(SecCertificateRef certificate
) {
3963 /* FIXME: Search for things other than just subject of our issuer if we
3964 have a subjectID or authorityKeyIdentifier. */
3965 CFDataRef normalizedIssuer
=
3966 SecCertificateGetNormalizedIssuerContent(certificate
);
3967 const void *keys
[] = {
3974 kSecClassCertificate
,
3979 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3980 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3982 OSStatus status
= SecItemCopyMatching(query
, &results
);
3985 secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3989 CFArrayRef certs
= (CFArrayRef
)results
;
3990 /* Since we already know the certificates we are providing as candidates
3991 have been checked for subject matching, we can ask
3992 SecCertificateSetParentFrom to skip everything except the signature
3994 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3999 OSStatus
SecCertificateCompleteChain(SecCertificateRef certificate
,
4000 CFArrayRef other_certificates
) {
4002 if (certificate
->_parent
== NULL
) {
4003 if (SecCertificateIsSelfSigned(certificate
))
4004 return errSecSuccess
;
4005 if (!other_certificates
||
4006 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
4008 if (!SecCertificateFindParent(certificate
))
4009 return errSecIssuerNotFound
;
4012 certificate
= certificate
->_parent
;
4017 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4018 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4019 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4020 if (gnType
== GNT_IPAddress
) {
4021 CFStringRef string
= copyIPAddressContentDescription(
4022 kCFAllocatorDefault
, generalName
);
4024 CFArrayAppendValue(ipAddresses
, string
);
4027 return errSecInvalidCertificate
;
4030 return errSecSuccess
;
4033 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4034 /* These can only exist in the subject alt name. */
4035 if (!certificate
->_subjectAltName
)
4038 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4039 0, &kCFTypeArrayCallBacks
);
4040 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4041 ipAddresses
, appendIPAddressesFromGeneralNames
);
4042 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4043 CFRelease(ipAddresses
);
4049 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4050 const DERItem
*generalName
) {
4051 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4052 if (gnType
== GNT_DNSName
) {
4053 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4054 generalName
->data
, generalName
->length
,
4055 kCFStringEncodingUTF8
, FALSE
);
4057 CFArrayAppendValue(dnsNames
, string
);
4060 return errSecInvalidCertificate
;
4063 return errSecSuccess
;
4066 /* Return true if the passed in string matches the
4067 Preferred name syntax from sections 2.3.1. in RFC 1035.
4068 With the added check that we disallow empty dns names.
4069 Also in order to support wildcard DNSNames we allow for the '*'
4070 character anywhere in a dns component where we currently allow
4073 <domain> ::= <subdomain> | " "
4075 <subdomain> ::= <label> | <subdomain> "." <label>
4077 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4079 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4081 <let-dig-hyp> ::= <let-dig> | "-"
4083 <let-dig> ::= <letter> | <digit>
4085 <letter> ::= any one of the 52 alphabetic characters A through Z in
4086 upper case and a through z in lower case
4088 <digit> ::= any one of the ten digits 0 through 9
4090 static bool isDNSName(CFStringRef string
) {
4091 CFStringInlineBuffer buf
;
4092 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4093 /* From RFC 1035 2.3.4. Size limits:
4094 labels 63 octets or less
4095 names 255 octets or less */
4096 require_quiet(length
<= 255, notDNS
);
4097 CFRange range
= { 0, length
};
4098 CFStringInitInlineBuffer(string
, &buf
, range
);
4102 kDNSStateAfterAlpha
,
4103 kDNSStateAfterDigit
,
4105 } state
= kDNSStateInital
;
4107 for (ix
= 0; ix
< length
; ++ix
) {
4108 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4111 require_quiet(labelLength
<= 64 &&
4112 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4114 state
= kDNSStateAfterDot
;
4116 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4118 state
= kDNSStateAfterAlpha
;
4119 } else if ('0' <= ch
&& ch
<= '9') {
4121 /* The requirement for labels to start with a letter was
4122 dropped so we don't check this anymore. */
4123 require_quiet(state
== kDNSStateAfterAlpha
||
4124 state
== kDNSStateAfterDigit
||
4125 state
== kDNSStateAfterDash
, notDNS
);
4127 state
= kDNSStateAfterDigit
;
4128 } else if (ch
== '-') {
4129 require_quiet(state
== kDNSStateAfterAlpha
||
4130 state
== kDNSStateAfterDigit
||
4131 state
== kDNSStateAfterDash
, notDNS
);
4132 state
= kDNSStateAfterDash
;
4138 /* We don't allow a dns name to end in a dot or dash. */
4139 require_quiet(labelLength
<= 63 &&
4140 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4148 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4149 const DERItem
*value
, CFIndex rdnIX
) {
4150 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4151 if (DEROidCompare(type
, &oidCommonName
)) {
4152 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4155 if (isDNSName(string
)) {
4156 /* We found a common name that is formatted like a valid
4158 CFArrayAppendValue(dnsNames
, string
);
4162 return errSecInvalidCertificate
;
4165 return errSecSuccess
;
4168 /* Not everything returned by this function is going to be a proper DNS name,
4169 we also return the certificates common name entries from the subject,
4170 assuming they look like dns names as specified in RFC 1035. */
4171 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4172 /* These can exist in the subject alt name or in the subject. */
4173 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4174 0, &kCFTypeArrayCallBacks
);
4175 OSStatus status
= errSecSuccess
;
4176 if (certificate
->_subjectAltName
) {
4177 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4178 dnsNames
, appendDNSNamesFromGeneralNames
);
4180 /* RFC 2818 section 3.1. Server Identity
4182 If a subjectAltName extension of type dNSName is present, that MUST
4183 be used as the identity. Otherwise, the (most specific) Common Name
4184 field in the Subject field of the certificate MUST be used. Although
4185 the use of the Common Name is existing practice, it is deprecated and
4186 Certification Authorities are encouraged to use the dNSName instead.
4189 This implies that if we found 1 or more DNSNames in the
4190 subjectAltName, we should not use the Common Name of the subject as
4193 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4194 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4195 appendDNSNamesFromX501Name
);
4197 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4198 CFRelease(dnsNames
);
4204 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4205 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4206 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4207 if (gnType
== GNT_RFC822Name
) {
4208 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4209 generalName
->data
, generalName
->length
,
4210 kCFStringEncodingASCII
, FALSE
);
4212 CFArrayAppendValue(dnsNames
, string
);
4215 return errSecInvalidCertificate
;
4218 return errSecSuccess
;
4221 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4222 const DERItem
*value
, CFIndex rdnIX
) {
4223 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4224 if (DEROidCompare(type
, &oidEmailAddress
)) {
4225 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4228 CFArrayAppendValue(dnsNames
, string
);
4231 return errSecInvalidCertificate
;
4234 return errSecSuccess
;
4237 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4238 /* These can exist in the subject alt name or in the subject. */
4239 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4240 0, &kCFTypeArrayCallBacks
);
4241 OSStatus status
= errSecSuccess
;
4242 if (certificate
->_subjectAltName
) {
4243 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4244 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4247 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4248 appendRFC822NamesFromX501Name
);
4250 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4251 CFRelease(rfc822Names
);
4257 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4258 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4259 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4260 if (DEROidCompare(type
, &oidCommonName
)) {
4261 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4264 CFArrayAppendValue(commonNames
, string
);
4267 return errSecInvalidCertificate
;
4270 return errSecSuccess
;
4273 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4274 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4275 0, &kCFTypeArrayCallBacks
);
4277 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4278 appendCommonNamesFromX501Name
);
4279 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4280 CFRelease(commonNames
);
4286 static OSStatus
appendOrganizationFromX501Name(void *context
,
4287 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4288 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4289 if (DEROidCompare(type
, &oidOrganizationName
)) {
4290 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4293 CFArrayAppendValue(organization
, string
);
4296 return errSecInvalidCertificate
;
4299 return errSecSuccess
;
4302 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4303 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4304 0, &kCFTypeArrayCallBacks
);
4306 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4307 appendOrganizationFromX501Name
);
4308 if (status
|| CFArrayGetCount(organization
) == 0) {
4309 CFRelease(organization
);
4310 organization
= NULL
;
4312 return organization
;
4315 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4316 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4317 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4318 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4319 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4322 CFArrayAppendValue(organizationalUnit
, string
);
4325 return errSecInvalidCertificate
;
4328 return errSecSuccess
;
4331 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4332 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4333 0, &kCFTypeArrayCallBacks
);
4335 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4336 appendOrganizationalUnitFromX501Name
);
4337 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4338 CFRelease(organizationalUnit
);
4339 organizationalUnit
= NULL
;
4341 return organizationalUnit
;
4344 const SecCEBasicConstraints
*
4345 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4346 if (certificate
->_basicConstraints
.present
)
4347 return &certificate
->_basicConstraints
;
4352 const SecCEPolicyConstraints
*
4353 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4354 if (certificate
->_policyConstraints
.present
)
4355 return &certificate
->_policyConstraints
;
4361 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4362 return certificate
->_policyMappings
;
4365 const SecCECertificatePolicies
*
4366 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4367 if (certificate
->_certificatePolicies
.present
)
4368 return &certificate
->_certificatePolicies
;
4374 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4375 return certificate
->_inhibitAnyPolicySkipCerts
;
4378 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4379 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4380 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4381 if (gnType
== GNT_OtherName
) {
4383 DERReturn drtn
= DERParseSequenceContent(generalName
,
4384 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4386 require_noerr_quiet(drtn
, badDER
);
4387 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4389 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4390 &on
.value
, true), badDER
);
4391 CFArrayAppendValue(ntPrincipalNames
, string
);
4395 return errSecSuccess
;
4398 return errSecInvalidCertificate
;
4402 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
4403 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4404 0, &kCFTypeArrayCallBacks
);
4405 OSStatus status
= errSecSuccess
;
4406 if (certificate
->_subjectAltName
) {
4407 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4408 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4410 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4411 CFRelease(ntPrincipalNames
);
4412 ntPrincipalNames
= NULL
;
4414 return ntPrincipalNames
;
4417 static OSStatus
appendToRFC2253String(void *context
,
4418 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4419 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4423 ST stateOrProvinceName
4425 OU organizationalUnitName
4427 STREET streetAddress
4431 /* Prepend a + if this is not the first RDN in an RDN set.
4432 Otherwise prepend a , if this is not the first RDN. */
4434 CFStringAppend(string
, CFSTR("+"));
4435 else if (CFStringGetLength(string
)) {
4436 CFStringAppend(string
, CFSTR(","));
4439 CFStringRef label
, oid
= NULL
;
4440 /* @@@ Consider changing this to a dictionary lookup keyed by the
4441 decimal representation. */
4442 if (DEROidCompare(type
, &oidCommonName
)) {
4443 label
= CFSTR("CN");
4444 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4446 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4447 label
= CFSTR("ST");
4448 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4450 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4451 label
= CFSTR("OU");
4452 } else if (DEROidCompare(type
, &oidCountryName
)) {
4455 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4456 label
= CFSTR("STREET");
4457 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4458 label
= CFSTR("DC");
4459 } else if (DEROidCompare(type
, &oidUserID
)) {
4460 label
= CFSTR("UID");
4463 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4466 CFStringAppend(string
, label
);
4467 CFStringAppend(string
, CFSTR("="));
4468 CFStringRef raw
= NULL
;
4470 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4473 /* Append raw to string while escaping:
4474 a space or "#" character occurring at the beginning of the string
4475 a space character occurring at the end of the string
4476 one of the characters ",", "+", """, "\", "<", ">" or ";"
4478 CFStringInlineBuffer buffer
;
4479 CFIndex ix
, length
= CFStringGetLength(raw
);
4480 CFRange range
= { 0, length
};
4481 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4482 for (ix
= 0; ix
< length
; ++ix
) {
4483 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4485 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4486 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4487 ch
== '<' || ch
== '>' || ch
== ';' ||
4488 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4489 (ch
== '#' && ix
== 0)) {
4490 UniChar chars
[] = { '\\', ch
};
4491 CFStringAppendCharacters(string
, chars
, 2);
4493 CFStringAppendCharacters(string
, &ch
, 1);
4498 /* Append the value in hex. */
4499 CFStringAppend(string
, CFSTR("#"));
4501 for (ix
= 0; ix
< value
->length
; ++ix
)
4502 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4507 return errSecSuccess
;
4510 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
4511 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4512 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4513 if (status
|| CFStringGetLength(string
) == 0) {
4520 static OSStatus
appendToCompanyNameString(void *context
,
4521 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4522 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4523 if (CFStringGetLength(string
) != 0)
4524 return errSecSuccess
;
4526 if (!DEROidCompare(type
, &oidOrganizationName
))
4527 return errSecSuccess
;
4530 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4532 return errSecSuccess
;
4533 CFStringAppend(string
, raw
);
4536 return errSecSuccess
;
4539 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
4540 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4541 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4542 appendToCompanyNameString
);
4543 if (status
|| CFStringGetLength(string
) == 0) {
4550 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4551 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4552 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4553 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4555 CFDataSetLength(sequence
, sequence_length
);
4556 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4557 *sequence_ptr
++ = 0x30; /* ASN1_CONSTR_SEQUENCE */
4558 require_noerr_quiet(DEREncodeLength(content
->length
,
4559 sequence_ptr
, &seq_len_length
), out
);
4560 sequence_ptr
+= seq_len_length
;
4561 memcpy(sequence_ptr
, content
->data
, content
->length
);
4564 CFReleaseSafe(sequence
);
4568 CFDataRef
SecCertificateCopyIssuerSequence(
4569 SecCertificateRef certificate
) {
4570 return SecDERItemCopySequence(&certificate
->_issuer
);
4573 CFDataRef
SecCertificateCopySubjectSequence(
4574 SecCertificateRef certificate
) {
4575 return SecDERItemCopySequence(&certificate
->_subject
);
4578 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4579 SecCertificateRef certificate
) {
4580 return &certificate
->_algId
;
4583 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
4584 return &certificate
->_pubKeyDER
;
4587 SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
) {
4588 const DERAlgorithmId
*algId
=
4589 SecCertificateGetPublicKeyAlgorithm(certificate
);
4590 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4591 const DERItem
*params
= NULL
;
4592 if (algId
->params
.length
!= 0) {
4593 params
= &algId
->params
;
4595 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
4596 SecAsn1Item params1
= {
4597 .Data
= params
? params
->data
: NULL
,
4598 .Length
= params
? params
->length
: 0
4600 SecAsn1Item keyData1
= {
4601 .Data
= keyData
? keyData
->data
: NULL
,
4602 .Length
= keyData
? keyData
->length
: 0
4604 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
4608 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
4609 if (!certificate
->_sha1Digest
) {
4610 certificate
->_sha1Digest
=
4611 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4612 certificate
->_der
.data
, certificate
->_der
.length
);
4615 return certificate
->_sha1Digest
;
4618 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
4619 CFDataRef digest
= NULL
;
4620 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
4622 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4623 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4629 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
4630 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4631 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4634 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
4635 if (!certificate
->_authorityKeyID
&&
4636 certificate
->_authorityKeyIdentifier
.length
) {
4637 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4638 certificate
->_authorityKeyIdentifier
.data
,
4639 certificate
->_authorityKeyIdentifier
.length
);
4642 return certificate
->_authorityKeyID
;
4645 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
4646 if (!certificate
->_subjectKeyID
&&
4647 certificate
->_subjectKeyIdentifier
.length
) {
4648 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4649 certificate
->_subjectKeyIdentifier
.data
,
4650 certificate
->_subjectKeyIdentifier
.length
);
4653 return certificate
->_subjectKeyID
;
4656 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
4657 return certificate
->_crlDistributionPoints
;
4660 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
4661 return certificate
->_ocspResponders
;
4664 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
4665 return certificate
->_caIssuers
;
4668 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
4669 return certificate
->_subjectAltName
&&
4670 certificate
->_subjectAltName
->critical
;
4673 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
4674 /* Since the _subject field is the content of the subject and not the
4675 whole thing, we can simply check for a 0 length subject here. */
4676 return certificate
->_subject
.length
!= 0;
4679 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
4680 return certificate
->_foundUnknownCriticalExtension
;
4683 /* Private API functions. */
4684 void SecCertificateShow(SecCertificateRef certificate
) {
4686 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4687 fprintf(stderr
, "\n");
4691 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
4692 SecCertificateRef certificate
) {
4693 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4694 CFNumberRef certificateType
, certificateEncoding
;
4695 CFStringRef label
, alias
;
4696 CFDataRef skid
, pubKeyDigest
, certData
;
4697 CFDictionaryRef dict
= NULL
;
4701 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4702 SInt32 ctv
= certificate
->_version
+ 1;
4703 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4704 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4705 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4706 certData
= SecCertificateCopyData(certificate
);
4707 skid
= SecCertificateGetSubjectKeyID(certificate
);
4708 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4709 certificate
->_pubKeyDER
.length
);
4711 /* We still need to figure out how to deal with multi valued attributes. */
4712 alias
= SecCertificateCopyRFC822Names(certificate
);
4713 label
= SecCertificateCopySubjectSummary(certificate
);
4719 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4720 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4721 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4723 DICT_ADDPAIR(kSecAttrLabel
, label
);
4725 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4726 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4727 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4728 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4730 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4731 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4732 DICT_ADDPAIR(kSecValueData
, certData
);
4733 dict
= DICT_CREATE(allocator
);
4735 CFReleaseSafe(label
);
4736 CFReleaseSafe(pubKeyDigest
);
4737 CFReleaseSafe(certData
);
4738 CFReleaseSafe(certificateEncoding
);
4739 CFReleaseSafe(certificateType
);
4744 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
4745 CFDictionaryRef refAttributes
) {
4746 /* @@@ Support having an allocator in refAttributes. */
4747 CFAllocatorRef allocator
= NULL
;
4748 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4749 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
4753 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
4754 bool result
= false;
4755 SecKeyRef publicKey
;
4756 require(publicKey
= SecCertificateCopyPublicKey(certificate
), out
);
4757 CFDataRef normalizedIssuer
=
4758 SecCertificateGetNormalizedIssuerContent(certificate
);
4759 CFDataRef normalizedSubject
=
4760 SecCertificateGetNormalizedSubjectContent(certificate
);
4761 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4762 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4764 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
4765 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
4766 if (authorityKeyID
) {
4767 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4770 if (SecCertificateVersion(certificate
) >= 3) {
4771 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
4772 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4773 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
4778 CFReleaseSafe(publicKey
);
4782 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
4783 return certificate
->_keyUsage
;
4786 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
4788 CFMutableArrayRef extended_key_usage_oids
=
4789 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4790 require_quiet(extended_key_usage_oids
, out
);
4792 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4793 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4794 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4795 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4798 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4799 require_noerr_quiet(drtn
, out
);
4800 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4801 DERDecodedInfo currDecoded
;
4803 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4804 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4805 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4806 currDecoded
.content
.data
, currDecoded
.content
.length
);
4808 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4812 require_quiet(drtn
== DR_EndOfSequence
, out
);
4813 return extended_key_usage_oids
;
4817 CFReleaseSafe(extended_key_usage_oids
);
4821 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
4822 if (der
.length
> 1) {
4823 DERDecodedInfo decoded
;
4824 DERDecodeItem(&der
, &decoded
);
4825 switch (decoded
.tag
) {
4828 return decoded
.content
.length
== 0 && expected
== NULL
;
4832 case ASN1_UTF8_STRING
: {
4833 if (isString(expected
)) {
4834 CFStringRef expectedString
= (CFStringRef
) expected
;
4835 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
4837 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
4838 CFReleaseNull(itemString
);
4844 case ASN1_OCTET_STRING
: {
4845 if (isData(expected
)) {
4846 CFDataRef expectedData
= (CFDataRef
) expected
;
4847 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
4849 bool result
= CFEqual(expectedData
, itemData
);
4850 CFReleaseNull(itemData
);
4856 case ASN1_INTEGER
: {
4857 SInt32 expected_value
= 0;
4858 if (isString(expected
))
4860 CFStringRef aStr
= (CFStringRef
)expected
;
4861 expected_value
= CFStringGetIntValue(aStr
);
4863 else if (isNumber(expected
))
4865 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
4868 uint32_t num_value
= 0;
4869 if (!DERParseInteger(&decoded
.content
, &num_value
))
4871 return ((uint32_t)expected_value
== num_value
);
4884 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
4887 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
4888 size_t oid_len
= CFDataGetLength(oid
);
4890 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4891 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4892 if (extn
->extnID
.length
== oid_len
4893 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
4895 return matches_expected(extn
->extnValue
, expectedValue
);
4901 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
4903 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
4906 struct search_context
{
4908 SecCertificateRef certificate
;
4911 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
4913 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
4914 bool result
= false;
4916 if ( CFStringGetLength(string
) > 0
4917 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
4920 *value
= CFStringGetIntValue(string
);
4924 CFReleaseNull(nonDecimalDigit
);
4929 static CFDataRef
CreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
4931 CFMutableDataRef currentResult
= NULL
;
4932 CFDataRef encodedResult
= NULL
;
4934 CFArrayRef parts
= NULL
;
4940 parts
= CFStringCreateArrayBySeparatingStrings(NULL
, string
, CFSTR("."));
4945 count
= CFArrayGetCount(parts
);
4949 // assume no more than 5 bytes needed to represent any part of the oid,
4950 // since we limit parts to 32-bit values,
4951 // but the first two parts only need 1 byte
4952 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
4958 part
= CFArrayGetValueAtIndex(parts
, 0);
4960 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
4967 part
= CFArrayGetValueAtIndex(parts
, 1);
4969 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
4975 CFDataAppendBytes(currentResult
, &firstByte
, 1);
4977 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
4978 uint8_t b
[5] = {0, 0, 0, 0, 0};
4980 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
4981 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
4982 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
4983 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
4985 // Skip the unused extension bytes.
4986 size_t skipBytes
= 0;
4987 while (b
[skipBytes
] == 0x80)
4990 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
4993 encodedResult
= currentResult
;
4994 currentResult
= NULL
;
4997 CFReleaseNull(parts
);
4998 CFReleaseNull(currentResult
);
5000 return encodedResult
;
5003 static void check_for_marker(const void *key
, const void *value
, void *context
)
5005 struct search_context
* search_ctx
= (struct search_context
*) context
;
5006 CFStringRef key_string
= (CFStringRef
) key
;
5007 CFTypeRef value_ref
= (CFTypeRef
) value
;
5009 // If we could have short circuted the iteration
5010 // we would have, but the best we can do
5011 // is not waste time comparing once a match
5013 if (search_ctx
->found
)
5016 if (CFGetTypeID(key_string
) != CFStringGetTypeID())
5019 CFDataRef key_data
= CreateOidDataFromString(NULL
, key_string
);
5021 if (NULL
== key_data
)
5024 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
5025 search_ctx
->found
= true;
5027 CFReleaseNull(key_data
);
5031 // CFType Ref is either:
5033 // CFData - OID to match with no data permitted
5034 // CFDictionary - OID -> Value table for expected values Single Object or Array
5035 // CFArray - Array of the above.
5037 // This returns true if any of the requirements are met.
5038 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
5040 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
5041 CFIndex ix
, length
= CFArrayGetCount(oids
);
5042 for (ix
= 0; ix
< length
; ix
++)
5043 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
5045 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
5046 struct search_context context
= { .found
= false, .certificate
= certificate
};
5047 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
5048 return context
.found
;
5049 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
5050 return cert_contains_marker_extension(certificate
, oids
);
5055 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
5056 CFDataRef pem_certificate
)
5058 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
5059 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
5060 uint8_t *base64_data
= NULL
;
5061 SecCertificateRef cert
= NULL
;
5062 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
5063 //const size_t length = CFDataGetLength(pem_certificate);
5064 char *begin
= strstr((const char *)data
, begin_cert
);
5065 char *end
= strstr((const char *)data
, end_cert
);
5068 begin
+= sizeof(begin_cert
) - 1;
5069 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
5070 if (base64_length
) {
5071 require_quiet(base64_data
= calloc(1, base64_length
), out
);
5072 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
5073 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
5082 // -- MARK -- XPC encoding/decoding
5085 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5087 return true; // NOOP
5089 size_t length
= SecCertificateGetLength(certificate
);
5090 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
5091 if (!length
|| !bytes
)
5092 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
5094 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
5098 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
5099 SecCertificateRef certificate
= NULL
;
5101 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
5103 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
5106 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
5111 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
5112 xpc_object_t xpc_certificates
;
5113 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
5114 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
5115 CFIndex ix
, count
= CFArrayGetCount(certificates
);
5116 for (ix
= 0; ix
< count
; ++ix
) {
5117 if (!SecCertificateAppendToXPCArray((SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, ix
), xpc_certificates
, error
)) {
5118 xpc_release(xpc_certificates
);
5124 return xpc_certificates
;
5127 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5128 CFMutableArrayRef certificates
= NULL
;
5129 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
5130 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
5131 size_t count
= xpc_array_get_count(xpc_certificates
);
5132 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
5133 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
5136 for (ix
= 0; ix
< count
; ++ix
) {
5137 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
5139 CFRelease(certificates
);
5142 CFArraySetValueAtIndex(certificates
, ix
, cert
);
5147 return certificates
;
5150 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
5153 static CFArrayRef
CopyEscrowCertificates(CFErrorRef
* error
)
5155 __block CFArrayRef result
= NULL
;
5157 do_if_registered(ota_CopyEscrowCertificates
, error
);
5159 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
, NULL
,
5160 ^bool(xpc_object_t response
, CFErrorRef
*error
)
5162 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
5164 if (response
&& (NULL
!= xpc_array
))
5166 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
5170 return SecError(errSecInternal
, error
, CFSTR("Did not get the Escrow certificates"));
5172 return result
!= NULL
;
5177 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
5179 CFArrayRef result
= NULL
;
5181 CFDataRef certData
= NULL
;
5184 // The request is for the base line certificates.
5185 // Use the hard coded data to generate the return array
5186 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
)
5188 // Get the hard coded set of roots
5189 numRoots
= kNumberOfBaseLineEscrowRoots
;
5190 SecCertificateRef baseLineCerts
[numRoots
];
5191 struct RootRecord
* pRootRecord
= NULL
;
5193 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5195 pRootRecord
= kBaseLineEscrowRoots
[iCnt
];
5196 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
)
5198 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
5199 if (NULL
!= certData
)
5201 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5202 CFRelease(certData
);
5206 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5207 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5209 if (NULL
!= baseLineCerts
[iCnt
])
5211 CFRelease(baseLineCerts
[iCnt
]);
5215 // The request is for the current certificates.
5216 else if (kSecCertificateProductionEscrowRoot
== escrowRootType
)
5218 CFErrorRef error
= NULL
;
5219 CFArrayRef cert_datas
= CopyEscrowCertificates(&error
);
5220 if (NULL
!= error
|| NULL
== cert_datas
)
5227 if (NULL
!= cert_datas
)
5229 CFRelease(cert_datas
);
5234 numRoots
= (int)(CFArrayGetCount(cert_datas
));
5236 SecCertificateRef assetCerts
[numRoots
];
5237 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5239 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
5240 if (NULL
!= certData
)
5242 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5243 assetCerts
[iCnt
] = aCertRef
;
5247 assetCerts
[iCnt
] = NULL
;
5253 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5254 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5256 if (NULL
!= assetCerts
[iCnt
])
5258 CFRelease(assetCerts
[iCnt
]);
5262 CFReleaseSafe(cert_datas
);