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 certificate
->_certificatePolicies
.policies
= NULL
;
241 if (certificate
->_policyMappings
.mappings
) {
242 free(certificate
->_policyMappings
.mappings
);
243 certificate
->_policyMappings
.mappings
= NULL
;
245 CFReleaseNull(certificate
->_crlDistributionPoints
);
246 CFReleaseNull(certificate
->_ocspResponders
);
247 CFReleaseNull(certificate
->_caIssuers
);
248 if (certificate
->_extensions
) {
249 free(certificate
->_extensions
);
250 certificate
->_extensions
= NULL
;
252 CFReleaseNull(certificate
->_pubKey
);
253 CFReleaseNull(certificate
->_der_data
);
254 CFReleaseNull(certificate
->_properties
);
255 CFReleaseNull(certificate
->_serialNumber
);
256 CFReleaseNull(certificate
->_normalizedIssuer
);
257 CFReleaseNull(certificate
->_normalizedSubject
);
258 CFReleaseNull(certificate
->_authorityKeyID
);
259 CFReleaseNull(certificate
->_subjectKeyID
);
260 CFReleaseNull(certificate
->_sha1Digest
);
261 CFReleaseNull(certificate
->_keychain_item
);
262 CFReleaseNull(certificate
->_permittedSubtrees
);
263 CFReleaseNull(certificate
->_excludedSubtrees
);
266 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
267 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
268 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
271 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
273 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
276 /* Hash of the certificate is der length + signature length + last 4 bytes
278 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
279 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
280 size_t der_length
= certificate
->_der
.length
;
281 size_t sig_length
= certificate
->_signature
.length
;
282 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
283 CFHashCode hashCode
= 0;
284 for (; ix
< sig_length
; ++ix
)
285 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
287 return (hashCode
+ der_length
+ sig_length
);
292 /************************************************************************/
293 /************************* General Name Parsing *************************/
294 /************************************************************************/
296 GeneralName ::= CHOICE {
297 otherName [0] OtherName,
298 rfc822Name [1] IA5String,
299 dNSName [2] IA5String,
300 x400Address [3] ORAddress,
301 directoryName [4] Name,
302 ediPartyName [5] EDIPartyName,
303 uniformResourceIdentifier [6] IA5String,
304 iPAddress [7] OCTET STRING,
305 registeredID [8] OBJECT IDENTIFIER}
307 OtherName ::= SEQUENCE {
308 type-id OBJECT IDENTIFIER,
309 value [0] EXPLICIT ANY DEFINED BY type-id }
311 EDIPartyName ::= SEQUENCE {
312 nameAssigner [0] DirectoryString OPTIONAL,
313 partyName [1] DirectoryString }
315 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
316 const DERItem
*generalNameContent
,
317 void *context
, parseGeneralNameCallback callback
) {
319 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
320 return callback(context
, GNT_OtherName
, generalNameContent
);
321 case ASN1_CONTEXT_SPECIFIC
| 1:
322 return callback(context
, GNT_RFC822Name
, generalNameContent
);
323 case ASN1_CONTEXT_SPECIFIC
| 2:
324 return callback(context
, GNT_DNSName
, generalNameContent
);
325 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
326 return callback(context
, GNT_X400Address
, generalNameContent
);
327 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
328 return callback(context
, GNT_DirectoryName
, generalNameContent
);
329 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
330 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
331 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
333 /* Technically I don't think this is valid, but there are certs out
334 in the wild that use a constructed IA5String. In particular the
335 VeriSign Time Stamping Authority CA.cer does this. */
336 DERDecodedInfo uriContent
;
337 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
338 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
339 return callback(context
, GNT_URI
, &uriContent
.content
);
341 case ASN1_CONTEXT_SPECIFIC
| 6:
342 return callback(context
, GNT_URI
, generalNameContent
);
343 case ASN1_CONTEXT_SPECIFIC
| 7:
344 return callback(context
, GNT_IPAddress
, generalNameContent
);
345 case ASN1_CONTEXT_SPECIFIC
| 8:
346 return callback(context
, GNT_RegisteredID
, generalNameContent
);
351 return errSecInvalidCertificate
;
354 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
355 void *context
, parseGeneralNameCallback callback
) {
357 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
358 require_noerr_quiet(drtn
, badDER
);
359 DERDecodedInfo generalNameContent
;
360 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
362 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
363 generalNameContent
.tag
, &generalNameContent
.content
, context
,
368 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
369 return errSecSuccess
;
372 return errSecInvalidCertificate
;
375 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
376 parseGeneralNameCallback callback
) {
377 DERDecodedInfo generalNamesContent
;
378 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
379 require_noerr_quiet(drtn
, badDER
);
380 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
381 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
384 return errSecInvalidCertificate
;
390 GeneralName ::= CHOICE {
391 otherName [0] OtherName,
392 rfc822Name [1] IA5String,
393 dNSName [2] IA5String,
394 x400Address [3] ORAddress,
395 directoryName [4] Name,
396 ediPartyName [5] EDIPartyName,
397 uniformResourceIdentifier [6] IA5String,
398 iPAddress [7] OCTET STRING,
399 registeredID [8] OBJECT IDENTIFIER}
401 EDIPartyName ::= SEQUENCE {
402 nameAssigner [0] DirectoryString OPTIONAL,
403 partyName [1] DirectoryString }
405 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
406 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
408 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
409 generalName
->nameType
= GNT_OtherName
;
410 generalName
->berEncoded
= true;
411 generalName
->name
= *generalNameContent
;
413 case ASN1_CONTEXT_SPECIFIC
| 1:
415 generalName
->nameType
= GNT_RFC822Name
;
416 generalName
->berEncoded
= false;
417 generalName
->name
= *generalNameContent
;
419 case ASN1_CONTEXT_SPECIFIC
| 2:
421 generalName
->nameType
= GNT_DNSName
;
422 generalName
->berEncoded
= false;
423 generalName
->name
= *generalNameContent
;
425 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
426 generalName
->nameType
= GNT_X400Address
;
427 generalName
->berEncoded
= true;
428 generalName
->name
= *generalNameContent
;
430 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
431 generalName
->nameType
= GNT_DirectoryName
;
432 generalName
->berEncoded
= true;
433 generalName
->name
= *generalNameContent
;
435 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
436 generalName
->nameType
= GNT_EdiPartyName
;
437 generalName
->berEncoded
= true;
438 generalName
->name
= *generalNameContent
;
440 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
442 /* Technically I don't think this is valid, but there are certs out
443 in the wild that use a constructed IA5String. In particular the
444 VeriSign Time Stamping Authority CA.cer does this. */
445 DERDecodedInfo decoded
;
446 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
447 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
448 generalName
->nameType
= GNT_URI
;
449 generalName
->berEncoded
= false;
450 generalName
->name
= decoded
.content
;
453 case ASN1_CONTEXT_SPECIFIC
| 6:
454 generalName
->nameType
= GNT_URI
;
455 generalName
->berEncoded
= false;
456 generalName
->name
= *generalNameContent
;
458 case ASN1_CONTEXT_SPECIFIC
| 7:
459 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
460 8 octects, addr/mask for ipv6 it's 32. */
461 generalName
->nameType
= GNT_IPAddress
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= *generalNameContent
;
465 case ASN1_CONTEXT_SPECIFIC
| 8:
466 /* name is the content of an OID. */
467 generalName
->nameType
= GNT_RegisteredID
;
468 generalName
->berEncoded
= false;
469 generalName
->name
= *generalNameContent
;
475 return errSecSuccess
;
477 return errSecInvalidCertificate
;
481 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
483 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
484 CFIndex
*count
, SecCEGeneralName
**name
) {
485 SecCEGeneralName
*generalNames
= NULL
;
487 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
488 require_noerr_quiet(drtn
, badDER
);
489 DERDecodedInfo generalNameContent
;
490 CFIndex generalNamesCount
= 0;
491 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
495 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
497 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
499 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
501 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
503 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
504 &generalNameContent
.content
, &generalNames
[ix
])) {
509 *count
= generalNamesCount
;
510 *name
= generalNames
;
511 return errSecSuccess
;
516 return errSecInvalidCertificate
;
519 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
520 CFIndex
*count
, SecCEGeneralName
**name
) {
521 DERDecodedInfo generalNamesContent
;
522 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
523 require_noerr_quiet(drtn
, badDER
);
524 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
526 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
527 return errSecSuccess
;
529 return errSecInvalidCertificate
;
533 /************************************************************************/
534 /************************** X.509 Name Parsing **************************/
535 /************************************************************************/
537 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
538 const DERItem
*value
, CFIndex rdnIX
);
540 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
541 parseX501NameCallback callback
) {
543 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
544 require_noerr_quiet(drtn
, badDER
);
545 DERDecodedInfo atvContent
;
547 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
548 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
549 DERAttributeTypeAndValue atv
;
550 drtn
= DERParseSequenceContent(&atvContent
.content
,
551 DERNumAttributeTypeAndValueItemSpecs
,
552 DERAttributeTypeAndValueItemSpecs
,
554 require_noerr_quiet(drtn
, badDER
);
555 require_quiet(atv
.type
.length
!= 0, badDER
);
556 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
560 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
562 return errSecSuccess
;
564 return errSecInvalidCertificate
;
567 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
568 parseX501NameCallback callback
) {
570 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
571 require_noerr_quiet(drtn
, badDER
);
572 DERDecodedInfo currDecoded
;
573 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
574 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
575 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
580 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
582 return errSecSuccess
;
585 return errSecInvalidCertificate
;
588 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
589 parseX501NameCallback callback
) {
590 DERDecodedInfo x501NameContent
;
591 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
592 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
593 return errSecInvalidCertificate
;
595 return parseX501NameContent(&x501NameContent
.content
, context
,
600 /************************************************************************/
601 /********************** Extension Parsing Routines **********************/
602 /************************************************************************/
604 static void SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
605 const SecCertificateExtension
*extn
) {
606 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
607 DERDecodedInfo keyIdentifier
;
608 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
609 require_noerr_quiet(drtn
, badDER
);
610 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
611 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
615 secwarning("Invalid SubjectKeyIdentifier Extension");
618 static void SecCEPKeyUsage(SecCertificateRef certificate
,
619 const SecCertificateExtension
*extn
) {
620 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
621 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
622 DERDecodedInfo bitStringContent
;
623 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
624 require_noerr_quiet(drtn
, badDER
);
625 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
626 DERSize len
= bitStringContent
.content
.length
- 1;
627 require_quiet(len
== 1 || len
== 2, badDER
);
628 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
629 require_quiet(numUnusedBits
< 8, badDER
);
630 /* Flip the bits in the bit string so the first bit in the lsb. */
631 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
632 uint_fast16_t value
= bitStringContent
.content
.data
[1];
635 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
641 for (ix
= 0; ix
< bits
; ++ix
) {
647 certificate
->_keyUsage
= keyUsage
;
650 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
653 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
654 const SecCertificateExtension
*extn
) {
655 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
658 static void SecCEPSubjectAltName(SecCertificateRef certificate
,
659 const SecCertificateExtension
*extn
) {
660 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
661 certificate
->_subjectAltName
= extn
;
664 static void SecCEPIssuerAltName(SecCertificateRef certificate
,
665 const SecCertificateExtension
*extn
) {
666 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
669 static void SecCEPBasicConstraints(SecCertificateRef certificate
,
670 const SecCertificateExtension
*extn
) {
671 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
672 DERBasicConstraints basicConstraints
;
673 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
674 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
675 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
676 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
677 &certificate
->_basicConstraints
.isCA
), badDER
);
678 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
679 require_noerr_quiet(DERParseInteger(
680 &basicConstraints
.pathLenConstraint
,
681 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
682 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
684 certificate
->_basicConstraints
.present
= true;
685 certificate
->_basicConstraints
.critical
= extn
->critical
;
688 certificate
->_basicConstraints
.present
= false;
689 secwarning("Invalid BasicConstraints Extension");
694 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
696 * NameConstraints ::= SEQUENCE {
697 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
698 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
700 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
702 * GeneralSubtree ::= SEQUENCE {
704 * minimum [0] BaseDistance DEFAULT 0,
705 * maximum [1] BaseDistance OPTIONAL }
707 * BaseDistance ::= INTEGER (0..MAX)
709 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
710 CFMutableArrayRef gs
= NULL
;
712 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
713 require_noerr_quiet(drtn
, badDER
);
714 DERDecodedInfo gsContent
;
715 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
716 &kCFTypeArrayCallBacks
),
718 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
719 DERGeneralSubtree derGS
;
720 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
721 drtn
= DERParseSequenceContent(&gsContent
.content
,
722 DERNumGeneralSubtreeItemSpecs
,
723 DERGeneralSubtreeItemSpecs
,
724 &derGS
, sizeof(derGS
));
725 require_noerr_quiet(drtn
, badDER
);
728 * Within this profile, the minimum and maximum fields are not used with
729 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
732 * Because minimum DEFAULT 0, absence equivalent to present and 0.
734 if (derGS
.minimum
.length
) {
736 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
738 require_quiet(minimum
== 0, badDER
);
740 require_quiet(derGS
.maximum
.length
== 0, badDER
);
741 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
743 CFDataRef generalName
= NULL
;
744 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
745 derGS
.generalName
.data
,
746 derGS
.generalName
.length
),
748 CFArrayAppendValue(gs
, generalName
);
749 CFReleaseNull(generalName
);
751 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
753 // since generalSubtrees is a pointer to an instance variable,
754 // make sure we release the existing array before assignment.
755 CFReleaseSafe(*generalSubtrees
);
756 *generalSubtrees
= gs
;
762 secdebug("cert","failed to parse GeneralSubtrees");
766 static void SecCEPNameConstraints(SecCertificateRef certificate
,
767 const SecCertificateExtension
*extn
) {
768 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
769 DERNameConstraints nc
;
771 drtn
= DERParseSequence(&extn
->extnValue
,
772 DERNumNameConstraintsItemSpecs
,
773 DERNameConstraintsItemSpecs
,
775 require_noerr_quiet(drtn
, badDER
);
776 if (nc
.permittedSubtrees
.length
) {
777 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
779 if (nc
.excludedSubtrees
.length
) {
780 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
785 secdebug("cert", "failed to parse Name Constraints extension");
788 static OSStatus
appendCRLDPFromGeneralNames(void *context
, SecCEGeneralNameType type
,
789 const DERItem
*value
) {
790 CFMutableArrayRef
*crlDPs
= (CFMutableArrayRef
*)context
;
791 if (type
== GNT_URI
) {
793 url
= CFURLCreateWithBytes(NULL
, value
->data
, value
->length
, kCFStringEncodingASCII
, NULL
);
796 *crlDPs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
798 CFArrayAppendValue(*crlDPs
, url
);
802 return errSecSuccess
;
806 id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
808 CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
810 DistributionPoint ::= SEQUENCE {
811 distributionPoint [0] DistributionPointName OPTIONAL,
812 reasons [1] ReasonFlags OPTIONAL,
813 cRLIssuer [2] GeneralNames OPTIONAL }
815 DistributionPointName ::= CHOICE {
816 fullName [0] GeneralNames,
817 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
819 static void SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
820 const SecCertificateExtension
*extn
) {
821 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
822 DERSequence crlDPSeq
;
824 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &crlDPSeq
);
825 require_noerr_quiet(drtn
, badDER
);
826 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
827 DERDecodedInfo dpContent
;
828 while ((drtn
= DERDecodeSeqNext(&crlDPSeq
, &dpContent
)) == DR_Success
) {
829 require_quiet(dpContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
830 DERDistributionPoint dp
;
831 drtn
= DERParseSequenceContent(&dpContent
.content
, DERNumDistributionPointItemSpecs
,
832 DERDistributionPointItemSpecs
, &dp
, sizeof(dp
));
833 require_noerr_quiet(drtn
, badDER
);
834 require_quiet(dp
.distributionPoint
.data
|| dp
.cRLIssuer
.data
, badDER
);
835 if (dp
.distributionPoint
.data
) {
836 DERDecodedInfo dpName
;
837 drtn
= DERDecodeItem(&dp
.distributionPoint
, &dpName
);
838 require_noerr_quiet(drtn
, badDER
);
839 switch (dpName
.tag
) {
840 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
841 drtn
= parseGeneralNamesContent(&dpName
.content
, &certificate
->_crlDistributionPoints
,
842 appendCRLDPFromGeneralNames
);
843 require_noerr_quiet(drtn
, badDER
);
845 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1:
846 /* RelativeDistinguishName. Nothing we can do with that. */
852 if (dp
.cRLIssuer
.data
) {
853 drtn
= SecCertificateParseGeneralNames(&dp
.cRLIssuer
, &certificate
->_crlDistributionPoints
,
854 appendCRLDPFromGeneralNames
);
855 require_noerr_quiet(drtn
, badDER
);
858 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
861 secdebug("cert", "failed to parse CRL Distribution Points extension");
865 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
867 PolicyInformation ::= SEQUENCE {
868 policyIdentifier CertPolicyId,
869 policyQualifiers SEQUENCE SIZE (1..MAX) OF
870 PolicyQualifierInfo OPTIONAL }
872 CertPolicyId ::= OBJECT IDENTIFIER
874 PolicyQualifierInfo ::= SEQUENCE {
875 policyQualifierId PolicyQualifierId,
876 qualifier ANY DEFINED BY policyQualifierId }
878 /* maximum number of policies of 8192 seems more than adequate */
879 #define MAX_CERTIFICATE_POLICIES 8192
880 static void SecCEPCertificatePolicies(SecCertificateRef certificate
,
881 const SecCertificateExtension
*extn
) {
882 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
885 SecCEPolicyInformation
*policies
= NULL
;
886 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
887 require_noerr_quiet(drtn
, badDER
);
888 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
889 DERDecodedInfo piContent
;
890 DERSize policy_count
= 0;
891 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
892 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
893 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
896 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
897 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
898 * (policy_count
> 0 ? policy_count
: 1));
899 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
900 require_noerr_quiet(drtn
, badDER
);
901 DERSize policy_ix
= 0;
902 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
903 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
904 DERPolicyInformation pi
;
905 drtn
= DERParseSequenceContent(&piContent
.content
,
906 DERNumPolicyInformationItemSpecs
,
907 DERPolicyInformationItemSpecs
,
909 require_noerr_quiet(drtn
, badDER
);
910 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
911 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
913 certificate
->_certificatePolicies
.present
= true;
914 certificate
->_certificatePolicies
.critical
= extn
->critical
;
915 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
916 certificate
->_certificatePolicies
.policies
= policies
;
921 certificate
->_certificatePolicies
.present
= false;
922 secwarning("Invalid CertificatePolicies Extension");
926 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
928 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
929 issuerDomainPolicy CertPolicyId,
930 subjectDomainPolicy CertPolicyId }
932 #define MAX_POLICY_MAPPINGS 8192
933 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
934 const SecCertificateExtension
*extn
) {
935 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
938 SecCEPolicyMapping
*mappings
= NULL
;
939 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
940 require_noerr_quiet(drtn
, badDER
);
941 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
942 DERDecodedInfo pmContent
;
943 DERSize mapping_count
= 0;
944 while ((mapping_count
< MAX_POLICY_MAPPINGS
) &&
945 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
946 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
949 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
950 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
951 * (mapping_count
> 0 ? mapping_count
: 1));
952 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
953 require_noerr_quiet(drtn
, badDER
);
954 DERSize mapping_ix
= 0;
955 while ((mapping_ix
< (mapping_count
> 0 ? mapping_count
: 1)) &&
956 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
957 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
959 drtn
= DERParseSequenceContent(&pmContent
.content
,
960 DERNumPolicyMappingItemSpecs
,
961 DERPolicyMappingItemSpecs
,
963 require_noerr_quiet(drtn
, badDER
);
964 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
965 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
967 certificate
->_policyMappings
.present
= true;
968 certificate
->_policyMappings
.critical
= extn
->critical
;
969 certificate
->_policyMappings
.numMappings
= mapping_count
;
970 certificate
->_policyMappings
.mappings
= mappings
;
976 certificate
->_policyMappings
.present
= false;
977 secwarning("Invalid CertificatePolicies Extension");
981 AuthorityKeyIdentifier ::= SEQUENCE {
982 keyIdentifier [0] KeyIdentifier OPTIONAL,
983 authorityCertIssuer [1] GeneralNames OPTIONAL,
984 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
985 -- authorityCertIssuer and authorityCertSerialNumber MUST both
986 -- be present or both be absent
988 KeyIdentifier ::= OCTET STRING
990 static void SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
991 const SecCertificateExtension
*extn
) {
992 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
993 DERAuthorityKeyIdentifier akid
;
995 drtn
= DERParseSequence(&extn
->extnValue
,
996 DERNumAuthorityKeyIdentifierItemSpecs
,
997 DERAuthorityKeyIdentifierItemSpecs
,
998 &akid
, sizeof(akid
));
999 require_noerr_quiet(drtn
, badDER
);
1000 if (akid
.keyIdentifier
.length
) {
1001 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
1003 if (akid
.authorityCertIssuer
.length
||
1004 akid
.authorityCertSerialNumber
.length
) {
1005 require_quiet(akid
.authorityCertIssuer
.length
&&
1006 akid
.authorityCertSerialNumber
.length
, badDER
);
1007 /* Perhaps put in a subsection called Authority Certificate Issuer. */
1008 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
1009 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1014 secwarning("Invalid AuthorityKeyIdentifier Extension");
1017 static void SecCEPPolicyConstraints(SecCertificateRef certificate
,
1018 const SecCertificateExtension
*extn
) {
1019 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1020 DERPolicyConstraints pc
;
1022 drtn
= DERParseSequence(&extn
->extnValue
,
1023 DERNumPolicyConstraintsItemSpecs
,
1024 DERPolicyConstraintsItemSpecs
,
1026 require_noerr_quiet(drtn
, badDER
);
1027 if (pc
.requireExplicitPolicy
.length
) {
1028 require_noerr_quiet(DERParseInteger(
1029 &pc
.requireExplicitPolicy
,
1030 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1031 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1033 if (pc
.inhibitPolicyMapping
.length
) {
1034 require_noerr_quiet(DERParseInteger(
1035 &pc
.inhibitPolicyMapping
,
1036 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1037 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1040 certificate
->_policyConstraints
.present
= true;
1041 certificate
->_policyConstraints
.critical
= extn
->critical
;
1045 certificate
->_policyConstraints
.present
= false;
1046 secwarning("Invalid PolicyConstraints Extension");
1049 static void SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1050 const SecCertificateExtension
*extn
) {
1051 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1055 InhibitAnyPolicy ::= SkipCerts
1057 SkipCerts ::= INTEGER (0..MAX)
1059 static void SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1060 const SecCertificateExtension
*extn
) {
1061 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1062 DERDecodedInfo iapContent
;
1063 require_noerr_quiet(DERDecodeItem(&extn
->extnValue
, &iapContent
), badDER
);
1064 require_quiet(iapContent
.tag
== ASN1_INTEGER
, badDER
);
1065 require_noerr_quiet(DERParseInteger(
1066 &iapContent
.content
,
1067 &certificate
->_inhibitAnyPolicySkipCerts
.skipCerts
), badDER
);
1069 certificate
->_inhibitAnyPolicySkipCerts
.present
= true;
1070 certificate
->_inhibitAnyPolicySkipCerts
.critical
= extn
->critical
;
1073 certificate
->_inhibitAnyPolicySkipCerts
.present
= false;
1074 secwarning("Invalid InhibitAnyPolicy Extension");
1078 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1080 AuthorityInfoAccessSyntax ::=
1081 SEQUENCE SIZE (1..MAX) OF AccessDescription
1083 AccessDescription ::= SEQUENCE {
1084 accessMethod OBJECT IDENTIFIER,
1085 accessLocation GeneralName }
1087 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1089 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1091 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1093 static void SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1094 const SecCertificateExtension
*extn
) {
1095 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1098 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1099 require_noerr_quiet(drtn
, badDER
);
1100 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1101 DERDecodedInfo adContent
;
1102 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1103 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1104 DERAccessDescription ad
;
1105 drtn
= DERParseSequenceContent(&adContent
.content
,
1106 DERNumAccessDescriptionItemSpecs
,
1107 DERAccessDescriptionItemSpecs
,
1109 require_noerr_quiet(drtn
, badDER
);
1110 CFMutableArrayRef
*urls
;
1111 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1112 urls
= &certificate
->_ocspResponders
;
1113 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1114 urls
= &certificate
->_caIssuers
;
1118 DERDecodedInfo generalNameContent
;
1119 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1120 require_noerr_quiet(drtn
, badDER
);
1121 switch (generalNameContent
.tag
) {
1123 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1124 /* Technically I don't think this is valid, but there are certs out
1125 in the wild that use a constructed IA5String. In particular the
1126 VeriSign Time Stamping Authority CA.cer does this. */
1128 case ASN1_CONTEXT_SPECIFIC
| 6:
1130 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1131 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1132 kCFStringEncodingASCII
, NULL
);
1135 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1136 CFArrayAppendValue(*urls
, url
);
1142 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1143 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1148 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1151 secdebug("cert", "failed to parse Authority Information Access extension");
1154 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1155 * This is a DER sequence with the leading tag and length bytes removed,
1156 * to match what tbsCert.issuer contains.
1158 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1159 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1160 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1161 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1162 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1163 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1164 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1165 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1166 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1167 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1168 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1171 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1173 certificate
->_crlDistributionPoints
||
1174 certificate
->_ocspResponders
) {
1175 /* We already have an OCSP or CRL URI (or no cert) */
1178 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1179 CFURLRef url
= NULL
;
1180 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1181 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1182 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1183 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1184 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1185 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1186 kCFStringEncodingASCII
, NULL
);
1189 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1190 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1191 CFArrayAppendValue(*urls
, url
);
1196 static void SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1197 const SecCertificateExtension
*extn
) {
1198 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1201 static void SecCEPNetscapeCertType(SecCertificateRef certificate
,
1202 const SecCertificateExtension
*extn
) {
1203 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1206 static void SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1207 const SecCertificateExtension
*extn
) {
1208 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1211 static void SecCEPEscrowMarker(SecCertificateRef certificate
,
1212 const SecCertificateExtension
*extn
) {
1213 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1217 /* Dictionary key callback for comparing to DERItems. */
1218 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1219 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1222 /* Dictionary key callback calculating the hash of a DERItem. */
1223 static CFHashCode
SecDERItemHash(const void *value
) {
1224 const DERItem
*derItem
= (const DERItem
*)value
;
1225 CFHashCode hash
= derItem
->length
;
1226 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1227 for (; ix
< derItem
->length
; ++ix
) {
1228 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1234 /* Dictionary key callbacks using the above 2 functions. */
1235 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1239 NULL
, /* copyDescription */
1240 SecDERItemEqual
, /* equal */
1241 SecDERItemHash
/* hash */
1244 static void SecCertificateInitializeExtensionParsers(void) {
1245 /* Build a dictionary that maps from extension OIDs to callback functions
1246 which can parse the extension of the type given. */
1247 static const void *extnOIDs
[] = {
1248 &oidSubjectKeyIdentifier
,
1250 &oidPrivateKeyUsagePeriod
,
1253 &oidBasicConstraints
,
1254 &oidNameConstraints
,
1255 &oidCrlDistributionPoints
,
1256 &oidCertificatePolicies
,
1258 &oidAuthorityKeyIdentifier
,
1259 &oidPolicyConstraints
,
1260 &oidExtendedKeyUsage
,
1261 &oidInhibitAnyPolicy
,
1262 &oidAuthorityInfoAccess
,
1263 &oidSubjectInfoAccess
,
1264 &oidNetscapeCertType
,
1265 &oidEntrustVersInfo
,
1266 &oidApplePolicyEscrowService
1268 static const void *extnParsers
[] = {
1269 SecCEPSubjectKeyIdentifier
,
1271 SecCEPPrivateKeyUsagePeriod
,
1272 SecCEPSubjectAltName
,
1273 SecCEPIssuerAltName
,
1274 SecCEPBasicConstraints
,
1275 SecCEPNameConstraints
,
1276 SecCEPCrlDistributionPoints
,
1277 SecCEPCertificatePolicies
,
1278 SecCEPPolicyMappings
,
1279 SecCEPAuthorityKeyIdentifier
,
1280 SecCEPPolicyConstraints
,
1281 SecCEPExtendedKeyUsage
,
1282 SecCEPInhibitAnyPolicy
,
1283 SecCEPAuthorityInfoAccess
,
1284 SecCEPSubjectInfoAccess
,
1285 SecCEPNetscapeCertType
,
1286 SecCEPEntrustVersInfo
,
1289 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1290 extnParsers
, array_size(extnOIDs
),
1291 &SecDERItemKeyCallBacks
, NULL
);
1294 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateCopyDescription
, NULL
, NULL
, ^{
1295 SecCertificateInitializeExtensionParsers();
1298 static bool isAppleExtensionOID(const DERItem
*extnID
)
1300 static const uint8_t appleExtension
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1301 return (extnID
&& extnID
->data
&&
1302 extnID
->length
> sizeof(appleExtension
) &&
1303 !memcmp(extnID
->data
, appleExtension
, sizeof(appleExtension
)));
1306 /* Given the contents of an X.501 Name return the contents of a normalized
1308 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1309 const DERItem
*x501name
) {
1310 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1311 CFIndex length
= x501name
->length
;
1312 CFDataSetLength(result
, length
);
1313 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1316 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1318 require_noerr_quiet(drtn
, badDER
);
1321 /* Always points to last rdn tag. */
1322 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1323 /* Offset relative to base of current rdn set tag. */
1324 CFIndex rdnTagLocation
= 0;
1325 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1326 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1327 /* We don't allow empty RDNs. */
1328 require_quiet(rdn
.content
.length
!= 0, badDER
);
1329 /* Length of the tag and length of the current rdn. */
1330 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1331 CFIndex rdnContentLength
= rdn
.content
.length
;
1332 /* Copy the tag and length of the RDN. */
1333 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1336 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1337 require_quiet(drtn
== DR_Success
, badDER
);
1340 /* Always points to tag of current atv sequence. */
1341 const DERByte
*atvTag
= atvSeq
.nextItem
;
1342 /* Offset relative to base of current atv sequence tag. */
1343 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1344 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1345 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1346 /* Length of the tag and length of the current atv. */
1347 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1348 CFIndex atvContentLength
= atv
.content
.length
;
1349 /* Copy the tag and length of the atv and the atv itself. */
1350 memcpy(base
+ atvTagLocation
, atvTag
,
1351 atvTLLength
+ atv
.content
.length
);
1353 /* Now decode the atv sequence. */
1354 DERAttributeTypeAndValue atvPair
;
1355 drtn
= DERParseSequenceContent(&atv
.content
,
1356 DERNumAttributeTypeAndValueItemSpecs
,
1357 DERAttributeTypeAndValueItemSpecs
,
1358 &atvPair
, sizeof(atvPair
));
1359 require_noerr_quiet(drtn
, badDER
);
1360 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1361 DERDecodedInfo value
;
1362 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1363 require_noerr_quiet(drtn
, badDER
);
1365 /* (c) attribute values in PrintableString are not case sensitive
1366 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1368 (d) attribute values in PrintableString are compared after
1369 removing leading and trailing white space and converting internal
1370 substrings of one or more consecutive white space characters to a
1372 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1373 /* Offset relative to base of current value tag. */
1374 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1375 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1376 CFIndex valueContentLength
= value
.content
.length
;
1378 /* Now copy all the bytes, but convert to upper case while
1379 doing so and convert multiple whitespace chars into a
1381 bool lastWasBlank
= false;
1382 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1383 CFIndex valueCurrentLocation
= valueLocation
;
1385 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1386 UInt8 ch
= value
.content
.data
[ix
];
1391 /* Don't insert a space for first character
1393 if (valueCurrentLocation
> valueLocation
) {
1394 base
[valueCurrentLocation
++] = ' ';
1396 lastWasBlank
= true;
1399 lastWasBlank
= false;
1400 if ('a' <= ch
&& ch
<= 'z') {
1401 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1403 base
[valueCurrentLocation
++] = ch
;
1407 /* Finally if lastWasBlank remove the trailing space. */
1408 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1409 valueCurrentLocation
--;
1411 /* Adjust content length to normalized length. */
1412 valueContentLength
= valueCurrentLocation
- valueLocation
;
1414 /* Number of bytes by which the length should be shorted. */
1415 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1416 if (lengthDiff
== 0) {
1417 /* Easy case no need to adjust lengths. */
1419 /* Hard work we need to go back and fix up length fields
1421 1) The value itself.
1422 2) The ATV Sequence containing type/value
1423 3) The RDN Set containing one or more atv pairs.
1427 /* Step 1 fix up length of value. */
1428 /* Length of value tag and length minus the tag. */
1429 DERSize newValueTLLength
= valueTLLength
- 1;
1430 drtn
= DEREncodeLength(valueContentLength
,
1431 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1432 require(drtn
== DR_Success
, badDER
);
1433 /* Add the length of the tag back in. */
1435 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1437 /* The size of the length field changed, let's slide
1438 the value back by valueLLDiff bytes. */
1439 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1440 base
+ valueTagLocation
+ valueTLLength
,
1441 valueContentLength
);
1442 /* The length diff for the enclosing object. */
1443 lengthDiff
+= valueLLDiff
;
1446 /* Step 2 fix up length of the enclosing ATV Sequence. */
1447 atvContentLength
-= lengthDiff
;
1448 DERSize newATVTLLength
= atvTLLength
- 1;
1449 drtn
= DEREncodeLength(atvContentLength
,
1450 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1451 require(drtn
== DR_Success
, badDER
);
1452 /* Add the length of the tag back in. */
1454 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1456 /* The size of the length field changed, let's slide
1457 the value back by valueLLDiff bytes. */
1458 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1459 base
+ atvTagLocation
+ atvTLLength
,
1461 /* The length diff for the enclosing object. */
1462 lengthDiff
+= atvLLDiff
;
1463 atvTLLength
= newATVTLLength
;
1466 /* Step 3 fix up length of enclosing RDN Set. */
1467 rdnContentLength
-= lengthDiff
;
1468 DERSize newRDNTLLength
= rdnTLLength
- 1;
1469 drtn
= DEREncodeLength(rdnContentLength
,
1470 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1471 require_quiet(drtn
== DR_Success
, badDER
);
1472 /* Add the length of the tag back in. */
1474 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1476 /* The size of the length field changed, let's slide
1477 the value back by valueLLDiff bytes. */
1478 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1479 base
+ rdnTagLocation
+ rdnTLLength
,
1481 /* The length diff for the enclosing object. */
1482 lengthDiff
+= rdnLLDiff
;
1483 rdnTLLength
= newRDNTLLength
;
1485 /* Adjust the locations that might have changed due to
1487 atvTagLocation
-= rdnLLDiff
;
1489 (void) lengthDiff
; // No next object, silence analyzer
1492 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1493 atvTag
= atvSeq
.nextItem
;
1495 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1496 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1497 rdnTag
= rdnSeq
.nextItem
;
1499 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1500 /* Truncate the result to the proper length. */
1501 CFDataSetLength(result
, rdnTagLocation
);
1510 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1512 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1513 DERDecodedInfo content
;
1514 /* Decode top level sequence into DERItem */
1515 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1516 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1520 /* AUDIT[securityd]:
1521 certificate->_der is a caller provided data of any length (might be 0).
1523 Top level certificate decode.
1525 static bool SecCertificateParse(SecCertificateRef certificate
)
1530 require_quiet(certificate
, badCert
);
1531 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1533 /* top level decode */
1534 DERSignedCertCrl signedCert
;
1535 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1536 DERSignedCertCrlItemSpecs
, &signedCert
,
1537 sizeof(signedCert
));
1538 require_noerr_quiet(drtn
, badCert
);
1539 /* Store tbs since we need to digest it for verification later on. */
1540 certificate
->_tbs
= signedCert
.tbs
;
1542 /* decode the TBSCert - it was saved in full DER form */
1544 drtn
= DERParseSequence(&signedCert
.tbs
,
1545 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1546 &tbsCert
, sizeof(tbsCert
));
1547 require_noerr_quiet(drtn
, badCert
);
1549 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1550 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1551 of the params field. */
1552 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1553 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1554 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1555 require_noerr_quiet(drtn
, badCert
);
1557 /* The contents of signedCert.sig is a bit string whose contents
1558 are the signature itself. */
1559 DERByte numUnusedBits
;
1560 drtn
= DERParseBitString(&signedCert
.sig
,
1561 &certificate
->_signature
, &numUnusedBits
);
1562 require_noerr_quiet(drtn
, badCert
);
1564 /* Now decode the tbsCert. */
1566 /* First we turn the optional version into an int. */
1567 if (tbsCert
.version
.length
) {
1568 DERDecodedInfo decoded
;
1569 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1570 require_noerr_quiet(drtn
, badCert
);
1571 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1572 require_quiet(decoded
.content
.length
== 1, badCert
);
1573 certificate
->_version
= decoded
.content
.data
[0];
1574 if (certificate
->_version
> 2) {
1575 secwarning("Invalid certificate version (%d), must be 0..2",
1576 certificate
->_version
);
1578 require_quiet(certificate
->_version
> 0, badCert
);
1579 require_quiet(certificate
->_version
< 3, badCert
);
1581 certificate
->_version
= 0;
1584 /* The serial number is in the tbsCert.serialNum - it was saved in
1585 INTEGER form without the tag and length. */
1586 certificate
->_serialNum
= tbsCert
.serialNum
;
1587 certificate
->_serialNumber
= CFDataCreate(allocator
,
1588 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1589 /* RFC5280 4.1.2.2 limits serial number values to 20 octets.
1590 %%% At some point, this should be treated as a hard error.*/
1591 if (tbsCert
.serialNum
.length
< 1 || tbsCert
.serialNum
.length
> 20) {
1592 secwarning("Invalid serial number length (%ld), must be 1..20",
1593 tbsCert
.serialNum
.length
);
1596 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1597 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1598 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1599 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1600 require_noerr_quiet(drtn
, badCert
);
1602 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1603 and length fields. */
1604 certificate
->_issuer
= tbsCert
.issuer
;
1605 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1608 /* sequence we're given: decode the tbsCerts Validity sequence. */
1609 DERValidity validity
;
1610 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1611 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1612 &validity
, sizeof(validity
));
1613 require_noerr_quiet(drtn
, badCert
);
1614 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1615 &certificate
->_notBefore
), badCert
);
1616 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1617 &certificate
->_notAfter
), badCert
);
1619 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1620 and length fields. */
1621 certificate
->_subject
= tbsCert
.subject
;
1622 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1625 /* Keep the SPKI around for CT */
1626 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1628 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1629 DERSubjPubKeyInfo pubKeyInfo
;
1630 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1631 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1632 &pubKeyInfo
, sizeof(pubKeyInfo
));
1633 require_noerr_quiet(drtn
, badCert
);
1635 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1636 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1637 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1638 &certificate
->_algId
, sizeof(certificate
->_algId
));
1639 require_noerr_quiet(drtn
, badCert
);
1641 /* Now we can figure out the key's algorithm id and params based on
1642 certificate->_algId.oid. */
1644 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1645 are a PKCS1 format RSA key. */
1646 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1647 &certificate
->_pubKeyDER
, &numUnusedBits
);
1648 require_noerr_quiet(drtn
, badCert
);
1650 /* The contents of tbsCert.issuerID is a bit string. */
1651 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1653 /* The contents of tbsCert.subjectID is a bit string. */
1654 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1657 if (tbsCert
.extensions
.length
) {
1658 CFIndex extensionCount
= 0;
1661 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1663 require_noerr_quiet(drtn
, badCert
);
1664 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1665 DERDecodedInfo currDecoded
;
1666 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1668 /* ! = MUST recognize ? = SHOULD recognize
1671 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1672 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1673 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1674 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1675 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1676 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1677 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1678 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1680 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1681 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1682 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1683 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1684 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1685 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1686 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1687 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1689 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1690 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1695 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1697 /* Put some upper limit on the number of extensions allowed. */
1698 require_quiet(extensionCount
< 10000, badCert
);
1699 certificate
->_extensionCount
= extensionCount
;
1700 certificate
->_extensions
=
1701 malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1704 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1705 require_noerr_quiet(drtn
, badCert
);
1706 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1707 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1708 require_quiet(drtn
== DR_Success
||
1709 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1710 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1712 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1713 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1714 &extn
, sizeof(extn
));
1715 require_noerr_quiet(drtn
, badCert
);
1716 /* Copy stuff into certificate->extensions[ix]. */
1717 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1718 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1719 &certificate
->_extensions
[ix
].critical
), badCert
);
1720 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1722 SecCertificateExtensionParser parser
=
1723 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1724 sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1726 /* Invoke the parser. */
1727 parser(certificate
, &certificate
->_extensions
[ix
]);
1728 } else if (certificate
->_extensions
[ix
].critical
) {
1729 if (isAppleExtensionOID(&extn
.extnID
)) {
1732 secdebug("cert", "Found unknown critical extension");
1733 certificate
->_foundUnknownCriticalExtension
= true;
1735 secdebug("cert", "Found unknown non critical extension");
1739 checkForMissingRevocationInfo(certificate
);
1748 /* Public API functions. */
1749 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1750 const UInt8
*der_bytes
, CFIndex der_length
) {
1751 if (der_bytes
== NULL
) return NULL
;
1752 if (der_length
== 0) return NULL
;
1754 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1755 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1756 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1758 memset((char*)result
+ sizeof(result
->_base
), 0,
1759 sizeof(*result
) - sizeof(result
->_base
));
1760 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1761 result
->_der
.length
= der_length
;
1762 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1763 if (!SecCertificateParse(result
)) {
1771 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1772 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1773 const UInt8
*der_bytes
, CFIndex der_length
);
1775 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1776 const UInt8
*der_bytes
, CFIndex der_length
) {
1777 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1779 /* @@@ End of placeholder. */
1781 /* AUDIT[securityd](done):
1782 der_certificate is a caller provided data of any length (might be 0), only
1783 its cf type has been checked.
1785 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1786 CFDataRef der_certificate
) {
1787 if (!der_certificate
) {
1790 CFIndex size
= sizeof(struct __SecCertificate
);
1791 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1792 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1794 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1795 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1796 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1797 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1798 if (!SecCertificateParse(result
)) {
1806 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1807 CFDataRef der_certificate
,
1808 CFTypeRef keychain_item
)
1810 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1812 CFRetainSafe(keychain_item
);
1813 result
->_keychain_item
= keychain_item
;
1818 OSStatus
SecCertificateSetKeychainItem(SecCertificateRef certificate
,
1819 CFTypeRef keychain_item
)
1824 CFRetainSafe(keychain_item
);
1825 CFReleaseSafe(certificate
->_keychain_item
);
1826 certificate
->_keychain_item
= keychain_item
;
1827 return errSecSuccess
;
1830 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1832 CFDataRef result
= NULL
;
1836 if (certificate
->_der_data
) {
1837 CFRetain(certificate
->_der_data
);
1838 result
= certificate
->_der_data
;
1840 result
= CFDataCreate(CFGetAllocator(certificate
),
1841 certificate
->_der
.data
, certificate
->_der
.length
);
1843 /* FIXME: If we wish to cache result we need to lock the certificate.
1844 Also this create 2 copies of the certificate data which is somewhat
1847 certificate
->_der_data
= result
;
1854 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1855 return certificate
->_der
.length
;
1858 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1859 return certificate
->_der
.data
;
1862 /* Used to recreate preCert from cert for Certificate Transparency */
1863 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1865 CFDataRef outData
= NULL
;
1866 DERItem tbsIn
= certificate
->_tbs
;
1867 DERItem tbsOut
= {0,};
1868 DERItem extensionsOut
= {0,};
1869 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1870 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
1874 /* decode the TBSCert - it was saved in full DER form */
1875 drtn
= DERParseSequence(&tbsIn
,
1876 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1877 &tbsCert
, sizeof(tbsCert
));
1878 require_noerr_quiet(drtn
, out
);
1880 /* Go over extensions and filter any SCT extension */
1881 CFIndex extensionsCount
= 0;
1883 if (tbsCert
.extensions
.length
) {
1886 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1888 require_noerr_quiet(drtn
, out
);
1889 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
1890 DERDecodedInfo currDecoded
;
1891 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1893 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
1895 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1896 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1897 &extn
, sizeof(extn
));
1898 require_noerr_quiet(drtn
, out
);
1900 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
1901 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
1904 extensionsList
[extensionsCount
] = currDecoded
.content
;
1905 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
1906 extensionsListSpecs
[extensionsCount
].options
= 0;
1907 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
1912 require_quiet(drtn
== DR_EndOfSequence
, out
);
1916 /* Encode extensions */
1917 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
1918 extensionsOut
.data
= malloc(extensionsOut
.length
);
1919 require_quiet(extensionsOut
.data
, out
);
1920 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
1921 require_noerr_quiet(drtn
, out
);
1923 tbsCert
.extensions
= extensionsOut
;
1925 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
1926 tbsOut
.data
= malloc(tbsOut
.length
);
1927 require_quiet(tbsOut
.data
, out
);
1928 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
1929 require_noerr_quiet(drtn
, out
);
1931 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
1934 free(extensionsOut
.data
);
1936 free(extensionsList
);
1937 free(extensionsListSpecs
);
1942 /* From rfc3280 - Appendix B. ASN.1 Notes
1944 Object Identifiers (OIDs) are used throughout this specification to
1945 identify certificate policies, public key and signature algorithms,
1946 certificate extensions, etc. There is no maximum size for OIDs.
1947 This specification mandates support for OIDs which have arc elements
1948 with values that are less than 2^28, that is, they MUST be between 0
1949 and 268,435,455, inclusive. This allows each arc element to be
1950 represented within a single 32 bit word. Implementations MUST also
1951 support OIDs where the length of the dotted decimal (see [RFC 2252],
1952 section 4.1) string representation can be up to 100 bytes
1953 (inclusive). Implementations MUST be able to handle OIDs with up to
1954 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1955 contain OIDs that exceed these requirements. Likewise, CRL issuers
1956 SHOULD NOT issue CRLs which contain OIDs that exceed these
1960 /* Oids longer than this are considered invalid. */
1961 #define MAX_OID_SIZE 32
1963 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1964 const DERItem
*oid
) {
1966 if (oid
->length
== 0) {
1967 return SecCopyCertString(SEC_NULL_KEY
);
1969 if (oid
->length
> MAX_OID_SIZE
) {
1970 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
1973 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1975 // The first two levels are encoded into one byte, since the root level
1976 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1977 // y may be > 39, so we have to add special-case handling for this.
1978 uint32_t x
= oid
->data
[0] / 40;
1979 uint32_t y
= oid
->data
[0] % 40;
1982 // Handle special case for large y if x = 2
1986 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1989 for (x
= 1; x
< oid
->length
; ++x
)
1991 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1992 /* @@@ value may not span more than 4 bytes. */
1993 /* A max number of 20 values is allowed. */
1994 if (!(oid
->data
[x
] & 0x80))
1996 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
2003 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
2004 const DERItem
*oid
) {
2005 if (oid
->length
== 0) {
2006 return SecCopyCertString(SEC_NULL_KEY
);
2009 /* Build the key we use to lookup the localized OID description. */
2010 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
2011 oid
->length
* 3 + 5);
2012 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
2014 for (ix
= 0; ix
< oid
->length
; ++ix
)
2015 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
2017 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
2018 if (CFEqual(oidKey
, name
)) {
2020 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2027 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
2028 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
2029 have a length of exactly 4 or 16 octects. */
2030 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
2031 const DERItem
*ip
) {
2032 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
2033 4 octects addr, or 8 octects, addr/mask for ipv6 it's
2034 16 octects addr, or 32 octects addr/mask. */
2035 CFStringRef value
= NULL
;
2036 if (ip
->length
== 4) {
2037 value
= CFStringCreateWithFormat(allocator
, NULL
,
2038 CFSTR("%u.%u.%u.%u"),
2039 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2040 } else if (ip
->length
== 16) {
2041 value
= CFStringCreateWithFormat(allocator
, NULL
,
2042 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
2043 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
2044 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2045 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2046 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2047 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2053 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2054 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
) {
2055 CFDictionaryRef property
;
2058 if (localizedLabel
) {
2061 ll
= localizedLabel
= SecCopyCertString(label
);
2063 const void *all_keys
[4];
2064 all_keys
[0] = kSecPropertyKeyType
;
2065 all_keys
[1] = kSecPropertyKeyLabel
;
2066 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2067 all_keys
[3] = kSecPropertyKeyValue
;
2068 const void *property_values
[] = {
2074 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2075 all_keys
, property_values
, value
? 4 : 3,
2076 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2079 const void *nolabel_keys
[2];
2080 nolabel_keys
[0] = kSecPropertyKeyType
;
2081 nolabel_keys
[1] = kSecPropertyKeyValue
;
2082 const void *property_values
[] = {
2086 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2087 nolabel_keys
, property_values
, 2,
2088 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2091 CFArrayAppendValue(properties
, property
);
2092 CFRelease(property
);
2096 #define UTC_TIME_NOSEC_ZULU_LEN 11
2098 #define UTC_TIME_ZULU_LEN 13
2099 /* YYMMDDhhmmssThhmm */
2100 #define UTC_TIME_LOCALIZED_LEN 17
2101 /* YYYYMMDDhhmmssZ */
2102 #define GENERALIZED_TIME_ZULU_LEN 15
2103 /* YYYYMMDDhhmmssThhmm */
2104 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2106 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2108 static inline int parseDecimalPair(const DERByte
**p
) {
2109 const DERByte
*cp
= *p
;
2111 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2114 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime.
2115 Return a CFErrorRef in the error parameter if decoding fails.
2116 Note that this is needed to distinguish an error condition from a
2117 valid time which specifies 2001-01-01 00:00:00 (i.e. a value of 0).
2119 static CFAbsoluteTime
SecAbsoluteTimeFromDateContentWithError(DERTag tag
,
2120 const uint8_t *bytes
,
2122 CFErrorRef
*error
) {
2126 if (NULL
== bytes
|| 0 == length
) {
2130 bool isUtcLength
= false;
2131 bool isLocalized
= false;
2132 bool noSeconds
= false;
2134 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2138 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2141 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2143 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2146 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2149 default: /* unknown format */
2153 /* Make sure the der tag fits the thing inside it. */
2154 if (tag
== ASN1_UTC_TIME
) {
2158 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2166 const DERByte
*cp
= bytes
;
2167 /* Check that all characters are digits, except if localized the timezone
2168 indicator or if not localized the 'Z' at the end. */
2170 for (ix
= 0; ix
< length
; ++ix
) {
2171 if (!(isdigit(cp
[ix
]))) {
2172 if ((isLocalized
&& ix
== length
- 5 &&
2173 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2174 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2181 /* Parse the date and time fields. */
2182 int year
, month
, day
, hour
, minute
, second
;
2184 year
= parseDecimalPair(&cp
);
2186 /* 0 <= year < 50 : assume century 21 */
2188 } else if (year
< 70) {
2189 /* 50 <= year < 70 : illegal per PKIX */
2192 /* 70 < year <= 99 : assume century 20 */
2196 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2198 month
= parseDecimalPair(&cp
);
2199 day
= parseDecimalPair(&cp
);
2200 hour
= parseDecimalPair(&cp
);
2201 minute
= parseDecimalPair(&cp
);
2205 second
= parseDecimalPair(&cp
);
2208 CFTimeInterval timeZoneOffset
;
2210 /* ZONE INDICATOR */
2211 int multiplier
= *cp
++ == '+' ? 60 : -60;
2212 timeZoneOffset
= multiplier
*
2213 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2218 secdebug("dateparse",
2219 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2220 (int) length
, bytes
, year
, month
,
2221 day
, hour
, minute
, second
,
2222 timeZoneOffset
/ 60);
2224 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2225 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2226 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
2227 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2228 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2233 int dy
= year
- 2001;
2238 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2239 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2241 day
+= is_leap_year
;
2243 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2244 return absTime
- timeZoneOffset
;
2248 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
2253 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2255 return SecAbsoluteTimeFromDateContentWithError(tag
, bytes
, length
, NULL
);
2258 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2259 CFAbsoluteTime
*pabsTime
) {
2260 CFErrorRef error
= NULL
;
2261 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContentWithError(tag
, date
->data
,
2262 date
->length
, &error
);
2264 secwarning("Invalid date specification in certificate (see RFC5280 4.1.2.5)");
2269 *pabsTime
= absTime
;
2273 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2274 true if the date was valid and properly decoded, also return the result in
2275 absTime. Return false otherwise. */
2276 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2277 CFAbsoluteTime
*absTime
) {
2278 if (dateChoice
->length
== 0) return false;
2280 DERDecodedInfo decoded
;
2281 if (DERDecodeItem(dateChoice
, &decoded
))
2284 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2288 static void appendDataProperty(CFMutableArrayRef properties
,
2289 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2290 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2291 der_data
->data
, der_data
->length
);
2292 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2297 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2299 CFStringRef localizedLabel
,
2300 const DERItem
*der_data
,
2301 CFStringRef labelFormat
) {
2302 CFStringRef newLabel
=
2303 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2304 labelFormat
, label
);
2306 if (localizedLabel
) {
2309 ll
= localizedLabel
= SecCopyCertString(label
);
2311 CFStringRef localizedLabelFormat
= SecCopyCertString(labelFormat
);
2312 CFStringRef newLocalizedLabel
=
2313 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2314 localizedLabelFormat
, localizedLabel
);
2316 CFReleaseSafe(localizedLabelFormat
);
2317 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
);
2318 CFReleaseSafe(newLabel
);
2319 CFReleaseSafe(newLocalizedLabel
);
2323 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2324 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2325 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2329 static void appendInvalidProperty(CFMutableArrayRef properties
,
2330 CFStringRef label
, const DERItem
*der_data
) {
2331 appendRelabeledProperty(properties
, label
, NULL
, der_data
, SEC_INVALID_KEY
);
2334 static void appendDateContentProperty(CFMutableArrayRef properties
,
2335 CFStringRef label
, DERTag tag
,
2336 const DERItem
*dateContent
) {
2337 CFAbsoluteTime absTime
;
2338 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2339 /* Date decode failure insert hex bytes instead. */
2340 return appendInvalidProperty(properties
, label
, dateContent
);
2342 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2343 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2347 static void appendDateProperty(CFMutableArrayRef properties
,
2348 CFStringRef label
, CFAbsoluteTime absTime
) {
2349 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2350 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2354 static void appendValidityPeriodProperty(CFMutableArrayRef parent
, CFStringRef label
,
2355 SecCertificateRef certificate
) {
2356 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2357 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2359 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2360 certificate
->_notBefore
);
2361 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2362 certificate
->_notAfter
);
2364 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2365 CFReleaseNull(properties
);
2368 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2369 CFStringRef label
, const DERItem
*ip
) {
2371 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2373 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2376 appendUnparsedProperty(properties
, label
, NULL
, ip
);
2380 static void appendURLContentProperty(CFMutableArrayRef properties
,
2381 CFStringRef label
, const DERItem
*urlContent
) {
2382 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2383 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2385 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
);
2388 appendInvalidProperty(properties
, label
, urlContent
);
2392 static void appendURLProperty(CFMutableArrayRef properties
,
2393 CFStringRef label
, const DERItem
*url
) {
2394 DERDecodedInfo decoded
;
2397 drtn
= DERDecodeItem(url
, &decoded
);
2398 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2399 appendInvalidProperty(properties
, label
, url
);
2401 appendURLContentProperty(properties
, label
, &decoded
.content
);
2405 static void appendOIDProperty(CFMutableArrayRef properties
,
2406 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
) {
2407 CFStringRef oid_string
=
2408 copyLocalizedOidDescription(CFGetAllocator(properties
), oid
);
2409 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2411 CFRelease(oid_string
);
2414 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2415 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2416 CFMutableArrayRef alg_props
=
2417 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2418 &kCFTypeArrayCallBacks
);
2419 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
, &algorithm
->oid
);
2420 if (algorithm
->params
.length
) {
2421 if (algorithm
->params
.length
== 2 &&
2422 algorithm
->params
.data
[0] == ASN1_NULL
&&
2423 algorithm
->params
.data
[1] == 0) {
2424 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2425 appendProperty(alg_props
, kSecPropertyTypeString
,
2426 SEC_PARAMETERS_KEY
, NULL
, value
);
2429 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2430 &algorithm
->params
);
2433 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
, alg_props
);
2434 CFRelease(alg_props
);
2437 static void appendPublicKeyProperty(CFMutableArrayRef parent
, CFStringRef label
,
2438 SecCertificateRef certificate
) {
2439 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2440 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2442 /* Public key algorithm. */
2443 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
2444 &certificate
->_algId
);
2446 /* Public Key Size */
2447 #if TARGET_OS_IPHONE
2448 SecKeyRef publicKey
= SecCertificateCopyPublicKey(certificate
);
2450 SecKeyRef publicKey
= SecCertificateCopyPublicKey_ios(certificate
);
2453 size_t sizeInBytes
= SecKeyGetBlockSize(publicKey
);
2454 CFStringRef sizeInBitsString
= CFStringCreateWithFormat(allocator
, NULL
,
2455 CFSTR("%ld"), (sizeInBytes
*8));
2456 if (sizeInBitsString
) {
2457 appendProperty(properties
, kSecPropertyTypeString
, SEC_PUBLIC_KEY_SIZE_KEY
,
2458 NULL
, sizeInBitsString
);
2460 CFReleaseNull(sizeInBitsString
);
2462 CFReleaseNull(publicKey
);
2464 /* Consider breaking down an RSA public key into modulus and
2466 appendDataProperty(properties
, SEC_PUBLIC_KEY_DATA_KEY
, NULL
,
2467 &certificate
->_pubKeyDER
);
2469 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2470 CFReleaseNull(properties
);
2473 static void appendSignatureProperty(CFMutableArrayRef parent
, CFStringRef label
,
2474 SecCertificateRef certificate
) {
2475 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2476 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2478 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
2479 &certificate
->_tbsSigAlg
);
2481 appendDataProperty(properties
, SEC_SIGNATURE_DATA_KEY
, NULL
,
2482 &certificate
->_signature
);
2484 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2485 CFReleaseNull(properties
);
2488 static void appendFingerprintsProperty(CFMutableArrayRef parent
, CFStringRef label
, SecCertificateRef certificate
) {
2489 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2490 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2492 CFDataRef sha256Fingerprint
= SecCertificateCopySHA256Digest(certificate
);
2493 if (sha256Fingerprint
) {
2494 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA2_FINGERPRINT_KEY
,
2495 NULL
, sha256Fingerprint
);
2497 CFReleaseNull(sha256Fingerprint
);
2499 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA1_FINGERPRINT_KEY
,
2500 NULL
, SecCertificateGetSHA1Digest(certificate
));
2502 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2503 CFReleaseNull(properties
);
2506 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2507 const DERItem
*blob
) {
2508 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2509 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2510 blob
->length
* 3 - 1);
2511 for (ix
= 0; ix
< length
; ++ix
)
2513 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2515 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2520 /* Returns a (localized) blob string. */
2521 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2522 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2523 CFStringRef localizedBlobType
= SecCopyCertString(blobType
);
2524 CFStringRef localizedQuanta
= SecCopyCertString(quanta
);
2525 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2526 "data = 00 00 ...)" */
2527 CFStringRef blobFormat
= SecCopyCertString(SEC_BLOB_KEY
);
2528 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2529 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2530 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2532 CFRelease(blobFormat
);
2533 CFReleaseSafe(localizedQuanta
);
2534 CFReleaseSafe(localizedBlobType
);
2539 /* Return a string verbatim (unlocalized) from a DER field. */
2540 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2541 const DERItem
*string
, CFStringEncoding encoding
,
2542 bool printableOnly
) {
2543 /* Strip potential bogus trailing zero from printable strings. */
2544 DERSize length
= string
->length
;
2545 if (length
&& string
->data
[length
- 1] == 0) {
2546 /* Don't mess with the length of UTF16 strings though. */
2547 if (encoding
!= kCFStringEncodingUTF16
)
2550 /* A zero length string isn't considered printable. */
2551 if (!length
&& printableOnly
)
2554 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2555 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2556 passing false makes it treat it as native endian by default. */
2557 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2558 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2562 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2565 /* From rfc3280 - Appendix B. ASN.1 Notes
2567 CAs MUST force the serialNumber to be a non-negative integer, that
2568 is, the sign bit in the DER encoding of the INTEGER value MUST be
2569 zero - this can be done by adding a leading (leftmost) `00'H octet if
2570 necessary. This removes a potential ambiguity in mapping between a
2571 string of octets and an integer value.
2573 As noted in section 4.1.2.2, serial numbers can be expected to
2574 contain long integers. Certificate users MUST be able to handle
2575 serialNumber values up to 20 octets in length. Conformant CAs MUST
2576 NOT use serialNumber values longer than 20 octets.
2579 /* Return the given numeric data as a string: decimal up to 64 bits,
2581 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2582 const DERItem
*integer
) {
2584 CFIndex ix
, length
= integer
->length
;
2586 if (length
== 0 || length
> 8)
2587 return copyHexDescription(allocator
, integer
);
2589 for(ix
= 0; ix
< length
; ++ix
) {
2591 value
+= integer
->data
[ix
];
2594 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2597 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2598 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2602 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2603 case ASN1_PRINTABLE_STRING
:
2604 case ASN1_IA5_STRING
:
2605 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2606 case ASN1_UTF8_STRING
:
2607 case ASN1_GENERAL_STRING
:
2608 case ASN1_UNIVERSAL_STRING
:
2609 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2610 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2611 case ASN1_VIDEOTEX_STRING
: // 21
2612 case ASN1_VISIBLE_STRING
: // 26
2613 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2614 case ASN1_BMP_STRING
: // 30
2615 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2616 case ASN1_OCTET_STRING
:
2617 return printableOnly
? NULL
:
2618 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2620 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2621 case ASN1_BIT_STRING
:
2622 return printableOnly
? NULL
:
2623 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2625 case ASN1_CONSTR_SEQUENCE
:
2626 return printableOnly
? NULL
:
2627 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2629 case ASN1_CONSTR_SET
:
2630 return printableOnly
? NULL
:
2631 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
, derThing
);
2632 case ASN1_OBJECT_ID
:
2633 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2635 if (printableOnly
) {
2638 CFStringRef fmt
= SecCopyCertString(SEC_NOT_DISPLAYED_KEY
);
2639 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2640 tag
, derThing
->length
);
2647 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2648 const DERItem
*derThing
, bool printableOnly
) {
2649 DERDecodedInfo decoded
;
2652 drtn
= DERDecodeItem(derThing
, &decoded
);
2654 /* TODO: Perhaps put something in the label saying we couldn't parse
2656 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2658 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2659 &decoded
.content
, false);
2663 static void appendDERThingProperty(CFMutableArrayRef properties
,
2664 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*derThing
) {
2665 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2667 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2669 CFReleaseSafe(value
);
2672 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2673 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2674 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2676 /* If there is more than one value pair we create a subsection for the
2677 second pair, and append things to the subsection for subsequent
2679 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2680 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2682 /* Since this is the second rdn pair for a given rdn, we setup a
2683 new subsection for this rdn. We remove the first property
2684 from the properties array and make it the first element in the
2685 subsection instead. */
2686 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2687 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2688 CFArrayAppendValue(rdn_props
, lastValue
);
2689 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2690 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2692 properties
= rdn_props
;
2694 /* Since this is the third or later rdn pair we have already
2695 created a subsection in the top level properties array. Instead
2696 of appending to that directly we append to the array inside the
2698 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2699 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2703 /* Finally we append the new rdn value to the property array. */
2704 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(
2705 CFGetAllocator(properties
), rdnType
);
2706 CFStringRef localizedLabel
=
2707 copyLocalizedOidDescription(CFGetAllocator(properties
), rdnType
);
2708 appendDERThingProperty(properties
, label
, localizedLabel
, rdnValue
);
2709 CFReleaseSafe(label
);
2710 CFReleaseSafe(localizedLabel
);
2711 return errSecSuccess
;
2714 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2715 const DERItem
*rdnSetContent
) {
2716 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2717 &kCFTypeArrayCallBacks
);
2718 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2721 CFArrayRemoveAllValues(properties
);
2722 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
);
2729 From rfc3739 - 3.1.2. Subject
2731 When parsing the subject here are some tips for a short name of the cert.
2732 Choice I: commonName
2733 Choice II: givenName
2734 Choice III: pseudonym
2736 The commonName attribute value SHALL, when present, contain a name
2737 of the subject. This MAY be in the subject's preferred
2738 presentation format, or a format preferred by the CA, or some
2739 other format. Pseudonyms, nicknames, and names with spelling
2740 other than defined by the registered name MAY be used. To
2741 understand the nature of the name presented in commonName,
2742 complying applications MAY have to examine present values of the
2743 givenName and surname attributes, or the pseudonym attribute.
2746 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2747 const DERItem
*x501NameContent
) {
2748 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2749 &kCFTypeArrayCallBacks
);
2750 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2753 CFArrayRemoveAllValues(properties
);
2754 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501NameContent
);
2760 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2761 const DERItem
*x501Name
) {
2762 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2763 &kCFTypeArrayCallBacks
);
2764 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2766 CFArrayRemoveAllValues(properties
);
2767 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501Name
);
2773 static void appendIntegerProperty(CFMutableArrayRef properties
,
2774 CFStringRef label
, const DERItem
*integer
) {
2775 CFStringRef string
= copyIntegerContentDescription(
2776 CFGetAllocator(properties
), integer
);
2777 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2781 static void appendBoolProperty(CFMutableArrayRef properties
,
2782 CFStringRef label
, bool boolean
) {
2783 CFStringRef value
= SecCopyCertString(boolean
? SEC_YES_KEY
: SEC_NO_KEY
);
2784 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2788 static void appendBooleanProperty(CFMutableArrayRef properties
,
2789 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2791 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2793 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2794 appendInvalidProperty(properties
, label
, boolean
);
2796 appendBoolProperty(properties
, label
, result
);
2800 static void appendSerialNumberProperty(CFMutableArrayRef parent
, CFStringRef label
,
2801 DERItem
*serialNum
) {
2802 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2803 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2805 if (serialNum
->length
) {
2806 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
2808 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
);
2811 CFReleaseNull(properties
);
2814 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2815 CFStringRef label
, const DERItem
*bitStringContent
,
2816 const CFStringRef
*names
, CFIndex namesCount
) {
2817 DERSize len
= bitStringContent
->length
- 1;
2818 require_quiet(len
== 1 || len
== 2, badDER
);
2819 DERByte numUnusedBits
= bitStringContent
->data
[0];
2820 require_quiet(numUnusedBits
< 8, badDER
);
2821 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2822 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2823 uint_fast16_t value
= bitStringContent
->data
[1];
2826 value
= (value
<< 8) + bitStringContent
->data
[2];
2832 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2833 CFStringRef string
= NULL
;
2834 for (ix
= 0; ix
< bits
; ++ix
) {
2838 CFStringCreateWithFormat(CFGetAllocator(properties
),
2839 NULL
, fmt
, string
, names
[ix
]);
2850 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2851 string
? string
: CFSTR(""));
2852 CFReleaseSafe(string
);
2855 appendInvalidProperty(properties
, label
, bitStringContent
);
2858 static void appendBitStringNames(CFMutableArrayRef properties
,
2859 CFStringRef label
, const DERItem
*bitString
,
2860 const CFStringRef
*names
, CFIndex namesCount
) {
2861 DERDecodedInfo bitStringContent
;
2862 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2863 require_noerr_quiet(drtn
, badDER
);
2864 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2865 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2869 appendInvalidProperty(properties
, label
, bitString
);
2872 static void appendKeyUsage(CFMutableArrayRef properties
,
2873 const DERItem
*extnValue
) {
2874 static const CFStringRef usageNames
[] = {
2875 SEC_DIGITAL_SIGNATURE_KEY
,
2876 SEC_NON_REPUDIATION_KEY
,
2877 SEC_KEY_ENCIPHERMENT_KEY
,
2878 SEC_DATA_ENCIPHERMENT_KEY
,
2879 SEC_KEY_AGREEMENT_KEY
,
2882 SEC_ENCIPHER_ONLY_KEY
,
2883 SEC_DECIPHER_ONLY_KEY
2885 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2886 usageNames
, array_size(usageNames
));
2889 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2890 const DERItem
*extnValue
) {
2891 DERPrivateKeyUsagePeriod pkup
;
2892 DERReturn drtn
= DERParseSequence(extnValue
,
2893 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2894 &pkup
, sizeof(pkup
));
2895 require_noerr_quiet(drtn
, badDER
);
2896 if (pkup
.notBefore
.length
) {
2897 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2898 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2900 if (pkup
.notAfter
.length
) {
2901 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2902 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2906 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
, extnValue
);
2909 static void appendStringContentProperty(CFMutableArrayRef properties
,
2910 CFStringRef label
, const DERItem
*stringContent
,
2911 CFStringEncoding encoding
) {
2912 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2913 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2915 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2918 appendInvalidProperty(properties
, label
, stringContent
);
2923 OtherName ::= SEQUENCE {
2924 type-id OBJECT IDENTIFIER,
2925 value [0] EXPLICIT ANY DEFINED BY type-id }
2927 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2928 const DERItem
*otherNameContent
) {
2930 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2931 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2933 require_noerr_quiet(drtn
, badDER
);
2934 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2936 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
2937 CFStringRef localizedLabel
=
2938 copyLocalizedOidDescription(allocator
, &on
.typeIdentifier
);
2939 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2941 appendProperty(properties
, kSecPropertyTypeString
, label
,
2942 localizedLabel
, value_string
);
2944 appendUnparsedProperty(properties
, label
, localizedLabel
, &on
.value
);
2946 CFReleaseSafe(value_string
);
2947 CFReleaseSafe(label
);
2948 CFReleaseSafe(localizedLabel
);
2951 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
, otherNameContent
);
2955 GeneralName ::= CHOICE {
2956 otherName [0] OtherName,
2957 rfc822Name [1] IA5String,
2958 dNSName [2] IA5String,
2959 x400Address [3] ORAddress,
2960 directoryName [4] Name,
2961 ediPartyName [5] EDIPartyName,
2962 uniformResourceIdentifier [6] IA5String,
2963 iPAddress [7] OCTET STRING,
2964 registeredID [8] OBJECT IDENTIFIER}
2966 EDIPartyName ::= SEQUENCE {
2967 nameAssigner [0] DirectoryString OPTIONAL,
2968 partyName [1] DirectoryString }
2970 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2971 DERTag tag
, const DERItem
*generalName
) {
2973 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2974 appendOtherNameContentProperty(properties
, generalName
);
2976 case ASN1_CONTEXT_SPECIFIC
| 1:
2978 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
2979 generalName
, kCFStringEncodingASCII
);
2981 case ASN1_CONTEXT_SPECIFIC
| 2:
2983 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
2984 kCFStringEncodingASCII
);
2986 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2987 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
2990 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2992 CFArrayRef directory_plist
=
2993 createPropertiesForX501Name(CFGetAllocator(properties
),
2995 appendProperty(properties
, kSecPropertyTypeSection
,
2996 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
);
2997 CFRelease(directory_plist
);
3000 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
3001 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
3004 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
3005 /* Technically I don't think this is valid, but there are certs out
3006 in the wild that use a constructed IA5String. In particular the
3007 VeriSign Time Stamping Authority CA.cer does this. */
3008 appendURLProperty(properties
, SEC_URI_KEY
, generalName
);
3010 case ASN1_CONTEXT_SPECIFIC
| 6:
3011 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
);
3013 case ASN1_CONTEXT_SPECIFIC
| 7:
3014 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
3017 case ASN1_CONTEXT_SPECIFIC
| 8:
3018 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
, generalName
);
3029 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
3030 const DERItem
*generalName
) {
3031 DERDecodedInfo generalNameContent
;
3032 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
3033 require_noerr_quiet(drtn
, badDER
);
3034 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
3035 &generalNameContent
.content
))
3038 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
, generalName
);
3043 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
3045 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
3046 const DERItem
*generalNamesContent
) {
3048 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
3049 require_noerr_quiet(drtn
, badDER
);
3050 DERDecodedInfo generalNameContent
;
3051 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
3053 if (!appendGeneralNameContentProperty(properties
,
3054 generalNameContent
.tag
, &generalNameContent
.content
)) {
3058 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3061 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3062 generalNamesContent
);
3065 static void appendGeneralNames(CFMutableArrayRef properties
,
3066 const DERItem
*generalNames
) {
3067 DERDecodedInfo generalNamesContent
;
3068 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
3069 require_noerr_quiet(drtn
, badDER
);
3070 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3072 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
3075 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
, generalNames
);
3079 BasicConstraints ::= SEQUENCE {
3080 cA BOOLEAN DEFAULT FALSE,
3081 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3083 static void appendBasicConstraints(CFMutableArrayRef properties
,
3084 const DERItem
*extnValue
) {
3085 DERBasicConstraints basicConstraints
;
3086 DERReturn drtn
= DERParseSequence(extnValue
,
3087 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3088 &basicConstraints
, sizeof(basicConstraints
));
3089 require_noerr_quiet(drtn
, badDER
);
3091 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3092 &basicConstraints
.cA
, false);
3094 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3095 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3096 &basicConstraints
.pathLenConstraint
);
3100 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
, extnValue
);
3104 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3106 * NameConstraints ::= SEQUENCE {
3107 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3108 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3110 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3112 * GeneralSubtree ::= SEQUENCE {
3114 * minimum [0] BaseDistance DEFAULT 0,
3115 * maximum [1] BaseDistance OPTIONAL }
3117 * BaseDistance ::= INTEGER (0..MAX)
3119 static void appendNameConstraints(CFMutableArrayRef properties
,
3120 const DERItem
*extnValue
) {
3121 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3122 DERNameConstraints nc
;
3124 drtn
= DERParseSequence(extnValue
,
3125 DERNumNameConstraintsItemSpecs
,
3126 DERNameConstraintsItemSpecs
,
3128 require_noerr_quiet(drtn
, badDER
);
3129 if (nc
.permittedSubtrees
.length
) {
3131 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3132 DERDecodedInfo gsContent
;
3133 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3134 DERGeneralSubtree derGS
;
3135 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3136 drtn
= DERParseSequenceContent(&gsContent
.content
,
3137 DERNumGeneralSubtreeItemSpecs
,
3138 DERGeneralSubtreeItemSpecs
,
3139 &derGS
, sizeof(derGS
));
3140 require_noerr_quiet(drtn
, badDER
);
3141 if (derGS
.minimum
.length
) {
3142 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
, &derGS
.minimum
);
3144 if (derGS
.maximum
.length
) {
3145 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
, &derGS
.maximum
);
3147 if (derGS
.generalName
.length
) {
3148 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3149 &kCFTypeArrayCallBacks
);
3150 appendProperty(properties
, kSecPropertyTypeSection
,
3151 SEC_PERMITTED_NAME_KEY
, NULL
, base
);
3152 appendGeneralNameProperty(base
, &derGS
.generalName
);
3156 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3158 if (nc
.excludedSubtrees
.length
) {
3160 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3161 DERDecodedInfo gsContent
;
3162 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3163 DERGeneralSubtree derGS
;
3164 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3165 drtn
= DERParseSequenceContent(&gsContent
.content
,
3166 DERNumGeneralSubtreeItemSpecs
,
3167 DERGeneralSubtreeItemSpecs
,
3168 &derGS
, sizeof(derGS
));
3169 require_noerr_quiet(drtn
, badDER
);
3170 if (derGS
.minimum
.length
) {
3171 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
, &derGS
.minimum
);
3173 if (derGS
.maximum
.length
) {
3174 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
, &derGS
.maximum
);
3176 if (derGS
.generalName
.length
) {
3177 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3178 &kCFTypeArrayCallBacks
);
3179 appendProperty(properties
, kSecPropertyTypeSection
,
3180 SEC_EXCLUDED_NAME_KEY
, NULL
, base
);
3181 appendGeneralNameProperty(base
, &derGS
.generalName
);
3185 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3190 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
, extnValue
);
3194 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3196 DistributionPoint ::= SEQUENCE {
3197 distributionPoint [0] DistributionPointName OPTIONAL,
3198 reasons [1] ReasonFlags OPTIONAL,
3199 cRLIssuer [2] GeneralNames OPTIONAL }
3201 DistributionPointName ::= CHOICE {
3202 fullName [0] GeneralNames,
3203 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3205 ReasonFlags ::= BIT STRING {
3209 affiliationChanged (3),
3211 cessationOfOperation (5),
3212 certificateHold (6),
3213 privilegeWithdrawn (7),
3216 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3217 const DERItem
*extnValue
) {
3218 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3221 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3222 require_noerr_quiet(drtn
, badDER
);
3223 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3224 DERDecodedInfo dpSeqContent
;
3225 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3226 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3227 DERDistributionPoint dp
;
3228 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3229 DERNumDistributionPointItemSpecs
,
3230 DERDistributionPointItemSpecs
,
3232 require_noerr_quiet(drtn
, badDER
);
3233 if (dp
.distributionPoint
.length
) {
3234 DERDecodedInfo distributionPointName
;
3235 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3236 require_noerr_quiet(drtn
, badDER
);
3237 if (distributionPointName
.tag
==
3238 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3240 appendGeneralNamesContent(properties
,
3241 &distributionPointName
.content
);
3242 } else if (distributionPointName
.tag
==
3243 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3244 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3246 appendProperty(properties
, kSecPropertyTypeSection
,
3247 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
);
3248 CFRelease(rdn_props
);
3253 if (dp
.reasons
.length
) {
3254 static const CFStringRef reasonNames
[] = {
3256 SEC_KEY_COMPROMISE_KEY
,
3257 SEC_CA_COMPROMISE_KEY
,
3258 SEC_AFFILIATION_CHANGED_KEY
,
3260 SEC_CESSATION_OF_OPER_KEY
,
3261 SEC_CERTIFICATE_HOLD_KEY
,
3262 SEC_PRIV_WITHDRAWN_KEY
,
3263 SEC_AA_COMPROMISE_KEY
3265 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3267 reasonNames
, array_size(reasonNames
));
3269 if (dp
.cRLIssuer
.length
) {
3270 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3271 &kCFTypeArrayCallBacks
);
3272 appendProperty(properties
, kSecPropertyTypeSection
,
3273 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
);
3274 CFRelease(crlIssuer
);
3275 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
3278 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3281 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
, extnValue
);
3284 /* Decode a sequence of integers into a comma separated list of ints. */
3285 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3286 CFStringRef label
, const DERItem
*intSequenceContent
) {
3287 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3289 CFStringRef fmt
= NULL
, value
= NULL
, intDesc
= NULL
, v
= NULL
;
3290 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3291 require_noerr_quiet(drtn
, badDER
);
3292 DERDecodedInfo intContent
;
3293 fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3294 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3295 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3296 intDesc
= copyIntegerContentDescription(
3297 allocator
, &intContent
.content
);
3299 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3300 CFReleaseNull(value
);
3302 CFReleaseNull(intDesc
);
3308 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3310 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
3314 /* DROPTHOUGH if !value. */
3317 CFReleaseNull(intDesc
);
3319 appendInvalidProperty(properties
, label
, intSequenceContent
);
3322 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3323 const DERItem
*extnValue
) {
3324 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3327 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3328 require_noerr_quiet(drtn
, badDER
);
3329 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3330 DERDecodedInfo piContent
;
3332 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3333 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3334 DERPolicyInformation pi
;
3335 drtn
= DERParseSequenceContent(&piContent
.content
,
3336 DERNumPolicyInformationItemSpecs
,
3337 DERPolicyInformationItemSpecs
,
3339 require_noerr_quiet(drtn
, badDER
);
3340 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3341 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3342 CFStringRef piFmt
= SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
);
3343 CFStringRef lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3346 appendOIDProperty(properties
, piLabel
, lpiLabel
, &pi
.policyIdentifier
);
3348 CFRelease(lpiLabel
);
3349 if (pi
.policyQualifiers
.length
== 0)
3353 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3354 require_noerr_quiet(drtn
, badDER
);
3355 DERDecodedInfo pqContent
;
3357 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3358 DERPolicyQualifierInfo pqi
;
3359 drtn
= DERParseSequenceContent(&pqContent
.content
,
3360 DERNumPolicyQualifierInfoItemSpecs
,
3361 DERPolicyQualifierInfoItemSpecs
,
3363 require_noerr_quiet(drtn
, badDER
);
3364 DERDecodedInfo qualifierContent
;
3365 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3366 require_noerr_quiet(drtn
, badDER
);
3367 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3368 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3369 CFStringRef pqFmt
= SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
);
3370 CFStringRef lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3373 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3374 &pqi
.policyQualifierID
);
3376 CFRelease(lpqLabel
);
3377 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3378 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3379 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3380 &qualifierContent
.content
);
3381 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3382 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3384 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3385 DERNumUserNoticeItemSpecs
,
3386 DERUserNoticeItemSpecs
,
3388 require_noerr_quiet(drtn
, badDER
);
3389 if (un
.noticeRef
.length
) {
3390 DERNoticeReference nr
;
3391 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3392 DERNumNoticeReferenceItemSpecs
,
3393 DERNoticeReferenceItemSpecs
,
3395 require_noerr_quiet(drtn
, badDER
);
3396 appendDERThingProperty(properties
,
3397 SEC_ORGANIZATION_KEY
, NULL
,
3399 appendIntegerSequenceContent(properties
,
3400 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
);
3402 if (un
.explicitText
.length
) {
3403 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3404 NULL
, &un
.explicitText
);
3407 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3411 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3413 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3416 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
, extnValue
);
3419 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3420 const DERItem
*extnValue
) {
3422 DERDecodedInfo keyIdentifier
;
3423 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3424 require_noerr_quiet(drtn
, badDER
);
3425 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3426 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3427 &keyIdentifier
.content
);
3431 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3436 AuthorityKeyIdentifier ::= SEQUENCE {
3437 keyIdentifier [0] KeyIdentifier OPTIONAL,
3438 authorityCertIssuer [1] GeneralNames OPTIONAL,
3439 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3440 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3441 -- be present or both be absent
3443 KeyIdentifier ::= OCTET STRING
3445 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3446 const DERItem
*extnValue
) {
3447 DERAuthorityKeyIdentifier akid
;
3449 drtn
= DERParseSequence(extnValue
,
3450 DERNumAuthorityKeyIdentifierItemSpecs
,
3451 DERAuthorityKeyIdentifierItemSpecs
,
3452 &akid
, sizeof(akid
));
3453 require_noerr_quiet(drtn
, badDER
);
3454 if (akid
.keyIdentifier
.length
) {
3455 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3456 &akid
.keyIdentifier
);
3458 if (akid
.authorityCertIssuer
.length
||
3459 akid
.authorityCertSerialNumber
.length
) {
3460 require_quiet(akid
.authorityCertIssuer
.length
&&
3461 akid
.authorityCertSerialNumber
.length
, badDER
);
3462 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3463 appendGeneralNamesContent(properties
,
3464 &akid
.authorityCertIssuer
);
3465 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3466 &akid
.authorityCertSerialNumber
);
3471 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
, extnValue
);
3475 PolicyConstraints ::= SEQUENCE {
3476 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3477 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3479 SkipCerts ::= INTEGER (0..MAX)
3481 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3482 const DERItem
*extnValue
) {
3483 DERPolicyConstraints pc
;
3485 drtn
= DERParseSequence(extnValue
,
3486 DERNumPolicyConstraintsItemSpecs
,
3487 DERPolicyConstraintsItemSpecs
,
3489 require_noerr_quiet(drtn
, badDER
);
3490 if (pc
.requireExplicitPolicy
.length
) {
3491 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3492 &pc
.requireExplicitPolicy
);
3494 if (pc
.inhibitPolicyMapping
.length
) {
3495 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3496 &pc
.inhibitPolicyMapping
);
3502 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
, extnValue
);
3506 extendedKeyUsage EXTENSION ::= {
3507 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3508 IDENTIFIED BY id-ce-extKeyUsage }
3510 KeyPurposeId ::= OBJECT IDENTIFIER
3512 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3513 const DERItem
*extnValue
) {
3516 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3517 require_noerr_quiet(drtn
, badDER
);
3518 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3519 DERDecodedInfo currDecoded
;
3520 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3521 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3522 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3523 &currDecoded
.content
);
3525 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3528 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
, extnValue
);
3532 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3534 AuthorityInfoAccessSyntax ::=
3535 SEQUENCE SIZE (1..MAX) OF AccessDescription
3537 AccessDescription ::= SEQUENCE {
3538 accessMethod OBJECT IDENTIFIER,
3539 accessLocation GeneralName }
3541 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3543 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3545 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3547 static void appendInfoAccess(CFMutableArrayRef properties
,
3548 const DERItem
*extnValue
) {
3551 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3552 require_noerr_quiet(drtn
, badDER
);
3553 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3554 DERDecodedInfo adContent
;
3555 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3556 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3557 DERAccessDescription ad
;
3558 drtn
= DERParseSequenceContent(&adContent
.content
,
3559 DERNumAccessDescriptionItemSpecs
,
3560 DERAccessDescriptionItemSpecs
,
3562 require_noerr_quiet(drtn
, badDER
);
3563 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3565 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3566 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3568 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3571 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
, extnValue
);
3574 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3575 const DERItem
*extnValue
) {
3576 static const CFStringRef certTypes
[] = {
3580 SEC_OBJECT_SIGNING_KEY
,
3584 SEC_OBJECT_SIGNING_CA_KEY
3586 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3587 certTypes
, array_size(certTypes
));
3590 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3591 CFStringRef label
, const DERItem
*sequence
) {
3594 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3595 require_noerr_quiet(drtn
, badSequence
);
3596 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3597 DERDecodedInfo currDecoded
;
3598 bool appendedSomething
= false;
3599 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3600 switch (currDecoded
.tag
)
3603 case ASN1_SEQUENCE
: // 16
3604 case ASN1_SET
: // 17
3605 // skip constructed object lengths
3608 case ASN1_UTF8_STRING
: // 12
3609 case ASN1_NUMERIC_STRING
: // 18
3610 case ASN1_PRINTABLE_STRING
: // 19
3611 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3612 case ASN1_VIDEOTEX_STRING
: // 21
3613 case ASN1_IA5_STRING
: // 22
3614 case ASN1_GRAPHIC_STRING
: // 25
3615 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3616 case ASN1_GENERAL_STRING
: // 27
3617 case ASN1_UNIVERSAL_STRING
: // 28
3619 CFStringRef string
=
3620 copyDERThingContentDescription(CFGetAllocator(properties
),
3621 currDecoded
.tag
, &currDecoded
.content
, false);
3622 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3624 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3627 appendedSomething
= true;
3634 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3635 return appendedSomething
;
3640 static void appendExtension(CFMutableArrayRef parent
,
3641 const SecCertificateExtension
*extn
) {
3642 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3643 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3644 &kCFTypeArrayCallBacks
);
3646 *extnID
= &extn
->extnID
,
3647 *extnValue
= &extn
->extnValue
;
3648 CFStringRef label
= NULL
;
3649 CFStringRef localizedLabel
= NULL
;
3651 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
);
3652 require_quiet(extnID
, xit
);
3654 bool handled
= true;
3655 /* Extensions that we know how to handle ourselves... */
3656 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3657 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3659 switch (extnID
->data
[extnID
->length
- 1]) {
3660 case 14: /* SubjectKeyIdentifier id-ce 14 */
3661 appendSubjectKeyIdentifier(properties
, extnValue
);
3663 case 15: /* KeyUsage id-ce 15 */
3664 appendKeyUsage(properties
, extnValue
);
3666 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3667 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3669 case 17: /* SubjectAltName id-ce 17 */
3670 case 18: /* IssuerAltName id-ce 18 */
3671 appendGeneralNames(properties
, extnValue
);
3673 case 19: /* BasicConstraints id-ce 19 */
3674 appendBasicConstraints(properties
, extnValue
);
3676 case 30: /* NameConstraints id-ce 30 */
3677 appendNameConstraints(properties
, extnValue
);
3679 case 31: /* CRLDistributionPoints id-ce 31 */
3680 appendCrlDistributionPoints(properties
, extnValue
);
3682 case 32: /* CertificatePolicies id-ce 32 */
3683 appendCertificatePolicies(properties
, extnValue
);
3685 case 33: /* PolicyMappings id-ce 33 */
3688 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3689 appendAuthorityKeyIdentifier(properties
, extnValue
);
3691 case 36: /* PolicyConstraints id-ce 36 */
3692 appendPolicyConstraints(properties
, extnValue
);
3694 case 37: /* ExtKeyUsage id-ce 37 */
3695 appendExtendedKeyUsage(properties
, extnValue
);
3697 case 46: /* FreshestCRL id-ce 46 */
3700 case 54: /* InhibitAnyPolicy id-ce 54 */
3707 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3708 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3710 switch (extnID
->data
[extnID
->length
- 1]) {
3711 case 1: /* AuthorityInfoAccess id-pe 1 */
3712 appendInfoAccess(properties
, extnValue
);
3714 case 3: /* QCStatements id-pe 3 */
3717 case 11: /* SubjectInfoAccess id-pe 11 */
3718 appendInfoAccess(properties
, extnValue
);
3724 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3725 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3726 appendNetscapeCertType(properties
, extnValue
);
3732 /* Try to parse and display printable string(s). */
3733 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3734 /* Nothing to do here appendPrintableDERSequence did the work. */
3736 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3737 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3740 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3741 localizedLabel
= copyLocalizedOidDescription(allocator
, extnID
);
3742 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
, properties
);
3745 CFReleaseSafe(localizedLabel
);
3746 CFReleaseSafe(label
);
3747 CFReleaseSafe(properties
);
3750 /* Different types of summary types from least desired to most desired. */
3753 kSummaryTypePrintable
,
3754 kSummaryTypeOrganizationName
,
3755 kSummaryTypeOrganizationalUnitName
,
3756 kSummaryTypeCommonName
,
3760 enum SummaryType type
;
3761 CFStringRef summary
;
3762 CFStringRef description
;
3765 static OSStatus
obtainSummaryFromX501Name(void *context
,
3766 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3767 struct Summary
*summary
= (struct Summary
*)context
;
3768 enum SummaryType stype
= kSummaryTypeNone
;
3769 CFStringRef string
= NULL
;
3770 if (DEROidCompare(type
, &oidCommonName
)) {
3771 stype
= kSummaryTypeCommonName
;
3772 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3773 stype
= kSummaryTypeOrganizationalUnitName
;
3774 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3775 stype
= kSummaryTypeOrganizationName
;
3776 } else if (DEROidCompare(type
, &oidDescription
)) {
3777 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3779 if (summary
->description
) {
3780 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3781 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->description
);
3783 CFRelease(summary
->description
);
3784 summary
->description
= newDescription
;
3786 summary
->description
= string
;
3789 stype
= kSummaryTypePrintable
;
3792 stype
= kSummaryTypePrintable
;
3795 /* Build a string with all instances of the most desired
3796 component type in reverse order encountered comma separated list,
3797 The order of desirability is defined by enum SummaryType. */
3798 if (summary
->type
<= stype
) {
3800 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3803 if (summary
->type
== stype
) {
3804 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3805 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->summary
);
3808 string
= newSummary
;
3810 summary
->type
= stype
;
3812 CFReleaseSafe(summary
->summary
);
3813 summary
->summary
= string
;
3816 CFReleaseSafe(string
);
3819 return errSecSuccess
;
3822 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3823 struct Summary summary
= {};
3824 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3825 /* If we found a description and a common name we change the summary to
3826 CommonName (Description). */
3827 if (summary
.description
) {
3828 if (summary
.type
== kSummaryTypeCommonName
) {
3829 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3830 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3832 CFRelease(summary
.summary
);
3833 summary
.summary
= newSummary
;
3835 CFRelease(summary
.description
);
3838 if (!summary
.summary
) {
3839 /* If we didn't find a suitable printable string in the subject at all, we try
3840 the first email address in the certificate instead. */
3841 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3843 /* If we didn't find any email addresses in the certificate, we try finding
3844 a DNS name instead. */
3845 names
= SecCertificateCopyDNSNames(certificate
);
3848 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3849 CFRetain(summary
.summary
);
3854 return summary
.summary
;
3857 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
3858 struct Summary summary
= {};
3859 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3860 /* If we found a description and a common name we change the summary to
3861 CommonName (Description). */
3862 if (summary
.description
) {
3863 if (summary
.type
== kSummaryTypeCommonName
) {
3864 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3865 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3867 CFRelease(summary
.summary
);
3868 summary
.summary
= newSummary
;
3870 CFRelease(summary
.description
);
3873 return summary
.summary
;
3876 /* Return the earliest date on which all certificates in this chain are still
3878 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3879 SecCertificateRef certificate
) {
3880 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3882 while (certificate
->_parent
) {
3883 certificate
= certificate
->_parent
;
3884 if (earliest
> certificate
->_notAfter
)
3885 earliest
= certificate
->_notAfter
;
3892 /* Return the latest date on which all certificates in this chain will be
3894 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3895 SecCertificateRef certificate
) {
3896 CFAbsoluteTime latest
= certificate
->_notBefore
;
3898 while (certificate
->_parent
) {
3899 certificate
= certificate
->_parent
;
3900 if (latest
< certificate
->_notBefore
)
3901 latest
= certificate
->_notBefore
;
3908 bool SecCertificateIsValid(SecCertificateRef certificate
,
3909 CFAbsoluteTime verifyTime
) {
3910 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
3911 verifyTime
<= certificate
->_notAfter
;
3914 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
3915 return certificate
->_version
+ 1;
3918 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
3919 return certificate
->_notBefore
;
3922 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
3923 return certificate
->_notAfter
;
3926 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3927 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
3928 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3929 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3930 &kCFTypeArrayCallBacks
);
3932 /* First we put the subject summary name. */
3933 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
3935 appendProperty(summary
, kSecPropertyTypeTitle
,
3936 NULL
, NULL
, ssummary
);
3937 CFRelease(ssummary
);
3940 /* Let see if this certificate is currently valid. */
3942 CFAbsoluteTime when
;
3943 CFStringRef message
;
3945 if (verifyTime
> certificate
->_notAfter
) {
3946 label
= SEC_EXPIRED_KEY
;
3947 when
= certificate
->_notAfter
;
3948 ptype
= kSecPropertyTypeError
;
3949 message
= SEC_CERT_EXPIRED_KEY
;
3950 } else if (certificate
->_notBefore
> verifyTime
) {
3951 label
= SEC_VALID_FROM_KEY
;
3952 when
= certificate
->_notBefore
;
3953 ptype
= kSecPropertyTypeError
;
3954 message
= SEC_CERT_NOT_YET_VALID_KEY
;
3956 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3957 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3958 if (verifyTime
> last
) {
3959 label
= SEC_EXPIRED_KEY
;
3961 ptype
= kSecPropertyTypeError
;
3962 message
= SEC_ISSUER_EXPIRED_KEY
;
3963 } else if (verifyTime
< first
) {
3964 label
= SEC_VALID_FROM_KEY
;
3966 ptype
= kSecPropertyTypeError
;
3967 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
3969 label
= SEC_EXPIRES_KEY
;
3970 when
= certificate
->_notAfter
;
3971 ptype
= kSecPropertyTypeSuccess
;
3972 message
= SEC_CERT_VALID_KEY
;
3976 appendDateProperty(summary
, label
, when
);
3977 CFStringRef lmessage
= SecCopyCertString(message
);
3978 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
);
3979 CFRelease(lmessage
);
3984 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
3985 if (!certificate
->_properties
) {
3986 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3987 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3988 &kCFTypeArrayCallBacks
);
3990 /* First we put the Subject Name in the property list. */
3991 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3992 &certificate
->_subject
);
3993 appendProperty(properties
, kSecPropertyTypeSection
,
3994 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
);
3995 CFRelease(subject_plist
);
3997 /* Next we put the Issuer Name in the property list. */
3998 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3999 &certificate
->_issuer
);
4000 appendProperty(properties
, kSecPropertyTypeSection
,
4001 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
);
4002 CFRelease(issuer_plist
);
4005 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4006 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4007 NULL
, fmt
, certificate
->_version
+ 1);
4009 appendProperty(properties
, kSecPropertyTypeString
,
4010 SEC_VERSION_KEY
, NULL
, versionString
);
4011 CFRelease(versionString
);
4014 appendSerialNumberProperty(properties
, SEC_SERIAL_NUMBER_KEY
, &certificate
->_serialNum
);
4016 /* Validity dates. */
4017 appendValidityPeriodProperty(properties
, SEC_VALIDITY_PERIOD_KEY
, certificate
);
4019 if (certificate
->_subjectUniqueID
.length
) {
4020 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4021 &certificate
->_subjectUniqueID
);
4023 if (certificate
->_issuerUniqueID
.length
) {
4024 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4025 &certificate
->_issuerUniqueID
);
4028 appendPublicKeyProperty(properties
, SEC_PUBLIC_KEY_KEY
, certificate
);
4031 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4032 appendExtension(properties
, &certificate
->_extensions
[ix
]);
4036 appendSignatureProperty(properties
, SEC_SIGNATURE_KEY
, certificate
);
4038 appendFingerprintsProperty(properties
, SEC_FINGERPRINTS_KEY
, certificate
);
4040 certificate
->_properties
= properties
;
4043 CFRetain(certificate
->_properties
);
4044 return certificate
->_properties
;
4048 /* On OS X, the SecCertificateCopySerialNumber API takes two arguments. */
4049 CFDataRef
SecCertificateCopySerialNumber(
4050 SecCertificateRef certificate
,
4051 CFErrorRef
*error
) {
4054 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4058 if (certificate
->_serialNumber
) {
4059 CFRetain(certificate
->_serialNumber
);
4061 return certificate
->_serialNumber
;
4064 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4065 CFDataRef
SecCertificateCopySerialNumber(
4066 SecCertificateRef certificate
) {
4067 if (certificate
->_serialNumber
) {
4068 CFRetain(certificate
->_serialNumber
);
4070 return certificate
->_serialNumber
;
4074 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4075 SecCertificateRef certificate
) {
4076 return certificate
->_normalizedIssuer
;
4079 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4080 SecCertificateRef certificate
) {
4081 return certificate
->_normalizedSubject
;
4084 /* Verify that certificate was signed by issuerKey. */
4085 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4086 SecKeyRef issuerKey
) {
4087 /* Setup algId in SecAsn1AlgId format. */
4089 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4090 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4091 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4092 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4094 /* RFC5280 4.1.1.2, 4.1.2.3 requires the actual signature algorithm
4095 must match the specified algorithm in the TBSCertificate. */
4096 bool sigAlgMatch
= DEROidCompare(&certificate
->_sigAlg
.oid
,
4097 &certificate
->_tbsSigAlg
.oid
);
4099 secwarning("Signature algorithm mismatch in certificate (see RFC5280 4.1.1.2)");
4102 CFErrorRef error
= NULL
;
4104 !SecVerifySignatureWithPublicKey(issuerKey
, &algId
,
4105 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4106 certificate
->_signature
.data
, certificate
->_signature
.length
, &error
))
4108 #if !defined(NDEBUG)
4109 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, (error
) ? (OSStatus
)CFErrorGetCode(error
) : errSecNotSigner
);
4111 CFReleaseSafe(error
);
4112 return errSecNotSigner
;
4115 return errSecSuccess
;
4118 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4119 if (!certificate
->_subjectAltName
) {
4122 return &certificate
->_subjectAltName
->extnValue
;
4125 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4126 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4127 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4128 if (gnType
== GNT_IPAddress
) {
4129 CFStringRef string
= copyIPAddressContentDescription(
4130 kCFAllocatorDefault
, generalName
);
4132 CFArrayAppendValue(ipAddresses
, string
);
4135 return errSecInvalidCertificate
;
4138 return errSecSuccess
;
4141 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4142 /* These can only exist in the subject alt name. */
4143 if (!certificate
->_subjectAltName
)
4146 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4147 0, &kCFTypeArrayCallBacks
);
4148 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4149 ipAddresses
, appendIPAddressesFromGeneralNames
);
4150 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4151 CFRelease(ipAddresses
);
4157 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4158 const DERItem
*generalName
) {
4159 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4160 if (gnType
== GNT_DNSName
) {
4161 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4162 generalName
->data
, generalName
->length
,
4163 kCFStringEncodingUTF8
, FALSE
);
4165 CFArrayAppendValue(dnsNames
, string
);
4168 return errSecInvalidCertificate
;
4171 return errSecSuccess
;
4174 /* Return true if the passed in string matches the
4175 Preferred name syntax from sections 2.3.1. in RFC 1035.
4176 With the added check that we disallow empty dns names.
4177 Also in order to support wildcard DNSNames we allow for the '*'
4178 character anywhere in a dns component where we currently allow
4181 <domain> ::= <subdomain> | " "
4183 <subdomain> ::= <label> | <subdomain> "." <label>
4185 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4187 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4189 <let-dig-hyp> ::= <let-dig> | "-"
4191 <let-dig> ::= <letter> | <digit>
4193 <letter> ::= any one of the 52 alphabetic characters A through Z in
4194 upper case and a through z in lower case
4196 <digit> ::= any one of the ten digits 0 through 9
4198 static bool isDNSName(CFStringRef string
) {
4199 CFStringInlineBuffer buf
= {};
4200 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4201 /* From RFC 1035 2.3.4. Size limits:
4202 labels 63 octets or less
4203 names 255 octets or less */
4204 require_quiet(length
<= 255, notDNS
);
4205 CFRange range
= { 0, length
};
4206 CFStringInitInlineBuffer(string
, &buf
, range
);
4210 kDNSStateAfterAlpha
,
4211 kDNSStateAfterDigit
,
4213 } state
= kDNSStateInital
;
4215 for (ix
= 0; ix
< length
; ++ix
) {
4216 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4219 require_quiet(labelLength
<= 64 &&
4220 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4222 state
= kDNSStateAfterDot
;
4224 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4226 state
= kDNSStateAfterAlpha
;
4227 } else if ('0' <= ch
&& ch
<= '9') {
4229 /* The requirement for labels to start with a letter was
4230 dropped so we don't check this anymore. */
4231 require_quiet(state
== kDNSStateAfterAlpha
||
4232 state
== kDNSStateAfterDigit
||
4233 state
== kDNSStateAfterDash
, notDNS
);
4235 state
= kDNSStateAfterDigit
;
4236 } else if (ch
== '-') {
4237 require_quiet(state
== kDNSStateAfterAlpha
||
4238 state
== kDNSStateAfterDigit
||
4239 state
== kDNSStateAfterDash
, notDNS
);
4240 state
= kDNSStateAfterDash
;
4246 /* We don't allow a dns name to end in a dot or dash. */
4247 require_quiet(labelLength
<= 63 &&
4248 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4256 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4257 const DERItem
*value
, CFIndex rdnIX
) {
4258 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4259 if (DEROidCompare(type
, &oidCommonName
)) {
4260 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4263 if (isDNSName(string
)) {
4264 /* We found a common name that is formatted like a valid
4266 CFArrayAppendValue(dnsNames
, string
);
4270 return errSecInvalidCertificate
;
4273 return errSecSuccess
;
4276 /* Not everything returned by this function is going to be a proper DNS name,
4277 we also return the certificates common name entries from the subject,
4278 assuming they look like dns names as specified in RFC 1035. */
4279 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4280 /* These can exist in the subject alt name or in the subject. */
4281 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4282 0, &kCFTypeArrayCallBacks
);
4283 OSStatus status
= errSecSuccess
;
4284 if (certificate
->_subjectAltName
) {
4285 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4286 dnsNames
, appendDNSNamesFromGeneralNames
);
4288 /* RFC 2818 section 3.1. Server Identity
4290 If a subjectAltName extension of type dNSName is present, that MUST
4291 be used as the identity. Otherwise, the (most specific) Common Name
4292 field in the Subject field of the certificate MUST be used. Although
4293 the use of the Common Name is existing practice, it is deprecated and
4294 Certification Authorities are encouraged to use the dNSName instead.
4297 This implies that if we found 1 or more DNSNames in the
4298 subjectAltName, we should not use the Common Name of the subject as
4301 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4302 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4303 appendDNSNamesFromX501Name
);
4305 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4306 CFRelease(dnsNames
);
4312 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4313 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4314 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4315 if (gnType
== GNT_RFC822Name
) {
4316 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4317 generalName
->data
, generalName
->length
,
4318 kCFStringEncodingASCII
, FALSE
);
4320 CFArrayAppendValue(dnsNames
, string
);
4323 return errSecInvalidCertificate
;
4326 return errSecSuccess
;
4329 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4330 const DERItem
*value
, CFIndex rdnIX
) {
4331 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4332 if (DEROidCompare(type
, &oidEmailAddress
)) {
4333 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4336 CFArrayAppendValue(dnsNames
, string
);
4339 return errSecInvalidCertificate
;
4342 return errSecSuccess
;
4345 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4346 /* These can exist in the subject alt name or in the subject. */
4347 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4348 0, &kCFTypeArrayCallBacks
);
4349 OSStatus status
= errSecSuccess
;
4350 if (certificate
->_subjectAltName
) {
4351 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4352 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4355 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4356 appendRFC822NamesFromX501Name
);
4358 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4359 CFRelease(rfc822Names
);
4365 OSStatus
SecCertificateCopyEmailAddresses(SecCertificateRef certificate
, CFArrayRef
* __nonnull CF_RETURNS_RETAINED emailAddresses
) {
4366 if (!certificate
|| !emailAddresses
) {
4369 *emailAddresses
= SecCertificateCopyRFC822Names(certificate
);
4370 return errSecSuccess
;
4373 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4374 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4375 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4376 if (DEROidCompare(type
, &oidCommonName
)) {
4377 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4380 CFArrayAppendValue(commonNames
, string
);
4383 return errSecInvalidCertificate
;
4386 return errSecSuccess
;
4389 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4390 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4391 0, &kCFTypeArrayCallBacks
);
4393 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4394 appendCommonNamesFromX501Name
);
4395 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4396 CFRelease(commonNames
);
4402 OSStatus
SecCertificateCopyCommonName(SecCertificateRef certificate
, CFStringRef
*commonName
)
4407 CFArrayRef commonNames
= SecCertificateCopyCommonNames(certificate
);
4409 return errSecInternal
;
4413 CFIndex count
= CFArrayGetCount(commonNames
);
4414 *commonName
= CFRetainSafe(CFArrayGetValueAtIndex(commonNames
, count
-1));
4416 CFReleaseSafe(commonNames
);
4417 return errSecSuccess
;
4420 static OSStatus
appendOrganizationFromX501Name(void *context
,
4421 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4422 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4423 if (DEROidCompare(type
, &oidOrganizationName
)) {
4424 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4427 CFArrayAppendValue(organization
, string
);
4430 return errSecInvalidCertificate
;
4433 return errSecSuccess
;
4436 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4437 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4438 0, &kCFTypeArrayCallBacks
);
4440 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4441 appendOrganizationFromX501Name
);
4442 if (status
|| CFArrayGetCount(organization
) == 0) {
4443 CFRelease(organization
);
4444 organization
= NULL
;
4446 return organization
;
4449 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4450 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4451 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4452 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4453 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4456 CFArrayAppendValue(organizationalUnit
, string
);
4459 return errSecInvalidCertificate
;
4462 return errSecSuccess
;
4465 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4466 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4467 0, &kCFTypeArrayCallBacks
);
4469 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4470 appendOrganizationalUnitFromX501Name
);
4471 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4472 CFRelease(organizationalUnit
);
4473 organizationalUnit
= NULL
;
4475 return organizationalUnit
;
4478 static OSStatus
appendCountryFromX501Name(void *context
,
4479 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4480 CFMutableArrayRef countries
= (CFMutableArrayRef
)context
;
4481 if (DEROidCompare(type
, &oidCountryName
)) {
4482 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4485 CFArrayAppendValue(countries
, string
);
4488 return errSecInvalidCertificate
;
4491 return errSecSuccess
;
4494 CFArrayRef
SecCertificateCopyCountry(SecCertificateRef certificate
) {
4495 CFMutableArrayRef countries
= CFArrayCreateMutable(kCFAllocatorDefault
,
4496 0, &kCFTypeArrayCallBacks
);
4498 status
= parseX501NameContent(&certificate
->_subject
, countries
,
4499 appendCountryFromX501Name
);
4500 if (status
|| CFArrayGetCount(countries
) == 0) {
4501 CFRelease(countries
);
4507 const SecCEBasicConstraints
*
4508 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4509 if (certificate
->_basicConstraints
.present
)
4510 return &certificate
->_basicConstraints
;
4515 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
4516 return (certificate
->_permittedSubtrees
);
4519 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
4520 return (certificate
->_excludedSubtrees
);
4523 const SecCEPolicyConstraints
*
4524 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4525 if (certificate
->_policyConstraints
.present
)
4526 return &certificate
->_policyConstraints
;
4531 const SecCEPolicyMappings
*
4532 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4533 if (certificate
->_policyMappings
.present
) {
4534 return &certificate
->_policyMappings
;
4540 const SecCECertificatePolicies
*
4541 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4542 if (certificate
->_certificatePolicies
.present
)
4543 return &certificate
->_certificatePolicies
;
4548 const SecCEInhibitAnyPolicy
*
4549 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4550 if (certificate
->_inhibitAnyPolicySkipCerts
.present
) {
4551 return &certificate
->_inhibitAnyPolicySkipCerts
;
4557 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4558 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4559 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4560 if (gnType
== GNT_OtherName
) {
4562 DERReturn drtn
= DERParseSequenceContent(generalName
,
4563 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4565 require_noerr_quiet(drtn
, badDER
);
4566 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4568 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4569 &on
.value
, true), badDER
);
4570 CFArrayAppendValue(ntPrincipalNames
, string
);
4574 return errSecSuccess
;
4577 return errSecInvalidCertificate
;
4581 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
4582 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4583 0, &kCFTypeArrayCallBacks
);
4584 OSStatus status
= errSecSuccess
;
4585 if (certificate
->_subjectAltName
) {
4586 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4587 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4589 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4590 CFRelease(ntPrincipalNames
);
4591 ntPrincipalNames
= NULL
;
4593 return ntPrincipalNames
;
4596 static OSStatus
appendToRFC2253String(void *context
,
4597 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4598 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4602 ST stateOrProvinceName
4604 OU organizationalUnitName
4606 STREET streetAddress
4610 /* Prepend a + if this is not the first RDN in an RDN set.
4611 Otherwise prepend a , if this is not the first RDN. */
4613 CFStringAppend(string
, CFSTR("+"));
4614 else if (CFStringGetLength(string
)) {
4615 CFStringAppend(string
, CFSTR(","));
4618 CFStringRef label
, oid
= NULL
;
4619 /* @@@ Consider changing this to a dictionary lookup keyed by the
4620 decimal representation. */
4621 if (DEROidCompare(type
, &oidCommonName
)) {
4622 label
= CFSTR("CN");
4623 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4625 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4626 label
= CFSTR("ST");
4627 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4629 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4630 label
= CFSTR("OU");
4631 } else if (DEROidCompare(type
, &oidCountryName
)) {
4634 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4635 label
= CFSTR("STREET");
4636 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4637 label
= CFSTR("DC");
4638 } else if (DEROidCompare(type
, &oidUserID
)) {
4639 label
= CFSTR("UID");
4642 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4645 CFStringAppend(string
, label
);
4646 CFStringAppend(string
, CFSTR("="));
4647 CFStringRef raw
= NULL
;
4649 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4652 /* Append raw to string while escaping:
4653 a space or "#" character occurring at the beginning of the string
4654 a space character occurring at the end of the string
4655 one of the characters ",", "+", """, "\", "<", ">" or ";"
4657 CFStringInlineBuffer buffer
= {};
4658 CFIndex ix
, length
= CFStringGetLength(raw
);
4659 CFRange range
= { 0, length
};
4660 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4661 for (ix
= 0; ix
< length
; ++ix
) {
4662 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4664 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4665 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4666 ch
== '<' || ch
== '>' || ch
== ';' ||
4667 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4668 (ch
== '#' && ix
== 0)) {
4669 UniChar chars
[] = { '\\', ch
};
4670 CFStringAppendCharacters(string
, chars
, 2);
4672 CFStringAppendCharacters(string
, &ch
, 1);
4677 /* Append the value in hex. */
4678 CFStringAppend(string
, CFSTR("#"));
4680 for (ix
= 0; ix
< value
->length
; ++ix
)
4681 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4686 return errSecSuccess
;
4689 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
4690 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4691 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4692 if (status
|| CFStringGetLength(string
) == 0) {
4699 static OSStatus
appendToCompanyNameString(void *context
,
4700 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4701 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4702 if (CFStringGetLength(string
) != 0)
4703 return errSecSuccess
;
4705 if (!DEROidCompare(type
, &oidOrganizationName
))
4706 return errSecSuccess
;
4709 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4711 return errSecSuccess
;
4712 CFStringAppend(string
, raw
);
4715 return errSecSuccess
;
4718 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
4719 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4720 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4721 appendToCompanyNameString
);
4722 if (status
|| CFStringGetLength(string
) == 0) {
4729 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4730 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4731 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4732 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4734 CFDataSetLength(sequence
, sequence_length
);
4735 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4736 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
4737 require_noerr_quiet(DEREncodeLength(content
->length
,
4738 sequence_ptr
, &seq_len_length
), out
);
4739 sequence_ptr
+= seq_len_length
;
4740 memcpy(sequence_ptr
, content
->data
, content
->length
);
4743 CFReleaseSafe(sequence
);
4747 CFDataRef
SecCertificateCopyIssuerSequence(
4748 SecCertificateRef certificate
) {
4749 return SecDERItemCopySequence(&certificate
->_issuer
);
4752 CFDataRef
SecCertificateCopySubjectSequence(
4753 SecCertificateRef certificate
) {
4754 return SecDERItemCopySequence(&certificate
->_subject
);
4757 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(SecCertificateRef certificate
) {
4758 if (!certificate
|| !certificate
->_normalizedIssuer
) {
4762 tmpItem
.data
= (void *)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
4763 tmpItem
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
4765 return SecDERItemCopySequence(&tmpItem
);
4768 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(SecCertificateRef certificate
) {
4769 if (!certificate
|| !certificate
->_normalizedSubject
) {
4773 tmpItem
.data
= (void *)CFDataGetBytePtr(certificate
->_normalizedSubject
);
4774 tmpItem
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
4776 return SecDERItemCopySequence(&tmpItem
);
4779 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4780 SecCertificateRef certificate
) {
4781 return &certificate
->_algId
;
4784 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
4785 return &certificate
->_pubKeyDER
;
4789 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
4790 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
4792 __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
4794 __nullable SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
4797 if (certificate
->_pubKey
== NULL
) {
4798 const DERAlgorithmId
*algId
=
4799 SecCertificateGetPublicKeyAlgorithm(certificate
);
4800 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4801 const DERItem
*params
= NULL
;
4802 if (algId
->params
.length
!= 0) {
4803 params
= &algId
->params
;
4805 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
4806 SecAsn1Item params1
= {
4807 .Data
= params
? params
->data
: NULL
,
4808 .Length
= params
? params
->length
: 0
4810 SecAsn1Item keyData1
= {
4811 .Data
= keyData
? keyData
->data
: NULL
,
4812 .Length
= keyData
? keyData
->length
: 0
4814 certificate
->_pubKey
= SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
4818 return CFRetainSafe(certificate
->_pubKey
);
4821 bool SecCertificateIsWeakKey(SecCertificateRef certificate
) {
4823 SecKeyRef pubKey
= NULL
;
4825 require_quiet(pubKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
4827 require_quiet(pubKey
= SecCertificateCopyPublicKey(certificate
) ,out
);
4829 size_t size
= SecKeyGetBlockSize(pubKey
);
4830 switch (SecKeyGetAlgorithmIdentifier(pubKey
)) {
4831 case kSecRSAAlgorithmID
:
4832 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
4834 case kSecECDSAAlgorithmID
:
4835 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
4842 CFReleaseSafe(pubKey
);
4846 bool SecCertificateIsWeakHash(SecCertificateRef certificate
) {
4847 SecSignatureHashAlgorithm certAlg
= 0;
4848 certAlg
= SecCertificateGetSignatureHashAlgorithm(certificate
);
4849 if (certAlg
== kSecSignatureHashAlgorithmUnknown
||
4850 certAlg
== kSecSignatureHashAlgorithmMD2
||
4851 certAlg
== kSecSignatureHashAlgorithmMD4
||
4852 certAlg
== kSecSignatureHashAlgorithmMD5
||
4853 certAlg
== kSecSignatureHashAlgorithmSHA1
) {
4859 bool SecCertificateIsAtLeastMinKeySize(SecCertificateRef certificate
,
4860 CFDictionaryRef keySizes
) {
4861 bool goodSize
= false;
4862 SecKeyRef pubKey
= NULL
;
4864 require_quiet(pubKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
4866 require_quiet(pubKey
= SecCertificateCopyPublicKey(certificate
) ,out
);
4868 size_t size
= SecKeyGetBlockSize(pubKey
);
4869 CFNumberRef minSize
;
4870 size_t minSizeInBits
;
4871 switch (SecKeyGetAlgorithmIdentifier(pubKey
)) {
4872 case kSecRSAAlgorithmID
:
4873 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeRSA
, (const void**)&minSize
)
4874 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
4875 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
4878 case kSecECDSAAlgorithmID
:
4879 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeEC
, (const void**)&minSize
)
4880 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
4881 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
4888 CFReleaseSafe(pubKey
);
4892 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
4893 if (!certificate
|| !certificate
->_der
.data
) {
4896 if (!certificate
->_sha1Digest
) {
4897 certificate
->_sha1Digest
=
4898 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4899 certificate
->_der
.data
, certificate
->_der
.length
);
4901 return certificate
->_sha1Digest
;
4904 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
4905 if (!certificate
|| !certificate
->_der
.data
) {
4908 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4909 certificate
->_der
.data
, certificate
->_der
.length
);
4912 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
4913 CFDataRef digest
= NULL
;
4914 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
4916 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4917 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4923 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
4924 if (!certificate
|| !certificate
->_pubKeyDER
.data
) {
4927 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4928 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4931 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA1Digest(SecCertificateRef certificate
) {
4932 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
4935 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4936 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
4939 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
4940 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
4943 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4944 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
4947 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
4952 CFRetainSafe(certificate
->_keychain_item
);
4953 return certificate
->_keychain_item
;
4956 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
4960 if (!certificate
->_authorityKeyID
&&
4961 certificate
->_authorityKeyIdentifier
.length
) {
4962 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4963 certificate
->_authorityKeyIdentifier
.data
,
4964 certificate
->_authorityKeyIdentifier
.length
);
4967 return certificate
->_authorityKeyID
;
4970 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
4974 if (!certificate
->_subjectKeyID
&&
4975 certificate
->_subjectKeyIdentifier
.length
) {
4976 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4977 certificate
->_subjectKeyIdentifier
.data
,
4978 certificate
->_subjectKeyIdentifier
.length
);
4981 return certificate
->_subjectKeyID
;
4984 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
4988 return certificate
->_crlDistributionPoints
;
4991 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
4995 return certificate
->_ocspResponders
;
4998 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5002 return certificate
->_caIssuers
;
5005 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5009 return certificate
->_subjectAltName
&&
5010 certificate
->_subjectAltName
->critical
;
5013 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5017 /* Since the _subject field is the content of the subject and not the
5018 whole thing, we can simply check for a 0 length subject here. */
5019 return certificate
->_subject
.length
!= 0;
5022 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5026 return certificate
->_foundUnknownCriticalExtension
;
5029 /* Private API functions. */
5030 void SecCertificateShow(SecCertificateRef certificate
) {
5032 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5033 fprintf(stderr
, "\n");
5037 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5038 SecCertificateRef certificate
) {
5039 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5040 CFNumberRef certificateType
, certificateEncoding
;
5041 CFStringRef label
, alias
;
5042 CFDataRef skid
, pubKeyDigest
, certData
;
5043 CFDictionaryRef dict
= NULL
;
5047 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5048 SInt32 ctv
= certificate
->_version
+ 1;
5049 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5050 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5051 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5052 certData
= SecCertificateCopyData(certificate
);
5053 skid
= SecCertificateGetSubjectKeyID(certificate
);
5054 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5055 certificate
->_pubKeyDER
.length
);
5057 /* We still need to figure out how to deal with multi valued attributes. */
5058 alias
= SecCertificateCopyRFC822Names(certificate
);
5059 label
= SecCertificateCopySubjectSummary(certificate
);
5065 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5066 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5067 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5069 DICT_ADDPAIR(kSecAttrLabel
, label
);
5071 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5072 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5073 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5074 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5076 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5077 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5078 DICT_ADDPAIR(kSecValueData
, certData
);
5079 dict
= DICT_CREATE(allocator
);
5081 CFReleaseSafe(label
);
5082 CFReleaseSafe(pubKeyDigest
);
5083 CFReleaseSafe(certData
);
5084 CFReleaseSafe(certificateEncoding
);
5085 CFReleaseSafe(certificateType
);
5090 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5091 CFDictionaryRef refAttributes
) {
5092 /* @@@ Support having an allocator in refAttributes. */
5093 CFAllocatorRef allocator
= NULL
;
5094 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5095 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5099 static bool _SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
5100 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
5101 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
5102 SecKeyRef publicKey
= NULL
;
5103 require(certificate
&& (CFGetTypeID(certificate
) == SecCertificateGetTypeID()), out
);
5105 require(publicKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
5107 require(publicKey
= SecCertificateCopyPublicKey(certificate
), out
);
5109 CFDataRef normalizedIssuer
=
5110 SecCertificateGetNormalizedIssuerContent(certificate
);
5111 CFDataRef normalizedSubject
=
5112 SecCertificateGetNormalizedSubjectContent(certificate
);
5113 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5114 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5116 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5117 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5118 if (authorityKeyID
) {
5119 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5122 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5124 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
5126 CFReleaseSafe(publicKey
);
5129 return (certificate
->_isSelfSigned
== kSecSelfSignedTrue
);
5132 bool SecCertificateIsCA(SecCertificateRef certificate
) {
5133 bool result
= false;
5134 require(certificate
&& (CFGetTypeID(certificate
) == SecCertificateGetTypeID()), out
);
5135 if (SecCertificateVersion(certificate
) >= 3) {
5136 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5137 result
= (basicConstraints
&& basicConstraints
->isCA
);
5140 result
= _SecCertificateIsSelfSigned(certificate
);
5146 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5147 return (_SecCertificateIsSelfSigned(certificate
) && SecCertificateIsCA(certificate
));
5150 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5151 if (!certificate
|| (CFGetTypeID(certificate
) != SecCertificateGetTypeID())) {
5152 return errSecInvalidCertificate
;
5154 if (!isSelfSigned
) {
5157 *isSelfSigned
= _SecCertificateIsSelfSigned(certificate
);
5158 return errSecSuccess
;
5161 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5163 return kSecKeyUsageUnspecified
;
5165 return certificate
->_keyUsage
;
5168 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5170 CFMutableArrayRef extended_key_usage_oids
=
5171 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5172 require_quiet(certificate
&& extended_key_usage_oids
, out
);
5174 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5175 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5176 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5177 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5180 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5181 require_noerr_quiet(drtn
, out
);
5182 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5183 DERDecodedInfo currDecoded
;
5185 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5186 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5187 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5188 currDecoded
.content
.data
, currDecoded
.content
.length
);
5190 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5194 require_quiet(drtn
== DR_EndOfSequence
, out
);
5195 return extended_key_usage_oids
;
5199 CFReleaseSafe(extended_key_usage_oids
);
5203 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5205 require_quiet(certificate
, out
);
5208 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5209 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5210 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5211 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5212 /* Got the SCT oid */
5213 DERDecodedInfo sctList
;
5214 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5215 require_noerr_quiet(drtn
, out
);
5216 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5217 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5225 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5226 if (der
.length
> 1) {
5227 DERDecodedInfo decoded
;
5228 DERDecodeItem(&der
, &decoded
);
5229 switch (decoded
.tag
) {
5232 return decoded
.content
.length
== 0 && expected
== NULL
;
5236 case ASN1_IA5_STRING
:
5237 case ASN1_UTF8_STRING
: {
5238 if (isString(expected
)) {
5239 CFStringRef expectedString
= (CFStringRef
) expected
;
5240 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5242 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5243 CFReleaseNull(itemString
);
5249 case ASN1_OCTET_STRING
: {
5250 if (isData(expected
)) {
5251 CFDataRef expectedData
= (CFDataRef
) expected
;
5252 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5254 bool result
= CFEqual(expectedData
, itemData
);
5255 CFReleaseNull(itemData
);
5261 case ASN1_INTEGER
: {
5262 SInt32 expected_value
= 0;
5263 if (isString(expected
))
5265 CFStringRef aStr
= (CFStringRef
)expected
;
5266 expected_value
= CFStringGetIntValue(aStr
);
5268 else if (isNumber(expected
))
5270 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5273 uint32_t num_value
= 0;
5274 if (!DERParseInteger(&decoded
.content
, &num_value
))
5276 return ((uint32_t)expected_value
== num_value
);
5289 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5292 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5293 size_t oid_len
= CFDataGetLength(oid
);
5295 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5296 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5297 if (extn
->extnID
.length
== oid_len
5298 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5300 return matches_expected(extn
->extnValue
, expectedValue
);
5306 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5308 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5311 struct search_context
{
5313 SecCertificateRef certificate
;
5316 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5318 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5319 bool result
= false;
5321 if ( CFStringGetLength(string
) > 0
5322 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5325 *value
= CFStringGetIntValue(string
);
5329 CFReleaseNull(nonDecimalDigit
);
5334 bool SecCertificateIsOidString(CFStringRef oid
)
5336 if (!oid
) return false;
5337 if (2 >= CFStringGetLength(oid
)) return false;
5340 /* oid string only has the allowed characters */
5341 CFCharacterSetRef decimalOid
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
5342 CFCharacterSetRef nonDecimalOid
= CFCharacterSetCreateInvertedSet(NULL
, decimalOid
);
5343 if (CFStringFindCharacterFromSet(oid
, nonDecimalOid
, CFRangeMake(0, CFStringGetLength(oid
)), kCFCompareForcedOrdering
, NULL
)) {
5347 /* first arc is allowed */
5348 UniChar firstArc
[2];
5349 CFRange firstTwo
= {0, 2};
5350 CFStringGetCharacters(oid
, firstTwo
, firstArc
);
5351 if (firstArc
[1] != '.' ||
5352 (firstArc
[0] != '0' && firstArc
[0] != '1' && firstArc
[0] != '2')) {
5356 CFReleaseNull(decimalOid
);
5357 CFReleaseNull(nonDecimalOid
);
5362 CFDataRef
SecCertificateCreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
5364 CFMutableDataRef currentResult
= NULL
;
5365 CFDataRef encodedResult
= NULL
;
5367 CFArrayRef parts
= NULL
;
5370 if (!string
|| !SecCertificateIsOidString(string
))
5373 parts
= CFStringCreateArrayBySeparatingStrings(NULL
, string
, CFSTR("."));
5378 count
= CFArrayGetCount(parts
);
5382 // assume no more than 5 bytes needed to represent any part of the oid,
5383 // since we limit parts to 32-bit values,
5384 // but the first two parts only need 1 byte
5385 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
5391 part
= CFArrayGetValueAtIndex(parts
, 0);
5393 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
5400 part
= CFArrayGetValueAtIndex(parts
, 1);
5402 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
5408 CFDataAppendBytes(currentResult
, &firstByte
, 1);
5410 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
5411 uint8_t b
[5] = {0, 0, 0, 0, 0};
5413 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
5414 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
5415 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
5416 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
5418 // Skip the unused extension bytes.
5419 size_t skipBytes
= 0;
5420 while (b
[skipBytes
] == 0x80)
5423 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
5426 encodedResult
= currentResult
;
5427 currentResult
= NULL
;
5430 CFReleaseNull(parts
);
5431 CFReleaseNull(currentResult
);
5433 return encodedResult
;
5436 static void check_for_marker(const void *key
, const void *value
, void *context
)
5438 struct search_context
* search_ctx
= (struct search_context
*) context
;
5439 CFStringRef key_string
= (CFStringRef
) key
;
5440 CFTypeRef value_ref
= (CFTypeRef
) value
;
5442 // If we could have short circuted the iteration
5443 // we would have, but the best we can do
5444 // is not waste time comparing once a match
5446 if (search_ctx
->found
)
5449 if (CFGetTypeID(key_string
) != CFStringGetTypeID())
5452 CFDataRef key_data
= SecCertificateCreateOidDataFromString(NULL
, key_string
);
5454 if (NULL
== key_data
)
5457 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
5458 search_ctx
->found
= true;
5460 CFReleaseNull(key_data
);
5464 // CFType Ref is either:
5466 // CFData - OID to match with no data permitted
5467 // CFString - decimal OID to match
5468 // CFDictionary - OID -> Value table for expected values Single Object or Array
5469 // CFArray - Array of the above.
5471 // This returns true if any of the requirements are met.
5472 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
5474 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
5475 CFIndex ix
, length
= CFArrayGetCount(oids
);
5476 for (ix
= 0; ix
< length
; ix
++)
5477 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
5479 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
5480 struct search_context context
= { .found
= false, .certificate
= certificate
};
5481 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
5482 return context
.found
;
5483 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
5484 return cert_contains_marker_extension(certificate
, oids
);
5485 } else if (CFGetTypeID(oids
) == CFStringGetTypeID()) {
5486 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oids
);
5487 if (dataOid
== NULL
) return false;
5488 bool result
= cert_contains_marker_extension(certificate
, dataOid
);
5489 CFReleaseNull(dataOid
);
5495 static DERItem
*cert_extension_value_for_marker(SecCertificateRef certificate
, CFDataRef oid
) {
5497 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5498 size_t oid_len
= CFDataGetLength(oid
);
5500 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5501 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5502 if (extn
->extnID
.length
== oid_len
5503 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5505 return (DERItem
*)&extn
->extnValue
;
5512 // CFType Ref is either:
5514 // CFData - OID to match with no data permitted
5515 // CFString - decimal OID to match
5517 DERItem
*SecCertificateGetExtensionValue(SecCertificateRef certificate
, CFTypeRef oid
) {
5518 if (!certificate
|| !oid
) {
5522 if(CFGetTypeID(oid
) == CFDataGetTypeID()) {
5523 return cert_extension_value_for_marker(certificate
, oid
);
5524 } else if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
5525 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oid
);
5526 if (dataOid
== NULL
) return NULL
;
5527 DERItem
*result
= cert_extension_value_for_marker(certificate
, dataOid
);
5528 CFReleaseNull(dataOid
);
5535 CFDataRef
SecCertificateCopyiAPAuthCapabilities(SecCertificateRef certificate
) {
5539 CFDataRef extensionData
= NULL
;
5540 DERItem
*extensionValue
= NULL
;
5541 extensionValue
= SecCertificateGetExtensionValue(certificate
,
5542 CFSTR("1.2.840.113635.100.6.36"));
5543 require_quiet(extensionValue
, out
);
5544 /* The extension is a octet string containing the DER-encoded 32-byte octet string */
5545 require_quiet(extensionValue
->length
== 34, out
);
5546 DERDecodedInfo decodedValue
;
5547 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
5548 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
5549 require_quiet(decodedValue
.content
.length
== 32, out
);
5550 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
5551 decodedValue
.content
.length
);
5553 require_quiet(extensionValue
->data
[33] == 0x00 &&
5554 extensionValue
->data
[32] == 0x00, out
);
5555 extensionData
= CFDataCreate(NULL
, extensionValue
->data
, 32);
5558 return extensionData
;
5562 /* From iapd IAPAuthenticationTypes.h */
5563 typedef struct IapCertSerialNumber
5565 uint8_t xservID
; // Xserver ID
5566 uint8_t hsmID
; // Hardware security module ID (generated cert)
5567 uint8_t delimiter01
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5568 uint8_t dateYear
; // Date year cert was issued
5569 uint8_t dateMonth
; // Date month cert was issued
5570 uint8_t dateDay
; // Date day cert was issued
5571 uint8_t delimiter02
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5572 uint8_t devClass
; // iAP device class (maps to lingo permissions)
5573 uint8_t delimiter03
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5574 uint8_t batchNumHi
; // Batch number high byte (15:08)
5575 uint8_t batchNumLo
; // Batch number low byte (07:00)
5576 uint8_t delimiter04
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
5577 uint8_t serialNumHi
; // Serial number high byte (23:16)
5578 uint8_t serialNumMid
; // Serial number middle byte (15:08)
5579 uint8_t serialNumLo
; // Serial number low byte (07:00)
5581 } IapCertSerialNumber_t
, *pIapCertSerialNumber_t
;
5584 #define IAP_CERT_FIELD_DELIMITER 0xAA // "Apple_Accessory" delimiter
5585 SeciAuthVersion
SecCertificateGetiAuthVersion(SecCertificateRef certificate
) {
5587 return kSeciAuthInvalid
;
5589 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
5590 CFSTR("1.2.840.113635.100.6.36"))) {
5591 return kSeciAuthVersion3
;
5593 DERItem serialNumber
= certificate
->_serialNum
;
5594 require_quiet(serialNumber
.data
, out
);
5595 require_quiet(serialNumber
.length
== 15, out
);
5596 require_quiet(serialNumber
.data
[2] == IAP_CERT_FIELD_DELIMITER
&&
5597 serialNumber
.data
[6] == IAP_CERT_FIELD_DELIMITER
&&
5598 serialNumber
.data
[8] == IAP_CERT_FIELD_DELIMITER
&&
5599 serialNumber
.data
[11] == IAP_CERT_FIELD_DELIMITER
, out
);
5600 return kSeciAuthVersion2
;
5602 return kSeciAuthInvalid
;
5605 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
5606 CFDataRef pem_certificate
)
5608 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
5609 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
5610 uint8_t *base64_data
= NULL
;
5611 SecCertificateRef cert
= NULL
;
5612 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
5613 //const size_t length = CFDataGetLength(pem_certificate);
5614 char *begin
= strstr((const char *)data
, begin_cert
);
5615 char *end
= strstr((const char *)data
, end_cert
);
5618 begin
+= sizeof(begin_cert
) - 1;
5619 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
5620 if (base64_length
) {
5621 require_quiet(base64_data
= calloc(1, base64_length
), out
);
5622 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
5623 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
5632 // -- MARK -- XPC encoding/decoding
5635 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5637 return true; // NOOP
5639 size_t length
= SecCertificateGetLength(certificate
);
5640 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
5641 #if SECTRUST_VERBOSE_DEBUG
5642 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
5644 if (!length
|| !bytes
) {
5645 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
5647 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
5651 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
5652 SecCertificateRef certificate
= NULL
;
5654 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
5656 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
5659 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
5664 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
5665 xpc_object_t xpc_certificates
;
5666 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
5667 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
5668 CFIndex ix
, count
= CFArrayGetCount(certificates
);
5669 for (ix
= 0; ix
< count
; ++ix
) {
5670 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
5671 #if SECTRUST_VERBOSE_DEBUG
5672 CFIndex length
= SecCertificateGetLength(certificate
);
5673 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
5674 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
);
5676 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
5677 xpc_release(xpc_certificates
);
5678 xpc_certificates
= NULL
;
5684 return xpc_certificates
;
5687 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5688 CFMutableArrayRef certificates
= NULL
;
5689 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
5690 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
5691 size_t count
= xpc_array_get_count(xpc_certificates
);
5692 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
5693 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
5696 for (ix
= 0; ix
< count
; ++ix
) {
5697 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
5699 CFRelease(certificates
);
5702 CFArraySetValueAtIndex(certificates
, ix
, cert
);
5707 return certificates
;
5710 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
5713 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
5715 __block CFArrayRef result
= NULL
;
5717 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
5719 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
5720 ^bool(xpc_object_t message
, CFErrorRef
*error
)
5722 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
5725 ^bool(xpc_object_t response
, CFErrorRef
*error
)
5727 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
5729 if (response
&& (NULL
!= xpc_array
)) {
5730 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
5733 return SecError(errSecInternal
, error
, CFSTR("Did not get the Escrow certificates"));
5735 return result
!= NULL
;
5740 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
5742 CFArrayRef result
= NULL
;
5744 CFDataRef certData
= NULL
;
5747 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
5748 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
||
5749 kSecCertificateBaselineEscrowBackupRoot
== escrowRootType
||
5750 kSecCertificateBaselineEscrowEnrollmentRoot
== escrowRootType
)
5752 // The request is for the base line certificates.
5753 // Use the hard coded data to generate the return array.
5754 struct RootRecord
** pEscrowRoots
;
5755 switch (escrowRootType
) {
5756 case kSecCertificateBaselineEscrowRoot
:
5757 numRoots
= kNumberOfBaseLineEscrowRoots
;
5758 pEscrowRoots
= kBaseLineEscrowRoots
;
5760 case kSecCertificateBaselinePCSEscrowRoot
:
5761 numRoots
= kNumberOfBaseLinePCSEscrowRoots
;
5762 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
5764 case kSecCertificateBaselineEscrowBackupRoot
:
5765 numRoots
= kNumberOfBaseLineEscrowBackupRoots
;
5766 pEscrowRoots
= kBaseLineEscrowBackupRoots
;
5768 case kSecCertificateBaselineEscrowEnrollmentRoot
:
5770 numRoots
= kNumberOfBaseLineEscrowEnrollmentRoots
;
5771 pEscrowRoots
= kBaseLineEscrowEnrollmentRoots
;
5775 // Get the hard coded set of roots
5776 SecCertificateRef baseLineCerts
[numRoots
];
5777 struct RootRecord
* pRootRecord
= NULL
;
5779 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
5780 pRootRecord
= pEscrowRoots
[iCnt
];
5781 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
) {
5782 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
5783 if (NULL
!= certData
) {
5784 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5785 CFRelease(certData
);
5789 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5790 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
5791 if (NULL
!= baseLineCerts
[iCnt
]) {
5792 CFRelease(baseLineCerts
[iCnt
]);
5797 // The request is for the current certificates.
5798 CFErrorRef error
= NULL
;
5799 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
5800 if (NULL
!= error
|| NULL
== cert_datas
) {
5801 if (NULL
!= error
) {
5804 if (NULL
!= cert_datas
) {
5805 CFRelease(cert_datas
);
5810 numRoots
= (int)(CFArrayGetCount(cert_datas
));
5812 SecCertificateRef assetCerts
[numRoots
];
5813 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
5814 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
5815 if (NULL
!= certData
) {
5816 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5817 assetCerts
[iCnt
] = aCertRef
;
5820 assetCerts
[iCnt
] = NULL
;
5825 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5826 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
5827 if (NULL
!= assetCerts
[iCnt
]) {
5828 CFRelease(assetCerts
[iCnt
]);
5832 CFReleaseSafe(cert_datas
);
5837 SEC_CONST_DECL (kSecSignatureDigestAlgorithmUnknown
, "SignatureDigestUnknown");
5838 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD2
, "SignatureDigestMD2");
5839 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD4
, "SignatureDigestMD4");
5840 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD5
, "SignatureDigestMD5");
5841 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA1
, "SignatureDigestSHA1");
5842 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA224
, "SignatureDigestSHA224");
5843 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA256
, "SignatureDigestSHA256");
5844 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA384
, "SignatureDigestSHA284");
5845 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA512
, "SignatureDigestSHA512");
5847 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
5849 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
5850 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
5851 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
5853 if (!algOid
->data
|| !algOid
->length
) {
5856 /* classify the signature algorithm OID into one of our known types */
5857 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
5858 DEROidCompare(algOid
, &oidSha512Rsa
) ||
5859 DEROidCompare(algOid
, &oidSha512
)) {
5860 result
= kSecSignatureHashAlgorithmSHA512
;
5863 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
5864 DEROidCompare(algOid
, &oidSha384Rsa
) ||
5865 DEROidCompare(algOid
, &oidSha384
)) {
5866 result
= kSecSignatureHashAlgorithmSHA384
;
5869 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
5870 DEROidCompare(algOid
, &oidSha256Rsa
) ||
5871 DEROidCompare(algOid
, &oidSha256
)) {
5872 result
= kSecSignatureHashAlgorithmSHA256
;
5875 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
5876 DEROidCompare(algOid
, &oidSha224Rsa
) ||
5877 DEROidCompare(algOid
, &oidSha224
)) {
5878 result
= kSecSignatureHashAlgorithmSHA224
;
5881 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
5882 DEROidCompare(algOid
, &oidSha1Rsa
) ||
5883 DEROidCompare(algOid
, &oidSha1Dsa
) ||
5884 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
5885 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
5886 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
5887 DEROidCompare(algOid
, &oidSha1Fee
) ||
5888 DEROidCompare(algOid
, &oidSha1
)) {
5889 result
= kSecSignatureHashAlgorithmSHA1
;
5892 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
5893 DEROidCompare(algOid
, &oidMd5Fee
) ||
5894 DEROidCompare(algOid
, &oidMd5
)) {
5895 result
= kSecSignatureHashAlgorithmMD5
;
5898 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
5899 DEROidCompare(algOid
, &oidMd4
)) {
5900 result
= kSecSignatureHashAlgorithmMD4
;
5903 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
5904 DEROidCompare(algOid
, &oidMd2
)) {
5905 result
= kSecSignatureHashAlgorithmMD2
;