2 * Copyright (c) 2006-2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 /* Allows us to build genanchors against the BaseSDK. */
30 #undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31 #undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
34 #include <Security/SecCertificateInternal.h>
35 #include <utilities/SecIOFormat.h>
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFBundle.h>
40 #include <CoreFoundation/CFDictionary.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFCalendar.h>
43 #include <CoreFoundation/CFTimeZone.h>
44 #include <CoreFoundation/CFXPCBridge.h>
46 #include <AssertMacros.h>
47 #include <libDER/DER_CertCrl.h>
48 #include <libDER/DER_Encode.h>
49 #include <libDER/DER_Keys.h>
50 #include <libDER/asn1Types.h>
51 #include <libDER/oidsPriv.h>
52 #include "SecBasePriv.h"
53 #include "SecRSAKey.h"
54 #include "SecFramework.h"
56 #include "SecItemPriv.h"
57 #include "SecSignatureVerificationSupport.h"
59 #include <utilities/debugging.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <utilities/SecCFError.h>
62 #include <utilities/SecSCTUtils.h>
63 #include <utilities/array_size.h>
65 #include <libkern/OSByteOrder.h>
67 #include <Security/SecInternal.h>
68 #include <Security/SecFrameworkStrings.h>
69 #include "SecBase64.h"
70 #include "AppleBaselineEscrowCertificates.h"
71 #include <ipc/securityd_client.h>
72 #include <Security/SecKeyInternal.h>
74 /* The minimum key sizes necessary to not be considered "weak" */
75 #define MIN_RSA_KEY_SIZE 128 // 1024-bit
76 #define MIN_EC_KEY_SIZE 20 // 160-bit
78 typedef struct SecCertificateExtension
{
82 } SecCertificateExtension
;
85 kSecSelfSignedUnknown
= 0,
90 struct __SecCertificate
{
93 DERItem _der
; /* Entire certificate in DER form. */
94 DERItem _tbs
; /* To Be Signed cert DER bytes. */
95 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
96 DERItem _signature
; /* The content of the sig bit string. */
99 DERItem _serialNum
; /* Integer. */
100 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
101 DERItem _issuer
; /* Sequence of RDN. */
102 CFAbsoluteTime _notBefore
;
103 CFAbsoluteTime _notAfter
;
104 DERItem _subject
; /* Sequence of RDN. */
105 DERItem _subjectPublicKeyInfo
; /* SPKI */
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 */
111 bool _foundUnknownCriticalExtension
;
113 /* Well known certificate extensions. */
114 SecCEBasicConstraints _basicConstraints
;
115 SecCEPolicyConstraints _policyConstraints
;
116 SecCEPolicyMappings _policyMappings
;
117 SecCECertificatePolicies _certificatePolicies
;
118 SecCEInhibitAnyPolicy _inhibitAnyPolicySkipCerts
;
120 /* If KeyUsage extension is not present this is 0, otherwise it's
121 the value of the extension. */
122 SecKeyUsage _keyUsage
;
124 /* OCTETS of SubjectKeyIdentifier extensions KeyIdentifier.
125 Length = 0 if not present. */
126 DERItem _subjectKeyIdentifier
;
128 /* OCTETS of AuthorityKeyIdentifier extensions KeyIdentifier.
129 Length = 0 if not present. */
130 DERItem _authorityKeyIdentifier
;
131 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
132 _authorityKeyIdentifierSerialNumber have non zero length if present.
133 Both are either present or absent together. */
134 DERItem _authorityKeyIdentifierIssuer
;
135 DERItem _authorityKeyIdentifierSerialNumber
;
137 /* Subject alt name extension, if present. Not malloced, it's just a
138 pointer to an element in the _extensions array. */
139 const SecCertificateExtension
*_subjectAltName
;
141 /* Parsed extension values. */
143 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
144 CFMutableArrayRef _crlDistributionPoints
;
146 /* Array of CFURLRefs containing the URI values of accessLocations of each
147 id-ad-ocsp AccessDescription in the Authority Information Access
149 CFMutableArrayRef _ocspResponders
;
151 /* Array of CFURLRefs containing the URI values of accessLocations of each
152 id-ad-caIssuers AccessDescription in the Authority Information Access
154 CFMutableArrayRef _caIssuers
;
156 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
158 CFArrayRef _permittedSubtrees
;
160 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
162 CFArrayRef _excludedSubtrees
;
164 CFMutableArrayRef _embeddedSCTs
;
166 /* All other (non known) extensions. The _extensions array is malloced. */
167 CFIndex _extensionCount
;
168 SecCertificateExtension
*_extensions
;
170 /* Optional cached fields. */
173 CFArrayRef _properties
;
174 CFDataRef _serialNumber
;
175 CFDataRef _normalizedIssuer
;
176 CFDataRef _normalizedSubject
;
177 CFDataRef _authorityKeyID
;
178 CFDataRef _subjectKeyID
;
180 CFDataRef _sha1Digest
;
181 CFTypeRef _keychain_item
;
182 uint8_t _isSelfSigned
;
186 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
188 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
189 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
190 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
192 /* Public Constants for property list keys. */
193 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
194 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
195 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
196 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
198 /* Public Constants for property list values. */
199 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
200 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
201 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
202 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
203 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
204 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
205 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
206 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
207 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
209 /* Extension parsing routine. */
210 typedef void (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
211 const SecCertificateExtension
*extn
);
213 /* Mapping from extension OIDs (as a DERItem *) to
214 SecCertificateExtensionParser extension parsing routines. */
215 static CFDictionaryRef sExtensionParsers
;
217 /* Forward declarations of static functions. */
218 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
);
219 static void SecCertificateDestroy(CFTypeRef cf
);
220 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
221 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
223 /* Static functions. */
224 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
) {
225 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
226 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
227 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
228 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
229 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
230 CFReleaseSafe(issuer
);
231 CFReleaseSafe(subject
);
235 static void SecCertificateDestroy(CFTypeRef cf
) {
236 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
237 if (certificate
->_certificatePolicies
.policies
)
238 free(certificate
->_certificatePolicies
.policies
);
239 if (certificate
->_policyMappings
.mappings
) {
240 free(certificate
->_policyMappings
.mappings
);
242 CFReleaseSafe(certificate
->_crlDistributionPoints
);
243 CFReleaseSafe(certificate
->_ocspResponders
);
244 CFReleaseSafe(certificate
->_caIssuers
);
245 if (certificate
->_extensions
) {
246 free(certificate
->_extensions
);
248 CFReleaseSafe(certificate
->_pubKey
);
249 CFReleaseSafe(certificate
->_der_data
);
250 CFReleaseSafe(certificate
->_properties
);
251 CFReleaseSafe(certificate
->_serialNumber
);
252 CFReleaseSafe(certificate
->_normalizedIssuer
);
253 CFReleaseSafe(certificate
->_normalizedSubject
);
254 CFReleaseSafe(certificate
->_authorityKeyID
);
255 CFReleaseSafe(certificate
->_subjectKeyID
);
256 CFReleaseSafe(certificate
->_sha1Digest
);
257 CFReleaseSafe(certificate
->_keychain_item
);
258 CFReleaseSafe(certificate
->_permittedSubtrees
);
259 CFReleaseSafe(certificate
->_excludedSubtrees
);
262 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
263 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
264 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
267 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
269 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
272 /* Hash of the certificate is der length + signature length + last 4 bytes
274 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
275 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
276 size_t der_length
= certificate
->_der
.length
;
277 size_t sig_length
= certificate
->_signature
.length
;
278 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
279 CFHashCode hashCode
= 0;
280 for (; ix
< sig_length
; ++ix
)
281 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
283 return (hashCode
+ der_length
+ sig_length
);
288 /************************************************************************/
289 /************************* General Name Parsing *************************/
290 /************************************************************************/
292 GeneralName ::= CHOICE {
293 otherName [0] OtherName,
294 rfc822Name [1] IA5String,
295 dNSName [2] IA5String,
296 x400Address [3] ORAddress,
297 directoryName [4] Name,
298 ediPartyName [5] EDIPartyName,
299 uniformResourceIdentifier [6] IA5String,
300 iPAddress [7] OCTET STRING,
301 registeredID [8] OBJECT IDENTIFIER}
303 OtherName ::= SEQUENCE {
304 type-id OBJECT IDENTIFIER,
305 value [0] EXPLICIT ANY DEFINED BY type-id }
307 EDIPartyName ::= SEQUENCE {
308 nameAssigner [0] DirectoryString OPTIONAL,
309 partyName [1] DirectoryString }
311 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
312 const DERItem
*generalNameContent
,
313 void *context
, parseGeneralNameCallback callback
) {
315 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
316 return callback(context
, GNT_OtherName
, generalNameContent
);
317 case ASN1_CONTEXT_SPECIFIC
| 1:
318 return callback(context
, GNT_RFC822Name
, generalNameContent
);
319 case ASN1_CONTEXT_SPECIFIC
| 2:
320 return callback(context
, GNT_DNSName
, generalNameContent
);
321 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
322 return callback(context
, GNT_X400Address
, generalNameContent
);
323 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
324 return callback(context
, GNT_DirectoryName
, generalNameContent
);
325 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
326 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
327 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
329 /* Technically I don't think this is valid, but there are certs out
330 in the wild that use a constructed IA5String. In particular the
331 VeriSign Time Stamping Authority CA.cer does this. */
332 DERDecodedInfo uriContent
;
333 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
334 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
335 return callback(context
, GNT_URI
, &uriContent
.content
);
337 case ASN1_CONTEXT_SPECIFIC
| 6:
338 return callback(context
, GNT_URI
, generalNameContent
);
339 case ASN1_CONTEXT_SPECIFIC
| 7:
340 return callback(context
, GNT_IPAddress
, generalNameContent
);
341 case ASN1_CONTEXT_SPECIFIC
| 8:
342 return callback(context
, GNT_RegisteredID
, generalNameContent
);
347 return errSecInvalidCertificate
;
350 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
351 void *context
, parseGeneralNameCallback callback
) {
353 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
354 require_noerr_quiet(drtn
, badDER
);
355 DERDecodedInfo generalNameContent
;
356 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
358 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
359 generalNameContent
.tag
, &generalNameContent
.content
, context
,
364 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
365 return errSecSuccess
;
368 return errSecInvalidCertificate
;
371 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
372 parseGeneralNameCallback callback
) {
373 DERDecodedInfo generalNamesContent
;
374 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
375 require_noerr_quiet(drtn
, badDER
);
376 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
377 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
380 return errSecInvalidCertificate
;
386 GeneralName ::= CHOICE {
387 otherName [0] OtherName,
388 rfc822Name [1] IA5String,
389 dNSName [2] IA5String,
390 x400Address [3] ORAddress,
391 directoryName [4] Name,
392 ediPartyName [5] EDIPartyName,
393 uniformResourceIdentifier [6] IA5String,
394 iPAddress [7] OCTET STRING,
395 registeredID [8] OBJECT IDENTIFIER}
397 EDIPartyName ::= SEQUENCE {
398 nameAssigner [0] DirectoryString OPTIONAL,
399 partyName [1] DirectoryString }
401 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
402 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
404 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
405 generalName
->nameType
= GNT_OtherName
;
406 generalName
->berEncoded
= true;
407 generalName
->name
= *generalNameContent
;
409 case ASN1_CONTEXT_SPECIFIC
| 1:
411 generalName
->nameType
= GNT_RFC822Name
;
412 generalName
->berEncoded
= false;
413 generalName
->name
= *generalNameContent
;
415 case ASN1_CONTEXT_SPECIFIC
| 2:
417 generalName
->nameType
= GNT_DNSName
;
418 generalName
->berEncoded
= false;
419 generalName
->name
= *generalNameContent
;
421 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
422 generalName
->nameType
= GNT_X400Address
;
423 generalName
->berEncoded
= true;
424 generalName
->name
= *generalNameContent
;
426 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
427 generalName
->nameType
= GNT_DirectoryName
;
428 generalName
->berEncoded
= true;
429 generalName
->name
= *generalNameContent
;
431 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
432 generalName
->nameType
= GNT_EdiPartyName
;
433 generalName
->berEncoded
= true;
434 generalName
->name
= *generalNameContent
;
436 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
438 /* Technically I don't think this is valid, but there are certs out
439 in the wild that use a constructed IA5String. In particular the
440 VeriSign Time Stamping Authority CA.cer does this. */
441 DERDecodedInfo decoded
;
442 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
443 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
444 generalName
->nameType
= GNT_URI
;
445 generalName
->berEncoded
= false;
446 generalName
->name
= decoded
.content
;
449 case ASN1_CONTEXT_SPECIFIC
| 6:
450 generalName
->nameType
= GNT_URI
;
451 generalName
->berEncoded
= false;
452 generalName
->name
= *generalNameContent
;
454 case ASN1_CONTEXT_SPECIFIC
| 7:
455 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
456 8 octects, addr/mask for ipv6 it's 32. */
457 generalName
->nameType
= GNT_IPAddress
;
458 generalName
->berEncoded
= false;
459 generalName
->name
= *generalNameContent
;
461 case ASN1_CONTEXT_SPECIFIC
| 8:
462 /* name is the content of an OID. */
463 generalName
->nameType
= GNT_RegisteredID
;
464 generalName
->berEncoded
= false;
465 generalName
->name
= *generalNameContent
;
471 return errSecSuccess
;
473 return errSecInvalidCertificate
;
477 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
479 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
480 CFIndex
*count
, SecCEGeneralName
**name
) {
481 SecCEGeneralName
*generalNames
= NULL
;
483 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
484 require_noerr_quiet(drtn
, badDER
);
485 DERDecodedInfo generalNameContent
;
486 CFIndex generalNamesCount
= 0;
487 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
491 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
493 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
495 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
497 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
499 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
500 &generalNameContent
.content
, &generalNames
[ix
])) {
505 *count
= generalNamesCount
;
506 *name
= generalNames
;
507 return errSecSuccess
;
512 return errSecInvalidCertificate
;
515 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
516 CFIndex
*count
, SecCEGeneralName
**name
) {
517 DERDecodedInfo generalNamesContent
;
518 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
519 require_noerr_quiet(drtn
, badDER
);
520 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
522 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
523 return errSecSuccess
;
525 return errSecInvalidCertificate
;
529 /************************************************************************/
530 /************************** X.509 Name Parsing **************************/
531 /************************************************************************/
533 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
534 const DERItem
*value
, CFIndex rdnIX
);
536 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
537 parseX501NameCallback callback
) {
539 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
540 require_noerr_quiet(drtn
, badDER
);
541 DERDecodedInfo atvContent
;
543 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
544 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
545 DERAttributeTypeAndValue atv
;
546 drtn
= DERParseSequenceContent(&atvContent
.content
,
547 DERNumAttributeTypeAndValueItemSpecs
,
548 DERAttributeTypeAndValueItemSpecs
,
550 require_noerr_quiet(drtn
, badDER
);
551 require_quiet(atv
.type
.length
!= 0, badDER
);
552 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
556 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
558 return errSecSuccess
;
560 return errSecInvalidCertificate
;
563 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
564 parseX501NameCallback callback
) {
566 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
567 require_noerr_quiet(drtn
, badDER
);
568 DERDecodedInfo currDecoded
;
569 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
570 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
571 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
576 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
578 return errSecSuccess
;
581 return errSecInvalidCertificate
;
584 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
585 parseX501NameCallback callback
) {
586 DERDecodedInfo x501NameContent
;
587 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
588 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
589 return errSecInvalidCertificate
;
591 return parseX501NameContent(&x501NameContent
.content
, context
,
596 /************************************************************************/
597 /********************** Extension Parsing Routines **********************/
598 /************************************************************************/
600 static void SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
601 const SecCertificateExtension
*extn
) {
602 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
603 DERDecodedInfo keyIdentifier
;
604 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
605 require_noerr_quiet(drtn
, badDER
);
606 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
607 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
611 secwarning("Invalid SubjectKeyIdentifier Extension");
614 static void SecCEPKeyUsage(SecCertificateRef certificate
,
615 const SecCertificateExtension
*extn
) {
616 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
617 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
618 DERDecodedInfo bitStringContent
;
619 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
620 require_noerr_quiet(drtn
, badDER
);
621 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
622 DERSize len
= bitStringContent
.content
.length
- 1;
623 require_quiet(len
== 1 || len
== 2, badDER
);
624 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
625 require_quiet(numUnusedBits
< 8, badDER
);
626 /* Flip the bits in the bit string so the first bit in the lsb. */
627 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
628 uint_fast16_t value
= bitStringContent
.content
.data
[1];
631 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
637 for (ix
= 0; ix
< bits
; ++ix
) {
643 certificate
->_keyUsage
= keyUsage
;
646 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
649 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
650 const SecCertificateExtension
*extn
) {
651 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
654 static void SecCEPSubjectAltName(SecCertificateRef certificate
,
655 const SecCertificateExtension
*extn
) {
656 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
657 certificate
->_subjectAltName
= extn
;
660 static void SecCEPIssuerAltName(SecCertificateRef certificate
,
661 const SecCertificateExtension
*extn
) {
662 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
665 static void SecCEPBasicConstraints(SecCertificateRef certificate
,
666 const SecCertificateExtension
*extn
) {
667 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
668 DERBasicConstraints basicConstraints
;
669 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
670 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
671 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
672 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
673 &certificate
->_basicConstraints
.isCA
), badDER
);
674 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
675 require_noerr_quiet(DERParseInteger(
676 &basicConstraints
.pathLenConstraint
,
677 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
678 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
680 certificate
->_basicConstraints
.present
= true;
681 certificate
->_basicConstraints
.critical
= extn
->critical
;
684 certificate
->_basicConstraints
.present
= false;
685 secwarning("Invalid BasicConstraints Extension");
690 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
692 * NameConstraints ::= SEQUENCE {
693 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
694 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
696 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
698 * GeneralSubtree ::= SEQUENCE {
700 * minimum [0] BaseDistance DEFAULT 0,
701 * maximum [1] BaseDistance OPTIONAL }
703 * BaseDistance ::= INTEGER (0..MAX)
705 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
706 CFMutableArrayRef gs
= NULL
;
708 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
709 require_noerr_quiet(drtn
, badDER
);
710 DERDecodedInfo gsContent
;
711 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
712 &kCFTypeArrayCallBacks
),
714 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
715 DERGeneralSubtree derGS
;
716 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
717 drtn
= DERParseSequenceContent(&gsContent
.content
,
718 DERNumGeneralSubtreeItemSpecs
,
719 DERGeneralSubtreeItemSpecs
,
720 &derGS
, sizeof(derGS
));
721 require_noerr_quiet(drtn
, badDER
);
724 * Within this profile, the minimum and maximum fields are not used with
725 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
728 * Because minimum DEFAULT 0, absence equivalent to present and 0.
730 if (derGS
.minimum
.length
) {
732 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
734 require_quiet(minimum
== 0, badDER
);
736 require_quiet(derGS
.maximum
.length
== 0, badDER
);
737 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
739 CFDataRef generalName
= NULL
;
740 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
741 derGS
.generalName
.data
,
742 derGS
.generalName
.length
),
744 CFArrayAppendValue(gs
, generalName
);
745 CFReleaseNull(generalName
);
747 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
749 // since generalSubtrees is a pointer to an instance variable,
750 // make sure we release the existing array before assignment.
751 CFReleaseSafe(*generalSubtrees
);
752 *generalSubtrees
= gs
;
758 secdebug("cert","failed to parse GeneralSubtrees");
762 static void SecCEPNameConstraints(SecCertificateRef certificate
,
763 const SecCertificateExtension
*extn
) {
764 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
765 DERNameConstraints nc
;
767 drtn
= DERParseSequence(&extn
->extnValue
,
768 DERNumNameConstraintsItemSpecs
,
769 DERNameConstraintsItemSpecs
,
771 require_noerr_quiet(drtn
, badDER
);
772 if (nc
.permittedSubtrees
.length
) {
773 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
775 if (nc
.excludedSubtrees
.length
) {
776 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
781 secdebug("cert", "failed to parse Name Constraints extension");
784 static OSStatus
appendCRLDPFromGeneralNames(void *context
, SecCEGeneralNameType type
,
785 const DERItem
*value
) {
786 CFMutableArrayRef
*crlDPs
= (CFMutableArrayRef
*)context
;
787 if (type
== GNT_URI
) {
789 url
= CFURLCreateWithBytes(NULL
, value
->data
, value
->length
, kCFStringEncodingASCII
, NULL
);
792 *crlDPs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
794 CFArrayAppendValue(*crlDPs
, url
);
798 return errSecSuccess
;
802 id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
804 CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
806 DistributionPoint ::= SEQUENCE {
807 distributionPoint [0] DistributionPointName OPTIONAL,
808 reasons [1] ReasonFlags OPTIONAL,
809 cRLIssuer [2] GeneralNames OPTIONAL }
811 DistributionPointName ::= CHOICE {
812 fullName [0] GeneralNames,
813 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
815 static void SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
816 const SecCertificateExtension
*extn
) {
817 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
818 DERSequence crlDPSeq
;
820 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &crlDPSeq
);
821 require_noerr_quiet(drtn
, badDER
);
822 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
823 DERDecodedInfo dpContent
;
824 while ((drtn
= DERDecodeSeqNext(&crlDPSeq
, &dpContent
)) == DR_Success
) {
825 require_quiet(dpContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
826 DERDistributionPoint dp
;
827 drtn
= DERParseSequenceContent(&dpContent
.content
, DERNumDistributionPointItemSpecs
,
828 DERDistributionPointItemSpecs
, &dp
, sizeof(dp
));
829 require_noerr_quiet(drtn
, badDER
);
830 require_quiet(dp
.distributionPoint
.data
|| dp
.cRLIssuer
.data
, badDER
);
831 if (dp
.distributionPoint
.data
) {
832 DERDecodedInfo dpName
;
833 drtn
= DERDecodeItem(&dp
.distributionPoint
, &dpName
);
834 require_noerr_quiet(drtn
, badDER
);
835 switch (dpName
.tag
) {
836 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
837 drtn
= parseGeneralNamesContent(&dpName
.content
, &certificate
->_crlDistributionPoints
,
838 appendCRLDPFromGeneralNames
);
839 require_noerr_quiet(drtn
, badDER
);
841 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1:
842 /* RelativeDistinguishName. Nothing we can do with that. */
848 if (dp
.cRLIssuer
.data
) {
849 drtn
= SecCertificateParseGeneralNames(&dp
.cRLIssuer
, &certificate
->_crlDistributionPoints
,
850 appendCRLDPFromGeneralNames
);
851 require_noerr_quiet(drtn
, badDER
);
854 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
857 secdebug("cert", "failed to parse CRL Distribution Points extension");
861 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
863 PolicyInformation ::= SEQUENCE {
864 policyIdentifier CertPolicyId,
865 policyQualifiers SEQUENCE SIZE (1..MAX) OF
866 PolicyQualifierInfo OPTIONAL }
868 CertPolicyId ::= OBJECT IDENTIFIER
870 PolicyQualifierInfo ::= SEQUENCE {
871 policyQualifierId PolicyQualifierId,
872 qualifier ANY DEFINED BY policyQualifierId }
874 /* maximum number of policies of 8192 seems more than adequate */
875 #define MAX_CERTIFICATE_POLICIES 8192
876 static void SecCEPCertificatePolicies(SecCertificateRef certificate
,
877 const SecCertificateExtension
*extn
) {
878 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
881 SecCEPolicyInformation
*policies
= NULL
;
882 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
883 require_noerr_quiet(drtn
, badDER
);
884 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
885 DERDecodedInfo piContent
;
886 DERSize policy_count
= 0;
887 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
888 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
889 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
892 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
893 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
894 * (policy_count
> 0 ? policy_count
: 1));
895 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
896 require_noerr_quiet(drtn
, badDER
);
897 DERSize policy_ix
= 0;
898 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
899 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
900 DERPolicyInformation pi
;
901 drtn
= DERParseSequenceContent(&piContent
.content
,
902 DERNumPolicyInformationItemSpecs
,
903 DERPolicyInformationItemSpecs
,
905 require_noerr_quiet(drtn
, badDER
);
906 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
907 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
909 certificate
->_certificatePolicies
.present
= true;
910 certificate
->_certificatePolicies
.critical
= extn
->critical
;
911 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
912 certificate
->_certificatePolicies
.policies
= policies
;
917 certificate
->_certificatePolicies
.present
= false;
918 secwarning("Invalid CertificatePolicies Extension");
922 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
924 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
925 issuerDomainPolicy CertPolicyId,
926 subjectDomainPolicy CertPolicyId }
928 #define MAX_POLICY_MAPPINGS 8192
929 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
930 const SecCertificateExtension
*extn
) {
931 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
934 SecCEPolicyMapping
*mappings
= NULL
;
935 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
936 require_noerr_quiet(drtn
, badDER
);
937 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
938 DERDecodedInfo pmContent
;
939 DERSize mapping_count
= 0;
940 while ((mapping_count
< MAX_POLICY_MAPPINGS
) &&
941 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
942 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
945 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
946 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
947 * (mapping_count
> 0 ? mapping_count
: 1));
948 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
949 require_noerr_quiet(drtn
, badDER
);
950 DERSize mapping_ix
= 0;
951 while ((mapping_ix
< (mapping_count
> 0 ? mapping_count
: 1)) &&
952 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
953 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
955 drtn
= DERParseSequenceContent(&pmContent
.content
,
956 DERNumPolicyMappingItemSpecs
,
957 DERPolicyMappingItemSpecs
,
959 require_noerr_quiet(drtn
, badDER
);
960 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
961 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
963 certificate
->_policyMappings
.present
= true;
964 certificate
->_policyMappings
.critical
= extn
->critical
;
965 certificate
->_policyMappings
.numMappings
= mapping_count
;
966 certificate
->_policyMappings
.mappings
= mappings
;
972 certificate
->_policyMappings
.present
= false;
973 secwarning("Invalid CertificatePolicies Extension");
977 AuthorityKeyIdentifier ::= SEQUENCE {
978 keyIdentifier [0] KeyIdentifier OPTIONAL,
979 authorityCertIssuer [1] GeneralNames OPTIONAL,
980 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
981 -- authorityCertIssuer and authorityCertSerialNumber MUST both
982 -- be present or both be absent
984 KeyIdentifier ::= OCTET STRING
986 static void SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
987 const SecCertificateExtension
*extn
) {
988 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
989 DERAuthorityKeyIdentifier akid
;
991 drtn
= DERParseSequence(&extn
->extnValue
,
992 DERNumAuthorityKeyIdentifierItemSpecs
,
993 DERAuthorityKeyIdentifierItemSpecs
,
994 &akid
, sizeof(akid
));
995 require_noerr_quiet(drtn
, badDER
);
996 if (akid
.keyIdentifier
.length
) {
997 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
999 if (akid
.authorityCertIssuer
.length
||
1000 akid
.authorityCertSerialNumber
.length
) {
1001 require_quiet(akid
.authorityCertIssuer
.length
&&
1002 akid
.authorityCertSerialNumber
.length
, badDER
);
1003 /* Perhaps put in a subsection called Authority Certificate Issuer. */
1004 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
1005 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1010 secwarning("Invalid AuthorityKeyIdentifier Extension");
1013 static void SecCEPPolicyConstraints(SecCertificateRef certificate
,
1014 const SecCertificateExtension
*extn
) {
1015 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1016 DERPolicyConstraints pc
;
1018 drtn
= DERParseSequence(&extn
->extnValue
,
1019 DERNumPolicyConstraintsItemSpecs
,
1020 DERPolicyConstraintsItemSpecs
,
1022 require_noerr_quiet(drtn
, badDER
);
1023 if (pc
.requireExplicitPolicy
.length
) {
1024 require_noerr_quiet(DERParseInteger(
1025 &pc
.requireExplicitPolicy
,
1026 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1027 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1029 if (pc
.inhibitPolicyMapping
.length
) {
1030 require_noerr_quiet(DERParseInteger(
1031 &pc
.inhibitPolicyMapping
,
1032 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1033 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1036 certificate
->_policyConstraints
.present
= true;
1037 certificate
->_policyConstraints
.critical
= extn
->critical
;
1041 certificate
->_policyConstraints
.present
= false;
1042 secwarning("Invalid PolicyConstraints Extension");
1045 static void SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1046 const SecCertificateExtension
*extn
) {
1047 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1051 InhibitAnyPolicy ::= SkipCerts
1053 SkipCerts ::= INTEGER (0..MAX)
1055 static void SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1056 const SecCertificateExtension
*extn
) {
1057 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1058 DERDecodedInfo iapContent
;
1059 require_noerr_quiet(DERDecodeItem(&extn
->extnValue
, &iapContent
), badDER
);
1060 require_quiet(iapContent
.tag
== ASN1_INTEGER
, badDER
);
1061 require_noerr_quiet(DERParseInteger(
1062 &iapContent
.content
,
1063 &certificate
->_inhibitAnyPolicySkipCerts
.skipCerts
), badDER
);
1065 certificate
->_inhibitAnyPolicySkipCerts
.present
= true;
1066 certificate
->_inhibitAnyPolicySkipCerts
.critical
= extn
->critical
;
1069 certificate
->_inhibitAnyPolicySkipCerts
.present
= false;
1070 secwarning("Invalid InhibitAnyPolicy Extension");
1074 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1076 AuthorityInfoAccessSyntax ::=
1077 SEQUENCE SIZE (1..MAX) OF AccessDescription
1079 AccessDescription ::= SEQUENCE {
1080 accessMethod OBJECT IDENTIFIER,
1081 accessLocation GeneralName }
1083 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1085 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1087 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1089 static void SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1090 const SecCertificateExtension
*extn
) {
1091 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1094 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1095 require_noerr_quiet(drtn
, badDER
);
1096 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1097 DERDecodedInfo adContent
;
1098 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1099 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1100 DERAccessDescription ad
;
1101 drtn
= DERParseSequenceContent(&adContent
.content
,
1102 DERNumAccessDescriptionItemSpecs
,
1103 DERAccessDescriptionItemSpecs
,
1105 require_noerr_quiet(drtn
, badDER
);
1106 CFMutableArrayRef
*urls
;
1107 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1108 urls
= &certificate
->_ocspResponders
;
1109 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1110 urls
= &certificate
->_caIssuers
;
1114 DERDecodedInfo generalNameContent
;
1115 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1116 require_noerr_quiet(drtn
, badDER
);
1117 switch (generalNameContent
.tag
) {
1119 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1120 /* Technically I don't think this is valid, but there are certs out
1121 in the wild that use a constructed IA5String. In particular the
1122 VeriSign Time Stamping Authority CA.cer does this. */
1124 case ASN1_CONTEXT_SPECIFIC
| 6:
1126 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1127 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1128 kCFStringEncodingASCII
, NULL
);
1131 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1132 CFArrayAppendValue(*urls
, url
);
1138 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1139 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1144 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1147 secdebug("cert", "failed to parse Authority Information Access extension");
1150 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1151 * This is a DER sequence with the leading tag and length bytes removed,
1152 * to match what tbsCert.issuer contains.
1154 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1155 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1156 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1157 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1158 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1159 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1160 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1161 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1162 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1163 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1164 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1167 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1169 certificate
->_crlDistributionPoints
||
1170 certificate
->_ocspResponders
) {
1171 /* We already have an OCSP or CRL URI (or no cert) */
1174 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1175 CFURLRef url
= NULL
;
1176 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1177 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1178 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1179 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1180 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1181 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1182 kCFStringEncodingASCII
, NULL
);
1185 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1186 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1187 CFArrayAppendValue(*urls
, url
);
1192 static void SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1193 const SecCertificateExtension
*extn
) {
1194 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1197 static void SecCEPNetscapeCertType(SecCertificateRef certificate
,
1198 const SecCertificateExtension
*extn
) {
1199 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1202 static void SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1203 const SecCertificateExtension
*extn
) {
1204 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1207 static void SecCEPEscrowMarker(SecCertificateRef certificate
,
1208 const SecCertificateExtension
*extn
) {
1209 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1213 /* Dictionary key callback for comparing to DERItems. */
1214 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1215 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1218 /* Dictionary key callback calculating the hash of a DERItem. */
1219 static CFHashCode
SecDERItemHash(const void *value
) {
1220 const DERItem
*derItem
= (const DERItem
*)value
;
1221 CFHashCode hash
= derItem
->length
;
1222 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1223 for (; ix
< derItem
->length
; ++ix
) {
1224 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1230 /* Dictionary key callbacks using the above 2 functions. */
1231 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1235 NULL
, /* copyDescription */
1236 SecDERItemEqual
, /* equal */
1237 SecDERItemHash
/* hash */
1240 static void SecCertificateInitializeExtensionParsers(void) {
1241 /* Build a dictionary that maps from extension OIDs to callback functions
1242 which can parse the extension of the type given. */
1243 static const void *extnOIDs
[] = {
1244 &oidSubjectKeyIdentifier
,
1246 &oidPrivateKeyUsagePeriod
,
1249 &oidBasicConstraints
,
1250 &oidNameConstraints
,
1251 &oidCrlDistributionPoints
,
1252 &oidCertificatePolicies
,
1254 &oidAuthorityKeyIdentifier
,
1255 &oidPolicyConstraints
,
1256 &oidExtendedKeyUsage
,
1257 &oidInhibitAnyPolicy
,
1258 &oidAuthorityInfoAccess
,
1259 &oidSubjectInfoAccess
,
1260 &oidNetscapeCertType
,
1261 &oidEntrustVersInfo
,
1262 &oidApplePolicyEscrowService
1264 static const void *extnParsers
[] = {
1265 SecCEPSubjectKeyIdentifier
,
1267 SecCEPPrivateKeyUsagePeriod
,
1268 SecCEPSubjectAltName
,
1269 SecCEPIssuerAltName
,
1270 SecCEPBasicConstraints
,
1271 SecCEPNameConstraints
,
1272 SecCEPCrlDistributionPoints
,
1273 SecCEPCertificatePolicies
,
1274 SecCEPPolicyMappings
,
1275 SecCEPAuthorityKeyIdentifier
,
1276 SecCEPPolicyConstraints
,
1277 SecCEPExtendedKeyUsage
,
1278 SecCEPInhibitAnyPolicy
,
1279 SecCEPAuthorityInfoAccess
,
1280 SecCEPSubjectInfoAccess
,
1281 SecCEPNetscapeCertType
,
1282 SecCEPEntrustVersInfo
,
1285 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1286 extnParsers
, array_size(extnOIDs
),
1287 &SecDERItemKeyCallBacks
, NULL
);
1290 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateCopyDescription
, NULL
, NULL
, ^{
1291 SecCertificateInitializeExtensionParsers();
1294 static bool isAppleExtensionOID(const DERItem
*extnID
)
1296 static const uint8_t appleExtension
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1297 return (extnID
&& extnID
->data
&&
1298 extnID
->length
> sizeof(appleExtension
) &&
1299 !memcmp(extnID
->data
, appleExtension
, sizeof(appleExtension
)));
1302 /* Given the contents of an X.501 Name return the contents of a normalized
1304 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1305 const DERItem
*x501name
) {
1306 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1307 CFIndex length
= x501name
->length
;
1308 CFDataSetLength(result
, length
);
1309 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1312 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1314 require_noerr_quiet(drtn
, badDER
);
1317 /* Always points to last rdn tag. */
1318 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1319 /* Offset relative to base of current rdn set tag. */
1320 CFIndex rdnTagLocation
= 0;
1321 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1322 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1323 /* We don't allow empty RDNs. */
1324 require_quiet(rdn
.content
.length
!= 0, badDER
);
1325 /* Length of the tag and length of the current rdn. */
1326 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1327 CFIndex rdnContentLength
= rdn
.content
.length
;
1328 /* Copy the tag and length of the RDN. */
1329 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1332 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1333 require_quiet(drtn
== DR_Success
, badDER
);
1336 /* Always points to tag of current atv sequence. */
1337 const DERByte
*atvTag
= atvSeq
.nextItem
;
1338 /* Offset relative to base of current atv sequence tag. */
1339 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1340 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1341 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1342 /* Length of the tag and length of the current atv. */
1343 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1344 CFIndex atvContentLength
= atv
.content
.length
;
1345 /* Copy the tag and length of the atv and the atv itself. */
1346 memcpy(base
+ atvTagLocation
, atvTag
,
1347 atvTLLength
+ atv
.content
.length
);
1349 /* Now decode the atv sequence. */
1350 DERAttributeTypeAndValue atvPair
;
1351 drtn
= DERParseSequenceContent(&atv
.content
,
1352 DERNumAttributeTypeAndValueItemSpecs
,
1353 DERAttributeTypeAndValueItemSpecs
,
1354 &atvPair
, sizeof(atvPair
));
1355 require_noerr_quiet(drtn
, badDER
);
1356 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1357 DERDecodedInfo value
;
1358 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1359 require_noerr_quiet(drtn
, badDER
);
1361 /* (c) attribute values in PrintableString are not case sensitive
1362 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1364 (d) attribute values in PrintableString are compared after
1365 removing leading and trailing white space and converting internal
1366 substrings of one or more consecutive white space characters to a
1368 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1369 /* Offset relative to base of current value tag. */
1370 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1371 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1372 CFIndex valueContentLength
= value
.content
.length
;
1374 /* Now copy all the bytes, but convert to upper case while
1375 doing so and convert multiple whitespace chars into a
1377 bool lastWasBlank
= false;
1378 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1379 CFIndex valueCurrentLocation
= valueLocation
;
1381 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1382 UInt8 ch
= value
.content
.data
[ix
];
1387 /* Don't insert a space for first character
1389 if (valueCurrentLocation
> valueLocation
) {
1390 base
[valueCurrentLocation
++] = ' ';
1392 lastWasBlank
= true;
1395 lastWasBlank
= false;
1396 if ('a' <= ch
&& ch
<= 'z') {
1397 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1399 base
[valueCurrentLocation
++] = ch
;
1403 /* Finally if lastWasBlank remove the trailing space. */
1404 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1405 valueCurrentLocation
--;
1407 /* Adjust content length to normalized length. */
1408 valueContentLength
= valueCurrentLocation
- valueLocation
;
1410 /* Number of bytes by which the length should be shorted. */
1411 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1412 if (lengthDiff
== 0) {
1413 /* Easy case no need to adjust lengths. */
1415 /* Hard work we need to go back and fix up length fields
1417 1) The value itself.
1418 2) The ATV Sequence containing type/value
1419 3) The RDN Set containing one or more atv pairs.
1423 /* Step 1 fix up length of value. */
1424 /* Length of value tag and length minus the tag. */
1425 DERSize newValueTLLength
= valueTLLength
- 1;
1426 drtn
= DEREncodeLength(valueContentLength
,
1427 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1428 require(drtn
== DR_Success
, badDER
);
1429 /* Add the length of the tag back in. */
1431 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1433 /* The size of the length field changed, let's slide
1434 the value back by valueLLDiff bytes. */
1435 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1436 base
+ valueTagLocation
+ valueTLLength
,
1437 valueContentLength
);
1438 /* The length diff for the enclosing object. */
1439 lengthDiff
+= valueLLDiff
;
1442 /* Step 2 fix up length of the enclosing ATV Sequence. */
1443 atvContentLength
-= lengthDiff
;
1444 DERSize newATVTLLength
= atvTLLength
- 1;
1445 drtn
= DEREncodeLength(atvContentLength
,
1446 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1447 require(drtn
== DR_Success
, badDER
);
1448 /* Add the length of the tag back in. */
1450 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1452 /* The size of the length field changed, let's slide
1453 the value back by valueLLDiff bytes. */
1454 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1455 base
+ atvTagLocation
+ atvTLLength
,
1457 /* The length diff for the enclosing object. */
1458 lengthDiff
+= atvLLDiff
;
1459 atvTLLength
= newATVTLLength
;
1462 /* Step 3 fix up length of enclosing RDN Set. */
1463 rdnContentLength
-= lengthDiff
;
1464 DERSize newRDNTLLength
= rdnTLLength
- 1;
1465 drtn
= DEREncodeLength(rdnContentLength
,
1466 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1467 require_quiet(drtn
== DR_Success
, badDER
);
1468 /* Add the length of the tag back in. */
1470 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1472 /* The size of the length field changed, let's slide
1473 the value back by valueLLDiff bytes. */
1474 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1475 base
+ rdnTagLocation
+ rdnTLLength
,
1477 /* The length diff for the enclosing object. */
1478 lengthDiff
+= rdnLLDiff
;
1479 rdnTLLength
= newRDNTLLength
;
1481 /* Adjust the locations that might have changed due to
1483 atvTagLocation
-= rdnLLDiff
;
1485 (void) lengthDiff
; // No next object, silence analyzer
1488 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1489 atvTag
= atvSeq
.nextItem
;
1491 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1492 rdnTag
= rdnSeq
.nextItem
;
1494 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1495 /* Truncate the result to the proper length. */
1496 CFDataSetLength(result
, rdnTagLocation
);
1505 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1507 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1508 DERDecodedInfo content
;
1509 /* Decode top level sequence into DERItem */
1510 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1511 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1515 /* AUDIT[securityd]:
1516 certificate->_der is a caller provided data of any length (might be 0).
1518 Top level certificate decode.
1520 static bool SecCertificateParse(SecCertificateRef certificate
)
1525 require_quiet(certificate
, badCert
);
1526 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1528 /* top level decode */
1529 DERSignedCertCrl signedCert
;
1530 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1531 DERSignedCertCrlItemSpecs
, &signedCert
,
1532 sizeof(signedCert
));
1533 require_noerr_quiet(drtn
, badCert
);
1534 /* Store tbs since we need to digest it for verification later on. */
1535 certificate
->_tbs
= signedCert
.tbs
;
1537 /* decode the TBSCert - it was saved in full DER form */
1539 drtn
= DERParseSequence(&signedCert
.tbs
,
1540 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1541 &tbsCert
, sizeof(tbsCert
));
1542 require_noerr_quiet(drtn
, badCert
);
1544 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1545 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1546 of the params field. */
1547 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1548 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1549 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1550 require_noerr_quiet(drtn
, badCert
);
1552 /* The contents of signedCert.sig is a bit string whose contents
1553 are the signature itself. */
1554 DERByte numUnusedBits
;
1555 drtn
= DERParseBitString(&signedCert
.sig
,
1556 &certificate
->_signature
, &numUnusedBits
);
1557 require_noerr_quiet(drtn
, badCert
);
1559 /* Now decode the tbsCert. */
1561 /* First we turn the optional version into an int. */
1562 if (tbsCert
.version
.length
) {
1563 DERDecodedInfo decoded
;
1564 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1565 require_noerr_quiet(drtn
, badCert
);
1566 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1567 require_quiet(decoded
.content
.length
== 1, badCert
);
1568 certificate
->_version
= decoded
.content
.data
[0];
1569 if (certificate
->_version
> 2) {
1570 secwarning("Invalid certificate version (%d), must be 0..2",
1571 certificate
->_version
);
1573 require_quiet(certificate
->_version
> 0, badCert
);
1574 require_quiet(certificate
->_version
< 3, badCert
);
1576 certificate
->_version
= 0;
1579 /* The serial number is in the tbsCert.serialNum - it was saved in
1580 INTEGER form without the tag and length. */
1581 certificate
->_serialNum
= tbsCert
.serialNum
;
1582 certificate
->_serialNumber
= CFDataCreate(allocator
,
1583 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1584 /* RFC5280 4.1.2.2 limits serial number values to 20 octets.
1585 %%% At some point, this should be treated as a hard error.*/
1586 if (tbsCert
.serialNum
.length
< 1 || tbsCert
.serialNum
.length
> 20) {
1587 secwarning("Invalid serial number length (%ld), must be 1..20",
1588 tbsCert
.serialNum
.length
);
1591 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1592 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1593 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1594 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1595 require_noerr_quiet(drtn
, badCert
);
1597 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1598 and length fields. */
1599 certificate
->_issuer
= tbsCert
.issuer
;
1600 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1603 /* sequence we're given: decode the tbsCerts Validity sequence. */
1604 DERValidity validity
;
1605 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1606 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1607 &validity
, sizeof(validity
));
1608 require_noerr_quiet(drtn
, badCert
);
1609 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1610 &certificate
->_notBefore
), badCert
);
1611 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1612 &certificate
->_notAfter
), badCert
);
1614 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1615 and length fields. */
1616 certificate
->_subject
= tbsCert
.subject
;
1617 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1620 /* Keep the SPKI around for CT */
1621 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1623 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1624 DERSubjPubKeyInfo pubKeyInfo
;
1625 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1626 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1627 &pubKeyInfo
, sizeof(pubKeyInfo
));
1628 require_noerr_quiet(drtn
, badCert
);
1630 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1631 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1632 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1633 &certificate
->_algId
, sizeof(certificate
->_algId
));
1634 require_noerr_quiet(drtn
, badCert
);
1636 /* Now we can figure out the key's algorithm id and params based on
1637 certificate->_algId.oid. */
1639 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1640 are a PKCS1 format RSA key. */
1641 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1642 &certificate
->_pubKeyDER
, &numUnusedBits
);
1643 require_noerr_quiet(drtn
, badCert
);
1645 /* The contents of tbsCert.issuerID is a bit string. */
1646 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1648 /* The contents of tbsCert.subjectID is a bit string. */
1649 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1652 if (tbsCert
.extensions
.length
) {
1653 CFIndex extensionCount
= 0;
1656 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1658 require_noerr_quiet(drtn
, badCert
);
1659 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1660 DERDecodedInfo currDecoded
;
1661 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1663 /* ! = MUST recognize ? = SHOULD recognize
1666 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1667 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1668 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1669 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1670 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1671 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1672 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1673 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1675 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1676 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1677 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1678 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1679 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1680 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1681 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1682 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1684 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1685 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1690 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1692 /* Put some upper limit on the number of extensions allowed. */
1693 require_quiet(extensionCount
< 10000, badCert
);
1694 certificate
->_extensionCount
= extensionCount
;
1695 certificate
->_extensions
=
1696 malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1699 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1700 require_noerr_quiet(drtn
, badCert
);
1701 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1702 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1703 require_quiet(drtn
== DR_Success
||
1704 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1705 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1707 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1708 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1709 &extn
, sizeof(extn
));
1710 require_noerr_quiet(drtn
, badCert
);
1711 /* Copy stuff into certificate->extensions[ix]. */
1712 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1713 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1714 &certificate
->_extensions
[ix
].critical
), badCert
);
1715 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1717 SecCertificateExtensionParser parser
=
1718 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1719 sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1721 /* Invoke the parser. */
1722 parser(certificate
, &certificate
->_extensions
[ix
]);
1723 } else if (certificate
->_extensions
[ix
].critical
) {
1724 if (isAppleExtensionOID(&extn
.extnID
)) {
1727 secdebug("cert", "Found unknown critical extension");
1728 certificate
->_foundUnknownCriticalExtension
= true;
1730 secdebug("cert", "Found unknown non critical extension");
1734 checkForMissingRevocationInfo(certificate
);
1743 /* Public API functions. */
1744 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1745 const UInt8
*der_bytes
, CFIndex der_length
) {
1746 if (der_bytes
== NULL
) return NULL
;
1747 if (der_length
== 0) return NULL
;
1749 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1750 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1751 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1753 memset((char*)result
+ sizeof(result
->_base
), 0,
1754 sizeof(*result
) - sizeof(result
->_base
));
1755 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1756 result
->_der
.length
= der_length
;
1757 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1758 if (!SecCertificateParse(result
)) {
1766 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1767 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1768 const UInt8
*der_bytes
, CFIndex der_length
);
1770 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1771 const UInt8
*der_bytes
, CFIndex der_length
) {
1772 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1774 /* @@@ End of placeholder. */
1776 /* AUDIT[securityd](done):
1777 der_certificate is a caller provided data of any length (might be 0), only
1778 its cf type has been checked.
1780 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1781 CFDataRef der_certificate
) {
1782 if (!der_certificate
) {
1785 CFIndex size
= sizeof(struct __SecCertificate
);
1786 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1787 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1789 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1790 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1791 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1792 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1793 if (!SecCertificateParse(result
)) {
1801 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1802 CFDataRef der_certificate
,
1803 CFTypeRef keychain_item
)
1805 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1807 CFRetainSafe(keychain_item
);
1808 result
->_keychain_item
= keychain_item
;
1813 OSStatus
SecCertificateSetKeychainItem(SecCertificateRef certificate
,
1814 CFTypeRef keychain_item
)
1819 CFRetainSafe(keychain_item
);
1820 CFReleaseSafe(certificate
->_keychain_item
);
1821 certificate
->_keychain_item
= keychain_item
;
1822 return errSecSuccess
;
1825 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1827 CFDataRef result
= NULL
;
1831 if (certificate
->_der_data
) {
1832 CFRetain(certificate
->_der_data
);
1833 result
= certificate
->_der_data
;
1835 result
= CFDataCreate(CFGetAllocator(certificate
),
1836 certificate
->_der
.data
, certificate
->_der
.length
);
1838 /* FIXME: If we wish to cache result we need to lock the certificate.
1839 Also this create 2 copies of the certificate data which is somewhat
1842 certificate
->_der_data
= result
;
1849 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1850 return certificate
->_der
.length
;
1853 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1854 return certificate
->_der
.data
;
1857 /* Used to recreate preCert from cert for Certificate Transparency */
1858 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1860 CFDataRef outData
= NULL
;
1861 DERItem tbsIn
= certificate
->_tbs
;
1862 DERItem tbsOut
= {0,};
1863 DERItem extensionsOut
= {0,};
1864 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1865 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
1869 /* decode the TBSCert - it was saved in full DER form */
1870 drtn
= DERParseSequence(&tbsIn
,
1871 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1872 &tbsCert
, sizeof(tbsCert
));
1873 require_noerr_quiet(drtn
, out
);
1875 /* Go over extensions and filter any SCT extension */
1876 CFIndex extensionsCount
= 0;
1878 if (tbsCert
.extensions
.length
) {
1881 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1883 require_noerr_quiet(drtn
, out
);
1884 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
1885 DERDecodedInfo currDecoded
;
1886 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1888 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
1890 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1891 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1892 &extn
, sizeof(extn
));
1893 require_noerr_quiet(drtn
, out
);
1895 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
1896 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
1899 extensionsList
[extensionsCount
] = currDecoded
.content
;
1900 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
1901 extensionsListSpecs
[extensionsCount
].options
= 0;
1902 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
1907 require_quiet(drtn
== DR_EndOfSequence
, out
);
1911 /* Encode extensions */
1912 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
1913 extensionsOut
.data
= malloc(extensionsOut
.length
);
1914 require_quiet(extensionsOut
.data
, out
);
1915 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
1916 require_noerr_quiet(drtn
, out
);
1918 tbsCert
.extensions
= extensionsOut
;
1920 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
1921 tbsOut
.data
= malloc(tbsOut
.length
);
1922 require_quiet(tbsOut
.data
, out
);
1923 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
1924 require_noerr_quiet(drtn
, out
);
1926 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
1929 free(extensionsOut
.data
);
1931 free(extensionsList
);
1932 free(extensionsListSpecs
);
1937 /* From rfc3280 - Appendix B. ASN.1 Notes
1939 Object Identifiers (OIDs) are used throughout this specification to
1940 identify certificate policies, public key and signature algorithms,
1941 certificate extensions, etc. There is no maximum size for OIDs.
1942 This specification mandates support for OIDs which have arc elements
1943 with values that are less than 2^28, that is, they MUST be between 0
1944 and 268,435,455, inclusive. This allows each arc element to be
1945 represented within a single 32 bit word. Implementations MUST also
1946 support OIDs where the length of the dotted decimal (see [RFC 2252],
1947 section 4.1) string representation can be up to 100 bytes
1948 (inclusive). Implementations MUST be able to handle OIDs with up to
1949 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1950 contain OIDs that exceed these requirements. Likewise, CRL issuers
1951 SHOULD NOT issue CRLs which contain OIDs that exceed these
1955 /* Oids longer than this are considered invalid. */
1956 #define MAX_OID_SIZE 32
1958 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1959 const DERItem
*oid
) {
1961 if (oid
->length
== 0) {
1962 return SecCopyCertString(SEC_NULL_KEY
);
1964 if (oid
->length
> MAX_OID_SIZE
) {
1965 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
1968 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1970 // The first two levels are encoded into one byte, since the root level
1971 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1972 // y may be > 39, so we have to add special-case handling for this.
1973 uint32_t x
= oid
->data
[0] / 40;
1974 uint32_t y
= oid
->data
[0] % 40;
1977 // Handle special case for large y if x = 2
1981 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1984 for (x
= 1; x
< oid
->length
; ++x
)
1986 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1987 /* @@@ value may not span more than 4 bytes. */
1988 /* A max number of 20 values is allowed. */
1989 if (!(oid
->data
[x
] & 0x80))
1991 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
1998 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1999 const DERItem
*oid
) {
2000 if (oid
->length
== 0) {
2001 return SecCopyCertString(SEC_NULL_KEY
);
2004 /* Build the key we use to lookup the localized OID description. */
2005 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
2006 oid
->length
* 3 + 5);
2007 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
2009 for (ix
= 0; ix
< oid
->length
; ++ix
)
2010 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
2012 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
2013 if (CFEqual(oidKey
, name
)) {
2015 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2022 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
2023 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
2024 have a length of exactly 4 or 16 octects. */
2025 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
2026 const DERItem
*ip
) {
2027 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
2028 4 octects addr, or 8 octects, addr/mask for ipv6 it's
2029 16 octects addr, or 32 octects addr/mask. */
2030 CFStringRef value
= NULL
;
2031 if (ip
->length
== 4) {
2032 value
= CFStringCreateWithFormat(allocator
, NULL
,
2033 CFSTR("%u.%u.%u.%u"),
2034 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2035 } else if (ip
->length
== 16) {
2036 value
= CFStringCreateWithFormat(allocator
, NULL
,
2037 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
2038 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
2039 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2040 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2041 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2042 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2048 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2049 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
) {
2050 CFDictionaryRef property
;
2053 if (localizedLabel
) {
2056 ll
= localizedLabel
= SecCopyCertString(label
);
2058 const void *all_keys
[4];
2059 all_keys
[0] = kSecPropertyKeyType
;
2060 all_keys
[1] = kSecPropertyKeyLabel
;
2061 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2062 all_keys
[3] = kSecPropertyKeyValue
;
2063 const void *property_values
[] = {
2069 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2070 all_keys
, property_values
, value
? 4 : 3,
2071 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2074 const void *nolabel_keys
[2];
2075 nolabel_keys
[0] = kSecPropertyKeyType
;
2076 nolabel_keys
[1] = kSecPropertyKeyValue
;
2077 const void *property_values
[] = {
2081 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2082 nolabel_keys
, property_values
, 2,
2083 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2086 CFArrayAppendValue(properties
, property
);
2087 CFRelease(property
);
2091 #define UTC_TIME_NOSEC_ZULU_LEN 11
2093 #define UTC_TIME_ZULU_LEN 13
2094 /* YYMMDDhhmmssThhmm */
2095 #define UTC_TIME_LOCALIZED_LEN 17
2096 /* YYYYMMDDhhmmssZ */
2097 #define GENERALIZED_TIME_ZULU_LEN 15
2098 /* YYYYMMDDhhmmssThhmm */
2099 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2101 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2103 static inline int parseDecimalPair(const DERByte
**p
) {
2104 const DERByte
*cp
= *p
;
2106 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2109 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime.
2110 Return a CFErrorRef in the error parameter if decoding fails.
2111 Note that this is needed to distinguish an error condition from a
2112 valid time which specifies 2001-01-01 00:00:00 (i.e. a value of 0).
2114 static CFAbsoluteTime
SecAbsoluteTimeFromDateContentWithError(DERTag tag
,
2115 const uint8_t *bytes
,
2117 CFErrorRef
*error
) {
2121 if (NULL
== bytes
|| 0 == length
) {
2125 bool isUtcLength
= false;
2126 bool isLocalized
= false;
2127 bool noSeconds
= false;
2129 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2133 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2136 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2138 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2141 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2144 default: /* unknown format */
2148 /* Make sure the der tag fits the thing inside it. */
2149 if (tag
== ASN1_UTC_TIME
) {
2153 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2161 const DERByte
*cp
= bytes
;
2162 /* Check that all characters are digits, except if localized the timezone
2163 indicator or if not localized the 'Z' at the end. */
2165 for (ix
= 0; ix
< length
; ++ix
) {
2166 if (!(isdigit(cp
[ix
]))) {
2167 if ((isLocalized
&& ix
== length
- 5 &&
2168 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2169 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2176 /* Parse the date and time fields. */
2177 int year
, month
, day
, hour
, minute
, second
;
2179 year
= parseDecimalPair(&cp
);
2181 /* 0 <= year < 50 : assume century 21 */
2183 } else if (year
< 70) {
2184 /* 50 <= year < 70 : illegal per PKIX */
2187 /* 70 < year <= 99 : assume century 20 */
2191 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2193 month
= parseDecimalPair(&cp
);
2194 day
= parseDecimalPair(&cp
);
2195 hour
= parseDecimalPair(&cp
);
2196 minute
= parseDecimalPair(&cp
);
2200 second
= parseDecimalPair(&cp
);
2203 CFTimeInterval timeZoneOffset
;
2205 /* ZONE INDICATOR */
2206 int multiplier
= *cp
++ == '+' ? 60 : -60;
2207 timeZoneOffset
= multiplier
*
2208 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2213 secdebug("dateparse",
2214 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2215 (int) length
, bytes
, year
, month
,
2216 day
, hour
, minute
, second
,
2217 timeZoneOffset
/ 60);
2219 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2220 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2221 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
2222 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2223 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2228 int dy
= year
- 2001;
2233 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2234 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2236 day
+= is_leap_year
;
2238 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2239 return absTime
- timeZoneOffset
;
2243 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
2248 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2250 return SecAbsoluteTimeFromDateContentWithError(tag
, bytes
, length
, NULL
);
2253 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2254 CFAbsoluteTime
*pabsTime
) {
2255 CFErrorRef error
= NULL
;
2256 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContentWithError(tag
, date
->data
,
2257 date
->length
, &error
);
2259 secwarning("Invalid date specification in certificate (see RFC5280 4.1.2.5)");
2264 *pabsTime
= absTime
;
2268 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2269 true if the date was valid and properly decoded, also return the result in
2270 absTime. Return false otherwise. */
2271 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2272 CFAbsoluteTime
*absTime
) {
2273 if (dateChoice
->length
== 0) return false;
2275 DERDecodedInfo decoded
;
2276 if (DERDecodeItem(dateChoice
, &decoded
))
2279 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2283 static void appendDataProperty(CFMutableArrayRef properties
,
2284 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2285 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2286 der_data
->data
, der_data
->length
);
2287 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2292 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2294 CFStringRef localizedLabel
,
2295 const DERItem
*der_data
,
2296 CFStringRef labelFormat
) {
2297 CFStringRef newLabel
=
2298 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2299 labelFormat
, label
);
2301 if (localizedLabel
) {
2304 ll
= localizedLabel
= SecCopyCertString(label
);
2306 CFStringRef localizedLabelFormat
= SecCopyCertString(labelFormat
);
2307 CFStringRef newLocalizedLabel
=
2308 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2309 localizedLabelFormat
, localizedLabel
);
2311 CFReleaseSafe(localizedLabelFormat
);
2312 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
);
2313 CFReleaseSafe(newLabel
);
2314 CFReleaseSafe(newLocalizedLabel
);
2318 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2319 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2320 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2324 static void appendInvalidProperty(CFMutableArrayRef properties
,
2325 CFStringRef label
, const DERItem
*der_data
) {
2326 appendRelabeledProperty(properties
, label
, NULL
, der_data
, SEC_INVALID_KEY
);
2329 static void appendDateContentProperty(CFMutableArrayRef properties
,
2330 CFStringRef label
, DERTag tag
,
2331 const DERItem
*dateContent
) {
2332 CFAbsoluteTime absTime
;
2333 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2334 /* Date decode failure insert hex bytes instead. */
2335 return appendInvalidProperty(properties
, label
, dateContent
);
2337 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2338 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2342 static void appendDateProperty(CFMutableArrayRef properties
,
2343 CFStringRef label
, CFAbsoluteTime absTime
) {
2344 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2345 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2349 static void appendValidityPeriodProperty(CFMutableArrayRef parent
, CFStringRef label
,
2350 SecCertificateRef certificate
) {
2351 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2352 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2354 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2355 certificate
->_notBefore
);
2356 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2357 certificate
->_notAfter
);
2359 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2360 CFReleaseNull(properties
);
2363 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2364 CFStringRef label
, const DERItem
*ip
) {
2366 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2368 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2371 appendUnparsedProperty(properties
, label
, NULL
, ip
);
2375 static void appendURLContentProperty(CFMutableArrayRef properties
,
2376 CFStringRef label
, const DERItem
*urlContent
) {
2377 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2378 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2380 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
);
2383 appendInvalidProperty(properties
, label
, urlContent
);
2387 static void appendURLProperty(CFMutableArrayRef properties
,
2388 CFStringRef label
, const DERItem
*url
) {
2389 DERDecodedInfo decoded
;
2392 drtn
= DERDecodeItem(url
, &decoded
);
2393 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2394 appendInvalidProperty(properties
, label
, url
);
2396 appendURLContentProperty(properties
, label
, &decoded
.content
);
2400 static void appendOIDProperty(CFMutableArrayRef properties
,
2401 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
) {
2402 CFStringRef oid_string
=
2403 copyLocalizedOidDescription(CFGetAllocator(properties
), oid
);
2404 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2406 CFRelease(oid_string
);
2409 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2410 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2411 CFMutableArrayRef alg_props
=
2412 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2413 &kCFTypeArrayCallBacks
);
2414 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
, &algorithm
->oid
);
2415 if (algorithm
->params
.length
) {
2416 if (algorithm
->params
.length
== 2 &&
2417 algorithm
->params
.data
[0] == ASN1_NULL
&&
2418 algorithm
->params
.data
[1] == 0) {
2419 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2420 appendProperty(alg_props
, kSecPropertyTypeString
,
2421 SEC_PARAMETERS_KEY
, NULL
, value
);
2424 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2425 &algorithm
->params
);
2428 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
, alg_props
);
2429 CFRelease(alg_props
);
2432 static void appendPublicKeyProperty(CFMutableArrayRef parent
, CFStringRef label
,
2433 SecCertificateRef certificate
) {
2434 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2435 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2437 /* Public key algorithm. */
2438 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
2439 &certificate
->_algId
);
2441 /* Public Key Size */
2442 #if TARGET_OS_IPHONE
2443 SecKeyRef publicKey
= SecCertificateCopyPublicKey(certificate
);
2445 SecKeyRef publicKey
= SecCertificateCopyPublicKey_ios(certificate
);
2448 size_t sizeInBytes
= SecKeyGetBlockSize(publicKey
);
2449 CFStringRef sizeInBitsString
= CFStringCreateWithFormat(allocator
, NULL
,
2450 CFSTR("%ld"), (sizeInBytes
*8));
2451 if (sizeInBitsString
) {
2452 appendProperty(properties
, kSecPropertyTypeString
, SEC_PUBLIC_KEY_SIZE_KEY
,
2453 NULL
, sizeInBitsString
);
2455 CFReleaseNull(sizeInBitsString
);
2457 CFReleaseNull(publicKey
);
2459 /* Consider breaking down an RSA public key into modulus and
2461 appendDataProperty(properties
, SEC_PUBLIC_KEY_DATA_KEY
, NULL
,
2462 &certificate
->_pubKeyDER
);
2464 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2465 CFReleaseNull(properties
);
2468 static void appendSignatureProperty(CFMutableArrayRef parent
, CFStringRef label
,
2469 SecCertificateRef certificate
) {
2470 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2471 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2473 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
2474 &certificate
->_tbsSigAlg
);
2476 appendDataProperty(properties
, SEC_SIGNATURE_DATA_KEY
, NULL
,
2477 &certificate
->_signature
);
2479 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2480 CFReleaseNull(properties
);
2483 static void appendFingerprintsProperty(CFMutableArrayRef parent
, CFStringRef label
, SecCertificateRef certificate
) {
2484 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2485 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2487 CFDataRef sha256Fingerprint
= SecCertificateCopySHA256Digest(certificate
);
2488 if (sha256Fingerprint
) {
2489 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA2_FINGERPRINT_KEY
,
2490 NULL
, sha256Fingerprint
);
2492 CFReleaseNull(sha256Fingerprint
);
2494 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA1_FINGERPRINT_KEY
,
2495 NULL
, SecCertificateGetSHA1Digest(certificate
));
2497 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2498 CFReleaseNull(properties
);
2501 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2502 const DERItem
*blob
) {
2503 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2504 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2505 blob
->length
* 3 - 1);
2506 for (ix
= 0; ix
< length
; ++ix
)
2508 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2510 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2515 /* Returns a (localized) blob string. */
2516 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2517 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2518 CFStringRef localizedBlobType
= SecCopyCertString(blobType
);
2519 CFStringRef localizedQuanta
= SecCopyCertString(quanta
);
2520 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2521 "data = 00 00 ...)" */
2522 CFStringRef blobFormat
= SecCopyCertString(SEC_BLOB_KEY
);
2523 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2524 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2525 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2527 CFRelease(blobFormat
);
2528 CFReleaseSafe(localizedQuanta
);
2529 CFReleaseSafe(localizedBlobType
);
2534 /* Return a string verbatim (unlocalized) from a DER field. */
2535 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2536 const DERItem
*string
, CFStringEncoding encoding
,
2537 bool printableOnly
) {
2538 /* Strip potential bogus trailing zero from printable strings. */
2539 DERSize length
= string
->length
;
2540 if (length
&& string
->data
[length
- 1] == 0) {
2541 /* Don't mess with the length of UTF16 strings though. */
2542 if (encoding
!= kCFStringEncodingUTF16
)
2545 /* A zero length string isn't considered printable. */
2546 if (!length
&& printableOnly
)
2549 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2550 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2551 passing false makes it treat it as native endian by default. */
2552 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2553 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2557 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2560 /* From rfc3280 - Appendix B. ASN.1 Notes
2562 CAs MUST force the serialNumber to be a non-negative integer, that
2563 is, the sign bit in the DER encoding of the INTEGER value MUST be
2564 zero - this can be done by adding a leading (leftmost) `00'H octet if
2565 necessary. This removes a potential ambiguity in mapping between a
2566 string of octets and an integer value.
2568 As noted in section 4.1.2.2, serial numbers can be expected to
2569 contain long integers. Certificate users MUST be able to handle
2570 serialNumber values up to 20 octets in length. Conformant CAs MUST
2571 NOT use serialNumber values longer than 20 octets.
2574 /* Return the given numeric data as a string: decimal up to 64 bits,
2576 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2577 const DERItem
*integer
) {
2579 CFIndex ix
, length
= integer
->length
;
2581 if (length
== 0 || length
> 8)
2582 return copyHexDescription(allocator
, integer
);
2584 for(ix
= 0; ix
< length
; ++ix
) {
2586 value
+= integer
->data
[ix
];
2589 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2592 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2593 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2597 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2598 case ASN1_PRINTABLE_STRING
:
2599 case ASN1_IA5_STRING
:
2600 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2601 case ASN1_UTF8_STRING
:
2602 case ASN1_GENERAL_STRING
:
2603 case ASN1_UNIVERSAL_STRING
:
2604 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2605 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2606 case ASN1_VIDEOTEX_STRING
: // 21
2607 case ASN1_VISIBLE_STRING
: // 26
2608 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2609 case ASN1_BMP_STRING
: // 30
2610 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2611 case ASN1_OCTET_STRING
:
2612 return printableOnly
? NULL
:
2613 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2615 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2616 case ASN1_BIT_STRING
:
2617 return printableOnly
? NULL
:
2618 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2620 case ASN1_CONSTR_SEQUENCE
:
2621 return printableOnly
? NULL
:
2622 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2624 case ASN1_CONSTR_SET
:
2625 return printableOnly
? NULL
:
2626 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
, derThing
);
2627 case ASN1_OBJECT_ID
:
2628 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2630 if (printableOnly
) {
2633 CFStringRef fmt
= SecCopyCertString(SEC_NOT_DISPLAYED_KEY
);
2634 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2635 tag
, derThing
->length
);
2642 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2643 const DERItem
*derThing
, bool printableOnly
) {
2644 DERDecodedInfo decoded
;
2647 drtn
= DERDecodeItem(derThing
, &decoded
);
2649 /* TODO: Perhaps put something in the label saying we couldn't parse
2651 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2653 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2654 &decoded
.content
, false);
2658 static void appendDERThingProperty(CFMutableArrayRef properties
,
2659 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*derThing
) {
2660 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2662 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2664 CFReleaseSafe(value
);
2667 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2668 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2669 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2671 /* If there is more than one value pair we create a subsection for the
2672 second pair, and append things to the subsection for subsequent
2674 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2675 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2677 /* Since this is the second rdn pair for a given rdn, we setup a
2678 new subsection for this rdn. We remove the first property
2679 from the properties array and make it the first element in the
2680 subsection instead. */
2681 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2682 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2683 CFArrayAppendValue(rdn_props
, lastValue
);
2684 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2685 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2687 properties
= rdn_props
;
2689 /* Since this is the third or later rdn pair we have already
2690 created a subsection in the top level properties array. Instead
2691 of appending to that directly we append to the array inside the
2693 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2694 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2698 /* Finally we append the new rdn value to the property array. */
2699 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(
2700 CFGetAllocator(properties
), rdnType
);
2701 CFStringRef localizedLabel
=
2702 copyLocalizedOidDescription(CFGetAllocator(properties
), rdnType
);
2703 appendDERThingProperty(properties
, label
, localizedLabel
, rdnValue
);
2704 CFReleaseSafe(label
);
2705 CFReleaseSafe(localizedLabel
);
2706 return errSecSuccess
;
2709 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2710 const DERItem
*rdnSetContent
) {
2711 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2712 &kCFTypeArrayCallBacks
);
2713 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2716 CFArrayRemoveAllValues(properties
);
2717 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
);
2724 From rfc3739 - 3.1.2. Subject
2726 When parsing the subject here are some tips for a short name of the cert.
2727 Choice I: commonName
2728 Choice II: givenName
2729 Choice III: pseudonym
2731 The commonName attribute value SHALL, when present, contain a name
2732 of the subject. This MAY be in the subject's preferred
2733 presentation format, or a format preferred by the CA, or some
2734 other format. Pseudonyms, nicknames, and names with spelling
2735 other than defined by the registered name MAY be used. To
2736 understand the nature of the name presented in commonName,
2737 complying applications MAY have to examine present values of the
2738 givenName and surname attributes, or the pseudonym attribute.
2741 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2742 const DERItem
*x501NameContent
) {
2743 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2744 &kCFTypeArrayCallBacks
);
2745 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2748 CFArrayRemoveAllValues(properties
);
2749 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501NameContent
);
2755 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2756 const DERItem
*x501Name
) {
2757 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2758 &kCFTypeArrayCallBacks
);
2759 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2761 CFArrayRemoveAllValues(properties
);
2762 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501Name
);
2768 static void appendIntegerProperty(CFMutableArrayRef properties
,
2769 CFStringRef label
, const DERItem
*integer
) {
2770 CFStringRef string
= copyIntegerContentDescription(
2771 CFGetAllocator(properties
), integer
);
2772 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2776 static void appendBoolProperty(CFMutableArrayRef properties
,
2777 CFStringRef label
, bool boolean
) {
2778 CFStringRef value
= SecCopyCertString(boolean
? SEC_YES_KEY
: SEC_NO_KEY
);
2779 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2783 static void appendBooleanProperty(CFMutableArrayRef properties
,
2784 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2786 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2788 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2789 appendInvalidProperty(properties
, label
, boolean
);
2791 appendBoolProperty(properties
, label
, result
);
2795 static void appendSerialNumberProperty(CFMutableArrayRef parent
, CFStringRef label
,
2796 DERItem
*serialNum
) {
2797 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2798 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2800 if (serialNum
->length
) {
2801 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
2803 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2806 CFReleaseNull(properties
);
2809 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2810 CFStringRef label
, const DERItem
*bitStringContent
,
2811 const CFStringRef
*names
, CFIndex namesCount
) {
2812 DERSize len
= bitStringContent
->length
- 1;
2813 require_quiet(len
== 1 || len
== 2, badDER
);
2814 DERByte numUnusedBits
= bitStringContent
->data
[0];
2815 require_quiet(numUnusedBits
< 8, badDER
);
2816 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2817 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2818 uint_fast16_t value
= bitStringContent
->data
[1];
2821 value
= (value
<< 8) + bitStringContent
->data
[2];
2827 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2828 CFStringRef string
= NULL
;
2829 for (ix
= 0; ix
< bits
; ++ix
) {
2833 CFStringCreateWithFormat(CFGetAllocator(properties
),
2834 NULL
, fmt
, string
, names
[ix
]);
2845 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2846 string
? string
: CFSTR(""));
2847 CFReleaseSafe(string
);
2850 appendInvalidProperty(properties
, label
, bitStringContent
);
2853 static void appendBitStringNames(CFMutableArrayRef properties
,
2854 CFStringRef label
, const DERItem
*bitString
,
2855 const CFStringRef
*names
, CFIndex namesCount
) {
2856 DERDecodedInfo bitStringContent
;
2857 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2858 require_noerr_quiet(drtn
, badDER
);
2859 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2860 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2864 appendInvalidProperty(properties
, label
, bitString
);
2867 static void appendKeyUsage(CFMutableArrayRef properties
,
2868 const DERItem
*extnValue
) {
2869 static const CFStringRef usageNames
[] = {
2870 SEC_DIGITAL_SIGNATURE_KEY
,
2871 SEC_NON_REPUDIATION_KEY
,
2872 SEC_KEY_ENCIPHERMENT_KEY
,
2873 SEC_DATA_ENCIPHERMENT_KEY
,
2874 SEC_KEY_AGREEMENT_KEY
,
2877 SEC_ENCIPHER_ONLY_KEY
,
2878 SEC_DECIPHER_ONLY_KEY
2880 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2881 usageNames
, array_size(usageNames
));
2884 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2885 const DERItem
*extnValue
) {
2886 DERPrivateKeyUsagePeriod pkup
;
2887 DERReturn drtn
= DERParseSequence(extnValue
,
2888 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2889 &pkup
, sizeof(pkup
));
2890 require_noerr_quiet(drtn
, badDER
);
2891 if (pkup
.notBefore
.length
) {
2892 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2893 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2895 if (pkup
.notAfter
.length
) {
2896 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2897 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2901 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
, extnValue
);
2904 static void appendStringContentProperty(CFMutableArrayRef properties
,
2905 CFStringRef label
, const DERItem
*stringContent
,
2906 CFStringEncoding encoding
) {
2907 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2908 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2910 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2913 appendInvalidProperty(properties
, label
, stringContent
);
2918 OtherName ::= SEQUENCE {
2919 type-id OBJECT IDENTIFIER,
2920 value [0] EXPLICIT ANY DEFINED BY type-id }
2922 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2923 const DERItem
*otherNameContent
) {
2925 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2926 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2928 require_noerr_quiet(drtn
, badDER
);
2929 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2931 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
2932 CFStringRef localizedLabel
=
2933 copyLocalizedOidDescription(allocator
, &on
.typeIdentifier
);
2934 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2936 appendProperty(properties
, kSecPropertyTypeString
, label
,
2937 localizedLabel
, value_string
);
2939 appendUnparsedProperty(properties
, label
, localizedLabel
, &on
.value
);
2941 CFReleaseSafe(value_string
);
2942 CFReleaseSafe(label
);
2943 CFReleaseSafe(localizedLabel
);
2946 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
, otherNameContent
);
2950 GeneralName ::= CHOICE {
2951 otherName [0] OtherName,
2952 rfc822Name [1] IA5String,
2953 dNSName [2] IA5String,
2954 x400Address [3] ORAddress,
2955 directoryName [4] Name,
2956 ediPartyName [5] EDIPartyName,
2957 uniformResourceIdentifier [6] IA5String,
2958 iPAddress [7] OCTET STRING,
2959 registeredID [8] OBJECT IDENTIFIER}
2961 EDIPartyName ::= SEQUENCE {
2962 nameAssigner [0] DirectoryString OPTIONAL,
2963 partyName [1] DirectoryString }
2965 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2966 DERTag tag
, const DERItem
*generalName
) {
2968 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2969 appendOtherNameContentProperty(properties
, generalName
);
2971 case ASN1_CONTEXT_SPECIFIC
| 1:
2973 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
2974 generalName
, kCFStringEncodingASCII
);
2976 case ASN1_CONTEXT_SPECIFIC
| 2:
2978 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
2979 kCFStringEncodingASCII
);
2981 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2982 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
2985 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2987 CFArrayRef directory_plist
=
2988 createPropertiesForX501Name(CFGetAllocator(properties
),
2990 appendProperty(properties
, kSecPropertyTypeSection
,
2991 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
);
2992 CFRelease(directory_plist
);
2995 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2996 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
2999 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
3000 /* Technically I don't think this is valid, but there are certs out
3001 in the wild that use a constructed IA5String. In particular the
3002 VeriSign Time Stamping Authority CA.cer does this. */
3003 appendURLProperty(properties
, SEC_URI_KEY
, generalName
);
3005 case ASN1_CONTEXT_SPECIFIC
| 6:
3006 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
);
3008 case ASN1_CONTEXT_SPECIFIC
| 7:
3009 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
3012 case ASN1_CONTEXT_SPECIFIC
| 8:
3013 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
, generalName
);
3024 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
3025 const DERItem
*generalName
) {
3026 DERDecodedInfo generalNameContent
;
3027 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
3028 require_noerr_quiet(drtn
, badDER
);
3029 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
3030 &generalNameContent
.content
))
3033 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
, generalName
);
3038 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
3040 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
3041 const DERItem
*generalNamesContent
) {
3043 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
3044 require_noerr_quiet(drtn
, badDER
);
3045 DERDecodedInfo generalNameContent
;
3046 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
3048 if (!appendGeneralNameContentProperty(properties
,
3049 generalNameContent
.tag
, &generalNameContent
.content
)) {
3053 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3056 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3057 generalNamesContent
);
3060 static void appendGeneralNames(CFMutableArrayRef properties
,
3061 const DERItem
*generalNames
) {
3062 DERDecodedInfo generalNamesContent
;
3063 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
3064 require_noerr_quiet(drtn
, badDER
);
3065 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3067 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
3070 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
, generalNames
);
3074 BasicConstraints ::= SEQUENCE {
3075 cA BOOLEAN DEFAULT FALSE,
3076 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3078 static void appendBasicConstraints(CFMutableArrayRef properties
,
3079 const DERItem
*extnValue
) {
3080 DERBasicConstraints basicConstraints
;
3081 DERReturn drtn
= DERParseSequence(extnValue
,
3082 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3083 &basicConstraints
, sizeof(basicConstraints
));
3084 require_noerr_quiet(drtn
, badDER
);
3086 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3087 &basicConstraints
.cA
, false);
3089 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3090 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3091 &basicConstraints
.pathLenConstraint
);
3095 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
, extnValue
);
3099 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3101 * NameConstraints ::= SEQUENCE {
3102 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3103 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3105 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3107 * GeneralSubtree ::= SEQUENCE {
3109 * minimum [0] BaseDistance DEFAULT 0,
3110 * maximum [1] BaseDistance OPTIONAL }
3112 * BaseDistance ::= INTEGER (0..MAX)
3114 static void appendNameConstraints(CFMutableArrayRef properties
,
3115 const DERItem
*extnValue
) {
3116 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3117 DERNameConstraints nc
;
3119 drtn
= DERParseSequence(extnValue
,
3120 DERNumNameConstraintsItemSpecs
,
3121 DERNameConstraintsItemSpecs
,
3123 require_noerr_quiet(drtn
, badDER
);
3124 if (nc
.permittedSubtrees
.length
) {
3126 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3127 DERDecodedInfo gsContent
;
3128 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3129 DERGeneralSubtree derGS
;
3130 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3131 drtn
= DERParseSequenceContent(&gsContent
.content
,
3132 DERNumGeneralSubtreeItemSpecs
,
3133 DERGeneralSubtreeItemSpecs
,
3134 &derGS
, sizeof(derGS
));
3135 require_noerr_quiet(drtn
, badDER
);
3136 if (derGS
.minimum
.length
) {
3137 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
, &derGS
.minimum
);
3139 if (derGS
.maximum
.length
) {
3140 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
, &derGS
.maximum
);
3142 if (derGS
.generalName
.length
) {
3143 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3144 &kCFTypeArrayCallBacks
);
3145 appendProperty(properties
, kSecPropertyTypeSection
,
3146 SEC_PERMITTED_NAME_KEY
, NULL
, base
);
3147 appendGeneralNameProperty(base
, &derGS
.generalName
);
3151 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3153 if (nc
.excludedSubtrees
.length
) {
3155 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3156 DERDecodedInfo gsContent
;
3157 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3158 DERGeneralSubtree derGS
;
3159 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3160 drtn
= DERParseSequenceContent(&gsContent
.content
,
3161 DERNumGeneralSubtreeItemSpecs
,
3162 DERGeneralSubtreeItemSpecs
,
3163 &derGS
, sizeof(derGS
));
3164 require_noerr_quiet(drtn
, badDER
);
3165 if (derGS
.minimum
.length
) {
3166 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
, &derGS
.minimum
);
3168 if (derGS
.maximum
.length
) {
3169 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
, &derGS
.maximum
);
3171 if (derGS
.generalName
.length
) {
3172 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3173 &kCFTypeArrayCallBacks
);
3174 appendProperty(properties
, kSecPropertyTypeSection
,
3175 SEC_EXCLUDED_NAME_KEY
, NULL
, base
);
3176 appendGeneralNameProperty(base
, &derGS
.generalName
);
3180 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3185 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
, extnValue
);
3189 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3191 DistributionPoint ::= SEQUENCE {
3192 distributionPoint [0] DistributionPointName OPTIONAL,
3193 reasons [1] ReasonFlags OPTIONAL,
3194 cRLIssuer [2] GeneralNames OPTIONAL }
3196 DistributionPointName ::= CHOICE {
3197 fullName [0] GeneralNames,
3198 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3200 ReasonFlags ::= BIT STRING {
3204 affiliationChanged (3),
3206 cessationOfOperation (5),
3207 certificateHold (6),
3208 privilegeWithdrawn (7),
3211 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3212 const DERItem
*extnValue
) {
3213 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3216 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3217 require_noerr_quiet(drtn
, badDER
);
3218 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3219 DERDecodedInfo dpSeqContent
;
3220 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3221 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3222 DERDistributionPoint dp
;
3223 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3224 DERNumDistributionPointItemSpecs
,
3225 DERDistributionPointItemSpecs
,
3227 require_noerr_quiet(drtn
, badDER
);
3228 if (dp
.distributionPoint
.length
) {
3229 DERDecodedInfo distributionPointName
;
3230 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3231 require_noerr_quiet(drtn
, badDER
);
3232 if (distributionPointName
.tag
==
3233 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3235 appendGeneralNamesContent(properties
,
3236 &distributionPointName
.content
);
3237 } else if (distributionPointName
.tag
==
3238 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3239 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3241 appendProperty(properties
, kSecPropertyTypeSection
,
3242 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
);
3243 CFRelease(rdn_props
);
3248 if (dp
.reasons
.length
) {
3249 static const CFStringRef reasonNames
[] = {
3251 SEC_KEY_COMPROMISE_KEY
,
3252 SEC_CA_COMPROMISE_KEY
,
3253 SEC_AFFILIATION_CHANGED_KEY
,
3255 SEC_CESSATION_OF_OPER_KEY
,
3256 SEC_CERTIFICATE_HOLD_KEY
,
3257 SEC_PRIV_WITHDRAWN_KEY
,
3258 SEC_AA_COMPROMISE_KEY
3260 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3262 reasonNames
, array_size(reasonNames
));
3264 if (dp
.cRLIssuer
.length
) {
3265 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3266 &kCFTypeArrayCallBacks
);
3267 appendProperty(properties
, kSecPropertyTypeSection
,
3268 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
);
3269 CFRelease(crlIssuer
);
3270 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
3273 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3276 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
, extnValue
);
3279 /* Decode a sequence of integers into a comma separated list of ints. */
3280 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3281 CFStringRef label
, const DERItem
*intSequenceContent
) {
3282 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3284 CFStringRef fmt
= NULL
, value
= NULL
, intDesc
= NULL
, v
= NULL
;
3285 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3286 require_noerr_quiet(drtn
, badDER
);
3287 DERDecodedInfo intContent
;
3288 fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3289 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3290 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3291 intDesc
= copyIntegerContentDescription(
3292 allocator
, &intContent
.content
);
3294 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3295 CFReleaseNull(value
);
3297 CFReleaseNull(intDesc
);
3303 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3305 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
3309 /* DROPTHOUGH if !value. */
3312 CFReleaseNull(intDesc
);
3314 appendInvalidProperty(properties
, label
, intSequenceContent
);
3317 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3318 const DERItem
*extnValue
) {
3319 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3322 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3323 require_noerr_quiet(drtn
, badDER
);
3324 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3325 DERDecodedInfo piContent
;
3327 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3328 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3329 DERPolicyInformation pi
;
3330 drtn
= DERParseSequenceContent(&piContent
.content
,
3331 DERNumPolicyInformationItemSpecs
,
3332 DERPolicyInformationItemSpecs
,
3334 require_noerr_quiet(drtn
, badDER
);
3335 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3336 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3337 CFStringRef piFmt
= SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
);
3338 CFStringRef lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3341 appendOIDProperty(properties
, piLabel
, lpiLabel
, &pi
.policyIdentifier
);
3343 CFRelease(lpiLabel
);
3344 if (pi
.policyQualifiers
.length
== 0)
3348 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3349 require_noerr_quiet(drtn
, badDER
);
3350 DERDecodedInfo pqContent
;
3352 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3353 DERPolicyQualifierInfo pqi
;
3354 drtn
= DERParseSequenceContent(&pqContent
.content
,
3355 DERNumPolicyQualifierInfoItemSpecs
,
3356 DERPolicyQualifierInfoItemSpecs
,
3358 require_noerr_quiet(drtn
, badDER
);
3359 DERDecodedInfo qualifierContent
;
3360 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3361 require_noerr_quiet(drtn
, badDER
);
3362 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3363 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3364 CFStringRef pqFmt
= SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
);
3365 CFStringRef lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3368 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3369 &pqi
.policyQualifierID
);
3371 CFRelease(lpqLabel
);
3372 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3373 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3374 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3375 &qualifierContent
.content
);
3376 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3377 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3379 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3380 DERNumUserNoticeItemSpecs
,
3381 DERUserNoticeItemSpecs
,
3383 require_noerr_quiet(drtn
, badDER
);
3384 if (un
.noticeRef
.length
) {
3385 DERNoticeReference nr
;
3386 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3387 DERNumNoticeReferenceItemSpecs
,
3388 DERNoticeReferenceItemSpecs
,
3390 require_noerr_quiet(drtn
, badDER
);
3391 appendDERThingProperty(properties
,
3392 SEC_ORGANIZATION_KEY
, NULL
,
3394 appendIntegerSequenceContent(properties
,
3395 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
);
3397 if (un
.explicitText
.length
) {
3398 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3399 NULL
, &un
.explicitText
);
3402 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3407 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3410 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
, extnValue
);
3413 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3414 const DERItem
*extnValue
) {
3416 DERDecodedInfo keyIdentifier
;
3417 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3418 require_noerr_quiet(drtn
, badDER
);
3419 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3420 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3421 &keyIdentifier
.content
);
3425 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3430 AuthorityKeyIdentifier ::= SEQUENCE {
3431 keyIdentifier [0] KeyIdentifier OPTIONAL,
3432 authorityCertIssuer [1] GeneralNames OPTIONAL,
3433 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3434 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3435 -- be present or both be absent
3437 KeyIdentifier ::= OCTET STRING
3439 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3440 const DERItem
*extnValue
) {
3441 DERAuthorityKeyIdentifier akid
;
3443 drtn
= DERParseSequence(extnValue
,
3444 DERNumAuthorityKeyIdentifierItemSpecs
,
3445 DERAuthorityKeyIdentifierItemSpecs
,
3446 &akid
, sizeof(akid
));
3447 require_noerr_quiet(drtn
, badDER
);
3448 if (akid
.keyIdentifier
.length
) {
3449 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3450 &akid
.keyIdentifier
);
3452 if (akid
.authorityCertIssuer
.length
||
3453 akid
.authorityCertSerialNumber
.length
) {
3454 require_quiet(akid
.authorityCertIssuer
.length
&&
3455 akid
.authorityCertSerialNumber
.length
, badDER
);
3456 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3457 appendGeneralNamesContent(properties
,
3458 &akid
.authorityCertIssuer
);
3459 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3460 &akid
.authorityCertSerialNumber
);
3465 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
, extnValue
);
3469 PolicyConstraints ::= SEQUENCE {
3470 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3471 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3473 SkipCerts ::= INTEGER (0..MAX)
3475 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3476 const DERItem
*extnValue
) {
3477 DERPolicyConstraints pc
;
3479 drtn
= DERParseSequence(extnValue
,
3480 DERNumPolicyConstraintsItemSpecs
,
3481 DERPolicyConstraintsItemSpecs
,
3483 require_noerr_quiet(drtn
, badDER
);
3484 if (pc
.requireExplicitPolicy
.length
) {
3485 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3486 &pc
.requireExplicitPolicy
);
3488 if (pc
.inhibitPolicyMapping
.length
) {
3489 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3490 &pc
.inhibitPolicyMapping
);
3496 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
, extnValue
);
3500 extendedKeyUsage EXTENSION ::= {
3501 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3502 IDENTIFIED BY id-ce-extKeyUsage }
3504 KeyPurposeId ::= OBJECT IDENTIFIER
3506 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3507 const DERItem
*extnValue
) {
3510 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3511 require_noerr_quiet(drtn
, badDER
);
3512 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3513 DERDecodedInfo currDecoded
;
3514 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3515 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3516 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3517 &currDecoded
.content
);
3519 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3522 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
, extnValue
);
3526 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3528 AuthorityInfoAccessSyntax ::=
3529 SEQUENCE SIZE (1..MAX) OF AccessDescription
3531 AccessDescription ::= SEQUENCE {
3532 accessMethod OBJECT IDENTIFIER,
3533 accessLocation GeneralName }
3535 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3537 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3539 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3541 static void appendInfoAccess(CFMutableArrayRef properties
,
3542 const DERItem
*extnValue
) {
3545 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3546 require_noerr_quiet(drtn
, badDER
);
3547 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3548 DERDecodedInfo adContent
;
3549 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3550 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3551 DERAccessDescription ad
;
3552 drtn
= DERParseSequenceContent(&adContent
.content
,
3553 DERNumAccessDescriptionItemSpecs
,
3554 DERAccessDescriptionItemSpecs
,
3556 require_noerr_quiet(drtn
, badDER
);
3557 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3559 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3560 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3562 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3565 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
, extnValue
);
3568 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3569 const DERItem
*extnValue
) {
3570 static const CFStringRef certTypes
[] = {
3574 SEC_OBJECT_SIGNING_KEY
,
3578 SEC_OBJECT_SIGNING_CA_KEY
3580 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3581 certTypes
, array_size(certTypes
));
3584 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3585 CFStringRef label
, const DERItem
*sequence
) {
3588 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3589 require_noerr_quiet(drtn
, badSequence
);
3590 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3591 DERDecodedInfo currDecoded
;
3592 bool appendedSomething
= false;
3593 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3594 switch (currDecoded
.tag
)
3597 case ASN1_SEQUENCE
: // 16
3598 case ASN1_SET
: // 17
3599 // skip constructed object lengths
3602 case ASN1_UTF8_STRING
: // 12
3603 case ASN1_NUMERIC_STRING
: // 18
3604 case ASN1_PRINTABLE_STRING
: // 19
3605 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3606 case ASN1_VIDEOTEX_STRING
: // 21
3607 case ASN1_IA5_STRING
: // 22
3608 case ASN1_GRAPHIC_STRING
: // 25
3609 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3610 case ASN1_GENERAL_STRING
: // 27
3611 case ASN1_UNIVERSAL_STRING
: // 28
3613 CFStringRef string
=
3614 copyDERThingContentDescription(CFGetAllocator(properties
),
3615 currDecoded
.tag
, &currDecoded
.content
, false);
3616 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3618 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3621 appendedSomething
= true;
3628 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3629 return appendedSomething
;
3634 static void appendExtension(CFMutableArrayRef parent
,
3635 const SecCertificateExtension
*extn
) {
3636 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3637 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3638 &kCFTypeArrayCallBacks
);
3640 *extnID
= &extn
->extnID
,
3641 *extnValue
= &extn
->extnValue
;
3642 CFStringRef label
= NULL
;
3643 CFStringRef localizedLabel
= NULL
;
3645 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
);
3646 require_quiet(extnID
, xit
);
3648 bool handled
= true;
3649 /* Extensions that we know how to handle ourselves... */
3650 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3651 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3653 switch (extnID
->data
[extnID
->length
- 1]) {
3654 case 14: /* SubjectKeyIdentifier id-ce 14 */
3655 appendSubjectKeyIdentifier(properties
, extnValue
);
3657 case 15: /* KeyUsage id-ce 15 */
3658 appendKeyUsage(properties
, extnValue
);
3660 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3661 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3663 case 17: /* SubjectAltName id-ce 17 */
3664 case 18: /* IssuerAltName id-ce 18 */
3665 appendGeneralNames(properties
, extnValue
);
3667 case 19: /* BasicConstraints id-ce 19 */
3668 appendBasicConstraints(properties
, extnValue
);
3670 case 30: /* NameConstraints id-ce 30 */
3671 appendNameConstraints(properties
, extnValue
);
3673 case 31: /* CRLDistributionPoints id-ce 31 */
3674 appendCrlDistributionPoints(properties
, extnValue
);
3676 case 32: /* CertificatePolicies id-ce 32 */
3677 appendCertificatePolicies(properties
, extnValue
);
3679 case 33: /* PolicyMappings id-ce 33 */
3682 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3683 appendAuthorityKeyIdentifier(properties
, extnValue
);
3685 case 36: /* PolicyConstraints id-ce 36 */
3686 appendPolicyConstraints(properties
, extnValue
);
3688 case 37: /* ExtKeyUsage id-ce 37 */
3689 appendExtendedKeyUsage(properties
, extnValue
);
3691 case 46: /* FreshestCRL id-ce 46 */
3694 case 54: /* InhibitAnyPolicy id-ce 54 */
3701 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3702 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3704 switch (extnID
->data
[extnID
->length
- 1]) {
3705 case 1: /* AuthorityInfoAccess id-pe 1 */
3706 appendInfoAccess(properties
, extnValue
);
3708 case 3: /* QCStatements id-pe 3 */
3711 case 11: /* SubjectInfoAccess id-pe 11 */
3712 appendInfoAccess(properties
, extnValue
);
3718 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3719 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3720 appendNetscapeCertType(properties
, extnValue
);
3726 /* Try to parse and display printable string(s). */
3727 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3728 /* Nothing to do here appendPrintableDERSequence did the work. */
3730 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3731 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3734 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3735 localizedLabel
= copyLocalizedOidDescription(allocator
, extnID
);
3736 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
, properties
);
3739 CFReleaseSafe(localizedLabel
);
3740 CFReleaseSafe(label
);
3741 CFReleaseSafe(properties
);
3744 /* Different types of summary types from least desired to most desired. */
3747 kSummaryTypePrintable
,
3748 kSummaryTypeOrganizationName
,
3749 kSummaryTypeOrganizationalUnitName
,
3750 kSummaryTypeCommonName
,
3754 enum SummaryType type
;
3755 CFStringRef summary
;
3756 CFStringRef description
;
3759 static OSStatus
obtainSummaryFromX501Name(void *context
,
3760 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3761 struct Summary
*summary
= (struct Summary
*)context
;
3762 enum SummaryType stype
= kSummaryTypeNone
;
3763 CFStringRef string
= NULL
;
3764 if (DEROidCompare(type
, &oidCommonName
)) {
3765 stype
= kSummaryTypeCommonName
;
3766 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3767 stype
= kSummaryTypeOrganizationalUnitName
;
3768 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3769 stype
= kSummaryTypeOrganizationName
;
3770 } else if (DEROidCompare(type
, &oidDescription
)) {
3771 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3773 if (summary
->description
) {
3774 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3775 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->description
);
3777 CFRelease(summary
->description
);
3778 summary
->description
= newDescription
;
3780 summary
->description
= string
;
3783 stype
= kSummaryTypePrintable
;
3786 stype
= kSummaryTypePrintable
;
3789 /* Build a string with all instances of the most desired
3790 component type in reverse order encountered comma separated list,
3791 The order of desirability is defined by enum SummaryType. */
3792 if (summary
->type
<= stype
) {
3794 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3797 if (summary
->type
== stype
) {
3798 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3799 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->summary
);
3802 string
= newSummary
;
3804 summary
->type
= stype
;
3806 CFReleaseSafe(summary
->summary
);
3807 summary
->summary
= string
;
3810 CFReleaseSafe(string
);
3813 return errSecSuccess
;
3816 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3817 struct Summary summary
= {};
3818 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3819 /* If we found a description and a common name we change the summary to
3820 CommonName (Description). */
3821 if (summary
.description
) {
3822 if (summary
.type
== kSummaryTypeCommonName
) {
3823 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3824 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3826 CFRelease(summary
.summary
);
3827 summary
.summary
= newSummary
;
3829 CFRelease(summary
.description
);
3832 if (!summary
.summary
) {
3833 /* If we didn't find a suitable printable string in the subject at all, we try
3834 the first email address in the certificate instead. */
3835 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3837 /* If we didn't find any email addresses in the certificate, we try finding
3838 a DNS name instead. */
3839 names
= SecCertificateCopyDNSNames(certificate
);
3842 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3843 CFRetain(summary
.summary
);
3848 return summary
.summary
;
3851 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
3852 struct Summary summary
= {};
3853 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3854 /* If we found a description and a common name we change the summary to
3855 CommonName (Description). */
3856 if (summary
.description
) {
3857 if (summary
.type
== kSummaryTypeCommonName
) {
3858 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3859 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3861 CFRelease(summary
.summary
);
3862 summary
.summary
= newSummary
;
3864 CFRelease(summary
.description
);
3867 return summary
.summary
;
3870 /* Return the earliest date on which all certificates in this chain are still
3872 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3873 SecCertificateRef certificate
) {
3874 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3876 while (certificate
->_parent
) {
3877 certificate
= certificate
->_parent
;
3878 if (earliest
> certificate
->_notAfter
)
3879 earliest
= certificate
->_notAfter
;
3886 /* Return the latest date on which all certificates in this chain will be
3888 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3889 SecCertificateRef certificate
) {
3890 CFAbsoluteTime latest
= certificate
->_notBefore
;
3892 while (certificate
->_parent
) {
3893 certificate
= certificate
->_parent
;
3894 if (latest
< certificate
->_notBefore
)
3895 latest
= certificate
->_notBefore
;
3902 bool SecCertificateIsValid(SecCertificateRef certificate
,
3903 CFAbsoluteTime verifyTime
) {
3904 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
3905 verifyTime
<= certificate
->_notAfter
;
3908 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
3909 return certificate
->_version
+ 1;
3912 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
3913 return certificate
->_notBefore
;
3916 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
3917 return certificate
->_notAfter
;
3920 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3921 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
3922 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3923 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3924 &kCFTypeArrayCallBacks
);
3926 /* First we put the subject summary name. */
3927 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
3929 appendProperty(summary
, kSecPropertyTypeTitle
,
3930 NULL
, NULL
, ssummary
);
3931 CFRelease(ssummary
);
3934 /* Let see if this certificate is currently valid. */
3936 CFAbsoluteTime when
;
3937 CFStringRef message
;
3939 if (verifyTime
> certificate
->_notAfter
) {
3940 label
= SEC_EXPIRED_KEY
;
3941 when
= certificate
->_notAfter
;
3942 ptype
= kSecPropertyTypeError
;
3943 message
= SEC_CERT_EXPIRED_KEY
;
3944 } else if (certificate
->_notBefore
> verifyTime
) {
3945 label
= SEC_VALID_FROM_KEY
;
3946 when
= certificate
->_notBefore
;
3947 ptype
= kSecPropertyTypeError
;
3948 message
= SEC_CERT_NOT_YET_VALID_KEY
;
3950 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3951 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3952 if (verifyTime
> last
) {
3953 label
= SEC_EXPIRED_KEY
;
3955 ptype
= kSecPropertyTypeError
;
3956 message
= SEC_ISSUER_EXPIRED_KEY
;
3957 } else if (verifyTime
< first
) {
3958 label
= SEC_VALID_FROM_KEY
;
3960 ptype
= kSecPropertyTypeError
;
3961 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
3963 label
= SEC_EXPIRES_KEY
;
3964 when
= certificate
->_notAfter
;
3965 ptype
= kSecPropertyTypeSuccess
;
3966 message
= SEC_CERT_VALID_KEY
;
3970 appendDateProperty(summary
, label
, when
);
3971 CFStringRef lmessage
= SecCopyCertString(message
);
3972 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
);
3973 CFRelease(lmessage
);
3978 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
3979 if (!certificate
->_properties
) {
3980 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3981 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3982 &kCFTypeArrayCallBacks
);
3984 /* First we put the Subject Name in the property list. */
3985 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3986 &certificate
->_subject
);
3987 appendProperty(properties
, kSecPropertyTypeSection
,
3988 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
);
3989 CFRelease(subject_plist
);
3991 /* Next we put the Issuer Name in the property list. */
3992 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3993 &certificate
->_issuer
);
3994 appendProperty(properties
, kSecPropertyTypeSection
,
3995 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
);
3996 CFRelease(issuer_plist
);
3999 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4000 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4001 NULL
, fmt
, certificate
->_version
+ 1);
4003 appendProperty(properties
, kSecPropertyTypeString
,
4004 SEC_VERSION_KEY
, NULL
, versionString
);
4005 CFRelease(versionString
);
4008 appendSerialNumberProperty(properties
, SEC_SERIAL_NUMBER_KEY
, &certificate
->_serialNum
);
4010 /* Validity dates. */
4011 appendValidityPeriodProperty(properties
, SEC_VALIDITY_PERIOD_KEY
, certificate
);
4013 if (certificate
->_subjectUniqueID
.length
) {
4014 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4015 &certificate
->_subjectUniqueID
);
4017 if (certificate
->_issuerUniqueID
.length
) {
4018 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4019 &certificate
->_issuerUniqueID
);
4022 appendPublicKeyProperty(properties
, SEC_PUBLIC_KEY_KEY
, certificate
);
4025 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4026 appendExtension(properties
, &certificate
->_extensions
[ix
]);
4030 appendSignatureProperty(properties
, SEC_SIGNATURE_KEY
, certificate
);
4032 appendFingerprintsProperty(properties
, SEC_FINGERPRINTS_KEY
, certificate
);
4034 certificate
->_properties
= properties
;
4037 CFRetain(certificate
->_properties
);
4038 return certificate
->_properties
;
4042 /* On OS X, the SecCertificateCopySerialNumber API takes two arguments. */
4043 CFDataRef
SecCertificateCopySerialNumber(
4044 SecCertificateRef certificate
,
4045 CFErrorRef
*error
) {
4048 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4052 if (certificate
->_serialNumber
) {
4053 CFRetain(certificate
->_serialNumber
);
4055 return certificate
->_serialNumber
;
4058 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4059 CFDataRef
SecCertificateCopySerialNumber(
4060 SecCertificateRef certificate
) {
4061 if (certificate
->_serialNumber
) {
4062 CFRetain(certificate
->_serialNumber
);
4064 return certificate
->_serialNumber
;
4068 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4069 SecCertificateRef certificate
) {
4070 return certificate
->_normalizedIssuer
;
4073 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4074 SecCertificateRef certificate
) {
4075 return certificate
->_normalizedSubject
;
4078 /* Verify that certificate was signed by issuerKey. */
4079 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4080 SecKeyRef issuerKey
) {
4081 /* Setup algId in SecAsn1AlgId format. */
4083 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4084 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4085 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4086 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4088 /* RFC5280 4.1.1.2, 4.1.2.3 requires the actual signature algorithm
4089 must match the specified algorithm in the TBSCertificate. */
4090 bool sigAlgMatch
= DEROidCompare(&certificate
->_sigAlg
.oid
,
4091 &certificate
->_tbsSigAlg
.oid
);
4093 secwarning("Signature algorithm mismatch in certificate (see RFC5280 4.1.1.2)");
4096 CFErrorRef error
= NULL
;
4098 !SecVerifySignatureWithPublicKey(issuerKey
, &algId
,
4099 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4100 certificate
->_signature
.data
, certificate
->_signature
.length
, &error
))
4102 #if !defined(NDEBUG)
4103 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, (error
) ? (OSStatus
)CFErrorGetCode(error
) : errSecNotSigner
);
4105 CFReleaseSafe(error
);
4106 return errSecNotSigner
;
4109 return errSecSuccess
;
4112 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4113 if (!certificate
->_subjectAltName
) {
4116 return &certificate
->_subjectAltName
->extnValue
;
4119 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4120 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4121 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4122 if (gnType
== GNT_IPAddress
) {
4123 CFStringRef string
= copyIPAddressContentDescription(
4124 kCFAllocatorDefault
, generalName
);
4126 CFArrayAppendValue(ipAddresses
, string
);
4129 return errSecInvalidCertificate
;
4132 return errSecSuccess
;
4135 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4136 /* These can only exist in the subject alt name. */
4137 if (!certificate
->_subjectAltName
)
4140 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4141 0, &kCFTypeArrayCallBacks
);
4142 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4143 ipAddresses
, appendIPAddressesFromGeneralNames
);
4144 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4145 CFRelease(ipAddresses
);
4151 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4152 const DERItem
*generalName
) {
4153 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4154 if (gnType
== GNT_DNSName
) {
4155 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4156 generalName
->data
, generalName
->length
,
4157 kCFStringEncodingUTF8
, FALSE
);
4159 CFArrayAppendValue(dnsNames
, string
);
4162 return errSecInvalidCertificate
;
4165 return errSecSuccess
;
4168 /* Return true if the passed in string matches the
4169 Preferred name syntax from sections 2.3.1. in RFC 1035.
4170 With the added check that we disallow empty dns names.
4171 Also in order to support wildcard DNSNames we allow for the '*'
4172 character anywhere in a dns component where we currently allow
4175 <domain> ::= <subdomain> | " "
4177 <subdomain> ::= <label> | <subdomain> "." <label>
4179 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4181 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4183 <let-dig-hyp> ::= <let-dig> | "-"
4185 <let-dig> ::= <letter> | <digit>
4187 <letter> ::= any one of the 52 alphabetic characters A through Z in
4188 upper case and a through z in lower case
4190 <digit> ::= any one of the ten digits 0 through 9
4192 static bool isDNSName(CFStringRef string
) {
4193 CFStringInlineBuffer buf
= {};
4194 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4195 /* From RFC 1035 2.3.4. Size limits:
4196 labels 63 octets or less
4197 names 255 octets or less */
4198 require_quiet(length
<= 255, notDNS
);
4199 CFRange range
= { 0, length
};
4200 CFStringInitInlineBuffer(string
, &buf
, range
);
4204 kDNSStateAfterAlpha
,
4205 kDNSStateAfterDigit
,
4207 } state
= kDNSStateInital
;
4209 for (ix
= 0; ix
< length
; ++ix
) {
4210 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4213 require_quiet(labelLength
<= 64 &&
4214 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4216 state
= kDNSStateAfterDot
;
4218 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4220 state
= kDNSStateAfterAlpha
;
4221 } else if ('0' <= ch
&& ch
<= '9') {
4223 /* The requirement for labels to start with a letter was
4224 dropped so we don't check this anymore. */
4225 require_quiet(state
== kDNSStateAfterAlpha
||
4226 state
== kDNSStateAfterDigit
||
4227 state
== kDNSStateAfterDash
, notDNS
);
4229 state
= kDNSStateAfterDigit
;
4230 } else if (ch
== '-') {
4231 require_quiet(state
== kDNSStateAfterAlpha
||
4232 state
== kDNSStateAfterDigit
||
4233 state
== kDNSStateAfterDash
, notDNS
);
4234 state
= kDNSStateAfterDash
;
4240 /* We don't allow a dns name to end in a dot or dash. */
4241 require_quiet(labelLength
<= 63 &&
4242 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4250 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4251 const DERItem
*value
, CFIndex rdnIX
) {
4252 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4253 if (DEROidCompare(type
, &oidCommonName
)) {
4254 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4257 if (isDNSName(string
)) {
4258 /* We found a common name that is formatted like a valid
4260 CFArrayAppendValue(dnsNames
, string
);
4264 return errSecInvalidCertificate
;
4267 return errSecSuccess
;
4270 /* Not everything returned by this function is going to be a proper DNS name,
4271 we also return the certificates common name entries from the subject,
4272 assuming they look like dns names as specified in RFC 1035. */
4273 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4274 /* These can exist in the subject alt name or in the subject. */
4275 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4276 0, &kCFTypeArrayCallBacks
);
4277 OSStatus status
= errSecSuccess
;
4278 if (certificate
->_subjectAltName
) {
4279 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4280 dnsNames
, appendDNSNamesFromGeneralNames
);
4282 /* RFC 2818 section 3.1. Server Identity
4284 If a subjectAltName extension of type dNSName is present, that MUST
4285 be used as the identity. Otherwise, the (most specific) Common Name
4286 field in the Subject field of the certificate MUST be used. Although
4287 the use of the Common Name is existing practice, it is deprecated and
4288 Certification Authorities are encouraged to use the dNSName instead.
4291 This implies that if we found 1 or more DNSNames in the
4292 subjectAltName, we should not use the Common Name of the subject as
4295 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4296 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4297 appendDNSNamesFromX501Name
);
4299 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4300 CFRelease(dnsNames
);
4306 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4307 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4308 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4309 if (gnType
== GNT_RFC822Name
) {
4310 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4311 generalName
->data
, generalName
->length
,
4312 kCFStringEncodingASCII
, FALSE
);
4314 CFArrayAppendValue(dnsNames
, string
);
4317 return errSecInvalidCertificate
;
4320 return errSecSuccess
;
4323 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4324 const DERItem
*value
, CFIndex rdnIX
) {
4325 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4326 if (DEROidCompare(type
, &oidEmailAddress
)) {
4327 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4330 CFArrayAppendValue(dnsNames
, string
);
4333 return errSecInvalidCertificate
;
4336 return errSecSuccess
;
4339 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4340 /* These can exist in the subject alt name or in the subject. */
4341 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4342 0, &kCFTypeArrayCallBacks
);
4343 OSStatus status
= errSecSuccess
;
4344 if (certificate
->_subjectAltName
) {
4345 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4346 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4349 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4350 appendRFC822NamesFromX501Name
);
4352 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4353 CFRelease(rfc822Names
);
4359 OSStatus
SecCertificateCopyEmailAddresses(SecCertificateRef certificate
, CFArrayRef
* __nonnull CF_RETURNS_RETAINED emailAddresses
) {
4360 if (!certificate
|| !emailAddresses
) {
4363 *emailAddresses
= SecCertificateCopyRFC822Names(certificate
);
4364 return errSecSuccess
;
4367 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4368 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4369 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4370 if (DEROidCompare(type
, &oidCommonName
)) {
4371 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4374 CFArrayAppendValue(commonNames
, string
);
4377 return errSecInvalidCertificate
;
4380 return errSecSuccess
;
4383 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4384 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4385 0, &kCFTypeArrayCallBacks
);
4387 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4388 appendCommonNamesFromX501Name
);
4389 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4390 CFRelease(commonNames
);
4396 OSStatus
SecCertificateCopyCommonName(SecCertificateRef certificate
, CFStringRef
*commonName
)
4401 CFArrayRef commonNames
= SecCertificateCopyCommonNames(certificate
);
4403 return errSecInternal
;
4407 CFIndex count
= CFArrayGetCount(commonNames
);
4408 *commonName
= CFRetainSafe(CFArrayGetValueAtIndex(commonNames
, count
-1));
4410 CFReleaseSafe(commonNames
);
4411 return errSecSuccess
;
4414 static OSStatus
appendOrganizationFromX501Name(void *context
,
4415 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4416 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4417 if (DEROidCompare(type
, &oidOrganizationName
)) {
4418 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4421 CFArrayAppendValue(organization
, string
);
4424 return errSecInvalidCertificate
;
4427 return errSecSuccess
;
4430 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4431 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4432 0, &kCFTypeArrayCallBacks
);
4434 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4435 appendOrganizationFromX501Name
);
4436 if (status
|| CFArrayGetCount(organization
) == 0) {
4437 CFRelease(organization
);
4438 organization
= NULL
;
4440 return organization
;
4443 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4444 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4445 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4446 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4447 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4450 CFArrayAppendValue(organizationalUnit
, string
);
4453 return errSecInvalidCertificate
;
4456 return errSecSuccess
;
4459 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4460 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4461 0, &kCFTypeArrayCallBacks
);
4463 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4464 appendOrganizationalUnitFromX501Name
);
4465 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4466 CFRelease(organizationalUnit
);
4467 organizationalUnit
= NULL
;
4469 return organizationalUnit
;
4472 static OSStatus
appendCountryFromX501Name(void *context
,
4473 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4474 CFMutableArrayRef countries
= (CFMutableArrayRef
)context
;
4475 if (DEROidCompare(type
, &oidCountryName
)) {
4476 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4479 CFArrayAppendValue(countries
, string
);
4482 return errSecInvalidCertificate
;
4485 return errSecSuccess
;
4488 CFArrayRef
SecCertificateCopyCountry(SecCertificateRef certificate
) {
4489 CFMutableArrayRef countries
= CFArrayCreateMutable(kCFAllocatorDefault
,
4490 0, &kCFTypeArrayCallBacks
);
4492 status
= parseX501NameContent(&certificate
->_subject
, countries
,
4493 appendCountryFromX501Name
);
4494 if (status
|| CFArrayGetCount(countries
) == 0) {
4495 CFRelease(countries
);
4501 const SecCEBasicConstraints
*
4502 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4503 if (certificate
->_basicConstraints
.present
)
4504 return &certificate
->_basicConstraints
;
4509 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
4510 return (certificate
->_permittedSubtrees
);
4513 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
4514 return (certificate
->_excludedSubtrees
);
4517 const SecCEPolicyConstraints
*
4518 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4519 if (certificate
->_policyConstraints
.present
)
4520 return &certificate
->_policyConstraints
;
4525 const SecCEPolicyMappings
*
4526 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4527 if (certificate
->_policyMappings
.present
) {
4528 return &certificate
->_policyMappings
;
4534 const SecCECertificatePolicies
*
4535 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4536 if (certificate
->_certificatePolicies
.present
)
4537 return &certificate
->_certificatePolicies
;
4542 const SecCEInhibitAnyPolicy
*
4543 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4544 if (certificate
->_inhibitAnyPolicySkipCerts
.present
) {
4545 return &certificate
->_inhibitAnyPolicySkipCerts
;
4551 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4552 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4553 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4554 if (gnType
== GNT_OtherName
) {
4556 DERReturn drtn
= DERParseSequenceContent(generalName
,
4557 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4559 require_noerr_quiet(drtn
, badDER
);
4560 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4562 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4563 &on
.value
, true), badDER
);
4564 CFArrayAppendValue(ntPrincipalNames
, string
);
4568 return errSecSuccess
;
4571 return errSecInvalidCertificate
;
4575 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
4576 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4577 0, &kCFTypeArrayCallBacks
);
4578 OSStatus status
= errSecSuccess
;
4579 if (certificate
->_subjectAltName
) {
4580 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4581 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4583 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4584 CFRelease(ntPrincipalNames
);
4585 ntPrincipalNames
= NULL
;
4587 return ntPrincipalNames
;
4590 static OSStatus
appendToRFC2253String(void *context
,
4591 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4592 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4596 ST stateOrProvinceName
4598 OU organizationalUnitName
4600 STREET streetAddress
4604 /* Prepend a + if this is not the first RDN in an RDN set.
4605 Otherwise prepend a , if this is not the first RDN. */
4607 CFStringAppend(string
, CFSTR("+"));
4608 else if (CFStringGetLength(string
)) {
4609 CFStringAppend(string
, CFSTR(","));
4612 CFStringRef label
, oid
= NULL
;
4613 /* @@@ Consider changing this to a dictionary lookup keyed by the
4614 decimal representation. */
4615 if (DEROidCompare(type
, &oidCommonName
)) {
4616 label
= CFSTR("CN");
4617 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4619 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4620 label
= CFSTR("ST");
4621 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4623 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4624 label
= CFSTR("OU");
4625 } else if (DEROidCompare(type
, &oidCountryName
)) {
4628 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4629 label
= CFSTR("STREET");
4630 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4631 label
= CFSTR("DC");
4632 } else if (DEROidCompare(type
, &oidUserID
)) {
4633 label
= CFSTR("UID");
4636 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4639 CFStringAppend(string
, label
);
4640 CFStringAppend(string
, CFSTR("="));
4641 CFStringRef raw
= NULL
;
4643 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4646 /* Append raw to string while escaping:
4647 a space or "#" character occurring at the beginning of the string
4648 a space character occurring at the end of the string
4649 one of the characters ",", "+", """, "\", "<", ">" or ";"
4651 CFStringInlineBuffer buffer
= {};
4652 CFIndex ix
, length
= CFStringGetLength(raw
);
4653 CFRange range
= { 0, length
};
4654 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4655 for (ix
= 0; ix
< length
; ++ix
) {
4656 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4658 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4659 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4660 ch
== '<' || ch
== '>' || ch
== ';' ||
4661 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4662 (ch
== '#' && ix
== 0)) {
4663 UniChar chars
[] = { '\\', ch
};
4664 CFStringAppendCharacters(string
, chars
, 2);
4666 CFStringAppendCharacters(string
, &ch
, 1);
4671 /* Append the value in hex. */
4672 CFStringAppend(string
, CFSTR("#"));
4674 for (ix
= 0; ix
< value
->length
; ++ix
)
4675 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4680 return errSecSuccess
;
4683 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
4684 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4685 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4686 if (status
|| CFStringGetLength(string
) == 0) {
4693 static OSStatus
appendToCompanyNameString(void *context
,
4694 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4695 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4696 if (CFStringGetLength(string
) != 0)
4697 return errSecSuccess
;
4699 if (!DEROidCompare(type
, &oidOrganizationName
))
4700 return errSecSuccess
;
4703 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4705 return errSecSuccess
;
4706 CFStringAppend(string
, raw
);
4709 return errSecSuccess
;
4712 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
4713 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4714 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4715 appendToCompanyNameString
);
4716 if (status
|| CFStringGetLength(string
) == 0) {
4723 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4724 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4725 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4726 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4728 CFDataSetLength(sequence
, sequence_length
);
4729 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4730 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
4731 require_noerr_quiet(DEREncodeLength(content
->length
,
4732 sequence_ptr
, &seq_len_length
), out
);
4733 sequence_ptr
+= seq_len_length
;
4734 memcpy(sequence_ptr
, content
->data
, content
->length
);
4737 CFReleaseSafe(sequence
);
4741 CFDataRef
SecCertificateCopyIssuerSequence(
4742 SecCertificateRef certificate
) {
4743 return SecDERItemCopySequence(&certificate
->_issuer
);
4746 CFDataRef
SecCertificateCopySubjectSequence(
4747 SecCertificateRef certificate
) {
4748 return SecDERItemCopySequence(&certificate
->_subject
);
4751 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(SecCertificateRef certificate
) {
4752 if (!certificate
|| !certificate
->_normalizedIssuer
) {
4756 tmpItem
.data
= (void *)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
4757 tmpItem
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
4759 return SecDERItemCopySequence(&tmpItem
);
4762 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(SecCertificateRef certificate
) {
4763 if (!certificate
|| !certificate
->_normalizedSubject
) {
4767 tmpItem
.data
= (void *)CFDataGetBytePtr(certificate
->_normalizedSubject
);
4768 tmpItem
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
4770 return SecDERItemCopySequence(&tmpItem
);
4773 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4774 SecCertificateRef certificate
) {
4775 return &certificate
->_algId
;
4778 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
4779 return &certificate
->_pubKeyDER
;
4783 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
4784 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
4786 __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
4788 __nullable SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
4791 if (certificate
->_pubKey
== NULL
) {
4792 const DERAlgorithmId
*algId
=
4793 SecCertificateGetPublicKeyAlgorithm(certificate
);
4794 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4795 const DERItem
*params
= NULL
;
4796 if (algId
->params
.length
!= 0) {
4797 params
= &algId
->params
;
4799 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
4800 SecAsn1Item params1
= {
4801 .Data
= params
? params
->data
: NULL
,
4802 .Length
= params
? params
->length
: 0
4804 SecAsn1Item keyData1
= {
4805 .Data
= keyData
? keyData
->data
: NULL
,
4806 .Length
= keyData
? keyData
->length
: 0
4808 certificate
->_pubKey
= SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
4812 return CFRetainSafe(certificate
->_pubKey
);
4815 bool SecCertificateIsWeakKey(SecCertificateRef certificate
) {
4817 SecKeyRef pubKey
= NULL
;
4819 require_quiet(pubKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
4821 require_quiet(pubKey
= SecCertificateCopyPublicKey(certificate
) ,out
);
4823 size_t size
= SecKeyGetBlockSize(pubKey
);
4824 switch (SecKeyGetAlgorithmIdentifier(pubKey
)) {
4825 case kSecRSAAlgorithmID
:
4826 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
4828 case kSecECDSAAlgorithmID
:
4829 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
4836 CFReleaseSafe(pubKey
);
4840 bool SecCertificateIsWeakHash(SecCertificateRef certificate
) {
4841 SecSignatureHashAlgorithm certAlg
= 0;
4842 certAlg
= SecCertificateGetSignatureHashAlgorithm(certificate
);
4843 if (certAlg
== kSecSignatureHashAlgorithmUnknown
||
4844 certAlg
== kSecSignatureHashAlgorithmMD2
||
4845 certAlg
== kSecSignatureHashAlgorithmMD4
||
4846 certAlg
== kSecSignatureHashAlgorithmMD5
||
4847 certAlg
== kSecSignatureHashAlgorithmSHA1
) {
4853 bool SecCertificateIsAtLeastMinKeySize(SecCertificateRef certificate
,
4854 CFDictionaryRef keySizes
) {
4855 bool goodSize
= false;
4856 SecKeyRef pubKey
= NULL
;
4858 require_quiet(pubKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
4860 require_quiet(pubKey
= SecCertificateCopyPublicKey(certificate
) ,out
);
4862 size_t size
= SecKeyGetBlockSize(pubKey
);
4863 CFNumberRef minSize
;
4864 size_t minSizeInBits
;
4865 switch (SecKeyGetAlgorithmIdentifier(pubKey
)) {
4866 case kSecRSAAlgorithmID
:
4867 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeRSA
, (const void**)&minSize
)
4868 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
4869 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
4872 case kSecECDSAAlgorithmID
:
4873 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeEC
, (const void**)&minSize
)
4874 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
4875 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
4882 CFReleaseSafe(pubKey
);
4886 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
4887 if (!certificate
|| !certificate
->_der
.data
) {
4890 if (!certificate
->_sha1Digest
) {
4891 certificate
->_sha1Digest
=
4892 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4893 certificate
->_der
.data
, certificate
->_der
.length
);
4895 return certificate
->_sha1Digest
;
4898 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
4899 if (!certificate
|| !certificate
->_der
.data
) {
4902 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4903 certificate
->_der
.data
, certificate
->_der
.length
);
4906 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
4907 CFDataRef digest
= NULL
;
4908 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
4910 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4911 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4917 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
4918 if (!certificate
|| !certificate
->_pubKeyDER
.data
) {
4921 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4922 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4925 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA1Digest(SecCertificateRef certificate
) {
4926 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
4929 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4930 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
4933 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
4934 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
4937 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4938 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
4941 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
4946 CFRetainSafe(certificate
->_keychain_item
);
4947 return certificate
->_keychain_item
;
4950 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
4954 if (!certificate
->_authorityKeyID
&&
4955 certificate
->_authorityKeyIdentifier
.length
) {
4956 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4957 certificate
->_authorityKeyIdentifier
.data
,
4958 certificate
->_authorityKeyIdentifier
.length
);
4961 return certificate
->_authorityKeyID
;
4964 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
4968 if (!certificate
->_subjectKeyID
&&
4969 certificate
->_subjectKeyIdentifier
.length
) {
4970 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4971 certificate
->_subjectKeyIdentifier
.data
,
4972 certificate
->_subjectKeyIdentifier
.length
);
4975 return certificate
->_subjectKeyID
;
4978 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
4982 return certificate
->_crlDistributionPoints
;
4985 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
4989 return certificate
->_ocspResponders
;
4992 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
4996 return certificate
->_caIssuers
;
4999 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5003 return certificate
->_subjectAltName
&&
5004 certificate
->_subjectAltName
->critical
;
5007 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5011 /* Since the _subject field is the content of the subject and not the
5012 whole thing, we can simply check for a 0 length subject here. */
5013 return certificate
->_subject
.length
!= 0;
5016 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5020 return certificate
->_foundUnknownCriticalExtension
;
5023 /* Private API functions. */
5024 void SecCertificateShow(SecCertificateRef certificate
) {
5026 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5027 fprintf(stderr
, "\n");
5031 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5032 SecCertificateRef certificate
) {
5033 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5034 CFNumberRef certificateType
, certificateEncoding
;
5035 CFStringRef label
, alias
;
5036 CFDataRef skid
, pubKeyDigest
, certData
;
5037 CFDictionaryRef dict
= NULL
;
5041 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5042 SInt32 ctv
= certificate
->_version
+ 1;
5043 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5044 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5045 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5046 certData
= SecCertificateCopyData(certificate
);
5047 skid
= SecCertificateGetSubjectKeyID(certificate
);
5048 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5049 certificate
->_pubKeyDER
.length
);
5051 /* We still need to figure out how to deal with multi valued attributes. */
5052 alias
= SecCertificateCopyRFC822Names(certificate
);
5053 label
= SecCertificateCopySubjectSummary(certificate
);
5059 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5060 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5061 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5063 DICT_ADDPAIR(kSecAttrLabel
, label
);
5065 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5066 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5067 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5068 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5070 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5071 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5072 DICT_ADDPAIR(kSecValueData
, certData
);
5073 dict
= DICT_CREATE(allocator
);
5075 CFReleaseSafe(label
);
5076 CFReleaseSafe(pubKeyDigest
);
5077 CFReleaseSafe(certData
);
5078 CFReleaseSafe(certificateEncoding
);
5079 CFReleaseSafe(certificateType
);
5084 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5085 CFDictionaryRef refAttributes
) {
5086 /* @@@ Support having an allocator in refAttributes. */
5087 CFAllocatorRef allocator
= NULL
;
5088 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5089 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5093 static bool _SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
5094 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
5095 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
5096 SecKeyRef publicKey
= NULL
;
5097 require(certificate
&& (CFGetTypeID(certificate
) == SecCertificateGetTypeID()), out
);
5099 require(publicKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
5101 require(publicKey
= SecCertificateCopyPublicKey(certificate
), out
);
5103 CFDataRef normalizedIssuer
=
5104 SecCertificateGetNormalizedIssuerContent(certificate
);
5105 CFDataRef normalizedSubject
=
5106 SecCertificateGetNormalizedSubjectContent(certificate
);
5107 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5108 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5110 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5111 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5112 if (authorityKeyID
) {
5113 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5116 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5118 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
5120 CFReleaseSafe(publicKey
);
5123 return (certificate
->_isSelfSigned
== kSecSelfSignedTrue
);
5126 bool SecCertificateIsCA(SecCertificateRef certificate
) {
5127 bool result
= false;
5128 require(certificate
&& (CFGetTypeID(certificate
) == SecCertificateGetTypeID()), out
);
5129 if (SecCertificateVersion(certificate
) >= 3) {
5130 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5131 result
= (basicConstraints
&& basicConstraints
->isCA
);
5134 result
= _SecCertificateIsSelfSigned(certificate
);
5140 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5141 return (_SecCertificateIsSelfSigned(certificate
) && SecCertificateIsCA(certificate
));
5144 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5145 if (!certificate
|| (CFGetTypeID(certificate
) != SecCertificateGetTypeID())) {
5146 return errSecInvalidCertificate
;
5148 if (!isSelfSigned
) {
5151 *isSelfSigned
= _SecCertificateIsSelfSigned(certificate
);
5152 return errSecSuccess
;
5155 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5157 return kSecKeyUsageUnspecified
;
5159 return certificate
->_keyUsage
;
5162 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5164 CFMutableArrayRef extended_key_usage_oids
=
5165 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5166 require_quiet(certificate
&& extended_key_usage_oids
, out
);
5168 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5169 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5170 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5171 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5174 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5175 require_noerr_quiet(drtn
, out
);
5176 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5177 DERDecodedInfo currDecoded
;
5179 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5180 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5181 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5182 currDecoded
.content
.data
, currDecoded
.content
.length
);
5184 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5188 require_quiet(drtn
== DR_EndOfSequence
, out
);
5189 return extended_key_usage_oids
;
5193 CFReleaseSafe(extended_key_usage_oids
);
5197 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5199 require_quiet(certificate
, out
);
5202 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5203 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5204 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5205 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5206 /* Got the SCT oid */
5207 DERDecodedInfo sctList
;
5208 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5209 require_noerr_quiet(drtn
, out
);
5210 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5211 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5219 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5220 if (der
.length
> 1) {
5221 DERDecodedInfo decoded
;
5222 DERDecodeItem(&der
, &decoded
);
5223 switch (decoded
.tag
) {
5226 return decoded
.content
.length
== 0 && expected
== NULL
;
5230 case ASN1_IA5_STRING
:
5231 case ASN1_UTF8_STRING
: {
5232 if (isString(expected
)) {
5233 CFStringRef expectedString
= (CFStringRef
) expected
;
5234 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5236 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5237 CFReleaseNull(itemString
);
5243 case ASN1_OCTET_STRING
: {
5244 if (isData(expected
)) {
5245 CFDataRef expectedData
= (CFDataRef
) expected
;
5246 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5248 bool result
= CFEqual(expectedData
, itemData
);
5249 CFReleaseNull(itemData
);
5255 case ASN1_INTEGER
: {
5256 SInt32 expected_value
= 0;
5257 if (isString(expected
))
5259 CFStringRef aStr
= (CFStringRef
)expected
;
5260 expected_value
= CFStringGetIntValue(aStr
);
5262 else if (isNumber(expected
))
5264 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5267 uint32_t num_value
= 0;
5268 if (!DERParseInteger(&decoded
.content
, &num_value
))
5270 return ((uint32_t)expected_value
== num_value
);
5283 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5286 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5287 size_t oid_len
= CFDataGetLength(oid
);
5289 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5290 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5291 if (extn
->extnID
.length
== oid_len
5292 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5294 return matches_expected(extn
->extnValue
, expectedValue
);
5300 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5302 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5305 struct search_context
{
5307 SecCertificateRef certificate
;
5310 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5312 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5313 bool result
= false;
5315 if ( CFStringGetLength(string
) > 0
5316 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5319 *value
= CFStringGetIntValue(string
);
5323 CFReleaseNull(nonDecimalDigit
);
5328 bool SecCertificateIsOidString(CFStringRef oid
)
5330 if (!oid
) return false;
5331 if (2 >= CFStringGetLength(oid
)) return false;
5334 /* oid string only has the allowed characters */
5335 CFCharacterSetRef decimalOid
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
5336 CFCharacterSetRef nonDecimalOid
= CFCharacterSetCreateInvertedSet(NULL
, decimalOid
);
5337 if (CFStringFindCharacterFromSet(oid
, nonDecimalOid
, CFRangeMake(0, CFStringGetLength(oid
)), kCFCompareForcedOrdering
, NULL
)) {
5341 /* first arc is allowed */
5342 UniChar firstArc
[2];
5343 CFRange firstTwo
= {0, 2};
5344 CFStringGetCharacters(oid
, firstTwo
, firstArc
);
5345 if (firstArc
[1] != '.' ||
5346 (firstArc
[0] != '0' && firstArc
[0] != '1' && firstArc
[0] != '2')) {
5350 CFReleaseNull(decimalOid
);
5351 CFReleaseNull(nonDecimalOid
);
5356 CFDataRef
SecCertificateCreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
5358 CFMutableDataRef currentResult
= NULL
;
5359 CFDataRef encodedResult
= NULL
;
5361 CFArrayRef parts
= NULL
;
5364 if (!string
|| !SecCertificateIsOidString(string
))
5367 parts
= CFStringCreateArrayBySeparatingStrings(NULL
, string
, CFSTR("."));
5372 count
= CFArrayGetCount(parts
);
5376 // assume no more than 5 bytes needed to represent any part of the oid,
5377 // since we limit parts to 32-bit values,
5378 // but the first two parts only need 1 byte
5379 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
5385 part
= CFArrayGetValueAtIndex(parts
, 0);
5387 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
5394 part
= CFArrayGetValueAtIndex(parts
, 1);
5396 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
5402 CFDataAppendBytes(currentResult
, &firstByte
, 1);
5404 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
5405 uint8_t b
[5] = {0, 0, 0, 0, 0};
5407 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
5408 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
5409 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
5410 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
5412 // Skip the unused extension bytes.
5413 size_t skipBytes
= 0;
5414 while (b
[skipBytes
] == 0x80)
5417 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
5420 encodedResult
= currentResult
;
5421 currentResult
= NULL
;
5424 CFReleaseNull(parts
);
5425 CFReleaseNull(currentResult
);
5427 return encodedResult
;
5430 static void check_for_marker(const void *key
, const void *value
, void *context
)
5432 struct search_context
* search_ctx
= (struct search_context
*) context
;
5433 CFStringRef key_string
= (CFStringRef
) key
;
5434 CFTypeRef value_ref
= (CFTypeRef
) value
;
5436 // If we could have short circuted the iteration
5437 // we would have, but the best we can do
5438 // is not waste time comparing once a match
5440 if (search_ctx
->found
)
5443 if (CFGetTypeID(key_string
) != CFStringGetTypeID())
5446 CFDataRef key_data
= SecCertificateCreateOidDataFromString(NULL
, key_string
);
5448 if (NULL
== key_data
)
5451 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
5452 search_ctx
->found
= true;
5454 CFReleaseNull(key_data
);
5458 // CFType Ref is either:
5460 // CFData - OID to match with no data permitted
5461 // CFString - decimal OID to match
5462 // CFDictionary - OID -> Value table for expected values Single Object or Array
5463 // CFArray - Array of the above.
5465 // This returns true if any of the requirements are met.
5466 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
5468 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
5469 CFIndex ix
, length
= CFArrayGetCount(oids
);
5470 for (ix
= 0; ix
< length
; ix
++)
5471 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
5473 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
5474 struct search_context context
= { .found
= false, .certificate
= certificate
};
5475 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
5476 return context
.found
;
5477 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
5478 return cert_contains_marker_extension(certificate
, oids
);
5479 } else if (CFGetTypeID(oids
) == CFStringGetTypeID()) {
5480 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oids
);
5481 if (dataOid
== NULL
) return false;
5482 bool result
= cert_contains_marker_extension(certificate
, dataOid
);
5483 CFReleaseNull(dataOid
);
5489 static DERItem
*cert_extension_value_for_marker(SecCertificateRef certificate
, CFDataRef oid
) {
5491 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5492 size_t oid_len
= CFDataGetLength(oid
);
5494 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5495 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5496 if (extn
->extnID
.length
== oid_len
5497 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5499 return (DERItem
*)&extn
->extnValue
;
5506 // CFType Ref is either:
5508 // CFData - OID to match with no data permitted
5509 // CFString - decimal OID to match
5511 DERItem
*SecCertificateGetExtensionValue(SecCertificateRef certificate
, CFTypeRef oid
) {
5512 if (!certificate
|| !oid
) {
5516 if(CFGetTypeID(oid
) == CFDataGetTypeID()) {
5517 return cert_extension_value_for_marker(certificate
, oid
);
5518 } else if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
5519 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oid
);
5520 if (dataOid
== NULL
) return NULL
;
5521 DERItem
*result
= cert_extension_value_for_marker(certificate
, dataOid
);
5522 CFReleaseNull(dataOid
);
5529 CFDataRef
SecCertificateCopyiAPAuthCapabilities(SecCertificateRef certificate
) {
5533 CFDataRef extensionData
= NULL
;
5534 DERItem
*extensionValue
= NULL
;
5535 extensionValue
= SecCertificateGetExtensionValue(certificate
,
5536 CFSTR("1.2.840.113635.100.6.36"));
5537 require_quiet(extensionValue
, out
);
5538 /* The extension is a octet string containing the DER-encoded 32-byte octet string */
5539 require_quiet(extensionValue
->length
== 34, out
);
5540 DERDecodedInfo decodedValue
;
5541 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
5542 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
5543 require_quiet(decodedValue
.content
.length
== 32, out
);
5544 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
5545 decodedValue
.content
.length
);
5547 require_quiet(extensionValue
->data
[33] == 0x00 &&
5548 extensionValue
->data
[32] == 0x00, out
);
5549 extensionData
= CFDataCreate(NULL
, extensionValue
->data
, 32);
5552 return extensionData
;
5556 /* From iapd IAPAuthenticationTypes.h */
5557 typedef struct IapCertSerialNumber
5559 uint8_t xservID
; // Xserver ID
5560 uint8_t hsmID
; // Hardware security module ID (generated cert)
5561 uint8_t delimiter01
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5562 uint8_t dateYear
; // Date year cert was issued
5563 uint8_t dateMonth
; // Date month cert was issued
5564 uint8_t dateDay
; // Date day cert was issued
5565 uint8_t delimiter02
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5566 uint8_t devClass
; // iAP device class (maps to lingo permissions)
5567 uint8_t delimiter03
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5568 uint8_t batchNumHi
; // Batch number high byte (15:08)
5569 uint8_t batchNumLo
; // Batch number low byte (07:00)
5570 uint8_t delimiter04
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5571 uint8_t serialNumHi
; // Serial number high byte (23:16)
5572 uint8_t serialNumMid
; // Serial number middle byte (15:08)
5573 uint8_t serialNumLo
; // Serial number low byte (07:00)
5575 } IapCertSerialNumber_t
, *pIapCertSerialNumber_t
;
5578 #define IAP_CERT_FIELD_DELIMITER 0xAA // "Apple_Accessory" delimiter
5579 SeciAuthVersion
SecCertificateGetiAuthVersion(SecCertificateRef certificate
) {
5581 return kSeciAuthInvalid
;
5583 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
5584 CFSTR("1.2.840.113635.100.6.36"))) {
5585 return kSeciAuthVersion3
;
5587 DERItem serialNumber
= certificate
->_serialNum
;
5588 require_quiet(serialNumber
.data
, out
);
5589 require_quiet(serialNumber
.length
== 15, out
);
5590 require_quiet(serialNumber
.data
[2] == IAP_CERT_FIELD_DELIMITER
&&
5591 serialNumber
.data
[6] == IAP_CERT_FIELD_DELIMITER
&&
5592 serialNumber
.data
[8] == IAP_CERT_FIELD_DELIMITER
&&
5593 serialNumber
.data
[11] == IAP_CERT_FIELD_DELIMITER
, out
);
5594 return kSeciAuthVersion2
;
5596 return kSeciAuthInvalid
;
5599 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
5600 CFDataRef pem_certificate
)
5602 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
5603 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
5604 uint8_t *base64_data
= NULL
;
5605 SecCertificateRef cert
= NULL
;
5606 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
5607 //const size_t length = CFDataGetLength(pem_certificate);
5608 char *begin
= strstr((const char *)data
, begin_cert
);
5609 char *end
= strstr((const char *)data
, end_cert
);
5612 begin
+= sizeof(begin_cert
) - 1;
5613 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
5614 if (base64_length
) {
5615 require_quiet(base64_data
= calloc(1, base64_length
), out
);
5616 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
5617 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
5626 // -- MARK -- XPC encoding/decoding
5629 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5631 return true; // NOOP
5633 size_t length
= SecCertificateGetLength(certificate
);
5634 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
5635 #if SECTRUST_VERBOSE_DEBUG
5636 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
5638 if (!length
|| !bytes
) {
5639 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
5641 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
5645 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
5646 SecCertificateRef certificate
= NULL
;
5648 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
5650 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
5653 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
5658 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
5659 xpc_object_t xpc_certificates
;
5660 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
5661 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
5662 CFIndex ix
, count
= CFArrayGetCount(certificates
);
5663 for (ix
= 0; ix
< count
; ++ix
) {
5664 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
5665 #if SECTRUST_VERBOSE_DEBUG
5666 CFIndex length
= SecCertificateGetLength(certificate
);
5667 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
5668 secerror("idx=%d of %d; cert=0x%lX length=%ld bytes=0x%lX", (int)ix
, (int)count
, (uintptr_t)certificate
, (size_t)length
, (uintptr_t)bytes
);
5670 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
5671 xpc_release(xpc_certificates
);
5672 xpc_certificates
= NULL
;
5678 return xpc_certificates
;
5681 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5682 CFMutableArrayRef certificates
= NULL
;
5683 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
5684 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
5685 size_t count
= xpc_array_get_count(xpc_certificates
);
5686 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
5687 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
5690 for (ix
= 0; ix
< count
; ++ix
) {
5691 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
5693 CFRelease(certificates
);
5696 CFArraySetValueAtIndex(certificates
, ix
, cert
);
5701 return certificates
;
5704 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
5707 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
5709 __block CFArrayRef result
= NULL
;
5711 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
5713 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
5714 ^bool(xpc_object_t message
, CFErrorRef
*error
)
5716 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
5719 ^bool(xpc_object_t response
, CFErrorRef
*error
)
5721 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
5723 if (response
&& (NULL
!= xpc_array
)) {
5724 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
5727 return SecError(errSecInternal
, error
, CFSTR("Did not get the Escrow certificates"));
5729 return result
!= NULL
;
5734 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
5736 CFArrayRef result
= NULL
;
5738 CFDataRef certData
= NULL
;
5741 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
5742 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
||
5743 kSecCertificateBaselineEscrowBackupRoot
== escrowRootType
||
5744 kSecCertificateBaselineEscrowEnrollmentRoot
== escrowRootType
)
5746 // The request is for the base line certificates.
5747 // Use the hard coded data to generate the return array.
5748 struct RootRecord
** pEscrowRoots
;
5749 switch (escrowRootType
) {
5750 case kSecCertificateBaselineEscrowRoot
:
5751 numRoots
= kNumberOfBaseLineEscrowRoots
;
5752 pEscrowRoots
= kBaseLineEscrowRoots
;
5754 case kSecCertificateBaselinePCSEscrowRoot
:
5755 numRoots
= kNumberOfBaseLinePCSEscrowRoots
;
5756 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
5758 case kSecCertificateBaselineEscrowBackupRoot
:
5759 numRoots
= kNumberOfBaseLineEscrowBackupRoots
;
5760 pEscrowRoots
= kBaseLineEscrowBackupRoots
;
5762 case kSecCertificateBaselineEscrowEnrollmentRoot
:
5764 numRoots
= kNumberOfBaseLineEscrowEnrollmentRoots
;
5765 pEscrowRoots
= kBaseLineEscrowEnrollmentRoots
;
5769 // Get the hard coded set of roots
5770 SecCertificateRef baseLineCerts
[numRoots
];
5771 struct RootRecord
* pRootRecord
= NULL
;
5773 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
5774 pRootRecord
= pEscrowRoots
[iCnt
];
5775 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
) {
5776 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
5777 if (NULL
!= certData
) {
5778 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5779 CFRelease(certData
);
5783 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5784 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
5785 if (NULL
!= baseLineCerts
[iCnt
]) {
5786 CFRelease(baseLineCerts
[iCnt
]);
5791 // The request is for the current certificates.
5792 CFErrorRef error
= NULL
;
5793 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
5794 if (NULL
!= error
|| NULL
== cert_datas
) {
5795 if (NULL
!= error
) {
5798 if (NULL
!= cert_datas
) {
5799 CFRelease(cert_datas
);
5804 numRoots
= (int)(CFArrayGetCount(cert_datas
));
5806 SecCertificateRef assetCerts
[numRoots
];
5807 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
5808 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
5809 if (NULL
!= certData
) {
5810 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5811 assetCerts
[iCnt
] = aCertRef
;
5814 assetCerts
[iCnt
] = NULL
;
5819 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5820 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
5821 if (NULL
!= assetCerts
[iCnt
]) {
5822 CFRelease(assetCerts
[iCnt
]);
5826 CFReleaseSafe(cert_datas
);
5831 SEC_CONST_DECL (kSecSignatureDigestAlgorithmUnknown
, "SignatureDigestUnknown");
5832 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD2
, "SignatureDigestMD2");
5833 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD4
, "SignatureDigestMD4");
5834 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD5
, "SignatureDigestMD5");
5835 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA1
, "SignatureDigestSHA1");
5836 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA224
, "SignatureDigestSHA224");
5837 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA256
, "SignatureDigestSHA256");
5838 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA384
, "SignatureDigestSHA284");
5839 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA512
, "SignatureDigestSHA512");
5841 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
5843 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
5844 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
5845 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
5847 if (!algOid
->data
|| !algOid
->length
) {
5850 /* classify the signature algorithm OID into one of our known types */
5851 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
5852 DEROidCompare(algOid
, &oidSha512Rsa
) ||
5853 DEROidCompare(algOid
, &oidSha512
)) {
5854 result
= kSecSignatureHashAlgorithmSHA512
;
5857 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
5858 DEROidCompare(algOid
, &oidSha384Rsa
) ||
5859 DEROidCompare(algOid
, &oidSha384
)) {
5860 result
= kSecSignatureHashAlgorithmSHA384
;
5863 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
5864 DEROidCompare(algOid
, &oidSha256Rsa
) ||
5865 DEROidCompare(algOid
, &oidSha256
)) {
5866 result
= kSecSignatureHashAlgorithmSHA256
;
5869 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
5870 DEROidCompare(algOid
, &oidSha224Rsa
) ||
5871 DEROidCompare(algOid
, &oidSha224
)) {
5872 result
= kSecSignatureHashAlgorithmSHA224
;
5875 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
5876 DEROidCompare(algOid
, &oidSha1Rsa
) ||
5877 DEROidCompare(algOid
, &oidSha1Dsa
) ||
5878 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
5879 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
5880 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
5881 DEROidCompare(algOid
, &oidSha1Fee
) ||
5882 DEROidCompare(algOid
, &oidSha1
)) {
5883 result
= kSecSignatureHashAlgorithmSHA1
;
5886 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
5887 DEROidCompare(algOid
, &oidMd5Fee
) ||
5888 DEROidCompare(algOid
, &oidMd5
)) {
5889 result
= kSecSignatureHashAlgorithmMD5
;
5892 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
5893 DEROidCompare(algOid
, &oidMd4
)) {
5894 result
= kSecSignatureHashAlgorithmMD4
;
5897 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
5898 DEROidCompare(algOid
, &oidMd2
)) {
5899 result
= kSecSignatureHashAlgorithmMD2
;