2 * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 #include "SecCertificateInternalP.h"
31 #include <CommonCrypto/CommonDigest.h>
32 #include <CoreFoundation/CFRuntime.h>
33 #include <CoreFoundation/CFString.h>
34 #include <CoreFoundation/CFBundle.h>
35 #include <CoreFoundation/CFDictionary.h>
36 #include <CoreFoundation/CFNumber.h>
37 #include <CoreFoundation/CFTimeZone.h>
40 #include <AssertMacros.h>
41 #include <libDER/libDER.h>
42 #include <libDER/DER_CertCrl.h>
43 #include <libDER/DER_Encode.h>
44 #include <libDER/DER_Keys.h>
45 #include <libDER/asn1Types.h>
46 #include <libDER/oidsPriv.h>
48 #include "SecBasePriv.h"
50 #include "SecRSAKeyP.h"
51 #include "SecFrameworkP.h"
53 #include "SecItemPriv.h"
56 #include <libkern/OSByteOrder.h>
58 #include "SecInternalP.h"
59 #include "SecBase64P.h"
61 #include <security_utilities/debugging.h>
63 typedef struct SecCertificateExtension
{
67 } SecCertificateExtension
;
70 typedef struct KnownExtension
{
76 kSecSelfSignedUnknown
= 0,
82 struct __SecCertificate
{
85 DERItem _der
; /* Entire certificate in DER form. */
86 DERItem _tbs
; /* To Be Signed cert DER bytes. */
87 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
88 DERItem _signature
; /* The content of the sig bit string. */
91 DERItem _serialNum
; /* Integer. */
92 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
93 DERItem _issuer
; /* Sequence of RDN. */
94 CFAbsoluteTime _notBefore
;
95 CFAbsoluteTime _notAfter
;
96 DERItem _subject
; /* Sequence of RDN. */
97 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
98 DERItem _pubKeyDER
; /* contents of bit string */
99 DERItem _issuerUniqueID
; /* bit string, optional */
100 DERItem _subjectUniqueID
; /* bit string, optional */
103 /* Known extensions if the certificate contains them,
104 extnValue.length will be > 0. */
105 KnownExtension _authorityKeyID
;
107 /* This extension is used to uniquely identify a certificate from among
108 several that have the same subject name. If the extension is not
109 present, its value is calculated by performing a SHA-1 hash of the
110 certificate's DER encoded subjectPublicKeyInfo, as recommended by
112 KnownExtension _subjectKeyID
;
113 KnownExtension _keyUsage
;
114 KnownExtension _extendedKeyUsage
;
115 KnownExtension _basicConstraints
;
116 KnownExtension _netscapeCertType
;
117 KnownExtension _subjectAltName
;
118 KnownExtension _qualCertStatements
;
121 bool _foundUnknownCriticalExtension
;
123 /* Well known certificate extensions. */
124 SecCEBasicConstraints _basicConstraints
;
125 SecCEPolicyConstraints _policyConstraints
;
126 CFDictionaryRef _policyMappings
;
127 SecCECertificatePolicies _certificatePolicies
;
129 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
130 value of the SkipCerts field of the InhibitAnyPolicy extension
132 uint32_t _inhibitAnyPolicySkipCerts
;
134 /* If KeyUsage extension is not present this is 0, otherwise it's
135 the value of the extension. */
136 SecKeyUsage _keyUsage
;
138 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
139 Length = 0 if not present. */
140 DERItem _subjectKeyIdentifier
;
142 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
143 Length = 0 if not present. */
144 DERItem _authorityKeyIdentifier
;
145 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
146 _authorityKeyIdentifierSerialNumber have non zero length if present.
147 Both are either present or absent together. */
148 DERItem _authorityKeyIdentifierIssuer
;
149 DERItem _authorityKeyIdentifierSerialNumber
;
151 /* Subject alt name extension, if present. Not malloced, it's just a
152 pointer to an element in the _extensions array. */
153 const SecCertificateExtension
*_subjectAltName
;
155 /* Parsed extension values. */
157 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
158 CFMutableArrayRef _crlDistributionPoints
;
160 /* Array of CFURLRefs containing the URI values of accessLocations of each
161 id-ad-ocsp AccessDescription in the Authority Information Access
163 CFMutableArrayRef _ocspResponders
;
165 /* Array of CFURLRefs containing the URI values of accessLocations of each
166 id-ad-caIssuers AccessDescription in the Authority Information Access
168 CFMutableArrayRef _caIssuers
;
170 /* All other (non known) extensions. The _extensions array is malloced. */
171 CFIndex _extensionCount
;
172 SecCertificateExtension
*_extensions
;
174 /* Optional cached fields. */
177 CFArrayRef _properties
;
178 CFDataRef _serialNumber
;
179 CFDataRef _normalizedIssuer
;
180 CFDataRef _normalizedSubject
;
181 CFDataRef _authorityKeyID
;
182 CFDataRef _subjectKeyID
;
184 CFDataRef _sha1Digest
;
185 uint8_t _isSelfSigned
;
189 /* Public Constants for property list keys. */
190 const CFStringRef kSecPropertyKeyType
= CFSTR("type");
191 const CFStringRef kSecPropertyKeyLabel
= CFSTR("label");
192 const CFStringRef kSecPropertyKeyLocalizedLabel
= CFSTR("localized label");
193 const CFStringRef kSecPropertyKeyValue
= CFSTR("value");
195 /* Public Constants for property list values. */
196 const CFStringRef kSecPropertyTypeWarning
= CFSTR("warning");
197 const CFStringRef kSecPropertyTypeError
= CFSTR("error");
198 const CFStringRef kSecPropertyTypeSuccess
= CFSTR("success");
199 const CFStringRef kSecPropertyTypeTitle
= CFSTR("title");
200 const CFStringRef kSecPropertyTypeSection
= CFSTR("section");
201 const CFStringRef kSecPropertyTypeData
= CFSTR("data");
202 const CFStringRef kSecPropertyTypeString
= CFSTR("string");
203 const CFStringRef kSecPropertyTypeURL
= CFSTR("url");
204 const CFStringRef kSecPropertyTypeDate
= CFSTR("date");
206 /* Extension parsing routine. */
207 typedef void (*SecCertificateExtensionParser
)(SecCertificateRefP certificate
,
208 const SecCertificateExtension
*extn
);
210 /* CFRuntime regsitration data. */
211 static pthread_once_t kSecCertificateRegisterClass
= PTHREAD_ONCE_INIT
;
212 static CFTypeID kSecCertificateTypeID
= _kCFRuntimeNotATypeID
;
214 /* Mapping from extension OIDs (as a DERItem *) to
215 SecCertificateExtensionParser extension parsing routines. */
216 static CFDictionaryRef gExtensionParsers
;
218 /* Forward declartions of static functions. */
219 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
);
220 static void SecCertificateDestroy(CFTypeRef cf
);
221 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
222 CFAbsoluteTime
*absTime
);
224 /* Static functions. */
225 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
) {
226 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
227 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
228 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
,
229 SecCertificateCopySubjectSummaryP(certificate
),
230 SecCertificateCopyIssuerSummaryP(certificate
));
233 static void SecCertificateDestroy(CFTypeRef cf
) {
234 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
235 if (certificate
->_certificatePolicies
.policies
)
236 free(certificate
->_certificatePolicies
.policies
);
237 CFReleaseSafe(certificate
->_policyMappings
);
238 CFReleaseSafe(certificate
->_crlDistributionPoints
);
239 CFReleaseSafe(certificate
->_ocspResponders
);
240 CFReleaseSafe(certificate
->_caIssuers
);
241 if (certificate
->_extensions
) {
242 free(certificate
->_extensions
);
244 CFReleaseSafe(certificate
->_pubKey
);
245 CFReleaseSafe(certificate
->_der_data
);
246 CFReleaseSafe(certificate
->_properties
);
247 CFReleaseSafe(certificate
->_serialNumber
);
248 CFReleaseSafe(certificate
->_normalizedIssuer
);
249 CFReleaseSafe(certificate
->_normalizedSubject
);
250 CFReleaseSafe(certificate
->_authorityKeyID
);
251 CFReleaseSafe(certificate
->_subjectKeyID
);
252 CFReleaseSafe(certificate
->_sha1Digest
);
255 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
256 SecCertificateRefP cert1
= (SecCertificateRefP
)cf1
;
257 SecCertificateRefP cert2
= (SecCertificateRefP
)cf2
;
260 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
262 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
265 /* Hash of the certificate is der length + signature length + last 4 bytes
267 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
268 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
269 DERSize der_length
= certificate
->_der
.length
;
270 DERSize sig_length
= certificate
->_signature
.length
;
271 DERSize ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
272 CFHashCode hashCode
= 0;
273 for (; ix
< sig_length
; ++ix
)
274 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
276 return (hashCode
+ der_length
+ sig_length
);
281 /************************************************************************/
282 /************************* General Name Parsing *************************/
283 /************************************************************************/
285 typedef OSStatus (*parseGeneralNameCallback
)(void *context
,
286 SecCEGeneralNameType type
, const DERItem
*value
);
290 GeneralName ::= CHOICE {
291 otherName [0] OtherName,
292 rfc822Name [1] IA5String,
293 dNSName [2] IA5String,
294 x400Address [3] ORAddress,
295 directoryName [4] Name,
296 ediPartyName [5] EDIPartyName,
297 uniformResourceIdentifier [6] IA5String,
298 iPAddress [7] OCTET STRING,
299 registeredID [8] OBJECT IDENTIFIER}
301 OtherName ::= SEQUENCE {
302 type-id OBJECT IDENTIFIER,
303 value [0] EXPLICIT ANY DEFINED BY type-id }
305 EDIPartyName ::= SEQUENCE {
306 nameAssigner [0] DirectoryString OPTIONAL,
307 partyName [1] DirectoryString }
309 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
310 const DERItem
*generalNameContent
,
311 void *context
, parseGeneralNameCallback callback
) {
313 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
314 return callback(context
, GNT_OtherName
, generalNameContent
);
315 case ASN1_CONTEXT_SPECIFIC
| 1:
316 return callback(context
, GNT_RFC822Name
, generalNameContent
);
317 case ASN1_CONTEXT_SPECIFIC
| 2:
318 return callback(context
, GNT_DNSName
, generalNameContent
);
319 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
320 return callback(context
, GNT_X400Address
, generalNameContent
);
321 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
322 return callback(context
, GNT_DirectoryName
, generalNameContent
);
323 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
324 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
325 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
327 /* Technically I don't think this is valid, but there are certs out
328 in the wild that use a constructed IA5String. In particular the
329 VeriSign Time Stamping Authority CA.cer does this. */
330 DERDecodedInfo uriContent
;
331 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
332 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
333 return callback(context
, GNT_URI
, &uriContent
.content
);
335 case ASN1_CONTEXT_SPECIFIC
| 6:
336 return callback(context
, GNT_URI
, generalNameContent
);
337 case ASN1_CONTEXT_SPECIFIC
| 7:
338 return callback(context
, GNT_IPAddress
, generalNameContent
);
339 case ASN1_CONTEXT_SPECIFIC
| 8:
340 return callback(context
, GNT_RegisteredID
, generalNameContent
);
345 return errSecInvalidCertificate
;
348 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
349 void *context
, parseGeneralNameCallback callback
) {
351 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
352 require_noerr_quiet(drtn
, badDER
);
353 DERDecodedInfo generalNameContent
;
354 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
356 OSStatus status
= parseGeneralNameContentProperty(
357 generalNameContent
.tag
, &generalNameContent
.content
, context
,
362 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
363 return errSecSuccess
;
366 return errSecInvalidCertificate
;
369 static OSStatus
parseGeneralNames(const DERItem
*generalNames
, void *context
,
370 parseGeneralNameCallback callback
) {
371 DERDecodedInfo generalNamesContent
;
372 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
373 require_noerr_quiet(drtn
, badDER
);
374 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
375 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
378 return errSecInvalidCertificate
;
384 GeneralName ::= CHOICE {
385 otherName [0] OtherName,
386 rfc822Name [1] IA5String,
387 dNSName [2] IA5String,
388 x400Address [3] ORAddress,
389 directoryName [4] Name,
390 ediPartyName [5] EDIPartyName,
391 uniformResourceIdentifier [6] IA5String,
392 iPAddress [7] OCTET STRING,
393 registeredID [8] OBJECT IDENTIFIER}
395 EDIPartyName ::= SEQUENCE {
396 nameAssigner [0] DirectoryString OPTIONAL,
397 partyName [1] DirectoryString }
399 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
400 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
402 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
403 generalName
->nameType
= GNT_OtherName
;
404 generalName
->berEncoded
= true;
405 generalName
->name
= *generalNameContent
;
407 case ASN1_CONTEXT_SPECIFIC
| 1:
409 generalName
->nameType
= GNT_RFC822Name
;
410 generalName
->berEncoded
= false;
411 generalName
->name
= *generalNameContent
;
413 case ASN1_CONTEXT_SPECIFIC
| 2:
415 generalName
->nameType
= GNT_DNSName
;
416 generalName
->berEncoded
= false;
417 generalName
->name
= *generalNameContent
;
419 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
420 generalName
->nameType
= GNT_X400Address
;
421 generalName
->berEncoded
= true;
422 generalName
->name
= *generalNameContent
;
424 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
425 generalName
->nameType
= GNT_DirectoryName
;
426 generalName
->berEncoded
= true;
427 generalName
->name
= *generalNameContent
;
429 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
430 generalName
->nameType
= GNT_EdiPartyName
;
431 generalName
->berEncoded
= true;
432 generalName
->name
= *generalNameContent
;
434 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
436 /* Technically I don't think this is valid, but there are certs out
437 in the wild that use a constructed IA5String. In particular the
438 VeriSign Time Stamping Authority CA.cer does this. */
439 DERDecodedInfo decoded
;
440 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
441 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
442 generalName
->nameType
= GNT_URI
;
443 generalName
->berEncoded
= false;
444 generalName
->name
= decoded
.content
;
447 case ASN1_CONTEXT_SPECIFIC
| 6:
448 generalName
->nameType
= GNT_URI
;
449 generalName
->berEncoded
= false;
450 generalName
->name
= *generalNameContent
;
452 case ASN1_CONTEXT_SPECIFIC
| 7:
453 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
454 8 octects, addr/mask for ipv6 it's 32. */
455 generalName
->nameType
= GNT_IPAddress
;
456 generalName
->berEncoded
= false;
457 generalName
->name
= *generalNameContent
;
459 case ASN1_CONTEXT_SPECIFIC
| 8:
460 /* name is the content of an OID. */
461 generalName
->nameType
= GNT_RegisteredID
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= *generalNameContent
;
469 return errSecSuccess
;
471 return errSecInvalidCertificate
;
475 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
477 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
478 CFIndex
*count
, SecCEGeneralName
**name
) {
479 SecCEGeneralName
*generalNames
= NULL
;
481 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
482 require_noerr_quiet(drtn
, badDER
);
483 DERDecodedInfo generalNameContent
;
484 CFIndex generalNamesCount
= 0;
485 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
489 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
491 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
493 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
495 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
497 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
498 &generalNameContent
.content
, &generalNames
[ix
])) {
503 *count
= generalNamesCount
;
504 *name
= generalNames
;
505 return errSecSuccess
;
510 return errSecInvalidCertificate
;
513 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
514 CFIndex
*count
, SecCEGeneralName
**name
) {
515 DERDecodedInfo generalNamesContent
;
516 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
517 require_noerr_quiet(drtn
, badDER
);
518 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
520 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
521 return errSecSuccess
;
523 return errSecInvalidCertificate
;
527 /************************************************************************/
528 /************************** X.509 Name Parsing **************************/
529 /************************************************************************/
531 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
532 const DERItem
*value
, CFIndex rdnIX
);
534 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
535 parseX501NameCallback callback
) {
537 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
538 require_noerr_quiet(drtn
, badDER
);
539 DERDecodedInfo atvContent
;
541 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
542 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
543 DERAttributeTypeAndValue atv
;
544 drtn
= DERParseSequenceContent(&atvContent
.content
,
545 DERNumAttributeTypeAndValueItemSpecs
,
546 DERAttributeTypeAndValueItemSpecs
,
548 require_noerr_quiet(drtn
, badDER
);
549 require_quiet(atv
.type
.length
!= 0, badDER
);
550 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
554 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
556 return errSecSuccess
;
558 return errSecInvalidCertificate
;
561 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
562 parseX501NameCallback callback
) {
564 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
565 require_noerr_quiet(drtn
, badDER
);
566 DERDecodedInfo currDecoded
;
567 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
568 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
569 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
574 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
576 return errSecSuccess
;
579 return errSecInvalidCertificate
;
582 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
583 parseX501NameCallback callback
) {
584 DERDecodedInfo x501NameContent
;
585 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
586 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
587 return errSecInvalidCertificate
;
589 return parseX501NameContent(&x501NameContent
.content
, context
,
594 /************************************************************************/
595 /********************** Extension Parsing Routines **********************/
596 /************************************************************************/
598 static void SecCEPSubjectKeyIdentifier(SecCertificateRefP certificate
,
599 const SecCertificateExtension
*extn
) {
600 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
601 DERDecodedInfo keyIdentifier
;
602 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
603 require_noerr_quiet(drtn
, badDER
);
604 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
605 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
609 secinfo("cert", "Invalid SubjectKeyIdentifier Extension");
612 static void SecCEPKeyUsage(SecCertificateRefP certificate
,
613 const SecCertificateExtension
*extn
) {
614 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
615 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
616 DERDecodedInfo bitStringContent
;
617 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
618 require_noerr_quiet(drtn
, badDER
);
619 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
620 DERSize len
= bitStringContent
.content
.length
- 1;
621 require_quiet(len
== 1 || len
== 2, badDER
);
622 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
623 require_quiet(numUnusedBits
< 8, badDER
);
624 /* Flip the bits in the bit string so the first bit in the lsb. */
625 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
626 uint_fast16_t value
= bitStringContent
.content
.data
[1];
629 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
635 for (ix
= 0; ix
< bits
; ++ix
) {
641 certificate
->_keyUsage
= keyUsage
;
644 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
647 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRefP certificate
,
648 const SecCertificateExtension
*extn
) {
649 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
652 static void SecCEPSubjectAltName(SecCertificateRefP certificate
,
653 const SecCertificateExtension
*extn
) {
654 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
655 certificate
->_subjectAltName
= extn
;
658 static void SecCEPIssuerAltName(SecCertificateRefP certificate
,
659 const SecCertificateExtension
*extn
) {
660 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
663 static void SecCEPBasicConstraints(SecCertificateRefP certificate
,
664 const SecCertificateExtension
*extn
) {
665 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
666 DERBasicConstraints basicConstraints
;
667 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
668 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
669 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
670 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
671 &certificate
->_basicConstraints
.isCA
), badDER
);
672 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
673 require_noerr_quiet(DERParseInteger(
674 &basicConstraints
.pathLenConstraint
,
675 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
676 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
678 certificate
->_basicConstraints
.present
= true;
679 certificate
->_basicConstraints
.critical
= extn
->critical
;
682 certificate
->_basicConstraints
.present
= false;
683 secinfo("cert", "Invalid BasicConstraints Extension");
686 static void SecCEPCrlDistributionPoints(SecCertificateRefP certificate
,
687 const SecCertificateExtension
*extn
) {
688 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
692 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
694 PolicyInformation ::= SEQUENCE {
695 policyIdentifier CertPolicyId,
696 policyQualifiers SEQUENCE SIZE (1..MAX) OF
697 PolicyQualifierInfo OPTIONAL }
699 CertPolicyId ::= OBJECT IDENTIFIER
701 PolicyQualifierInfo ::= SEQUENCE {
702 policyQualifierId PolicyQualifierId,
703 qualifier ANY DEFINED BY policyQualifierId }
705 /* maximum number of policies of 8192 seems more than adequate */
706 #define MAX_CERTIFICATE_POLICIES 8192
707 static void SecCEPCertificatePolicies(SecCertificateRefP certificate
,
708 const SecCertificateExtension
*extn
) {
709 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
712 SecCEPolicyInformation
*policies
= NULL
;
713 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
714 require_noerr_quiet(drtn
, badDER
);
715 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
716 DERDecodedInfo piContent
;
717 DERSize policy_count
= 0;
718 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
719 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
720 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
723 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
724 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
726 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
727 DERSize policy_ix
= 0;
728 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
729 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
730 DERPolicyInformation pi
;
731 drtn
= DERParseSequenceContent(&piContent
.content
,
732 DERNumPolicyInformationItemSpecs
,
733 DERPolicyInformationItemSpecs
,
735 require_noerr_quiet(drtn
, badDER
);
736 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
737 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
739 certificate
->_certificatePolicies
.present
= true;
740 certificate
->_certificatePolicies
.critical
= extn
->critical
;
741 certificate
->_certificatePolicies
.numPolicies
= (uint32_t)policy_count
;
742 certificate
->_certificatePolicies
.policies
= policies
;
747 certificate
->_certificatePolicies
.present
= false;
748 secinfo("cert", "Invalid CertificatePolicies Extension");
752 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
754 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
755 issuerDomainPolicy CertPolicyId,
756 subjectDomainPolicy CertPolicyId }
759 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
760 const SecCertificateExtension
*extn
) {
761 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
764 SecCEPolicyMapping
*mappings
= NULL
;
765 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
766 require_noerr_quiet(drtn
, badDER
);
767 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
768 DERDecodedInfo pmContent
;
769 DERSize mapping_count
= 0;
770 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
771 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
774 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
776 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
777 DERSize mapping_ix
= 0;
778 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
780 drtn
= DERParseSequenceContent(&pmContent
.content
,
781 DERNumPolicyMappingItemSpecs
,
782 DERPolicyMappingItemSpecs
,
784 require_noerr_quiet(drtn
, badDER
);
785 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
786 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
788 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
789 certificate
->_policyMappings
.present
= true;
790 certificate
->_policyMappings
.critical
= extn
->critical
;
791 certificate
->_policyMappings
.numMappings
= mapping_count
;
792 certificate
->_policyMappings
.mappings
= mappings
;
797 CFReleaseSafe(mappings
);
798 certificate
->_policyMappings
.present
= false;
799 secinfo("cert", "Invalid CertificatePolicies Extension");
802 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
803 const SecCertificateExtension
*extn
) {
804 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
807 CFMutableDictionaryRef mappings
= NULL
;
808 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
809 require_noerr_quiet(drtn
, badDER
);
810 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
811 DERDecodedInfo pmContent
;
812 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
813 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
815 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
816 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
818 drtn
= DERParseSequenceContent(&pmContent
.content
,
819 DERNumPolicyMappingItemSpecs
,
820 DERPolicyMappingItemSpecs
,
822 require_noerr_quiet(drtn
, badDER
);
824 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
825 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
826 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
827 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
828 CFMutableArrayRef sdps
=
829 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
831 CFArrayAppendValue(sdps
, sdp
);
833 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
834 &kCFTypeArrayCallBacks
), badDER
);
835 CFDictionarySetValue(mappings
, idp
, sdps
);
839 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
840 certificate
->_policyMappings
= mappings
;
843 CFReleaseSafe(mappings
);
844 certificate
->_policyMappings
= NULL
;
845 secinfo("cert", "Invalid CertificatePolicies Extension");
850 AuthorityKeyIdentifier ::= SEQUENCE {
851 keyIdentifier [0] KeyIdentifier OPTIONAL,
852 authorityCertIssuer [1] GeneralNames OPTIONAL,
853 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
854 -- authorityCertIssuer and authorityCertSerialNumber MUST both
855 -- be present or both be absent
857 KeyIdentifier ::= OCTET STRING
859 static void SecCEPAuthorityKeyIdentifier(SecCertificateRefP certificate
,
860 const SecCertificateExtension
*extn
) {
861 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
862 DERAuthorityKeyIdentifier akid
;
864 drtn
= DERParseSequence(&extn
->extnValue
,
865 DERNumAuthorityKeyIdentifierItemSpecs
,
866 DERAuthorityKeyIdentifierItemSpecs
,
867 &akid
, sizeof(akid
));
868 require_noerr_quiet(drtn
, badDER
);
869 if (akid
.keyIdentifier
.length
) {
870 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
872 if (akid
.authorityCertIssuer
.length
||
873 akid
.authorityCertSerialNumber
.length
) {
874 require_quiet(akid
.authorityCertIssuer
.length
&&
875 akid
.authorityCertSerialNumber
.length
, badDER
);
876 /* Perhaps put in a subsection called Authority Certificate Issuer. */
877 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
878 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
883 secinfo("cert", "Invalid AuthorityKeyIdentifier Extension");
886 static void SecCEPPolicyConstraints(SecCertificateRefP certificate
,
887 const SecCertificateExtension
*extn
) {
888 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
889 DERPolicyConstraints pc
;
891 drtn
= DERParseSequence(&extn
->extnValue
,
892 DERNumPolicyConstraintsItemSpecs
,
893 DERPolicyConstraintsItemSpecs
,
895 require_noerr_quiet(drtn
, badDER
);
896 if (pc
.requireExplicitPolicy
.length
) {
897 require_noerr_quiet(DERParseInteger(
898 &pc
.requireExplicitPolicy
,
899 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
900 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
902 if (pc
.inhibitPolicyMapping
.length
) {
903 require_noerr_quiet(DERParseInteger(
904 &pc
.inhibitPolicyMapping
,
905 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
906 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
909 certificate
->_policyConstraints
.present
= true;
910 certificate
->_policyConstraints
.critical
= extn
->critical
;
914 certificate
->_policyConstraints
.present
= false;
915 secinfo("cert", "Invalid PolicyConstraints Extension");
918 static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate
,
919 const SecCertificateExtension
*extn
) {
920 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
924 InhibitAnyPolicy ::= SkipCerts
926 SkipCerts ::= INTEGER (0..MAX)
928 static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate
,
929 const SecCertificateExtension
*extn
) {
930 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
931 require_noerr_quiet(DERParseInteger(
933 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
936 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
937 secinfo("cert", "Invalid InhibitAnyPolicy Extension");
941 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
943 AuthorityInfoAccessSyntax ::=
944 SEQUENCE SIZE (1..MAX) OF AccessDescription
946 AccessDescription ::= SEQUENCE {
947 accessMethod OBJECT IDENTIFIER,
948 accessLocation GeneralName }
950 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
952 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
954 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
956 static void SecCEPAuthorityInfoAccess(SecCertificateRefP certificate
,
957 const SecCertificateExtension
*extn
) {
958 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
961 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
962 require_noerr_quiet(drtn
, badDER
);
963 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
964 DERDecodedInfo adContent
;
965 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
966 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
967 DERAccessDescription ad
;
968 drtn
= DERParseSequenceContent(&adContent
.content
,
969 DERNumAccessDescriptionItemSpecs
,
970 DERAccessDescriptionItemSpecs
,
972 require_noerr_quiet(drtn
, badDER
);
973 CFMutableArrayRef
*urls
;
974 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
975 urls
= &certificate
->_ocspResponders
;
976 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
977 urls
= &certificate
->_caIssuers
;
981 DERDecodedInfo generalNameContent
;
982 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
983 require_noerr_quiet(drtn
, badDER
);
984 switch (generalNameContent
.tag
) {
986 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
987 /* Technically I don't think this is valid, but there are certs out
988 in the wild that use a constructed IA5String. In particular the
989 VeriSign Time Stamping Authority CA.cer does this. */
991 case ASN1_CONTEXT_SPECIFIC
| 6:
993 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
994 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
995 kCFStringEncodingASCII
, NULL
);
998 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
999 CFArrayAppendValue(*urls
, url
);
1005 secinfo("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1006 generalNameContent
.tag
, (int)generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1011 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1014 secinfo("cert", "failed to parse Authority Information Access extension");
1017 static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate
,
1018 const SecCertificateExtension
*extn
) {
1019 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1022 static void SecCEPNetscapeCertType(SecCertificateRefP certificate
,
1023 const SecCertificateExtension
*extn
) {
1024 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1027 static void SecCEPEntrustVersInfo(SecCertificateRefP certificate
,
1028 const SecCertificateExtension
*extn
) {
1029 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1032 /* Dictionary key callback for comparing to DERItems. */
1033 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1034 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1037 /* Dictionary key callback calculating the hash of a DERItem. */
1038 static CFHashCode
SecDERItemHash(const void *value
) {
1039 const DERItem
*derItem
= (const DERItem
*)value
;
1040 CFHashCode hash
= derItem
->length
;
1041 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1042 for (; ix
< derItem
->length
; ++ix
) {
1043 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1049 /* Dictionary key callbacks using the above 2 functions. */
1050 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1054 NULL
, /* copyDescription */
1055 SecDERItemEqual
, /* equal */
1056 SecDERItemHash
/* hash */
1059 static void SecCertificateRegisterClass(void) {
1060 static const CFRuntimeClass kSecCertificateClass
= {
1062 "SecCertificate", /* class name */
1065 SecCertificateDestroy
, /* dealloc */
1066 SecCertificateEqual
, /* equal */
1067 SecCertificateHash
, /* hash */
1068 NULL
, /* copyFormattingDesc */
1069 SecCertificateDescribe
/* copyDebugDesc */
1072 kSecCertificateTypeID
= _CFRuntimeRegisterClass(&kSecCertificateClass
);
1074 /* Build a dictionary that maps from extension OIDs to callback functions
1075 which can parse the extension of the type given. */
1076 static const void *extnOIDs
[] = {
1077 &oidSubjectKeyIdentifier
,
1079 &oidPrivateKeyUsagePeriod
,
1082 &oidBasicConstraints
,
1083 &oidCrlDistributionPoints
,
1084 &oidCertificatePolicies
,
1086 &oidAuthorityKeyIdentifier
,
1087 &oidPolicyConstraints
,
1088 &oidExtendedKeyUsage
,
1089 &oidInhibitAnyPolicy
,
1090 &oidAuthorityInfoAccess
,
1091 &oidSubjectInfoAccess
,
1092 &oidNetscapeCertType
,
1095 static const void *extnParsers
[] = {
1096 SecCEPSubjectKeyIdentifier
,
1098 SecCEPPrivateKeyUsagePeriod
,
1099 SecCEPSubjectAltName
,
1100 SecCEPIssuerAltName
,
1101 SecCEPBasicConstraints
,
1102 SecCEPCrlDistributionPoints
,
1103 SecCEPCertificatePolicies
,
1104 SecCEPPolicyMappings
,
1105 SecCEPAuthorityKeyIdentifier
,
1106 SecCEPPolicyConstraints
,
1107 SecCEPExtendedKeyUsage
,
1108 SecCEPInhibitAnyPolicy
,
1109 SecCEPAuthorityInfoAccess
,
1110 SecCEPSubjectInfoAccess
,
1111 SecCEPNetscapeCertType
,
1112 SecCEPEntrustVersInfo
1114 gExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1115 extnParsers
, sizeof(extnOIDs
) / sizeof(*extnOIDs
),
1116 &SecDERItemKeyCallBacks
, NULL
);
1119 /* Given the contents of an X.501 Name return the contents of a normalized
1121 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1122 const DERItem
*x501name
) {
1123 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1124 CFIndex length
= x501name
->length
;
1125 CFDataSetLength(result
, length
);
1126 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1129 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1131 require_noerr_quiet(drtn
, badDER
);
1134 /* Always points to last rdn tag. */
1135 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1136 /* Offset relative to base of current rdn set tag. */
1137 CFIndex rdnTagLocation
= 0;
1138 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1139 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1140 /* We don't allow empty RDNs. */
1141 require_quiet(rdn
.content
.length
!= 0, badDER
);
1142 /* Length of the tag and length of the current rdn. */
1143 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1144 CFIndex rdnContentLength
= rdn
.content
.length
;
1145 /* Copy the tag and length of the RDN. */
1146 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1149 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1151 /* Always points to tag of current atv sequence. */
1152 const DERByte
*atvTag
= atvSeq
.nextItem
;
1153 /* Offset relative to base of current atv sequence tag. */
1154 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1155 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1156 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1157 /* Length of the tag and length of the current atv. */
1158 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1159 CFIndex atvContentLength
= atv
.content
.length
;
1160 /* Copy the tag and length of the atv and the atv itself. */
1161 memcpy(base
+ atvTagLocation
, atvTag
,
1162 atvTLLength
+ atv
.content
.length
);
1164 /* Now decode the atv sequence. */
1165 DERAttributeTypeAndValue atvPair
;
1166 drtn
= DERParseSequenceContent(&atv
.content
,
1167 DERNumAttributeTypeAndValueItemSpecs
,
1168 DERAttributeTypeAndValueItemSpecs
,
1169 &atvPair
, sizeof(atvPair
));
1170 require_noerr_quiet(drtn
, badDER
);
1171 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1172 DERDecodedInfo value
;
1173 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1174 require_noerr_quiet(drtn
, badDER
);
1176 /* (c) attribute values in PrintableString are not case sensitive
1177 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1179 (d) attribute values in PrintableString are compared after
1180 removing leading and trailing white space and converting internal
1181 substrings of one or more consecutive white space characters to a
1183 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1184 /* Offset relative to base of current value tag. */
1185 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1186 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1187 CFIndex valueContentLength
= value
.content
.length
;
1189 /* Now copy all the bytes, but convert to upper case while
1190 doing so and convert multiple whitespace chars into a
1192 bool lastWasBlank
= false;
1193 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1194 CFIndex valueCurrentLocation
= valueLocation
;
1196 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1197 UInt8 ch
= value
.content
.data
[ix
];
1202 /* Don't insert a space for first character
1204 if (valueCurrentLocation
> valueLocation
) {
1205 base
[valueCurrentLocation
++] = ' ';
1207 lastWasBlank
= true;
1210 lastWasBlank
= false;
1211 if ('a' <= ch
&& ch
<= 'z') {
1212 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1214 base
[valueCurrentLocation
++] = ch
;
1218 /* Finally if lastWasBlank remove the trailing space. */
1219 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1220 valueCurrentLocation
--;
1222 /* Adjust content length to normalized length. */
1223 valueContentLength
= valueCurrentLocation
- valueLocation
;
1225 /* Number of bytes by which the length should be shorted. */
1226 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1227 if (lengthDiff
== 0) {
1228 /* Easy case no need to adjust lengths. */
1230 /* Hard work we need to go back and fix up length fields
1232 1) The value itself.
1233 2) The ATV Sequence containing type/value
1234 3) The RDN Set containing one or more atv pairs.
1238 /* Step 1 fix up length of value. */
1239 /* Length of value tag and length minus the tag. */
1240 DERSize newValueTLLength
= valueTLLength
- 1;
1241 drtn
= DEREncodeLength(valueContentLength
,
1242 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1243 /* Add the length of the tag back in. */
1245 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1247 /* The size of the length field changed, let's slide
1248 the value back by valueLLDiff bytes. */
1249 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1250 base
+ valueTagLocation
+ valueTLLength
,
1251 valueContentLength
);
1252 /* The length diff for the enclosing object. */
1253 lengthDiff
+= valueLLDiff
;
1256 /* Step 2 fix up length of the enclosing ATV Sequence. */
1257 atvContentLength
-= lengthDiff
;
1258 DERSize newATVTLLength
= atvTLLength
- 1;
1259 drtn
= DEREncodeLength(atvContentLength
,
1260 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1261 /* Add the length of the tag back in. */
1263 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1265 /* The size of the length field changed, let's slide
1266 the value back by valueLLDiff bytes. */
1267 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1268 base
+ atvTagLocation
+ atvTLLength
,
1270 /* The length diff for the enclosing object. */
1271 lengthDiff
+= atvLLDiff
;
1272 atvTLLength
= newATVTLLength
;
1275 /* Step 3 fix up length of enclosing RDN Set. */
1276 rdnContentLength
-= lengthDiff
;
1277 DERSize newRDNTLLength
= rdnTLLength
- 1;
1278 drtn
= DEREncodeLength(rdnContentLength
,
1279 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1280 /* Add the length of the tag back in. */
1282 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1284 /* The size of the length field changed, let's slide
1285 the value back by valueLLDiff bytes. */
1286 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1287 base
+ rdnTagLocation
+ rdnTLLength
,
1289 /* The length diff for the enclosing object. */
1290 lengthDiff
+= rdnLLDiff
;
1291 rdnTLLength
= newRDNTLLength
;
1293 /* Adjust the locations that might have changed due to
1295 atvTagLocation
-= rdnLLDiff
;
1299 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1300 atvTag
= atvSeq
.nextItem
;
1302 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1303 rdnTag
= rdnSeq
.nextItem
;
1305 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1306 /* Truncate the result to the proper length. */
1307 CFDataSetLength(result
, rdnTagLocation
);
1316 /* AUDIT[securityd]:
1317 certificate->_der is a caller provided data of any length (might be 0).
1319 Top level certificate decode.
1321 static bool SecCertificateParse(SecCertificateRefP certificate
)
1326 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1328 /* top level decode */
1329 DERSignedCertCrl signedCert
;
1330 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1331 DERSignedCertCrlItemSpecs
, &signedCert
,
1332 sizeof(signedCert
));
1333 require_noerr_quiet(drtn
, badCert
);
1334 /* Store tbs since we need to digest it for verification later on. */
1335 certificate
->_tbs
= signedCert
.tbs
;
1337 /* decode the TBSCert - it was saved in full DER form */
1339 drtn
= DERParseSequence(&signedCert
.tbs
,
1340 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1341 &tbsCert
, sizeof(tbsCert
));
1342 require_noerr_quiet(drtn
, badCert
);
1344 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1345 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1346 of the params field. */
1347 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1348 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1349 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1350 require_noerr_quiet(drtn
, badCert
);
1352 /* The contents of signedCert.sig is a bit string whose contents
1353 are the signature itself. */
1354 DERByte numUnusedBits
;
1355 drtn
= DERParseBitString(&signedCert
.sig
,
1356 &certificate
->_signature
, &numUnusedBits
);
1357 require_noerr_quiet(drtn
, badCert
);
1359 /* Now decode the tbsCert. */
1361 /* First we turn the optional version into an int. */
1362 if (tbsCert
.version
.length
) {
1363 DERDecodedInfo decoded
;
1364 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1365 require_noerr_quiet(drtn
, badCert
);
1366 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1367 require_quiet(decoded
.content
.length
== 1, badCert
);
1368 certificate
->_version
= decoded
.content
.data
[0];
1369 require_quiet(certificate
->_version
> 0, badCert
);
1370 require_quiet(certificate
->_version
< 3, badCert
);
1372 certificate
->_version
= 0;
1375 /* The serial number is in the tbsCert.serialNum - it was saved in
1376 INTEGER form without the tag and length. */
1377 certificate
->_serialNum
= tbsCert
.serialNum
;
1378 certificate
->_serialNumber
= CFDataCreate(allocator
,
1379 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1381 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1382 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1383 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1384 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1385 require_noerr_quiet(drtn
, badCert
);
1387 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1388 and length fields. */
1389 certificate
->_issuer
= tbsCert
.issuer
;
1390 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1393 /* sequence we're given: decode the tbsCerts Validity sequence. */
1394 DERValidity validity
;
1395 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1396 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1397 &validity
, sizeof(validity
));
1398 require_noerr_quiet(drtn
, badCert
);
1399 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1400 &certificate
->_notBefore
), badCert
);
1401 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1402 &certificate
->_notAfter
), badCert
);
1404 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1405 and length fields. */
1406 certificate
->_subject
= tbsCert
.subject
;
1407 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1410 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1411 DERSubjPubKeyInfo pubKeyInfo
;
1412 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1413 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1414 &pubKeyInfo
, sizeof(pubKeyInfo
));
1415 require_noerr_quiet(drtn
, badCert
);
1417 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1418 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1419 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1420 &certificate
->_algId
, sizeof(certificate
->_algId
));
1421 require_noerr_quiet(drtn
, badCert
);
1423 /* Now we can figure out the key's algorithm id and params based on
1424 certificate->_algId.oid. */
1426 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1427 are a PKCS1 format RSA key. */
1428 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1429 &certificate
->_pubKeyDER
, &numUnusedBits
);
1430 require_noerr_quiet(drtn
, badCert
);
1432 /* The contents of tbsCert.issuerID is a bit string. */
1433 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1435 /* The contents of tbsCert.subjectID is a bit string. */
1436 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1439 if (tbsCert
.extensions
.length
) {
1440 CFIndex extensionCount
= 0;
1443 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1445 require_noerr_quiet(drtn
, badCert
);
1446 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1447 DERDecodedInfo currDecoded
;
1448 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1450 /* ! = MUST recognize ? = SHOULD recognize
1453 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1454 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1455 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1456 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1457 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1458 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1459 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1460 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1462 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1463 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1464 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1465 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1466 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1467 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1468 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1469 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1471 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1472 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1477 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1479 /* Put some upper limit on the number of extentions allowed. */
1480 require_quiet(extensionCount
< 10000, badCert
);
1481 certificate
->_extensionCount
= extensionCount
;
1482 certificate
->_extensions
=
1483 malloc(sizeof(SecCertificateExtension
) * extensionCount
);
1486 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1487 require_noerr_quiet(drtn
, badCert
);
1488 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1489 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1490 require_quiet(drtn
== DR_Success
||
1491 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1492 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1494 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1495 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1496 &extn
, sizeof(extn
));
1497 require_noerr_quiet(drtn
, badCert
);
1498 /* Copy stuff into certificate->extensions[ix]. */
1499 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1500 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1501 &certificate
->_extensions
[ix
].critical
), badCert
);
1502 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1504 SecCertificateExtensionParser parser
=
1505 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1506 gExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1508 /* Invoke the parser. */
1509 parser(certificate
, &certificate
->_extensions
[ix
]);
1510 } else if (certificate
->_extensions
[ix
].critical
) {
1511 secinfo("cert", "Found unknown critical extension");
1512 certificate
->_foundUnknownCriticalExtension
= true;
1514 secinfo("cert", "Found unknown non critical extension");
1526 /* Public API functions. */
1527 CFTypeID
SecCertificateGetTypeIDP(void) {
1528 pthread_once(&kSecCertificateRegisterClass
, SecCertificateRegisterClass
);
1529 return kSecCertificateTypeID
;
1532 SecCertificateRefP
SecCertificateCreateWithBytesP(CFAllocatorRef allocator
,
1533 const UInt8
*der_bytes
, CFIndex der_length
) {
1536 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1537 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1538 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1540 memset((char*)result
+ sizeof(result
->_base
), 0,
1541 sizeof(*result
) - sizeof(result
->_base
));
1542 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1543 result
->_der
.length
= der_length
;
1544 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1545 if (!SecCertificateParse(result
)) {
1553 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1554 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1555 const UInt8
*der_bytes
, CFIndex der_length
);
1557 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1558 const UInt8
*der_bytes
, CFIndex der_length
) {
1559 return SecCertificateCreateWithBytesP(allocator
, der_bytes
, der_length
);
1561 /* @@@ End of placeholder. */
1563 /* AUDIT[securityd](done):
1564 der_certificate is a caller provided data of any length (might be 0), only
1565 its cf type has been checked.
1567 SecCertificateRefP
SecCertificateCreateWithDataP(CFAllocatorRef allocator
,
1568 CFDataRef der_certificate
) {
1569 check(der_certificate
);
1570 CFIndex size
= sizeof(struct __SecCertificate
);
1571 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1572 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1574 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1575 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1576 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1577 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1578 if (!SecCertificateParse(result
)) {
1586 CFDataRef
SecCertificateCopyDataP(SecCertificateRefP certificate
) {
1589 if (certificate
->_der_data
) {
1590 CFRetain(certificate
->_der_data
);
1591 result
= certificate
->_der_data
;
1593 result
= CFDataCreate(CFGetAllocator(certificate
),
1594 certificate
->_der
.data
, certificate
->_der
.length
);
1596 /* FIXME: If we wish to cache result we need to lock the certificate.
1597 Also this create 2 copies of the certificate data which is somewhat
1600 certificate
->_der_data
= result
;
1607 CFIndex
SecCertificateGetLengthP(SecCertificateRefP certificate
) {
1608 return certificate
->_der
.length
;
1611 const UInt8
*SecCertificateGetBytePtrP(SecCertificateRefP certificate
) {
1612 return certificate
->_der
.data
;
1615 /* From rfc3280 - Appendix B. ASN.1 Notes
1617 Object Identifiers (OIDs) are used throughout this specification to
1618 identify certificate policies, public key and signature algorithms,
1619 certificate extensions, etc. There is no maximum size for OIDs.
1620 This specification mandates support for OIDs which have arc elements
1621 with values that are less than 2^28, that is, they MUST be between 0
1622 and 268,435,455, inclusive. This allows each arc element to be
1623 represented within a single 32 bit word. Implementations MUST also
1624 support OIDs where the length of the dotted decimal (see [RFC 2252],
1625 section 4.1) string representation can be up to 100 bytes
1626 (inclusive). Implementations MUST be able to handle OIDs with up to
1627 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1628 contain OIDs that exceed these requirements. Likewise, CRL issuers
1629 SHOULD NOT issue CRLs which contain OIDs that exceed these
1633 /* Oids longer than this are considered invalid. */
1634 #define MAX_OID_SIZE 32
1636 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1637 const DERItem
*oid
) {
1639 if (oid
->length
== 0) {
1640 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1641 CFSTR("SecCertificate"));
1643 if (oid
->length
> MAX_OID_SIZE
) {
1644 return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"),
1645 CFSTR("SecCertificate"));
1648 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1650 // The first two levels are encoded into one byte, since the root level
1651 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1652 // y may be > 39, so we have to add special-case handling for this.
1653 uint32_t x
= oid
->data
[0] / 40;
1654 uint32_t y
= oid
->data
[0] % 40;
1657 // Handle special case for large y if x = 2
1661 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1664 for (x
= 1; x
< oid
->length
; ++x
)
1666 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1667 /* @@@ value may not span more than 4 bytes. */
1668 /* A max number of 20 values is allowed. */
1669 if (!(oid
->data
[x
] & 0x80))
1671 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), (unsigned long)value
);
1678 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1679 const DERItem
*oid
) {
1680 if (oid
->length
== 0) {
1681 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1682 CFSTR("SecCertificate"));
1685 /* Build the key we use to lookup the localized OID description. */
1686 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1687 oid
->length
* 3 + 5);
1688 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), (unsigned long)oid
->length
);
1690 for (ix
= 0; ix
< oid
->length
; ++ix
)
1691 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1693 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1694 if (CFEqual(oidKey
, name
)) {
1696 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1703 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1704 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1705 have a length of exactly 4 or 16 octects. */
1706 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1707 const DERItem
*ip
) {
1708 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1709 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1710 16 octects addr, or 32 octects addr/mask. */
1711 CFStringRef value
= NULL
;
1712 if (ip
->length
== 4) {
1713 value
= CFStringCreateWithFormat(allocator
, NULL
,
1714 CFSTR("%u.%u.%u.%u"),
1715 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1716 } else if (ip
->length
== 16) {
1717 value
= CFStringCreateWithFormat(allocator
, NULL
,
1718 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1719 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1720 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
1721 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
1722 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
1723 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
1730 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
1731 const DERItem
*oid
) {
1732 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1733 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
1734 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
1735 CFSTR("%@ (%@)"), name
, decimal
);
1742 void appendPropertyP(CFMutableArrayRef properties
,
1743 CFStringRef propertyType
, CFStringRef label
, CFTypeRef value
) {
1744 CFDictionaryRef property
;
1746 CFStringRef localizedLabel
= SecFrameworkCopyLocalizedString(label
,
1747 CFSTR("SecCertificate"));
1748 const void *all_keys
[4];
1749 all_keys
[0] = kSecPropertyKeyType
;
1750 all_keys
[1] = kSecPropertyKeyLabel
;
1751 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
1752 all_keys
[3] = kSecPropertyKeyValue
;
1753 const void *property_values
[] = {
1759 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1760 all_keys
, property_values
, value
? 4 : 3,
1761 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1762 CFRelease(localizedLabel
);
1764 const void *nolabel_keys
[2];
1765 nolabel_keys
[0] = kSecPropertyKeyType
;
1766 nolabel_keys
[1] = kSecPropertyKeyValue
;
1767 const void *property_values
[] = {
1771 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1772 nolabel_keys
, property_values
, 2,
1773 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1776 CFArrayAppendValue(properties
, property
);
1777 CFRelease(property
);
1781 #define UTC_TIME_NOSEC_ZULU_LEN 11
1783 #define UTC_TIME_ZULU_LEN 13
1784 /* YYMMDDhhmmssThhmm */
1785 #define UTC_TIME_LOCALIZED_LEN 17
1786 /* YYYYMMDDhhmmssZ */
1787 #define GENERALIZED_TIME_ZULU_LEN 15
1788 /* YYYYMMDDhhmmssThhmm */
1789 #define GENERALIZED_TIME_LOCALIZED_LEN 19
1791 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
1793 static inline SInt32
parseDecimalPair(const DERByte
**p
) {
1794 const DERByte
*cp
= *p
;
1796 return 10 * (cp
[0] - '0') + cp
[1] - '0';
1799 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1800 true if the date was valid and properly decoded, also return the result in
1801 absTime. Return false otherwise. */
1802 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
1808 bool isUtcLength
= false;
1809 bool isLocalized
= false;
1810 bool noSeconds
= false;
1812 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
1816 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
1819 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
1821 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
1824 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
1827 default: /* unknown format */
1831 /* Make sure the der tag fits the thing inside it. */
1832 if (tag
== ASN1_UTC_TIME
) {
1835 } else if (tag
== ASN1_GENERALIZED_TIME
) {
1842 const DERByte
*cp
= bytes
;
1843 /* Check that all characters are digits, except if localized the timezone
1844 indicator or if not localized the 'Z' at the end. */
1846 for (ix
= 0; ix
< length
; ++ix
) {
1847 if (!(isdigit(cp
[ix
]))) {
1848 if ((isLocalized
&& ix
== length
- 5 &&
1849 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
1850 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
1857 /* Initialize the fields in a gregorian date struct. */
1858 CFGregorianDate gdate
;
1860 SInt32 year
= parseDecimalPair(&cp
);
1862 /* 0 <= year < 50 : assume century 21 */
1863 gdate
.year
= 2000 + year
;
1864 } else if (year
< 70) {
1865 /* 50 <= year < 70 : illegal per PKIX */
1868 /* 70 < year <= 99 : assume century 20 */
1869 gdate
.year
= 1900 + year
;
1872 gdate
.year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
1874 gdate
.month
= parseDecimalPair(&cp
);
1875 gdate
.day
= parseDecimalPair(&cp
);
1876 gdate
.hour
= parseDecimalPair(&cp
);
1877 gdate
.minute
= parseDecimalPair(&cp
);
1881 gdate
.second
= parseDecimalPair(&cp
);
1884 CFTimeInterval timeZoneOffset
= 0;
1886 /* ZONE INDICATOR */
1887 SInt32 multiplier
= *cp
++ == '+' ? 60 : -60;
1888 timeZoneOffset
= multiplier
*
1889 (parseDecimalPair(&cp
) + 60 * parseDecimalPair(&cp
));
1894 secinfo("dateparse",
1895 "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f",
1896 (int)length
, bytes
, (int)gdate
.year
, gdate
.month
,
1897 gdate
.day
, gdate
.hour
, gdate
.minute
, gdate
.second
,
1898 timeZoneOffset
/ 60);
1900 if (!CFGregorianDateIsValid(gdate
, kCFGregorianAllUnits
))
1902 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
,
1906 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(gdate
, timeZone
);
1907 CFRelease(timeZone
);
1911 static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
1912 CFAbsoluteTime
*pabsTime
) {
1913 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
1915 if (absTime
== NULL_TIME
)
1918 *pabsTime
= absTime
;
1922 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1923 true if the date was valid and properly decoded, also return the result in
1924 absTime. Return false otherwise. */
1925 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
1926 CFAbsoluteTime
*absTime
) {
1929 if (dateChoice
->length
== 0)
1932 DERDecodedInfo decoded
;
1933 if (DERDecodeItem(dateChoice
, &decoded
))
1936 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
1940 static void appendDataProperty(CFMutableArrayRef properties
,
1941 CFStringRef label
, const DERItem
*der_data
) {
1942 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
1943 der_data
->data
, der_data
->length
);
1944 appendPropertyP(properties
, kSecPropertyTypeData
, label
, data
);
1948 static void appendUnparsedProperty(CFMutableArrayRef properties
,
1949 CFStringRef label
, const DERItem
*der_data
) {
1950 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1951 NULL
, CFSTR("Unparsed %@"), label
);
1952 appendDataProperty(properties
, newLabel
, der_data
);
1953 CFRelease(newLabel
);
1956 static void appendInvalidProperty(CFMutableArrayRef properties
,
1957 CFStringRef label
, const DERItem
*der_data
) {
1958 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1959 NULL
, CFSTR("Invalid %@"), label
);
1960 appendDataProperty(properties
, newLabel
, der_data
);
1961 CFRelease(newLabel
);
1964 static void appendDateContentProperty(CFMutableArrayRef properties
,
1965 CFStringRef label
, DERTag tag
, const DERItem
*dateContent
) {
1966 CFAbsoluteTime absTime
;
1967 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
1968 /* Date decode failure insert hex bytes instead. */
1969 return appendInvalidProperty(properties
, label
, dateContent
);
1971 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1972 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1976 static void appendDateProperty(CFMutableArrayRef properties
,
1977 CFStringRef label
, CFAbsoluteTime absTime
) {
1978 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1979 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1983 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
1984 CFStringRef label
, const DERItem
*ip
) {
1986 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
1988 appendPropertyP(properties
, kSecPropertyTypeString
, label
, value
);
1991 appendUnparsedProperty(properties
, label
, ip
);
1995 static void appendURLContentProperty(CFMutableArrayRef properties
,
1996 CFStringRef label
, const DERItem
*urlContent
) {
1997 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
1998 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2000 appendPropertyP(properties
, kSecPropertyTypeURL
, label
, url
);
2003 appendInvalidProperty(properties
, label
, urlContent
);
2007 static void appendURLProperty(CFMutableArrayRef properties
,
2008 CFStringRef label
, const DERItem
*url
) {
2009 DERDecodedInfo decoded
;
2012 drtn
= DERDecodeItem(url
, &decoded
);
2013 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2014 appendInvalidProperty(properties
, label
, url
);
2016 appendURLContentProperty(properties
, label
, &decoded
.content
);
2020 static void appendOIDProperty(CFMutableArrayRef properties
,
2021 CFStringRef label
, const DERItem
*oid
) {
2022 CFStringRef oid_string
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2024 appendPropertyP(properties
, kSecPropertyTypeString
, label
, oid_string
);
2025 CFRelease(oid_string
);
2028 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2029 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2030 CFMutableArrayRef alg_props
=
2031 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2032 &kCFTypeArrayCallBacks
);
2033 appendOIDProperty(alg_props
, CFSTR("Algorithm"), &algorithm
->oid
);
2034 if (algorithm
->params
.length
) {
2035 if (algorithm
->params
.length
== 2 &&
2036 algorithm
->params
.data
[0] == ASN1_NULL
&&
2037 algorithm
->params
.data
[1] == 0) {
2038 /* @@@ Localize <NULL> or perhaps skip it? */
2039 appendPropertyP(alg_props
, kSecPropertyTypeString
,
2040 CFSTR("Parameters"), CFSTR("none"));
2042 appendUnparsedProperty(alg_props
, CFSTR("Parameters"),
2043 &algorithm
->params
);
2046 appendPropertyP(properties
, kSecPropertyTypeSection
, label
, alg_props
);
2047 CFRelease(alg_props
);
2050 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2051 const DERItem
*blob
) {
2052 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2053 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2054 blob
->length
* 3 - 1);
2055 for (ix
= 0; ix
< length
; ++ix
)
2057 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2059 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2064 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2065 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2066 CFStringRef blobFormat
= SecFrameworkCopyLocalizedString(
2067 CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate")
2068 /*, "format string for encoded field data (e.g. Sequence; 128 bytes; "
2069 "data = 00 00 ...)" */);
2070 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2071 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2072 blobFormat
, blobType
, blob
->length
, quanta
, hex
);
2074 CFRelease(blobFormat
);
2079 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2080 const DERItem
*string
, CFStringEncoding encoding
,
2081 bool printableOnly
) {
2082 /* Strip potential bogus trailing zero from printable strings. */
2083 DERSize length
= string
->length
;
2084 if (length
&& string
->data
[length
- 1] == 0) {
2085 /* Don't mess with the length of UTF16 strings though. */
2086 if (encoding
!= kCFStringEncodingUTF16
)
2089 /* A zero length string isn't considered printable. */
2090 if (!length
&& printableOnly
)
2093 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2094 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2095 passing false makes it treat it as native endian by default. */
2096 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2097 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2101 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2104 /* From rfc3280 - Appendix B. ASN.1 Notes
2106 CAs MUST force the serialNumber to be a non-negative integer, that
2107 is, the sign bit in the DER encoding of the INTEGER value MUST be
2108 zero - this can be done by adding a leading (leftmost) `00'H octet if
2109 necessary. This removes a potential ambiguity in mapping between a
2110 string of octets and an integer value.
2112 As noted in section 4.1.2.2, serial numbers can be expected to
2113 contain long integers. Certificate users MUST be able to handle
2114 serialNumber values up to 20 octets in length. Conformant CAs MUST
2115 NOT use serialNumber values longer than 20 octets.
2118 /* Return the given numeric data as a string: decimal up to 64 bits,
2120 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2121 const DERItem
*integer
) {
2123 CFIndex ix
, length
= integer
->length
;
2125 if (length
== 0 || length
> 8)
2126 return copyHexDescription(allocator
, integer
);
2128 for(ix
= 0; ix
< length
; ++ix
) {
2130 value
+= integer
->data
[ix
];
2133 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2136 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2137 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2141 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2142 case ASN1_PRINTABLE_STRING
:
2143 case ASN1_IA5_STRING
:
2144 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2145 case ASN1_UTF8_STRING
:
2146 case ASN1_GENERAL_STRING
:
2147 case ASN1_UNIVERSAL_STRING
:
2148 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2149 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2150 case ASN1_VIDEOTEX_STRING
: // 21
2151 case ASN1_VISIBLE_STRING
: // 26
2152 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2153 case ASN1_BMP_STRING
: // 30
2154 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2155 case ASN1_OCTET_STRING
:
2156 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Byte string"), CFSTR("bytes"),
2158 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2159 case ASN1_BIT_STRING
:
2160 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Bit string"), CFSTR("bits"),
2162 case (DERByte
)ASN1_CONSTR_SEQUENCE
:
2163 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Sequence"), CFSTR("bytes"),
2165 case (DERByte
)ASN1_CONSTR_SET
:
2166 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Set"), CFSTR("bytes"),
2168 case ASN1_OBJECT_ID
:
2169 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2172 /* "format string for undisplayed field data with a given DER tag" */
2173 return printableOnly
? NULL
: CFStringCreateWithFormat(allocator
, NULL
,
2174 CFSTR("not displayed (tag = %llu; length %d)"),
2175 tag
, (int)derThing
->length
);
2179 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2180 const DERItem
*derThing
, bool printableOnly
) {
2181 DERDecodedInfo decoded
;
2184 drtn
= DERDecodeItem(derThing
, &decoded
);
2186 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2188 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2189 &decoded
.content
, false);
2193 static void appendDERThingProperty(CFMutableArrayRef properties
,
2194 CFStringRef label
, const DERItem
*derThing
) {
2195 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2197 appendPropertyP(properties
, kSecPropertyTypeString
, label
, value
);
2201 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2202 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2203 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2205 /* If there is more than one value pair we create a subsection for the
2206 second pair, and append things to the subsection for subsequent
2208 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2209 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2211 /* Since this is the second rdn pair for a given rdn, we setup a
2212 new subsection for this rdn. We remove the first property
2213 from the properties array and make it the first element in the
2214 subsection instead. */
2215 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2216 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2217 CFArrayAppendValue(rdn_props
, lastValue
);
2218 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2219 appendPropertyP(properties
, kSecPropertyTypeSection
, NULL
, rdn_props
);
2220 properties
= rdn_props
;
2222 /* Since this is the third or later rdn pair we have already
2223 created a subsection in the top level properties array. Instead
2224 of appending to that directly we append to the array inside the
2226 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2227 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2231 /* Finally we append the new rdn value to the property array. */
2232 CFStringRef label
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2235 appendDERThingProperty(properties
, label
, rdnValue
);
2237 return errSecSuccess
;
2239 return errSecInvalidCertificate
;
2243 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2244 const DERItem
*rdnSetContent
) {
2245 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2246 &kCFTypeArrayCallBacks
);
2247 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2250 CFArrayRemoveAllValues(properties
);
2251 appendInvalidProperty(properties
, CFSTR("RDN"), rdnSetContent
);
2258 From rfc3739 - 3.1.2. Subject
2260 When parsing the subject here are some tips for a short name of the cert.
2261 Choice I: commonName
2262 Choice II: givenName
2263 Choice III: pseudonym
2265 The commonName attribute value SHALL, when present, contain a name
2266 of the subject. This MAY be in the subject's preferred
2267 presentation format, or a format preferred by the CA, or some
2268 other format. Pseudonyms, nicknames, and names with spelling
2269 other than defined by the registered name MAY be used. To
2270 understand the nature of the name presented in commonName,
2271 complying applications MAY have to examine present values of the
2272 givenName and surname attributes, or the pseudonym attribute.
2275 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2276 const DERItem
*x501NameContent
) {
2277 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2278 &kCFTypeArrayCallBacks
);
2279 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2282 CFArrayRemoveAllValues(properties
);
2283 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501NameContent
);
2289 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2290 const DERItem
*x501Name
) {
2291 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2292 &kCFTypeArrayCallBacks
);
2293 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2295 CFArrayRemoveAllValues(properties
);
2296 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501Name
);
2302 static void appendIntegerProperty(CFMutableArrayRef properties
,
2303 CFStringRef label
, const DERItem
*integer
) {
2304 CFStringRef string
= copyIntegerContentDescription(
2305 CFGetAllocator(properties
), integer
);
2306 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2310 static void appendBoolProperty(CFMutableArrayRef properties
,
2311 CFStringRef label
, bool boolean
) {
2312 appendPropertyP(properties
, kSecPropertyTypeString
,
2313 label
, boolean
? CFSTR("Yes") : CFSTR("No"));
2316 static void appendBooleanProperty(CFMutableArrayRef properties
,
2317 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2319 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2321 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2322 appendInvalidProperty(properties
, label
, boolean
);
2324 appendBoolProperty(properties
, label
, result
);
2328 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2329 CFStringRef label
, const DERItem
*bitStringContent
,
2330 const CFStringRef
*names
, CFIndex namesCount
) {
2331 DERSize len
= bitStringContent
->length
- 1;
2332 require_quiet(len
== 1 || len
== 2, badDER
);
2333 DERByte numUnusedBits
= bitStringContent
->data
[0];
2334 require_quiet(numUnusedBits
< 8, badDER
);
2335 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2336 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2337 uint_fast16_t value
= bitStringContent
->data
[1];
2340 value
= (value
<< 8) + bitStringContent
->data
[2];
2346 bool didOne
= false;
2347 CFMutableStringRef string
=
2348 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2349 for (ix
= 0; ix
< bits
; ++ix
) {
2352 CFStringAppend(string
, CFSTR(", "));
2356 CFStringAppend(string
, names
[ix
]);
2360 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2364 appendInvalidProperty(properties
, label
, bitStringContent
);
2367 static void appendBitStringNames(CFMutableArrayRef properties
,
2368 CFStringRef label
, const DERItem
*bitString
,
2369 const CFStringRef
*names
, CFIndex namesCount
) {
2370 DERDecodedInfo bitStringContent
;
2371 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2372 require_noerr_quiet(drtn
, badDER
);
2373 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2374 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2378 appendInvalidProperty(properties
, label
, bitString
);
2382 typedef uint16_t SecKeyUsage
;
2384 #define kSecKeyUsageDigitalSignature 0x8000
2385 #define kSecKeyUsageNonRepudiation 0x4000
2386 #define kSecKeyUsageKeyEncipherment 0x2000
2387 #define kSecKeyUsageDataEncipherment 0x1000
2388 #define kSecKeyUsageKeyAgreement 0x0800
2389 #define kSecKeyUsageKeyCertSign 0x0400
2390 #define kSecKeyUsageCRLSign 0x0200
2391 #define kSecKeyUsageEncipherOnly 0x0100
2392 #define kSecKeyUsageDecipherOnly 0x0080
2395 KeyUsage ::= BIT STRING {
2396 digitalSignature (0),
2398 keyEncipherment (2),
2399 dataEncipherment (3),
2406 static void appendKeyUsage(CFMutableArrayRef properties
,
2407 const DERItem
*extnValue
) {
2408 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2409 extnValue
->data
[0] != ASN1_BIT_STRING
||
2410 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2411 extnValue
->data
[2] > 7) {
2412 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2415 CFMutableStringRef string
=
2416 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2417 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2418 if (extnValue
->length
== 5)
2419 usage
+= extnValue
->data
[4];
2420 secinfo("keyusage", "keyusage: %04X", usage
);
2421 static const CFStringRef usageNames
[] = {
2422 CFSTR("Digital Signature"),
2423 CFSTR("Non-Repudiation"),
2424 CFSTR("Key Encipherment"),
2425 CFSTR("Data Encipherment"),
2426 CFSTR("Key Agreement"),
2432 bool didOne
= false;
2433 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2434 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2435 for (ix
= 0; ix
< bits
; ++ix
) {
2438 CFStringAppend(string
, CFSTR(", "));
2442 /* @@@ Localize usageNames[ix]. */
2443 CFStringAppend(string
, usageNames
[ix
]);
2447 appendPropertyP(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2453 static void appendKeyUsage(CFMutableArrayRef properties
,
2454 const DERItem
*extnValue
) {
2455 static const CFStringRef usageNames
[] = {
2456 CFSTR("Digital Signature"),
2457 CFSTR("Non-Repudiation"),
2458 CFSTR("Key Encipherment"),
2459 CFSTR("Data Encipherment"),
2460 CFSTR("Key Agreement"),
2463 CFSTR("Encipher Only"),
2464 CFSTR("Decipher Only")
2466 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
2467 usageNames
, sizeof(usageNames
) / sizeof(*usageNames
));
2471 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2472 const DERItem
*extnValue
) {
2473 DERPrivateKeyUsagePeriod pkup
;
2474 DERReturn drtn
= DERParseSequence(extnValue
,
2475 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2476 &pkup
, sizeof(pkup
));
2477 require_noerr_quiet(drtn
, badDER
);
2478 if (pkup
.notBefore
.length
) {
2479 appendDateContentProperty(properties
, CFSTR("Not Valid Before"),
2480 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2482 if (pkup
.notAfter
.length
) {
2483 appendDateContentProperty(properties
, CFSTR("Not Valid After"),
2484 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2488 appendInvalidProperty(properties
, CFSTR("Private Key Usage Period"),
2492 static void appendStringContentProperty(CFMutableArrayRef properties
,
2493 CFStringRef label
, const DERItem
*stringContent
,
2494 CFStringEncoding encoding
) {
2495 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2496 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2498 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2501 appendInvalidProperty(properties
, label
, stringContent
);
2506 OtherName ::= SEQUENCE {
2507 type-id OBJECT IDENTIFIER,
2508 value [0] EXPLICIT ANY DEFINED BY type-id }
2510 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2511 const DERItem
*otherNameContent
) {
2513 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2514 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2516 require_noerr_quiet(drtn
, badDER
);
2517 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2518 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
,
2519 &on
.typeIdentifier
);
2520 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2522 appendPropertyP(properties
, kSecPropertyTypeString
, oid_string
,
2525 appendUnparsedProperty(properties
, oid_string
, &on
.value
);
2529 appendInvalidProperty(properties
, CFSTR("Other Name"), otherNameContent
);
2533 GeneralName ::= CHOICE {
2534 otherName [0] OtherName,
2535 rfc822Name [1] IA5String,
2536 dNSName [2] IA5String,
2537 x400Address [3] ORAddress,
2538 directoryName [4] Name,
2539 ediPartyName [5] EDIPartyName,
2540 uniformResourceIdentifier [6] IA5String,
2541 iPAddress [7] OCTET STRING,
2542 registeredID [8] OBJECT IDENTIFIER}
2544 EDIPartyName ::= SEQUENCE {
2545 nameAssigner [0] DirectoryString OPTIONAL,
2546 partyName [1] DirectoryString }
2548 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2549 DERTag tag
, const DERItem
*generalName
) {
2551 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2552 appendOtherNameContentProperty(properties
, generalName
);
2554 case ASN1_CONTEXT_SPECIFIC
| 1:
2556 appendStringContentProperty(properties
, CFSTR("Email Address"),
2557 generalName
, kCFStringEncodingASCII
);
2559 case ASN1_CONTEXT_SPECIFIC
| 2:
2561 appendStringContentProperty(properties
, CFSTR("DNS Name"), generalName
,
2562 kCFStringEncodingASCII
);
2564 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2565 appendUnparsedProperty(properties
, CFSTR("X.400 Address"),
2568 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2570 CFArrayRef directory_plist
=
2571 createPropertiesForX501Name(CFGetAllocator(properties
),
2573 appendPropertyP(properties
, kSecPropertyTypeSection
,
2574 CFSTR("Directory Name"), directory_plist
);
2575 CFRelease(directory_plist
);
2578 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2579 appendUnparsedProperty(properties
, CFSTR("EDI Party Name"),
2582 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2583 /* Technically I don't think this is valid, but there are certs out
2584 in the wild that use a constructed IA5String. In particular the
2585 VeriSign Time Stamping Authority CA.cer does this. */
2586 appendURLProperty(properties
, CFSTR("URI"), generalName
);
2588 case ASN1_CONTEXT_SPECIFIC
| 6:
2589 appendURLContentProperty(properties
, CFSTR("URI"), generalName
);
2591 case ASN1_CONTEXT_SPECIFIC
| 7:
2592 appendIPAddressContentProperty(properties
, CFSTR("IP Address"),
2595 case ASN1_CONTEXT_SPECIFIC
| 8:
2596 appendOIDProperty(properties
, CFSTR("Registered ID"), generalName
);
2607 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2608 const DERItem
*generalName
) {
2609 DERDecodedInfo generalNameContent
;
2610 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2611 require_noerr_quiet(drtn
, badDER
);
2612 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2613 &generalNameContent
.content
))
2616 appendInvalidProperty(properties
, CFSTR("General Name"), generalName
);
2621 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2623 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2624 const DERItem
*generalNamesContent
) {
2626 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2627 require_noerr_quiet(drtn
, badDER
);
2628 DERDecodedInfo generalNameContent
;
2629 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2631 if (!appendGeneralNameContentProperty(properties
,
2632 generalNameContent
.tag
, &generalNameContent
.content
)) {
2636 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2639 appendInvalidProperty(properties
, CFSTR("General Names"),
2640 generalNamesContent
);
2643 static void appendGeneralNames(CFMutableArrayRef properties
,
2644 const DERItem
*generalNames
) {
2645 DERDecodedInfo generalNamesContent
;
2646 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2647 require_noerr_quiet(drtn
, badDER
);
2648 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2650 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2653 appendInvalidProperty(properties
, CFSTR("General Names"), generalNames
);
2657 BasicConstraints ::= SEQUENCE {
2658 cA BOOLEAN DEFAULT FALSE,
2659 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
2661 static void appendBasicConstraints(CFMutableArrayRef properties
,
2662 const DERItem
*extnValue
) {
2663 DERBasicConstraints basicConstraints
;
2664 DERReturn drtn
= DERParseSequence(extnValue
,
2665 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
2666 &basicConstraints
, sizeof(basicConstraints
));
2667 require_noerr_quiet(drtn
, badDER
);
2669 appendBooleanProperty(properties
, CFSTR("Certificate Authority"),
2670 &basicConstraints
.cA
, false);
2672 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
2673 appendIntegerProperty(properties
, CFSTR("Path Length Constraint"),
2674 &basicConstraints
.pathLenConstraint
);
2678 appendInvalidProperty(properties
, CFSTR("Basic Constraints"), extnValue
);
2682 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
2684 DistributionPoint ::= SEQUENCE {
2685 distributionPoint [0] DistributionPointName OPTIONAL,
2686 reasons [1] ReasonFlags OPTIONAL,
2687 cRLIssuer [2] GeneralNames OPTIONAL }
2689 DistributionPointName ::= CHOICE {
2690 fullName [0] GeneralNames,
2691 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
2693 ReasonFlags ::= BIT STRING {
2697 affiliationChanged (3),
2699 cessationOfOperation (5),
2700 certificateHold (6),
2701 privilegeWithdrawn (7),
2704 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
2705 const DERItem
*extnValue
) {
2706 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2709 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
2710 require_noerr_quiet(drtn
, badDER
);
2711 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2712 DERDecodedInfo dpSeqContent
;
2713 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
2714 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2715 DERDistributionPoint dp
;
2716 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
2717 DERNumDistributionPointItemSpecs
,
2718 DERDistributionPointItemSpecs
,
2720 require_noerr_quiet(drtn
, badDER
);
2721 if (dp
.distributionPoint
.length
) {
2722 DERDecodedInfo distributionPointName
;
2723 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
2724 require_noerr_quiet(drtn
, badDER
);
2725 if (distributionPointName
.tag
==
2726 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
2728 appendGeneralNamesContent(properties
,
2729 &distributionPointName
.content
);
2730 } else if (distributionPointName
.tag
==
2731 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
2732 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
2734 appendPropertyP(properties
, kSecPropertyTypeSection
,
2735 CFSTR("Name Relative To CRL Issuer"), rdn_props
);
2736 CFRelease(rdn_props
);
2741 if (dp
.reasons
.length
) {
2742 static const CFStringRef reasonNames
[] = {
2744 CFSTR("Key Compromise"),
2745 CFSTR("CA Compromise"),
2746 CFSTR("Affiliation Changed"),
2747 CFSTR("Superseded"),
2748 CFSTR("Cessation Of Operation"),
2749 CFSTR("Certificate Hold"),
2750 CFSTR("Priviledge Withdrawn"),
2751 CFSTR("AA Compromise")
2753 appendBitStringContentNames(properties
, CFSTR("Reasons"),
2755 reasonNames
, sizeof(reasonNames
) / sizeof(*reasonNames
));
2757 if (dp
.cRLIssuer
.length
) {
2758 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
2759 &kCFTypeArrayCallBacks
);
2760 appendPropertyP(properties
, kSecPropertyTypeSection
,
2761 CFSTR("CRL Issuer"), crlIssuer
);
2762 CFRelease(crlIssuer
);
2763 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
2766 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2769 appendInvalidProperty(properties
, CFSTR("Crl Distribution Points"),
2773 /* Decode a sequence of integers into a comma separated list of ints. */
2774 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
2775 CFStringRef label
, const DERItem
*intSequenceContent
) {
2776 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2778 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
2779 require_noerr_quiet(drtn
, badDER
);
2780 DERDecodedInfo intContent
;
2781 CFMutableStringRef value
= NULL
;
2782 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
))
2784 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
2785 CFStringRef intDesc
= copyIntegerContentDescription(
2786 allocator
, &intContent
.content
);
2788 CFStringAppendFormat(value
, NULL
, CFSTR(", %@"), intDesc
);
2790 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
2794 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2796 appendPropertyP(properties
, kSecPropertyTypeString
,
2797 CFSTR("Notice Numbers"), value
);
2801 /* DROPTHOUGH if !value. */
2803 appendInvalidProperty(properties
, label
, intSequenceContent
);
2806 static void appendCertificatePolicies(CFMutableArrayRef properties
,
2807 const DERItem
*extnValue
) {
2808 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2811 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
2812 require_noerr_quiet(drtn
, badDER
);
2813 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2814 DERDecodedInfo piContent
;
2816 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
2817 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2818 DERPolicyInformation pi
;
2819 drtn
= DERParseSequenceContent(&piContent
.content
,
2820 DERNumPolicyInformationItemSpecs
,
2821 DERPolicyInformationItemSpecs
,
2823 require_noerr_quiet(drtn
, badDER
);
2824 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2825 CFSTR("Policy Identifier #%d"), pin
++);
2826 appendOIDProperty(properties
, piLabel
, &pi
.policyIdentifier
);
2828 if (pi
.policyQualifiers
.length
== 0)
2832 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
2833 require_noerr_quiet(drtn
, badDER
);
2834 DERDecodedInfo pqContent
;
2836 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
2837 DERPolicyQualifierInfo pqi
;
2838 drtn
= DERParseSequenceContent(&pqContent
.content
,
2839 DERNumPolicyQualifierInfoItemSpecs
,
2840 DERPolicyQualifierInfoItemSpecs
,
2842 require_noerr_quiet(drtn
, badDER
);
2843 DERDecodedInfo qualifierContent
;
2844 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
2845 require_noerr_quiet(drtn
, badDER
);
2846 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2847 CFSTR("Policy Qualifier #%d"), pqn
++);
2848 appendOIDProperty(properties
, pqLabel
, &pqi
.policyQualifierID
);
2850 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
2851 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
2852 appendURLContentProperty(properties
,
2854 &qualifierContent
.content
);
2855 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
2856 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2858 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
2859 DERNumUserNoticeItemSpecs
,
2860 DERUserNoticeItemSpecs
,
2862 require_noerr_quiet(drtn
, badDER
);
2863 if (un
.noticeRef
.length
) {
2864 DERNoticeReference nr
;
2865 drtn
= DERParseSequenceContent(&un
.noticeRef
,
2866 DERNumNoticeReferenceItemSpecs
,
2867 DERNoticeReferenceItemSpecs
,
2869 require_noerr_quiet(drtn
, badDER
);
2870 appendDERThingProperty(properties
,
2871 CFSTR("Organization"),
2873 appendIntegerSequenceContent(properties
,
2874 CFSTR("Notice Numbers"), &nr
.noticeNumbers
);
2876 if (un
.explicitText
.length
) {
2877 appendDERThingProperty(properties
, CFSTR("Explicit Text"),
2881 appendUnparsedProperty(properties
, CFSTR("Qualifier"),
2886 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2889 appendInvalidProperty(properties
, CFSTR("Certificate Policies"),
2893 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
2894 const DERItem
*extnValue
) {
2896 DERDecodedInfo keyIdentifier
;
2897 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
2898 require_noerr_quiet(drtn
, badDER
);
2899 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
2900 appendDataProperty(properties
, CFSTR("Key Identifier"),
2901 &keyIdentifier
.content
);
2905 appendInvalidProperty(properties
, CFSTR("Invalid Subject Key Identifier"),
2910 AuthorityKeyIdentifier ::= SEQUENCE {
2911 keyIdentifier [0] KeyIdentifier OPTIONAL,
2912 authorityCertIssuer [1] GeneralNames OPTIONAL,
2913 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
2914 -- authorityCertIssuer and authorityCertSerialNumber MUST both
2915 -- be present or both be absent
2917 KeyIdentifier ::= OCTET STRING
2919 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
2920 const DERItem
*extnValue
) {
2921 DERAuthorityKeyIdentifier akid
;
2923 drtn
= DERParseSequence(extnValue
,
2924 DERNumAuthorityKeyIdentifierItemSpecs
,
2925 DERAuthorityKeyIdentifierItemSpecs
,
2926 &akid
, sizeof(akid
));
2927 require_noerr_quiet(drtn
, badDER
);
2928 if (akid
.keyIdentifier
.length
) {
2929 appendDataProperty(properties
, CFSTR("Key Identifier"),
2930 &akid
.keyIdentifier
);
2932 if (akid
.authorityCertIssuer
.length
||
2933 akid
.authorityCertSerialNumber
.length
) {
2934 require_quiet(akid
.authorityCertIssuer
.length
&&
2935 akid
.authorityCertSerialNumber
.length
, badDER
);
2936 /* Perhaps put in a subsection called Authority Certificate Issuer. */
2937 appendGeneralNamesContent(properties
,
2938 &akid
.authorityCertIssuer
);
2939 appendIntegerProperty(properties
,
2940 CFSTR("Authority Certificate Serial Number"),
2941 &akid
.authorityCertSerialNumber
);
2946 appendInvalidProperty(properties
, CFSTR("Authority Key Identifier"),
2951 PolicyConstraints ::= SEQUENCE {
2952 requireExplicitPolicy [0] SkipCerts OPTIONAL,
2953 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
2955 SkipCerts ::= INTEGER (0..MAX)
2957 static void appendPolicyConstraints(CFMutableArrayRef properties
,
2958 const DERItem
*extnValue
) {
2959 DERPolicyConstraints pc
;
2961 drtn
= DERParseSequence(extnValue
,
2962 DERNumPolicyConstraintsItemSpecs
,
2963 DERPolicyConstraintsItemSpecs
,
2965 require_noerr_quiet(drtn
, badDER
);
2966 if (pc
.requireExplicitPolicy
.length
) {
2967 appendIntegerProperty(properties
,
2968 CFSTR("Require Explicit Policy"), &pc
.requireExplicitPolicy
);
2970 if (pc
.inhibitPolicyMapping
.length
) {
2971 appendIntegerProperty(properties
,
2972 CFSTR("Inhibit Policy Mapping"), &pc
.inhibitPolicyMapping
);
2978 appendInvalidProperty(properties
, CFSTR("Policy Constraints"), extnValue
);
2982 extendedKeyUsage EXTENSION ::= {
2983 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
2984 IDENTIFIED BY id-ce-extKeyUsage }
2986 KeyPurposeId ::= OBJECT IDENTIFIER
2988 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
2989 const DERItem
*extnValue
) {
2992 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
2993 require_noerr_quiet(drtn
, badDER
);
2994 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2995 DERDecodedInfo currDecoded
;
2996 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
2997 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
2998 appendOIDProperty(properties
, CFSTR("Purpose"),
2999 &currDecoded
.content
);
3001 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3004 appendInvalidProperty(properties
, CFSTR("Extended Key Usage"), extnValue
);
3008 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3010 AuthorityInfoAccessSyntax ::=
3011 SEQUENCE SIZE (1..MAX) OF AccessDescription
3013 AccessDescription ::= SEQUENCE {
3014 accessMethod OBJECT IDENTIFIER,
3015 accessLocation GeneralName }
3017 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3019 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3021 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3023 static void appendInfoAccess(CFMutableArrayRef properties
,
3024 const DERItem
*extnValue
) {
3027 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3028 require_noerr_quiet(drtn
, badDER
);
3029 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3030 DERDecodedInfo adContent
;
3031 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3032 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3033 DERAccessDescription ad
;
3034 drtn
= DERParseSequenceContent(&adContent
.content
,
3035 DERNumAccessDescriptionItemSpecs
,
3036 DERAccessDescriptionItemSpecs
,
3038 require_noerr_quiet(drtn
, badDER
);
3039 appendOIDProperty(properties
, CFSTR("Access Method"),
3041 //CFSTR("Access Location");
3042 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3044 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3047 appendInvalidProperty(properties
, CFSTR("Authority Information Access"),
3051 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3052 const DERItem
*extnValue
) {
3053 static const CFStringRef certTypes
[] = {
3054 CFSTR("SSL client"),
3055 CFSTR("SSL server"),
3057 CFSTR("Object Signing"),
3061 CFSTR("Object Signing CA")
3063 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
3064 certTypes
, sizeof(certTypes
) / sizeof(*certTypes
));
3068 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3069 const DERItem
*extnValue
) {
3073 * The list of Qualified Cert Statement statementIds we understand, even though
3074 * we don't actually do anything with them; if these are found in a Qualified
3075 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3077 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3079 /* id-qcs := { id-pkix 11 } */
3080 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3081 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3082 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3083 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3084 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3085 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3087 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3089 static void appendQCCertStatements(CFMutableArrayRef properties
,
3090 const DERItem
*extnValue
) {
3095 static bool appendPrintableDERSequenceP(CFMutableArrayRef properties
,
3096 CFStringRef label
, const DERItem
*sequence
) {
3099 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3100 require_noerr_quiet(drtn
, badSequence
);
3101 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3102 DERDecodedInfo currDecoded
;
3103 bool appendedSomething
= false;
3104 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3105 switch (currDecoded
.tag
)
3108 case ASN1_SEQUENCE
: // 16
3109 case ASN1_SET
: // 17
3110 // skip constructed object lengths
3113 case ASN1_UTF8_STRING
: // 12
3114 case ASN1_NUMERIC_STRING
: // 18
3115 case ASN1_PRINTABLE_STRING
: // 19
3116 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3117 case ASN1_VIDEOTEX_STRING
: // 21
3118 case ASN1_IA5_STRING
: // 22
3119 case ASN1_GRAPHIC_STRING
: // 25
3120 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3121 case ASN1_GENERAL_STRING
: // 27
3122 case ASN1_UNIVERSAL_STRING
: // 28
3124 CFStringRef string
=
3125 copyDERThingContentDescription(CFGetAllocator(properties
),
3126 currDecoded
.tag
, &currDecoded
.content
, false);
3127 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3129 appendPropertyP(properties
, kSecPropertyTypeString
, label
,
3132 appendedSomething
= true;
3139 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3140 return appendedSomething
;
3145 static void appendExtension(CFMutableArrayRef parent
,
3146 const SecCertificateExtension
*extn
) {
3147 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3148 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3149 &kCFTypeArrayCallBacks
);
3151 *extnID
= &extn
->extnID
,
3152 *extnValue
= &extn
->extnValue
;
3154 appendBoolProperty(properties
, CFSTR("Critical"), extn
->critical
);
3157 bool handled
= true;
3158 /* Extensions that we know how to handle ourselves... */
3159 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3160 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3162 switch (extnID
->data
[extnID
->length
- 1]) {
3163 case 14: /* SubjectKeyIdentifier id-ce 14 */
3164 appendSubjectKeyIdentifier(properties
, extnValue
);
3166 case 15: /* KeyUsage id-ce 15 */
3167 appendKeyUsage(properties
, extnValue
);
3169 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3170 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3172 case 17: /* SubjectAltName id-ce 17 */
3173 case 18: /* IssuerAltName id-ce 18 */
3174 appendGeneralNames(properties
, extnValue
);
3176 case 19: /* BasicConstraints id-ce 19 */
3177 appendBasicConstraints(properties
, extnValue
);
3179 case 30: /* NameConstraints id-ce 30 */
3182 case 31: /* CRLDistributionPoints id-ce 31 */
3183 appendCrlDistributionPoints(properties
, extnValue
);
3185 case 32: /* CertificatePolicies id-ce 32 */
3186 appendCertificatePolicies(properties
, extnValue
);
3188 case 33: /* PolicyMappings id-ce 33 */
3191 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3192 appendAuthorityKeyIdentifier(properties
, extnValue
);
3194 case 36: /* PolicyConstraints id-ce 36 */
3195 appendPolicyConstraints(properties
, extnValue
);
3197 case 37: /* ExtKeyUsage id-ce 37 */
3198 appendExtendedKeyUsage(properties
, extnValue
);
3200 case 46: /* FreshestCRL id-ce 46 */
3203 case 54: /* InhibitAnyPolicy id-ce 54 */
3210 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3211 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3213 switch (extnID
->data
[extnID
->length
- 1]) {
3214 case 1: /* AuthorityInfoAccess id-pe 1 */
3215 appendInfoAccess(properties
, extnValue
);
3217 case 3: /* QCStatements id-pe 3 */
3220 case 11: /* SubjectInfoAccess id-pe 11 */
3221 appendInfoAccess(properties
, extnValue
);
3227 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3228 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3229 appendNetscapeCertType(properties
, extnValue
);
3235 /* Try to parse and display printable string(s). */
3236 if (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3237 /* Nothing to do here appendPrintableDERSequenceP did the work. */
3239 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3240 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3244 /* Extensions that we know how to handle ourselves... */
3245 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3246 appendSubjectKeyIdentifier(properties
, extnValue
);
3247 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3248 appendKeyUsage(properties
, extnValue
);
3249 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3250 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3251 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3252 appendGeneralNames(properties
, extnValue
);
3253 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3254 appendGeneralNames(properties
, extnValue
);
3255 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3256 appendBasicConstraints(properties
, extnValue
);
3257 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3258 appendCrlDistributionPoints(properties
, extnValue
);
3259 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3260 appendCertificatePolicies(properties
, extnValue
);
3261 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3262 appendAuthorityKeyIdentifier(properties
, extnValue
);
3263 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3264 appendPolicyConstraints(properties
, extnValue
);
3265 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3266 appendExtendedKeyUsage(properties
, extnValue
);
3267 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3268 appendInfoAccess(properties
, extnValue
);
3269 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3270 appendInfoAccess(properties
, extnValue
);
3271 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3272 appendNetscapeCertType(properties
, extnValue
);
3274 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3275 appendEntrustVersInfo(properties
, extnValue
);
3278 /* Try to parse and display printable string(s). */
3279 if (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3280 /* Nothing to do here appendPrintableDERSequenceP did the work. */
3282 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3283 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3286 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
, extnID
);
3287 appendPropertyP(parent
, kSecPropertyTypeSection
, oid_string
, properties
);
3288 CFRelease(oid_string
);
3289 CFRelease(properties
);
3292 /* Different types of summary types from least desired to most desired. */
3295 kSummaryTypePrintable
,
3296 kSummaryTypeOrganizationName
,
3297 kSummaryTypeOrganizationalUnitName
,
3298 kSummaryTypeCommonName
,
3302 enum SummaryType type
;
3303 CFStringRef summary
;
3304 CFStringRef description
;
3307 static OSStatus
obtainSummaryFromX501Name(void *context
,
3308 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3309 struct Summary
*summary
= (struct Summary
*)context
;
3310 enum SummaryType stype
= kSummaryTypeNone
;
3311 CFStringRef string
= NULL
;
3312 if (DEROidCompare(type
, &oidCommonName
)) {
3313 /* We skip Common Names that have generic values. */
3314 const char tfm
[] = "Thawte Freemail Member";
3315 if ((value
->length
== sizeof(tfm
) + 1) &&
3316 !memcmp(value
->data
+ 2, tfm
, sizeof(tfm
) - 1)) {
3317 return errSecSuccess
;
3319 stype
= kSummaryTypeCommonName
;
3320 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3321 stype
= kSummaryTypeOrganizationalUnitName
;
3322 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3323 stype
= kSummaryTypeOrganizationName
;
3324 } else if (DEROidCompare(type
, &oidDescription
)) {
3325 if (!summary
->description
) {
3326 summary
->description
= string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3329 stype
= kSummaryTypePrintable
;
3331 stype
= kSummaryTypePrintable
;
3334 /* Use the first field we encounter of the highest priority type. */
3335 if (summary
->type
< stype
) {
3337 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3341 CFReleaseSafe(summary
->summary
);
3342 summary
->summary
= string
;
3343 summary
->type
= stype
;
3346 CFReleaseSafe(string
);
3349 return errSecSuccess
;
3352 CFStringRef
SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate
) {
3353 struct Summary summary
= {};
3354 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3355 /* If we found a description and a common name we change the summary to
3356 CommonName (Description). */
3357 if (summary
.description
) {
3358 if (summary
.type
== kSummaryTypeCommonName
) {
3359 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3360 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3361 CFRelease(summary
.summary
);
3362 summary
.summary
= newSummary
;
3364 CFRelease(summary
.description
);
3367 if (!summary
.summary
) {
3368 /* If we didn't find a suitable printable string in the subject at all, we try
3369 the first email address in the certificate instead. */
3370 CFArrayRef names
= SecCertificateCopyRFC822NamesP(certificate
);
3372 /* If we didn't find any email addresses in the certificate, we try finding
3373 a DNS name instead. */
3374 names
= SecCertificateCopyDNSNamesP(certificate
);
3377 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3378 CFRetain(summary
.summary
);
3383 return summary
.summary
;
3386 CFStringRef
SecCertificateCopyIssuerSummaryP(SecCertificateRefP certificate
) {
3387 struct Summary summary
= {};
3388 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3389 /* If we found a description and a common name we change the summary to
3390 CommonName (Description). */
3391 if (summary
.description
) {
3392 if (summary
.type
== kSummaryTypeCommonName
) {
3393 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3394 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3395 CFRelease(summary
.summary
);
3396 summary
.summary
= newSummary
;
3398 CFRelease(summary
.description
);
3401 return summary
.summary
;
3404 /* Return the earliest date on which all certificates in this chain are still
3406 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3407 SecCertificateRefP certificate
) {
3408 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3410 while (certificate
->_parent
) {
3411 certificate
= certificate
->_parent
;
3412 if (earliest
> certificate
->_notAfter
)
3413 earliest
= certificate
->_notAfter
;
3420 /* Return the latest date on which all certificates in this chain will be
3422 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3423 SecCertificateRefP certificate
) {
3424 CFAbsoluteTime latest
= certificate
->_notBefore
;
3426 while (certificate
->_parent
) {
3427 certificate
= certificate
->_parent
;
3428 if (latest
< certificate
->_notBefore
)
3429 latest
= certificate
->_notBefore
;
3436 bool SecCertificateIsValidP(SecCertificateRefP certificate
,
3437 CFAbsoluteTime verifyTime
) {
3439 return certificate
->_notBefore
<= verifyTime
&&
3440 verifyTime
<= certificate
->_notAfter
;
3443 CFIndex
SecCertificateVersionP(SecCertificateRefP certificate
) {
3444 return certificate
->_version
+ 1;
3447 CFAbsoluteTime
SecCertificateNotValidBeforeP(SecCertificateRefP certificate
) {
3448 return certificate
->_notBefore
;
3451 CFAbsoluteTime
SecCertificateNotValidAfterP(SecCertificateRefP certificate
) {
3452 return certificate
->_notAfter
;
3455 CFMutableArrayRef
SecCertificateCopySummaryPropertiesP(
3456 SecCertificateRefP certificate
, CFAbsoluteTime verifyTime
) {
3457 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3458 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3459 &kCFTypeArrayCallBacks
);
3461 /* First we put the subject summary name. */
3462 CFStringRef ssummary
= SecCertificateCopySubjectSummaryP(certificate
);
3464 appendPropertyP(summary
, kSecPropertyTypeTitle
,
3466 CFRelease(ssummary
);
3469 CFStringRef isummary
= CFSTR("Issuer Summary");
3470 appendPropertyP(summary
, kSecPropertyTypeString
,
3471 CFSTR("Issued By"), isummary
);
3472 CFRelease(isummary
);
3475 /* Let see if this certificate is currently valid. */
3477 CFAbsoluteTime when
;
3478 CFStringRef message
;
3480 if (verifyTime
> certificate
->_notAfter
) {
3481 label
= CFSTR("Expired");
3482 when
= certificate
->_notAfter
;
3483 ptype
= kSecPropertyTypeError
;
3484 message
= CFSTR("This certificate has expired");
3485 } else if (certificate
->_notBefore
> verifyTime
) {
3486 label
= CFSTR("Valid from");
3487 when
= certificate
->_notBefore
;
3488 ptype
= kSecPropertyTypeError
;
3489 message
= CFSTR("This certificate is not yet valid");
3491 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3492 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3493 if (verifyTime
> last
) {
3494 label
= CFSTR("Expired");
3496 ptype
= kSecPropertyTypeError
;
3497 message
= CFSTR("This certificate has an issuer that has expired");
3498 } else if (verifyTime
< first
) {
3499 label
= CFSTR("Valid from");
3501 ptype
= kSecPropertyTypeError
;
3502 message
= CFSTR("This certificate has an issuer that is not yet valid");
3504 label
= CFSTR("Expires");
3505 when
= certificate
->_notAfter
;
3506 ptype
= kSecPropertyTypeSuccess
;
3507 message
= CFSTR("This certificate is valid");
3511 appendDateProperty(summary
, label
, when
);
3512 appendPropertyP(summary
, ptype
, NULL
, message
);
3517 CFArrayRef
SecCertificateCopyPropertiesP(SecCertificateRefP certificate
) {
3518 if (!certificate
->_properties
) {
3519 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3520 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3521 &kCFTypeArrayCallBacks
);
3523 /* First we put the Subject Name in the property list. */
3524 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3525 &certificate
->_subject
);
3526 appendPropertyP(properties
, kSecPropertyTypeSection
,
3527 CFSTR("Subject Name"), subject_plist
);
3528 CFRelease(subject_plist
);
3531 /* Put Normalized subject in for testing. */
3532 if (certificate
->_normalizedSubject
) {
3533 DERItem nsubject
= {
3534 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
3535 CFDataGetLength(certificate
->_normalizedSubject
)
3537 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
3539 appendPropertyP(properties
, kSecPropertyTypeSection
,
3540 CFSTR("Normalized Subject Name"), nsubject_plist
);
3541 CFRelease(nsubject_plist
);
3545 /* Next we put the Issuer Name in the property list. */
3546 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3547 &certificate
->_issuer
);
3548 appendPropertyP(properties
, kSecPropertyTypeSection
,
3549 CFSTR("Issuer Name"), issuer_plist
);
3550 CFRelease(issuer_plist
);
3553 /* Certificate version/type. */
3554 bool isRoot
= false;
3555 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
3556 CFSTR("X.509 version %d %scertificate"),
3557 certificate
->_version
+ 1, isRoot
? "root " : "");
3558 appendPropertyP(properties
, kSecPropertyTypeString
,
3559 CFSTR("Certificate Type"), typeString
);
3560 CFRelease(typeString
);
3564 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
3565 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
3566 appendPropertyP(properties
, kSecPropertyTypeString
,
3567 CFSTR("Version"), versionString
);
3568 CFRelease(versionString
);
3571 if (certificate
->_serialNum
.length
) {
3572 appendIntegerProperty(properties
, CFSTR("Serial Number"),
3573 &certificate
->_serialNum
);
3576 /* Signature algorithm. */
3578 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3579 &certificate
->_sigAlg
);
3581 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3582 &certificate
->_tbsSigAlg
);
3585 /* Validity dates. */
3586 appendDateProperty(properties
, CFSTR("Not Valid Before"),
3587 certificate
->_notBefore
);
3588 appendDateProperty(properties
, CFSTR("Not Valid After"),
3589 certificate
->_notAfter
);
3591 if (certificate
->_subjectUniqueID
.length
) {
3592 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
3593 &certificate
->_subjectUniqueID
);
3595 if (certificate
->_issuerUniqueID
.length
) {
3596 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
3597 &certificate
->_issuerUniqueID
);
3600 /* Public key algorithm. */
3601 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
3602 &certificate
->_algId
);
3604 /* Consider breaking down an RSA public key into modulus and
3606 appendDataProperty(properties
, CFSTR("Public Key Data"),
3607 &certificate
->_pubKeyDER
);
3609 /* @@@ Key Usage. */
3611 appendDataProperty(properties
, CFSTR("Signature"),
3612 &certificate
->_signature
);
3615 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
3616 appendExtension(properties
, &certificate
->_extensions
[ix
]);
3619 /* @@@ Key Fingerprints. */
3621 certificate
->_properties
= properties
;
3624 CFRetain(certificate
->_properties
);
3625 return certificate
->_properties
;
3628 CFDataRef
SecCertificateCopySerialNumberP(
3629 SecCertificateRefP certificate
) {
3630 if (certificate
->_serialNumber
) {
3631 CFRetain(certificate
->_serialNumber
);
3633 return certificate
->_serialNumber
;
3637 * Accessor for normalized issuer content
3639 CFDataRef
SecCertificateGetNormalizedIssuerContentP(
3640 SecCertificateRefP certificate
) {
3641 return certificate
->_normalizedIssuer
;
3645 * Accessor for normalized subject content
3647 CFDataRef
SecCertificateGetNormalizedSubjectContentP(
3648 SecCertificateRefP certificate
) {
3649 return certificate
->_normalizedSubject
;
3653 * Returns DER-encoded normalized issuer sequence
3654 * for use with SecItemCopyMatching; caller must release
3656 CFDataRef
SecCertificateCopyNormalizedIssuerSequenceP(
3657 SecCertificateRefP certificate
) {
3659 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
3660 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
3662 return SecDERItemCopySequenceP(&tmpdi
);
3666 * Returns DER-encoded normalized subject sequence
3667 * for use with SecItemCopyMatching; caller must release
3669 CFDataRef
SecCertificateCopyNormalizedSubjectSequenceP(
3670 SecCertificateRefP certificate
) {
3672 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
);
3673 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
3675 return SecDERItemCopySequenceP(&tmpdi
);
3678 /* Verify that certificate was signed by issuerKey. */
3679 OSStatus
SecCertificateIsSignedByP(SecCertificateRefP certificate
,
3680 SecKeyRefP issuerKey
) {
3681 /* Setup algId in SecAsn1AlgId format. */
3683 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3684 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3685 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3686 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3689 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3690 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3691 certificate
->_signature
.data
, certificate
->_signature
.length
);
3693 secinfo("verify", "signature verify failed: %d", status
);
3694 return errSecNotSigner
;
3698 return errSecSuccess
;
3702 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRefP certificate
,
3703 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3704 if (!signatureCheckOnly
) {
3705 /* It turns out we don't actually need to use normalized subject and
3706 issuer according to rfc2459. */
3708 /* If present we should check issuerID against the issuer subjectID. */
3710 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3711 then we should look for a SubjectKeyIdentifier in the issuer
3713 If we have a authorityCertSerialNumber we can use that for chaining.
3714 If we have a authorityCertIssuer we can use that? (or not) */
3716 /* Verify that this cert was issued by issuer. Do so by chaining
3717 either issuerID to subjectID or normalized issuer to normalized
3719 CFDataRef normalizedIssuer
=
3720 SecCertificateGetNormalizedIssuerContentP(certificate
);
3721 CFDataRef normalizedIssuerSubject
=
3722 SecCertificateGetNormalizedSubjectContentP(issuer
);
3723 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3724 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3725 return errSecIssuerMismatch
;
3728 /* Next verify that this cert was signed by issuer. */
3729 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3731 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3732 /* FIXME: We sould cache this (or at least the digest) until we find
3733 a suitable issuer. */
3734 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3735 CFIndex signedDataLength
;
3736 CertVerifyReturn crtn
;
3737 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3738 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3739 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3740 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3741 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3742 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3743 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3744 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3745 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3747 secinfo("verify", "unsupported algorithm");
3748 return errSecUnsupportedAlgorithm
;
3751 secinfo("verify", "*DigestInfo returned: %d", crtn
);
3752 /* FIXME: Do proper error code translation. */
3753 return errSecUnsupportedAlgorithm
;
3756 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3757 signedData
, signedDataLength
,
3758 certificate
->_signature
.data
, certificate
->_signature
.length
);
3760 secinfo("verify", "signature verify failed: %d", status
);
3761 return errSecNotSigner
;
3764 return errSecSuccess
;
3767 static OSStatus
_SecCertificateSetParent(SecCertificateRefP certificate
,
3768 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3770 if (certificate
->_parent
) {
3771 /* Setting a certificates issuer twice is only allowed if the new
3772 issuer is equal to the current one. */
3773 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3777 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3778 signatureCheckOnly
);
3780 OSStatus status
= errSecSuccess
;
3783 if (CFEqual(certificate
, issuer
)) {
3784 /* We don't retain ourselves cause that would be bad mojo,
3785 however we do record that we are properly self signed. */
3786 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3787 secinfo("cert", "set self as parent");
3788 return errSecSuccess
;
3792 certificate
->_parent
= issuer
;
3793 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3799 static bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate
) {
3800 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3801 certificate
->_isSelfSigned
=
3802 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3803 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3806 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3809 /* Return true iff we were able to set our own parent from one of the
3810 certificates in other_certificates, return false otherwise. If
3811 signatureCheckOnly is true, we can skip the subject == issuer or
3812 authorityKeyIdentifier tests. */
3813 static bool SecCertificateSetParentFrom(SecCertificateRefP certificate
,
3814 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3815 CFIndex count
= CFArrayGetCount(other_certificates
);
3817 for (ix
= 0; ix
< count
; ++ix
) {
3818 SecCertificateRefP candidate
= (SecCertificateRefP
)
3819 CFArrayGetValueAtIndex(other_certificates
, ix
);
3820 if (_SecCertificateSetParent(certificate
, candidate
,
3821 signatureCheckOnly
))
3827 /* Lookup the parent of certificate in the keychain and set it. */
3828 static bool SecCertificateFindParent(SecCertificateRefP certificate
) {
3829 /* FIXME: Search for things other than just subject of our issuer if we
3830 have a subjectID or authorityKeyIdentifier. */
3831 CFDataRef normalizedIssuer
=
3832 SecCertificateGetNormalizedIssuerContentP(certificate
);
3833 const void *keys
[] = {
3840 kSecClassCertificate
,
3845 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3846 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3848 OSStatus status
= SecItemCopyMatching(query
, &results
);
3851 secinfo("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3855 CFArrayRef certs
= (CFArrayRef
)results
;
3856 /* Since we already know the certificates we are providing as candidates
3857 have been checked for subject matching, we can ask
3858 SecCertificateSetParentFrom to skip everything except the signature
3860 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3865 OSStatus
SecCertificateCompleteChainP(SecCertificateRefP certificate
,
3866 CFArrayRef other_certificates
) {
3868 if (certificate
->_parent
== NULL
) {
3869 if (SecCertificateIsSelfSignedP(certificate
))
3870 return errSecSuccess
;
3871 if (!other_certificates
||
3872 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
3874 if (!SecCertificateFindParent(certificate
))
3875 return errSecIssuerNotFound
;
3878 certificate
= certificate
->_parent
;
3883 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
3884 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
3885 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
3886 if (gnType
== GNT_IPAddress
) {
3887 CFStringRef string
= copyIPAddressContentDescription(
3888 kCFAllocatorDefault
, generalName
);
3890 CFArrayAppendValue(ipAddresses
, string
);
3893 return errSecInvalidCertificate
;
3896 return errSecSuccess
;
3899 CFArrayRef
SecCertificateCopyIPAddressesP(SecCertificateRefP certificate
) {
3900 /* These can only exist in the subject alt name. */
3901 if (!certificate
->_subjectAltName
)
3904 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
3905 0, &kCFTypeArrayCallBacks
);
3906 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
3907 ipAddresses
, appendIPAddressesFromGeneralNames
);
3908 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
3909 CFRelease(ipAddresses
);
3915 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
3916 const DERItem
*generalName
) {
3917 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
3918 if (gnType
== GNT_DNSName
) {
3919 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
3920 generalName
->data
, generalName
->length
,
3921 kCFStringEncodingUTF8
, FALSE
);
3923 CFArrayAppendValue(dnsNames
, string
);
3926 return errSecInvalidCertificate
;
3929 return errSecSuccess
;
3932 /* Return true if the passed in string matches the
3933 Preferred name syntax from sections 2.3.1. in RFC 1035.
3934 With the added check that we disallow empty dns names.
3935 Also in order to support wildcard DNSNames we allow for the '*'
3936 character anywhere in a dns component where we currently allow
3939 <domain> ::= <subdomain> | " "
3941 <subdomain> ::= <label> | <subdomain> "." <label>
3943 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
3945 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
3947 <let-dig-hyp> ::= <let-dig> | "-"
3949 <let-dig> ::= <letter> | <digit>
3951 <letter> ::= any one of the 52 alphabetic characters A through Z in
3952 upper case and a through z in lower case
3954 <digit> ::= any one of the ten digits 0 through 9
3956 static bool isDNSName(CFStringRef string
) {
3957 CFStringInlineBuffer buf
;
3958 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
3959 /* From RFC 1035 2.3.4. Size limits:
3960 labels 63 octets or less
3961 names 255 octets or less */
3962 require_quiet(length
<= 255, notDNS
);
3963 CFRange range
= { 0, length
};
3964 CFStringInitInlineBuffer(string
, &buf
, range
);
3968 kDNSStateAfterAlpha
,
3969 kDNSStateAfterDigit
,
3971 } state
= kDNSStateInital
;
3973 bool nonAlpha
= false;
3974 for (ix
= 0; ix
< length
; ++ix
) {
3975 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
3978 require_quiet(labelLength
<= 64 &&
3979 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
3981 state
= kDNSStateAfterDot
;
3984 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
3986 state
= kDNSStateAfterAlpha
;
3987 } else if ('0' <= ch
&& ch
<= '9') {
3989 /* The requirement for labels to start with a letter was
3990 dropped so we don't check this anymore. */
3991 require_quiet(state
== kDNSStateAfterAlpha
||
3992 state
== kDNSStateAfterDigit
||
3993 state
== kDNSStateAfterDash
, notDNS
);
3995 state
= kDNSStateAfterDigit
;
3997 } else if (ch
== '-') {
3998 require_quiet(state
== kDNSStateAfterAlpha
||
3999 state
== kDNSStateAfterDigit
||
4000 state
== kDNSStateAfterDash
, notDNS
);
4001 state
= kDNSStateAfterDash
;
4008 /* We don't allow a dns name to end in a dot, and we require the
4009 final name component to only have alphanumeric chars. */
4010 require_quiet(!nonAlpha
&& labelLength
<= 63 &&
4011 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4019 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4020 const DERItem
*value
, CFIndex rdnIX
) {
4021 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4022 if (DEROidCompare(type
, &oidCommonName
)) {
4023 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4026 if (isDNSName(string
)) {
4027 /* We found a common name that is formatted like a valid
4029 CFArrayAppendValue(dnsNames
, string
);
4033 return errSecInvalidCertificate
;
4036 return errSecSuccess
;
4039 /* Not everything returned by this function is going to be a proper DNS name,
4040 we also return the certificates common name entries from the subject,
4041 assuming they look like dns names as specified in RFC 1035. */
4042 CFArrayRef
SecCertificateCopyDNSNamesP(SecCertificateRefP certificate
) {
4043 /* These can exist in the subject alt name or in the subject. */
4044 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4045 0, &kCFTypeArrayCallBacks
);
4046 OSStatus status
= errSecSuccess
;
4047 if (certificate
->_subjectAltName
) {
4048 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4049 dnsNames
, appendDNSNamesFromGeneralNames
);
4051 /* RFC 2818 section 3.1. Server Identity
4053 If a subjectAltName extension of type dNSName is present, that MUST
4054 be used as the identity. Otherwise, the (most specific) Common Name
4055 field in the Subject field of the certificate MUST be used. Although
4056 the use of the Common Name is existing practice, it is deprecated and
4057 Certification Authorities are encouraged to use the dNSName instead.
4060 This implies that if we found 1 or more DNSNames in the
4061 subjectAltName, we should not use the Common Name of the subject as
4064 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4065 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4066 appendDNSNamesFromX501Name
);
4068 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4069 CFRelease(dnsNames
);
4075 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4076 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4077 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4078 if (gnType
== GNT_RFC822Name
) {
4079 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4080 generalName
->data
, generalName
->length
,
4081 kCFStringEncodingASCII
, FALSE
);
4083 CFArrayAppendValue(dnsNames
, string
);
4086 return errSecInvalidCertificate
;
4089 return errSecSuccess
;
4092 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4093 const DERItem
*value
, CFIndex rdnIX
) {
4094 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4095 if (DEROidCompare(type
, &oidEmailAddress
)) {
4096 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4099 CFArrayAppendValue(dnsNames
, string
);
4102 return errSecInvalidCertificate
;
4105 return errSecSuccess
;
4108 CFArrayRef
SecCertificateCopyRFC822NamesP(SecCertificateRefP certificate
) {
4109 /* These can exist in the subject alt name or in the subject. */
4110 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4111 0, &kCFTypeArrayCallBacks
);
4112 OSStatus status
= errSecSuccess
;
4113 if (certificate
->_subjectAltName
) {
4114 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4115 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4118 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4119 appendRFC822NamesFromX501Name
);
4121 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4122 CFRelease(rfc822Names
);
4128 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4129 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4130 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4131 if (DEROidCompare(type
, &oidCommonName
)) {
4132 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4135 CFArrayAppendValue(commonNames
, string
);
4138 return errSecInvalidCertificate
;
4141 return errSecSuccess
;
4144 CFArrayRef
SecCertificateCopyCommonNamesP(SecCertificateRefP certificate
) {
4145 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4146 0, &kCFTypeArrayCallBacks
);
4148 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4149 appendCommonNamesFromX501Name
);
4150 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4151 CFRelease(commonNames
);
4157 static OSStatus
appendOrganizationFromX501Name(void *context
,
4158 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4159 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4160 if (DEROidCompare(type
, &oidOrganizationName
)) {
4161 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4164 CFArrayAppendValue(organization
, string
);
4167 return errSecInvalidCertificate
;
4170 return errSecSuccess
;
4173 CFArrayRef
SecCertificateCopyOrganizationP(SecCertificateRefP certificate
) {
4174 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4175 0, &kCFTypeArrayCallBacks
);
4177 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4178 appendOrganizationFromX501Name
);
4179 if (status
|| CFArrayGetCount(organization
) == 0) {
4180 CFRelease(organization
);
4181 organization
= NULL
;
4183 return organization
;
4186 const SecCEBasicConstraints
*
4187 SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate
) {
4188 if (certificate
->_basicConstraints
.present
)
4189 return &certificate
->_basicConstraints
;
4194 const SecCEPolicyConstraints
*
4195 SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate
) {
4196 if (certificate
->_policyConstraints
.present
)
4197 return &certificate
->_policyConstraints
;
4203 SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate
) {
4204 return certificate
->_policyMappings
;
4207 const SecCECertificatePolicies
*
4208 SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate
) {
4209 if (certificate
->_certificatePolicies
.present
)
4210 return &certificate
->_certificatePolicies
;
4216 SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate
) {
4217 return certificate
->_inhibitAnyPolicySkipCerts
;
4220 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4221 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4222 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4223 if (gnType
== GNT_OtherName
) {
4225 DERReturn drtn
= DERParseSequenceContent(generalName
,
4226 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4228 require_noerr_quiet(drtn
, badDER
);
4229 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4231 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4232 &on
.value
, true), badDER
);
4233 CFArrayAppendValue(ntPrincipalNames
, string
);
4237 return errSecSuccess
;
4240 return errSecInvalidCertificate
;
4244 CFArrayRef
SecCertificateCopyNTPrincipalNamesP(SecCertificateRefP certificate
) {
4245 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4246 0, &kCFTypeArrayCallBacks
);
4247 OSStatus status
= errSecSuccess
;
4248 if (certificate
->_subjectAltName
) {
4249 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4250 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4252 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4253 CFRelease(ntPrincipalNames
);
4254 ntPrincipalNames
= NULL
;
4256 return ntPrincipalNames
;
4259 static OSStatus
appendToRFC2253String(void *context
,
4260 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4261 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4265 ST stateOrProvinceName
4267 OU organizationalUnitName
4269 STREET streetAddress
4273 /* Prepend a + if this is not the first RDN in an RDN set.
4274 Otherwise prepend a , if this is not the first RDN. */
4276 CFStringAppend(string
, CFSTR("+"));
4277 else if (CFStringGetLength(string
)) {
4278 CFStringAppend(string
, CFSTR(","));
4281 CFStringRef label
, oid
= NULL
;
4282 /* @@@ Consider changing this to a dictionary lookup keyed by the
4283 decimal representation. */
4284 #if 0 // represent all labels as oids
4285 if (DEROidCompare(type
, &oidCommonName
)) {
4286 label
= CFSTR("CN");
4287 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4289 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4290 label
= CFSTR("ST");
4291 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4293 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4294 label
= CFSTR("OU");
4295 } else if (DEROidCompare(type
, &oidCountryName
)) {
4298 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4299 label
= CFSTR("STREET");
4300 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4301 label
= CFSTR("DC");
4302 } else if (DEROidCompare(type
, &oidUserID
)) {
4303 label
= CFSTR("UID");
4308 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4311 CFStringAppend(string
, label
);
4312 CFStringAppend(string
, CFSTR("="));
4313 CFStringRef raw
= NULL
;
4315 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4318 /* Append raw to string while escaping:
4319 a space or "#" character occurring at the beginning of the string
4320 a space character occurring at the end of the string
4321 one of the characters ",", "+", """, "\", "<", ">" or ";"
4323 CFStringInlineBuffer buffer
;
4324 CFIndex ix
, length
= CFStringGetLength(raw
);
4325 CFRange range
= { 0, length
};
4326 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4327 for (ix
= 0; ix
< length
; ++ix
) {
4328 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4330 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4331 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4332 ch
== '<' || ch
== '>' || ch
== ';' ||
4333 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4334 (ch
== '#' && ix
== 0)) {
4335 UniChar chars
[] = { '\\', ch
};
4336 CFStringAppendCharacters(string
, chars
, 2);
4338 CFStringAppendCharacters(string
, &ch
, 1);
4343 /* Append the value in hex. */
4344 CFStringAppend(string
, CFSTR("#"));
4346 for (ix
= 0; ix
< value
->length
; ++ix
)
4347 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4352 return errSecSuccess
;
4355 CFStringRef
SecCertificateCopySubjectStringP(SecCertificateRefP certificate
) {
4356 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4357 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4358 if (status
|| CFStringGetLength(string
) == 0) {
4365 static OSStatus
appendToCompanyNameString(void *context
,
4366 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4367 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4368 if (CFStringGetLength(string
) != 0)
4369 return errSecSuccess
;
4371 if (!DEROidCompare(type
, &oidOrganizationName
))
4372 return errSecSuccess
;
4375 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4377 return errSecSuccess
;
4378 CFStringAppend(string
, raw
);
4381 return errSecSuccess
;
4384 CFStringRef
SecCertificateCopyCompanyNameP(SecCertificateRefP certificate
) {
4385 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4386 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4387 appendToCompanyNameString
);
4388 if (status
|| CFStringGetLength(string
) == 0) {
4395 CFDataRef
SecDERItemCopySequenceP(DERItem
*content
) {
4396 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4397 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4398 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4400 CFDataSetLength(sequence
, sequence_length
);
4401 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4402 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
4403 require_noerr_quiet(DEREncodeLength(content
->length
,
4404 sequence_ptr
, &seq_len_length
), out
);
4405 sequence_ptr
+= seq_len_length
;
4406 memcpy(sequence_ptr
, content
->data
, content
->length
);
4409 CFReleaseSafe(sequence
);
4413 CFDataRef
SecCertificateCopyIssuerSequenceP(
4414 SecCertificateRefP certificate
) {
4415 return SecDERItemCopySequenceP(&certificate
->_issuer
);
4418 CFDataRef
SecCertificateCopySubjectSequenceP(
4419 SecCertificateRefP certificate
) {
4420 return SecDERItemCopySequenceP(&certificate
->_subject
);
4423 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithmP(
4424 SecCertificateRefP certificate
) {
4425 return &certificate
->_algId
;
4428 const DERItem
*SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate
) {
4429 return &certificate
->_pubKeyDER
;
4432 SecKeyRefP
SecCertificateCopyPublicKeyP(SecCertificateRefP certificate
) {
4433 SecKeyRefP publicKey
= NULL
;
4435 const DERAlgorithmId
*algId
=
4436 SecCertificateGetPublicKeyAlgorithmP(certificate
);
4437 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4438 if (DEROidCompare(&algId
->oid
, &oidRsa
)) {
4439 publicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
4440 keyData
->data
, keyData
->length
, kSecKeyEncodingPkcs1
);
4442 secinfo("cert", "Unsupported algorithm oid");
4449 CFDataRef
SecCertificateGetSHA1DigestP(SecCertificateRefP certificate
) {
4450 if (!certificate
->_sha1Digest
) {
4451 certificate
->_sha1Digest
=
4452 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4453 certificate
->_der
.data
, certificate
->_der
.length
);
4456 return certificate
->_sha1Digest
;
4459 CFDataRef
SecCertificateCopyIssuerSHA1DigestP(SecCertificateRefP certificate
) {
4460 CFDataRef digest
= NULL
;
4461 CFDataRef issuer
= SecCertificateCopyIssuerSequenceP(certificate
);
4463 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4464 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4470 CFDataRef
SecCertificateCopyPublicKeySHA1DigestP(SecCertificateRefP certificate
) {
4471 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4472 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4475 /* note: this function is exported with a non-P-suffix name.
4476 * since it doesn't accept or return a SecCertificateRefP type, this is OK for now.
4478 CFDataRef
SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator
,
4479 CFDataRef der_certificate
)
4481 CFDataRef result
= NULL
;
4482 SecCertificateRefP iosCertRef
= SecCertificateCreateWithDataP(allocator
, der_certificate
);
4483 if (NULL
== iosCertRef
)
4488 result
= SecCertificateCopyPublicKeySHA1DigestP(iosCertRef
);
4489 CFRelease(iosCertRef
);
4493 CFDataRef
SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate
) {
4494 if (!certificate
->_authorityKeyID
&&
4495 certificate
->_authorityKeyIdentifier
.length
) {
4496 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4497 certificate
->_authorityKeyIdentifier
.data
,
4498 certificate
->_authorityKeyIdentifier
.length
);
4501 return certificate
->_authorityKeyID
;
4504 CFDataRef
SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate
) {
4505 if (!certificate
->_subjectKeyID
&&
4506 certificate
->_subjectKeyIdentifier
.length
) {
4507 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4508 certificate
->_subjectKeyIdentifier
.data
,
4509 certificate
->_subjectKeyIdentifier
.length
);
4512 return certificate
->_subjectKeyID
;
4515 CFArrayRef
SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate
) {
4516 return certificate
->_crlDistributionPoints
;
4519 CFArrayRef
SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate
) {
4520 return certificate
->_ocspResponders
;
4523 CFArrayRef
SecCertificateGetCAIssuersP(SecCertificateRefP certificate
) {
4524 return certificate
->_caIssuers
;
4527 bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate
) {
4528 return certificate
->_subjectAltName
&&
4529 certificate
->_subjectAltName
->critical
;
4532 bool SecCertificateHasSubjectP(SecCertificateRefP certificate
) {
4533 /* Since the _subject field is the content of the subject and not the
4534 whole thing, we can simply check for a 0 length subject here. */
4535 return certificate
->_subject
.length
!= 0;
4538 bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate
) {
4539 return certificate
->_foundUnknownCriticalExtension
;
4542 /* Private API functions. */
4543 void SecCertificateShowP(SecCertificateRefP certificate
) {
4545 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4546 fprintf(stderr
, "\n");
4549 CFDictionaryRef
SecCertificateCopyAttributeDictionaryP(
4550 SecCertificateRefP certificate
) {
4551 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4552 CFNumberRef certificateType
, certificateEncoding
;
4553 CFStringRef label
, alias
;
4554 CFDataRef skid
, pubKeyDigest
, certData
;
4555 CFDictionaryRef dict
= NULL
;
4559 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4560 SInt32 ctv
= certificate
->_version
+ 1;
4561 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4562 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4563 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4564 certData
= SecCertificateCopyDataP(certificate
);
4565 skid
= SecCertificateGetSubjectKeyIDP(certificate
);
4566 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4567 certificate
->_pubKeyDER
.length
);
4569 /* We still need to figure out how to deal with multi valued attributes. */
4570 alias
= SecCertificateCopyRFC822NamesP(certificate
);
4571 label
= SecCertificateCopySubjectSummary(certificate
);
4577 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4578 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4579 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4581 DICT_ADDPAIR(kSecAttrLabel
, label
);
4583 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4584 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4585 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4586 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4588 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4589 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4590 DICT_ADDPAIR(kSecValueData
, certData
);
4591 dict
= DICT_CREATE(allocator
);
4593 CFReleaseSafe(label
);
4594 CFReleaseSafe(pubKeyDigest
);
4595 CFReleaseSafe(certData
);
4596 CFReleaseSafe(certificateEncoding
);
4597 CFReleaseSafe(certificateType
);
4602 SecCertificateRefP
SecCertificateCreateFromAttributeDictionaryP(
4603 CFDictionaryRef refAttributes
) {
4604 /* @@@ Support having an allocator in refAttributes. */
4605 CFAllocatorRef allocator
= NULL
;
4606 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4607 return SecCertificateCreateWithDataP(allocator
, data
);
4610 bool SecCertificateIsSelfSignedCAP(SecCertificateRefP certificate
) {
4611 bool result
= false;
4612 SecKeyRefP publicKey
;
4613 require(publicKey
= SecCertificateCopyPublicKeyP(certificate
), out
);
4614 CFDataRef normalizedIssuer
=
4615 SecCertificateGetNormalizedIssuerContentP(certificate
);
4616 CFDataRef normalizedSubject
=
4617 SecCertificateGetNormalizedSubjectContentP(certificate
);
4618 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4619 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4621 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyIDP(certificate
);
4622 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyIDP(certificate
);
4623 if (authorityKeyID
) {
4624 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4627 if (SecCertificateVersionP(certificate
) >= 3) {
4628 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraintsP(certificate
);
4629 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4630 require_noerr_quiet(SecCertificateIsSignedByP(certificate
, publicKey
), out
);
4635 CFReleaseSafe(publicKey
);
4639 SecKeyUsage
SecCertificateGetKeyUsageP(SecCertificateRefP certificate
) {
4640 return certificate
->_keyUsage
;
4643 CFArrayRef
SecCertificateCopyExtendedKeyUsageP(SecCertificateRefP certificate
)
4645 CFMutableArrayRef extended_key_usage_oids
=
4646 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4647 require_quiet(extended_key_usage_oids
, out
);
4649 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4650 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4651 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4652 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4655 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4656 require_noerr_quiet(drtn
, out
);
4657 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4658 DERDecodedInfo currDecoded
;
4660 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4661 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4662 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4663 currDecoded
.content
.data
, currDecoded
.content
.length
);
4665 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4669 require_quiet(drtn
== DR_EndOfSequence
, out
);
4670 return extended_key_usage_oids
;
4674 CFReleaseSafe(extended_key_usage_oids
);
4678 SecCertificateRefP
SecCertificateCreateWithPEMP(CFAllocatorRef allocator
,
4679 CFDataRef pem_certificate
)
4681 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
4682 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
4683 uint8_t *base64_data
= NULL
;
4684 SecCertificateRefP cert
= NULL
;
4685 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
4686 //const size_t length = CFDataGetLength(pem_certificate);
4687 char *begin
= strstr((const char *)data
, begin_cert
);
4688 char *end
= strstr((const char *)data
, end_cert
);
4691 begin
+= sizeof(begin_cert
) - 1;
4692 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
4693 if (base64_length
) {
4694 require_quiet(base64_data
= calloc(1, base64_length
), out
);
4695 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
4696 cert
= SecCertificateCreateWithBytesP(kCFAllocatorDefault
, base64_data
, base64_length
);
4703 static void convertCertificateToCFData(const void *value
, void *context
) {
4704 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4705 SecCertificateRefP certificate
= (SecCertificateRefP
)value
;
4706 CFDataRef data
= SecCertificateCopyDataP(certificate
);
4707 CFArrayAppendValue(result
, data
);
4711 /* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
4712 CFArrayRef
SecCertificateArrayCopyDataArrayP(CFArrayRef certificates
) {
4713 CFIndex count
= CFArrayGetCount(certificates
);
4714 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
4715 CFRange all_certs
= { 0, count
};
4716 CFArrayApplyFunction(certificates
, all_certs
, convertCertificateToCFData
, result
);
4720 /* AUDIT[securityd](done):
4721 value (ok) is an element in a caller provided array.
4723 static void convertCFDataToCertificate(const void *value
, void *context
) {
4724 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4725 CFDataRef data
= (CFDataRef
)value
;
4726 if (data
&& CFGetTypeID(data
) == CFDataGetTypeID()) {
4727 SecCertificateRefP certificate
= SecCertificateCreateWithDataP(kCFAllocatorDefault
, data
);
4729 CFArrayAppendValue(result
, certificate
);
4730 CFRelease(certificate
);
4735 /* AUDIT[securityd](done):
4736 certificates (ok) is a caller provided array, only its cf type has
4739 CFArrayRef
SecCertificateDataArrayCopyArrayP(CFArrayRef certificates
) {
4740 CFIndex count
= CFArrayGetCount(certificates
);
4741 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
4742 CFRange all_certs
= { 0, count
};
4743 CFArrayApplyFunction(certificates
, all_certs
, convertCFDataToCertificate
, result
);