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 "SecInternal.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
SecCertificateCopyDescription(CFTypeRef cf
);
220 static void SecCertificateDestroy(CFTypeRef cf
);
221 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
222 CFAbsoluteTime
*absTime
);
224 /* Static functions. */
225 static CFStringRef
SecCertificateCopyDescription(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 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
727 require_noerr_quiet(drtn
, badDER
);
728 DERSize policy_ix
= 0;
729 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
730 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
731 DERPolicyInformation pi
;
732 drtn
= DERParseSequenceContent(&piContent
.content
,
733 DERNumPolicyInformationItemSpecs
,
734 DERPolicyInformationItemSpecs
,
736 require_noerr_quiet(drtn
, badDER
);
737 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
738 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
740 certificate
->_certificatePolicies
.present
= true;
741 certificate
->_certificatePolicies
.critical
= extn
->critical
;
742 certificate
->_certificatePolicies
.numPolicies
= (uint32_t)policy_count
;
743 certificate
->_certificatePolicies
.policies
= policies
;
748 certificate
->_certificatePolicies
.present
= false;
749 secinfo("cert", "Invalid CertificatePolicies Extension");
753 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
755 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
756 issuerDomainPolicy CertPolicyId,
757 subjectDomainPolicy CertPolicyId }
760 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
761 const SecCertificateExtension
*extn
) {
762 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
765 SecCEPolicyMapping
*mappings
= NULL
;
766 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
767 require_noerr_quiet(drtn
, badDER
);
768 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
769 DERDecodedInfo pmContent
;
770 DERSize mapping_count
= 0;
771 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
772 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
775 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
777 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
778 require_noerr_quiet(drtn
, badDER
);
779 DERSize mapping_ix
= 0;
780 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
782 drtn
= DERParseSequenceContent(&pmContent
.content
,
783 DERNumPolicyMappingItemSpecs
,
784 DERPolicyMappingItemSpecs
,
786 require_noerr_quiet(drtn
, badDER
);
787 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
788 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
790 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
791 certificate
->_policyMappings
.present
= true;
792 certificate
->_policyMappings
.critical
= extn
->critical
;
793 certificate
->_policyMappings
.numMappings
= mapping_count
;
794 certificate
->_policyMappings
.mappings
= mappings
;
799 CFReleaseSafe(mappings
);
800 certificate
->_policyMappings
.present
= false;
801 secinfo("cert", "Invalid CertificatePolicies Extension");
804 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
805 const SecCertificateExtension
*extn
) {
806 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
809 CFMutableDictionaryRef mappings
= NULL
;
810 CFDataRef idp
= NULL
, sdp
= NULL
;
811 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
812 require_noerr_quiet(drtn
, badDER
);
813 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
814 DERDecodedInfo pmContent
;
815 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
816 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
818 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
819 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
821 drtn
= DERParseSequenceContent(&pmContent
.content
,
822 DERNumPolicyMappingItemSpecs
,
823 DERPolicyMappingItemSpecs
,
825 require_noerr_quiet(drtn
, badDER
);
827 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
828 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
829 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
830 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
831 CFMutableArrayRef sdps
=
832 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
834 CFArrayAppendValue(sdps
, sdp
);
836 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
837 &kCFTypeArrayCallBacks
), badDER
);
838 CFDictionarySetValue(mappings
, idp
, sdps
);
844 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
845 certificate
->_policyMappings
= mappings
;
850 CFReleaseSafe(mappings
);
851 certificate
->_policyMappings
= NULL
;
852 secinfo("cert", "Invalid CertificatePolicies Extension");
857 AuthorityKeyIdentifier ::= SEQUENCE {
858 keyIdentifier [0] KeyIdentifier OPTIONAL,
859 authorityCertIssuer [1] GeneralNames OPTIONAL,
860 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
861 -- authorityCertIssuer and authorityCertSerialNumber MUST both
862 -- be present or both be absent
864 KeyIdentifier ::= OCTET STRING
866 static void SecCEPAuthorityKeyIdentifier(SecCertificateRefP certificate
,
867 const SecCertificateExtension
*extn
) {
868 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
869 DERAuthorityKeyIdentifier akid
;
871 drtn
= DERParseSequence(&extn
->extnValue
,
872 DERNumAuthorityKeyIdentifierItemSpecs
,
873 DERAuthorityKeyIdentifierItemSpecs
,
874 &akid
, sizeof(akid
));
875 require_noerr_quiet(drtn
, badDER
);
876 if (akid
.keyIdentifier
.length
) {
877 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
879 if (akid
.authorityCertIssuer
.length
||
880 akid
.authorityCertSerialNumber
.length
) {
881 require_quiet(akid
.authorityCertIssuer
.length
&&
882 akid
.authorityCertSerialNumber
.length
, badDER
);
883 /* Perhaps put in a subsection called Authority Certificate Issuer. */
884 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
885 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
890 secinfo("cert", "Invalid AuthorityKeyIdentifier Extension");
893 static void SecCEPPolicyConstraints(SecCertificateRefP certificate
,
894 const SecCertificateExtension
*extn
) {
895 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
896 DERPolicyConstraints pc
;
898 drtn
= DERParseSequence(&extn
->extnValue
,
899 DERNumPolicyConstraintsItemSpecs
,
900 DERPolicyConstraintsItemSpecs
,
902 require_noerr_quiet(drtn
, badDER
);
903 if (pc
.requireExplicitPolicy
.length
) {
904 require_noerr_quiet(DERParseInteger(
905 &pc
.requireExplicitPolicy
,
906 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
907 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
909 if (pc
.inhibitPolicyMapping
.length
) {
910 require_noerr_quiet(DERParseInteger(
911 &pc
.inhibitPolicyMapping
,
912 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
913 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
916 certificate
->_policyConstraints
.present
= true;
917 certificate
->_policyConstraints
.critical
= extn
->critical
;
921 certificate
->_policyConstraints
.present
= false;
922 secinfo("cert", "Invalid PolicyConstraints Extension");
925 static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate
,
926 const SecCertificateExtension
*extn
) {
927 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
931 InhibitAnyPolicy ::= SkipCerts
933 SkipCerts ::= INTEGER (0..MAX)
935 static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate
,
936 const SecCertificateExtension
*extn
) {
937 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
938 require_noerr_quiet(DERParseInteger(
940 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
943 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
944 secinfo("cert", "Invalid InhibitAnyPolicy Extension");
948 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
950 AuthorityInfoAccessSyntax ::=
951 SEQUENCE SIZE (1..MAX) OF AccessDescription
953 AccessDescription ::= SEQUENCE {
954 accessMethod OBJECT IDENTIFIER,
955 accessLocation GeneralName }
957 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
959 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
961 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
963 static void SecCEPAuthorityInfoAccess(SecCertificateRefP certificate
,
964 const SecCertificateExtension
*extn
) {
965 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
968 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
969 require_noerr_quiet(drtn
, badDER
);
970 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
971 DERDecodedInfo adContent
;
972 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
973 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
974 DERAccessDescription ad
;
975 drtn
= DERParseSequenceContent(&adContent
.content
,
976 DERNumAccessDescriptionItemSpecs
,
977 DERAccessDescriptionItemSpecs
,
979 require_noerr_quiet(drtn
, badDER
);
980 CFMutableArrayRef
*urls
;
981 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
982 urls
= &certificate
->_ocspResponders
;
983 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
984 urls
= &certificate
->_caIssuers
;
988 DERDecodedInfo generalNameContent
;
989 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
990 require_noerr_quiet(drtn
, badDER
);
991 switch (generalNameContent
.tag
) {
993 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
994 /* Technically I don't think this is valid, but there are certs out
995 in the wild that use a constructed IA5String. In particular the
996 VeriSign Time Stamping Authority CA.cer does this. */
998 case ASN1_CONTEXT_SPECIFIC
| 6:
1000 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1001 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1002 kCFStringEncodingASCII
, NULL
);
1005 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1006 CFArrayAppendValue(*urls
, url
);
1012 secinfo("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1013 generalNameContent
.tag
, (int)generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1017 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1020 secinfo("cert", "failed to parse Authority Information Access extension");
1023 static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate
,
1024 const SecCertificateExtension
*extn
) {
1025 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1028 static void SecCEPNetscapeCertType(SecCertificateRefP certificate
,
1029 const SecCertificateExtension
*extn
) {
1030 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1033 static void SecCEPEntrustVersInfo(SecCertificateRefP certificate
,
1034 const SecCertificateExtension
*extn
) {
1035 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1038 /* Dictionary key callback for comparing to DERItems. */
1039 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1040 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1043 /* Dictionary key callback calculating the hash of a DERItem. */
1044 static CFHashCode
SecDERItemHash(const void *value
) {
1045 const DERItem
*derItem
= (const DERItem
*)value
;
1046 CFHashCode hash
= derItem
->length
;
1047 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1048 for (; ix
< derItem
->length
; ++ix
) {
1049 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1055 /* Dictionary key callbacks using the above 2 functions. */
1056 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1060 NULL
, /* copyDescription */
1061 SecDERItemEqual
, /* equal */
1062 SecDERItemHash
/* hash */
1065 static void SecCertificateRegisterClass(void) {
1066 static const CFRuntimeClass kSecCertificateClass
= {
1068 "SecCertificate", /* class name */
1071 SecCertificateDestroy
, /* dealloc */
1072 SecCertificateEqual
, /* equal */
1073 SecCertificateHash
, /* hash */
1074 NULL
, /* copyFormattingDesc */
1075 SecCertificateCopyDescription
/* copyDebugDesc */
1078 kSecCertificateTypeID
= _CFRuntimeRegisterClass(&kSecCertificateClass
);
1080 /* Build a dictionary that maps from extension OIDs to callback functions
1081 which can parse the extension of the type given. */
1082 static const void *extnOIDs
[] = {
1083 &oidSubjectKeyIdentifier
,
1085 &oidPrivateKeyUsagePeriod
,
1088 &oidBasicConstraints
,
1089 &oidCrlDistributionPoints
,
1090 &oidCertificatePolicies
,
1092 &oidAuthorityKeyIdentifier
,
1093 &oidPolicyConstraints
,
1094 &oidExtendedKeyUsage
,
1095 &oidInhibitAnyPolicy
,
1096 &oidAuthorityInfoAccess
,
1097 &oidSubjectInfoAccess
,
1098 &oidNetscapeCertType
,
1101 static const void *extnParsers
[] = {
1102 SecCEPSubjectKeyIdentifier
,
1104 SecCEPPrivateKeyUsagePeriod
,
1105 SecCEPSubjectAltName
,
1106 SecCEPIssuerAltName
,
1107 SecCEPBasicConstraints
,
1108 SecCEPCrlDistributionPoints
,
1109 SecCEPCertificatePolicies
,
1110 SecCEPPolicyMappings
,
1111 SecCEPAuthorityKeyIdentifier
,
1112 SecCEPPolicyConstraints
,
1113 SecCEPExtendedKeyUsage
,
1114 SecCEPInhibitAnyPolicy
,
1115 SecCEPAuthorityInfoAccess
,
1116 SecCEPSubjectInfoAccess
,
1117 SecCEPNetscapeCertType
,
1118 SecCEPEntrustVersInfo
1120 gExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1121 extnParsers
, sizeof(extnOIDs
) / sizeof(*extnOIDs
),
1122 &SecDERItemKeyCallBacks
, NULL
);
1125 /* Given the contents of an X.501 Name return the contents of a normalized
1127 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1128 const DERItem
*x501name
) {
1129 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1130 CFIndex length
= x501name
->length
;
1131 CFDataSetLength(result
, length
);
1132 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1135 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1137 require_noerr_quiet(drtn
, badDER
);
1140 /* Always points to last rdn tag. */
1141 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1142 /* Offset relative to base of current rdn set tag. */
1143 CFIndex rdnTagLocation
= 0;
1144 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1145 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1146 /* We don't allow empty RDNs. */
1147 require_quiet(rdn
.content
.length
!= 0, badDER
);
1148 /* Length of the tag and length of the current rdn. */
1149 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1150 CFIndex rdnContentLength
= rdn
.content
.length
;
1151 /* Copy the tag and length of the RDN. */
1152 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1155 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1157 /* Always points to tag of current atv sequence. */
1158 const DERByte
*atvTag
= atvSeq
.nextItem
;
1159 /* Offset relative to base of current atv sequence tag. */
1160 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1161 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1162 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1163 /* Length of the tag and length of the current atv. */
1164 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1165 CFIndex atvContentLength
= atv
.content
.length
;
1166 /* Copy the tag and length of the atv and the atv itself. */
1167 memcpy(base
+ atvTagLocation
, atvTag
,
1168 atvTLLength
+ atv
.content
.length
);
1170 /* Now decode the atv sequence. */
1171 DERAttributeTypeAndValue atvPair
;
1172 drtn
= DERParseSequenceContent(&atv
.content
,
1173 DERNumAttributeTypeAndValueItemSpecs
,
1174 DERAttributeTypeAndValueItemSpecs
,
1175 &atvPair
, sizeof(atvPair
));
1176 require_noerr_quiet(drtn
, badDER
);
1177 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1178 DERDecodedInfo value
;
1179 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1180 require_noerr_quiet(drtn
, badDER
);
1182 /* (c) attribute values in PrintableString are not case sensitive
1183 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1185 (d) attribute values in PrintableString are compared after
1186 removing leading and trailing white space and converting internal
1187 substrings of one or more consecutive white space characters to a
1189 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1190 /* Offset relative to base of current value tag. */
1191 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1192 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1193 CFIndex valueContentLength
= value
.content
.length
;
1195 /* Now copy all the bytes, but convert to upper case while
1196 doing so and convert multiple whitespace chars into a
1198 bool lastWasBlank
= false;
1199 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1200 CFIndex valueCurrentLocation
= valueLocation
;
1202 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1203 UInt8 ch
= value
.content
.data
[ix
];
1208 /* Don't insert a space for first character
1210 if (valueCurrentLocation
> valueLocation
) {
1211 base
[valueCurrentLocation
++] = ' ';
1213 lastWasBlank
= true;
1216 lastWasBlank
= false;
1217 if ('a' <= ch
&& ch
<= 'z') {
1218 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1220 base
[valueCurrentLocation
++] = ch
;
1224 /* Finally if lastWasBlank remove the trailing space. */
1225 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1226 valueCurrentLocation
--;
1228 /* Adjust content length to normalized length. */
1229 valueContentLength
= valueCurrentLocation
- valueLocation
;
1231 /* Number of bytes by which the length should be shorted. */
1232 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1233 if (lengthDiff
== 0) {
1234 /* Easy case no need to adjust lengths. */
1236 /* Hard work we need to go back and fix up length fields
1238 1) The value itself.
1239 2) The ATV Sequence containing type/value
1240 3) The RDN Set containing one or more atv pairs.
1244 /* Step 1 fix up length of value. */
1245 /* Length of value tag and length minus the tag. */
1246 DERSize newValueTLLength
= valueTLLength
- 1;
1247 drtn
= DEREncodeLength(valueContentLength
,
1248 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1249 /* Add the length of the tag back in. */
1251 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1253 /* The size of the length field changed, let's slide
1254 the value back by valueLLDiff bytes. */
1255 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1256 base
+ valueTagLocation
+ valueTLLength
,
1257 valueContentLength
);
1258 /* The length diff for the enclosing object. */
1259 lengthDiff
+= valueLLDiff
;
1262 /* Step 2 fix up length of the enclosing ATV Sequence. */
1263 atvContentLength
-= lengthDiff
;
1264 DERSize newATVTLLength
= atvTLLength
- 1;
1265 drtn
= DEREncodeLength(atvContentLength
,
1266 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1267 /* Add the length of the tag back in. */
1269 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1271 /* The size of the length field changed, let's slide
1272 the value back by valueLLDiff bytes. */
1273 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1274 base
+ atvTagLocation
+ atvTLLength
,
1276 /* The length diff for the enclosing object. */
1277 lengthDiff
+= atvLLDiff
;
1278 atvTLLength
= newATVTLLength
;
1281 /* Step 3 fix up length of enclosing RDN Set. */
1282 rdnContentLength
-= lengthDiff
;
1283 DERSize newRDNTLLength
= rdnTLLength
- 1;
1284 drtn
= DEREncodeLength(rdnContentLength
,
1285 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1286 /* Add the length of the tag back in. */
1288 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1290 /* The size of the length field changed, let's slide
1291 the value back by valueLLDiff bytes. */
1292 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1293 base
+ rdnTagLocation
+ rdnTLLength
,
1295 /* The length diff for the enclosing object. */
1296 lengthDiff
+= rdnLLDiff
;
1297 rdnTLLength
= newRDNTLLength
;
1299 /* Adjust the locations that might have changed due to
1301 atvTagLocation
-= rdnLLDiff
;
1305 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1306 atvTag
= atvSeq
.nextItem
;
1308 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1309 rdnTag
= rdnSeq
.nextItem
;
1311 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1312 /* Truncate the result to the proper length. */
1313 CFDataSetLength(result
, rdnTagLocation
);
1322 /* AUDIT[securityd]:
1323 certificate->_der is a caller provided data of any length (might be 0).
1325 Top level certificate decode.
1327 static bool SecCertificateParse(SecCertificateRefP certificate
)
1332 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1334 /* top level decode */
1335 DERSignedCertCrl signedCert
;
1336 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1337 DERSignedCertCrlItemSpecs
, &signedCert
,
1338 sizeof(signedCert
));
1339 require_noerr_quiet(drtn
, badCert
);
1340 /* Store tbs since we need to digest it for verification later on. */
1341 certificate
->_tbs
= signedCert
.tbs
;
1343 /* decode the TBSCert - it was saved in full DER form */
1345 drtn
= DERParseSequence(&signedCert
.tbs
,
1346 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1347 &tbsCert
, sizeof(tbsCert
));
1348 require_noerr_quiet(drtn
, badCert
);
1350 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1351 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1352 of the params field. */
1353 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1354 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1355 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1356 require_noerr_quiet(drtn
, badCert
);
1358 /* The contents of signedCert.sig is a bit string whose contents
1359 are the signature itself. */
1360 DERByte numUnusedBits
;
1361 drtn
= DERParseBitString(&signedCert
.sig
,
1362 &certificate
->_signature
, &numUnusedBits
);
1363 require_noerr_quiet(drtn
, badCert
);
1365 /* Now decode the tbsCert. */
1367 /* First we turn the optional version into an int. */
1368 if (tbsCert
.version
.length
) {
1369 DERDecodedInfo decoded
;
1370 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1371 require_noerr_quiet(drtn
, badCert
);
1372 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1373 require_quiet(decoded
.content
.length
== 1, badCert
);
1374 certificate
->_version
= decoded
.content
.data
[0];
1375 require_quiet(certificate
->_version
> 0, badCert
);
1376 require_quiet(certificate
->_version
< 3, badCert
);
1378 certificate
->_version
= 0;
1381 /* The serial number is in the tbsCert.serialNum - it was saved in
1382 INTEGER form without the tag and length. */
1383 certificate
->_serialNum
= tbsCert
.serialNum
;
1384 certificate
->_serialNumber
= CFDataCreate(allocator
,
1385 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1387 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1388 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1389 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1390 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1391 require_noerr_quiet(drtn
, badCert
);
1393 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1394 and length fields. */
1395 certificate
->_issuer
= tbsCert
.issuer
;
1396 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1399 /* sequence we're given: decode the tbsCerts Validity sequence. */
1400 DERValidity validity
;
1401 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1402 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1403 &validity
, sizeof(validity
));
1404 require_noerr_quiet(drtn
, badCert
);
1405 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1406 &certificate
->_notBefore
), badCert
);
1407 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1408 &certificate
->_notAfter
), badCert
);
1410 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1411 and length fields. */
1412 certificate
->_subject
= tbsCert
.subject
;
1413 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1416 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1417 DERSubjPubKeyInfo pubKeyInfo
;
1418 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1419 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1420 &pubKeyInfo
, sizeof(pubKeyInfo
));
1421 require_noerr_quiet(drtn
, badCert
);
1423 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1424 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1425 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1426 &certificate
->_algId
, sizeof(certificate
->_algId
));
1427 require_noerr_quiet(drtn
, badCert
);
1429 /* Now we can figure out the key's algorithm id and params based on
1430 certificate->_algId.oid. */
1432 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1433 are a PKCS1 format RSA key. */
1434 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1435 &certificate
->_pubKeyDER
, &numUnusedBits
);
1436 require_noerr_quiet(drtn
, badCert
);
1438 /* The contents of tbsCert.issuerID is a bit string. */
1439 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1441 /* The contents of tbsCert.subjectID is a bit string. */
1442 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1445 if (tbsCert
.extensions
.length
) {
1446 CFIndex extensionCount
= 0;
1449 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1451 require_noerr_quiet(drtn
, badCert
);
1452 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1453 DERDecodedInfo currDecoded
;
1454 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1456 /* ! = MUST recognize ? = SHOULD recognize
1459 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1460 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1461 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1462 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1463 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1464 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1465 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1466 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1468 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1469 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1470 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1471 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1472 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1473 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1474 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1475 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1477 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1478 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1483 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1485 /* Put some upper limit on the number of extentions allowed. */
1486 require_quiet(extensionCount
< 10000, badCert
);
1487 certificate
->_extensionCount
= extensionCount
;
1488 certificate
->_extensions
=
1489 malloc(sizeof(SecCertificateExtension
) * extensionCount
);
1492 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1493 require_noerr_quiet(drtn
, badCert
);
1494 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1495 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1496 require_quiet(drtn
== DR_Success
||
1497 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1498 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1500 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1501 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1502 &extn
, sizeof(extn
));
1503 require_noerr_quiet(drtn
, badCert
);
1504 /* Copy stuff into certificate->extensions[ix]. */
1505 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1506 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1507 &certificate
->_extensions
[ix
].critical
), badCert
);
1508 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1510 SecCertificateExtensionParser parser
=
1511 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1512 gExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1514 /* Invoke the parser. */
1515 parser(certificate
, &certificate
->_extensions
[ix
]);
1516 } else if (certificate
->_extensions
[ix
].critical
) {
1517 secinfo("cert", "Found unknown critical extension");
1518 certificate
->_foundUnknownCriticalExtension
= true;
1520 secinfo("cert", "Found unknown non critical extension");
1532 /* Public API functions. */
1533 CFTypeID
SecCertificateGetTypeIDP(void) {
1534 pthread_once(&kSecCertificateRegisterClass
, SecCertificateRegisterClass
);
1535 return kSecCertificateTypeID
;
1538 SecCertificateRefP
SecCertificateCreateWithBytesP(CFAllocatorRef allocator
,
1539 const UInt8
*der_bytes
, CFIndex der_length
) {
1542 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1543 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1544 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1546 memset((char*)result
+ sizeof(result
->_base
), 0,
1547 sizeof(*result
) - sizeof(result
->_base
));
1548 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1549 result
->_der
.length
= der_length
;
1550 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1551 if (!SecCertificateParse(result
)) {
1559 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1560 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1561 const UInt8
*der_bytes
, CFIndex der_length
);
1563 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1564 const UInt8
*der_bytes
, CFIndex der_length
) {
1565 return SecCertificateCreateWithBytesP(allocator
, der_bytes
, der_length
);
1567 /* @@@ End of placeholder. */
1569 /* AUDIT[securityd](done):
1570 der_certificate is a caller provided data of any length (might be 0), only
1571 its cf type has been checked.
1573 SecCertificateRefP
SecCertificateCreateWithDataP(CFAllocatorRef allocator
,
1574 CFDataRef der_certificate
) {
1575 check(der_certificate
);
1576 CFIndex size
= sizeof(struct __SecCertificate
);
1577 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1578 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1580 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1581 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1582 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1583 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1584 if (!SecCertificateParse(result
)) {
1592 CFDataRef
SecCertificateCopyDataP(SecCertificateRefP certificate
) {
1595 if (certificate
->_der_data
) {
1596 CFRetain(certificate
->_der_data
);
1597 result
= certificate
->_der_data
;
1599 result
= CFDataCreate(CFGetAllocator(certificate
),
1600 certificate
->_der
.data
, certificate
->_der
.length
);
1602 /* FIXME: If we wish to cache result we need to lock the certificate.
1603 Also this create 2 copies of the certificate data which is somewhat
1606 certificate
->_der_data
= result
;
1613 CFIndex
SecCertificateGetLengthP(SecCertificateRefP certificate
) {
1614 return certificate
->_der
.length
;
1617 const UInt8
*SecCertificateGetBytePtrP(SecCertificateRefP certificate
) {
1618 return certificate
->_der
.data
;
1621 /* From rfc3280 - Appendix B. ASN.1 Notes
1623 Object Identifiers (OIDs) are used throughout this specification to
1624 identify certificate policies, public key and signature algorithms,
1625 certificate extensions, etc. There is no maximum size for OIDs.
1626 This specification mandates support for OIDs which have arc elements
1627 with values that are less than 2^28, that is, they MUST be between 0
1628 and 268,435,455, inclusive. This allows each arc element to be
1629 represented within a single 32 bit word. Implementations MUST also
1630 support OIDs where the length of the dotted decimal (see [RFC 2252],
1631 section 4.1) string representation can be up to 100 bytes
1632 (inclusive). Implementations MUST be able to handle OIDs with up to
1633 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1634 contain OIDs that exceed these requirements. Likewise, CRL issuers
1635 SHOULD NOT issue CRLs which contain OIDs that exceed these
1639 /* Oids longer than this are considered invalid. */
1640 #define MAX_OID_SIZE 32
1642 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1643 const DERItem
*oid
) {
1645 if (oid
->length
== 0) {
1646 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1647 CFSTR("SecCertificate"));
1649 if (oid
->length
> MAX_OID_SIZE
) {
1650 return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"),
1651 CFSTR("SecCertificate"));
1654 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1656 // The first two levels are encoded into one byte, since the root level
1657 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1658 // y may be > 39, so we have to add special-case handling for this.
1659 uint32_t x
= oid
->data
[0] / 40;
1660 uint32_t y
= oid
->data
[0] % 40;
1663 // Handle special case for large y if x = 2
1667 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1670 for (x
= 1; x
< oid
->length
; ++x
)
1672 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1673 /* @@@ value may not span more than 4 bytes. */
1674 /* A max number of 20 values is allowed. */
1675 if (!(oid
->data
[x
] & 0x80))
1677 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), (unsigned long)value
);
1684 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1685 const DERItem
*oid
) {
1686 if (oid
->length
== 0) {
1687 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1688 CFSTR("SecCertificate"));
1691 /* Build the key we use to lookup the localized OID description. */
1692 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1693 oid
->length
* 3 + 5);
1694 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), (unsigned long)oid
->length
);
1696 for (ix
= 0; ix
< oid
->length
; ++ix
)
1697 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1699 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1700 if (CFEqual(oidKey
, name
)) {
1702 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1709 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1710 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1711 have a length of exactly 4 or 16 octects. */
1712 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1713 const DERItem
*ip
) {
1714 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1715 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1716 16 octects addr, or 32 octects addr/mask. */
1717 CFStringRef value
= NULL
;
1718 if (ip
->length
== 4) {
1719 value
= CFStringCreateWithFormat(allocator
, NULL
,
1720 CFSTR("%u.%u.%u.%u"),
1721 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1722 } else if (ip
->length
== 16) {
1723 value
= CFStringCreateWithFormat(allocator
, NULL
,
1724 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1725 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1726 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
1727 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
1728 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
1729 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
1736 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
1737 const DERItem
*oid
) {
1738 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1739 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
1740 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
1741 CFSTR("%@ (%@)"), name
, decimal
);
1748 void appendPropertyP(CFMutableArrayRef properties
,
1749 CFStringRef propertyType
, CFStringRef label
, CFTypeRef value
) {
1750 CFDictionaryRef property
;
1752 CFStringRef localizedLabel
= SecFrameworkCopyLocalizedString(label
,
1753 CFSTR("SecCertificate"));
1754 const void *all_keys
[4];
1755 all_keys
[0] = kSecPropertyKeyType
;
1756 all_keys
[1] = kSecPropertyKeyLabel
;
1757 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
1758 all_keys
[3] = kSecPropertyKeyValue
;
1759 const void *property_values
[] = {
1765 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1766 all_keys
, property_values
, value
? 4 : 3,
1767 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1768 CFRelease(localizedLabel
);
1770 const void *nolabel_keys
[2];
1771 nolabel_keys
[0] = kSecPropertyKeyType
;
1772 nolabel_keys
[1] = kSecPropertyKeyValue
;
1773 const void *property_values
[] = {
1777 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1778 nolabel_keys
, property_values
, 2,
1779 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1782 CFArrayAppendValue(properties
, property
);
1783 CFRelease(property
);
1787 #define UTC_TIME_NOSEC_ZULU_LEN 11
1789 #define UTC_TIME_ZULU_LEN 13
1790 /* YYMMDDhhmmssThhmm */
1791 #define UTC_TIME_LOCALIZED_LEN 17
1792 /* YYYYMMDDhhmmssZ */
1793 #define GENERALIZED_TIME_ZULU_LEN 15
1794 /* YYYYMMDDhhmmssThhmm */
1795 #define GENERALIZED_TIME_LOCALIZED_LEN 19
1797 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
1799 static inline SInt32
parseDecimalPair(const DERByte
**p
) {
1800 const DERByte
*cp
= *p
;
1802 return 10 * (cp
[0] - '0') + cp
[1] - '0';
1805 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1806 true if the date was valid and properly decoded, also return the result in
1807 absTime. Return false otherwise. */
1808 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
1814 bool isUtcLength
= false;
1815 bool isLocalized
= false;
1816 bool noSeconds
= false;
1818 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
1822 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
1825 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
1827 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
1830 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
1833 default: /* unknown format */
1837 /* Make sure the der tag fits the thing inside it. */
1838 if (tag
== ASN1_UTC_TIME
) {
1841 } else if (tag
== ASN1_GENERALIZED_TIME
) {
1848 const DERByte
*cp
= bytes
;
1849 /* Check that all characters are digits, except if localized the timezone
1850 indicator or if not localized the 'Z' at the end. */
1852 for (ix
= 0; ix
< length
; ++ix
) {
1853 if (!(isdigit(cp
[ix
]))) {
1854 if ((isLocalized
&& ix
== length
- 5 &&
1855 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
1856 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
1863 /* Initialize the fields in a gregorian date struct. */
1864 CFGregorianDate gdate
;
1866 SInt32 year
= parseDecimalPair(&cp
);
1868 /* 0 <= year < 50 : assume century 21 */
1869 gdate
.year
= 2000 + year
;
1870 } else if (year
< 70) {
1871 /* 50 <= year < 70 : illegal per PKIX */
1874 /* 70 < year <= 99 : assume century 20 */
1875 gdate
.year
= 1900 + year
;
1878 gdate
.year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
1880 gdate
.month
= parseDecimalPair(&cp
);
1881 gdate
.day
= parseDecimalPair(&cp
);
1882 gdate
.hour
= parseDecimalPair(&cp
);
1883 gdate
.minute
= parseDecimalPair(&cp
);
1887 gdate
.second
= parseDecimalPair(&cp
);
1890 CFTimeInterval timeZoneOffset
= 0;
1892 /* ZONE INDICATOR */
1893 SInt32 multiplier
= *cp
++ == '+' ? 60 : -60;
1894 timeZoneOffset
= multiplier
*
1895 (parseDecimalPair(&cp
) + 60 * parseDecimalPair(&cp
));
1900 secinfo("dateparse",
1901 "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f",
1902 (int)length
, bytes
, (int)gdate
.year
, gdate
.month
,
1903 gdate
.day
, gdate
.hour
, gdate
.minute
, gdate
.second
,
1904 timeZoneOffset
/ 60);
1906 if (!CFGregorianDateIsValid(gdate
, kCFGregorianAllUnits
))
1908 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
,
1912 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(gdate
, timeZone
);
1913 CFRelease(timeZone
);
1917 static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
1918 CFAbsoluteTime
*pabsTime
) {
1919 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
1921 if (absTime
== NULL_TIME
)
1924 *pabsTime
= absTime
;
1928 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1929 true if the date was valid and properly decoded, also return the result in
1930 absTime. Return false otherwise. */
1931 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
1932 CFAbsoluteTime
*absTime
) {
1935 if (dateChoice
->length
== 0)
1938 DERDecodedInfo decoded
;
1939 if (DERDecodeItem(dateChoice
, &decoded
))
1942 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
1946 static void appendDataProperty(CFMutableArrayRef properties
,
1947 CFStringRef label
, const DERItem
*der_data
) {
1948 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
1949 der_data
->data
, der_data
->length
);
1950 appendPropertyP(properties
, kSecPropertyTypeData
, label
, data
);
1954 static void appendUnparsedProperty(CFMutableArrayRef properties
,
1955 CFStringRef label
, const DERItem
*der_data
) {
1956 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1957 NULL
, CFSTR("Unparsed %@"), label
);
1958 appendDataProperty(properties
, newLabel
, der_data
);
1959 CFRelease(newLabel
);
1962 static void appendInvalidProperty(CFMutableArrayRef properties
,
1963 CFStringRef label
, const DERItem
*der_data
) {
1964 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1965 NULL
, CFSTR("Invalid %@"), label
);
1966 appendDataProperty(properties
, newLabel
, der_data
);
1967 CFRelease(newLabel
);
1970 static void appendDateContentProperty(CFMutableArrayRef properties
,
1971 CFStringRef label
, DERTag tag
, const DERItem
*dateContent
) {
1972 CFAbsoluteTime absTime
;
1973 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
1974 /* Date decode failure insert hex bytes instead. */
1975 return appendInvalidProperty(properties
, label
, dateContent
);
1977 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1978 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1982 static void appendDateProperty(CFMutableArrayRef properties
,
1983 CFStringRef label
, CFAbsoluteTime absTime
) {
1984 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1985 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1989 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
1990 CFStringRef label
, const DERItem
*ip
) {
1992 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
1994 appendPropertyP(properties
, kSecPropertyTypeString
, label
, value
);
1997 appendUnparsedProperty(properties
, label
, ip
);
2001 static void appendURLContentProperty(CFMutableArrayRef properties
,
2002 CFStringRef label
, const DERItem
*urlContent
) {
2003 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2004 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2006 appendPropertyP(properties
, kSecPropertyTypeURL
, label
, url
);
2009 appendInvalidProperty(properties
, label
, urlContent
);
2013 static void appendURLProperty(CFMutableArrayRef properties
,
2014 CFStringRef label
, const DERItem
*url
) {
2015 DERDecodedInfo decoded
;
2018 drtn
= DERDecodeItem(url
, &decoded
);
2019 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2020 appendInvalidProperty(properties
, label
, url
);
2022 appendURLContentProperty(properties
, label
, &decoded
.content
);
2026 static void appendOIDProperty(CFMutableArrayRef properties
,
2027 CFStringRef label
, const DERItem
*oid
) {
2028 CFStringRef oid_string
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2030 appendPropertyP(properties
, kSecPropertyTypeString
, label
, oid_string
);
2031 CFRelease(oid_string
);
2034 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2035 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2036 CFMutableArrayRef alg_props
=
2037 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2038 &kCFTypeArrayCallBacks
);
2039 appendOIDProperty(alg_props
, CFSTR("Algorithm"), &algorithm
->oid
);
2040 if (algorithm
->params
.length
) {
2041 if (algorithm
->params
.length
== 2 &&
2042 algorithm
->params
.data
[0] == ASN1_NULL
&&
2043 algorithm
->params
.data
[1] == 0) {
2044 /* @@@ Localize <NULL> or perhaps skip it? */
2045 appendPropertyP(alg_props
, kSecPropertyTypeString
,
2046 CFSTR("Parameters"), CFSTR("none"));
2048 appendUnparsedProperty(alg_props
, CFSTR("Parameters"),
2049 &algorithm
->params
);
2052 appendPropertyP(properties
, kSecPropertyTypeSection
, label
, alg_props
);
2053 CFRelease(alg_props
);
2056 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2057 const DERItem
*blob
) {
2058 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2059 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2060 blob
->length
* 3 - 1);
2061 for (ix
= 0; ix
< length
; ++ix
)
2063 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2065 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2070 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2071 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2072 CFStringRef blobFormat
= SecFrameworkCopyLocalizedString(
2073 CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate")
2074 /*, "format string for encoded field data (e.g. Sequence; 128 bytes; "
2075 "data = 00 00 ...)" */);
2076 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2077 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2078 blobFormat
, blobType
, blob
->length
, quanta
, hex
);
2080 CFRelease(blobFormat
);
2085 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2086 const DERItem
*string
, CFStringEncoding encoding
,
2087 bool printableOnly
) {
2088 /* Strip potential bogus trailing zero from printable strings. */
2089 DERSize length
= string
->length
;
2090 if (length
&& string
->data
[length
- 1] == 0) {
2091 /* Don't mess with the length of UTF16 strings though. */
2092 if (encoding
!= kCFStringEncodingUTF16
)
2095 /* A zero length string isn't considered printable. */
2096 if (!length
&& printableOnly
)
2099 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2100 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2101 passing false makes it treat it as native endian by default. */
2102 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2103 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2107 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2110 /* From rfc3280 - Appendix B. ASN.1 Notes
2112 CAs MUST force the serialNumber to be a non-negative integer, that
2113 is, the sign bit in the DER encoding of the INTEGER value MUST be
2114 zero - this can be done by adding a leading (leftmost) `00'H octet if
2115 necessary. This removes a potential ambiguity in mapping between a
2116 string of octets and an integer value.
2118 As noted in section 4.1.2.2, serial numbers can be expected to
2119 contain long integers. Certificate users MUST be able to handle
2120 serialNumber values up to 20 octets in length. Conformant CAs MUST
2121 NOT use serialNumber values longer than 20 octets.
2124 /* Return the given numeric data as a string: decimal up to 64 bits,
2126 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2127 const DERItem
*integer
) {
2129 CFIndex ix
, length
= integer
->length
;
2131 if (length
== 0 || length
> 8)
2132 return copyHexDescription(allocator
, integer
);
2134 for(ix
= 0; ix
< length
; ++ix
) {
2136 value
+= integer
->data
[ix
];
2139 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2142 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2143 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2147 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2148 case ASN1_PRINTABLE_STRING
:
2149 case ASN1_IA5_STRING
:
2150 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2151 case ASN1_UTF8_STRING
:
2152 case ASN1_GENERAL_STRING
:
2153 case ASN1_UNIVERSAL_STRING
:
2154 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2155 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2156 case ASN1_VIDEOTEX_STRING
: // 21
2157 case ASN1_VISIBLE_STRING
: // 26
2158 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2159 case ASN1_BMP_STRING
: // 30
2160 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2161 case ASN1_OCTET_STRING
:
2162 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Byte string"), CFSTR("bytes"),
2164 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2165 case ASN1_BIT_STRING
:
2166 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Bit string"), CFSTR("bits"),
2168 case (DERByte
)ASN1_CONSTR_SEQUENCE
:
2169 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Sequence"), CFSTR("bytes"),
2171 case (DERByte
)ASN1_CONSTR_SET
:
2172 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Set"), CFSTR("bytes"),
2174 case ASN1_OBJECT_ID
:
2175 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2178 /* "format string for undisplayed field data with a given DER tag" */
2179 return printableOnly
? NULL
: CFStringCreateWithFormat(allocator
, NULL
,
2180 CFSTR("not displayed (tag = %llu; length %d)"),
2181 tag
, (int)derThing
->length
);
2185 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2186 const DERItem
*derThing
, bool printableOnly
) {
2187 DERDecodedInfo decoded
;
2190 drtn
= DERDecodeItem(derThing
, &decoded
);
2192 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2194 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2195 &decoded
.content
, false);
2199 static void appendDERThingProperty(CFMutableArrayRef properties
,
2200 CFStringRef label
, const DERItem
*derThing
) {
2201 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2203 appendPropertyP(properties
, kSecPropertyTypeString
, label
, value
);
2204 CFReleaseSafe(value
);
2207 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2208 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2209 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2211 /* If there is more than one value pair we create a subsection for the
2212 second pair, and append things to the subsection for subsequent
2214 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2215 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2217 /* Since this is the second rdn pair for a given rdn, we setup a
2218 new subsection for this rdn. We remove the first property
2219 from the properties array and make it the first element in the
2220 subsection instead. */
2221 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2222 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2223 CFArrayAppendValue(rdn_props
, lastValue
);
2224 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2225 appendPropertyP(properties
, kSecPropertyTypeSection
, NULL
, rdn_props
);
2226 properties
= rdn_props
;
2228 /* Since this is the third or later rdn pair we have already
2229 created a subsection in the top level properties array. Instead
2230 of appending to that directly we append to the array inside the
2232 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2233 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2237 /* Finally we append the new rdn value to the property array. */
2238 CFStringRef label
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2241 appendDERThingProperty(properties
, label
, rdnValue
);
2243 return errSecSuccess
;
2245 return errSecInvalidCertificate
;
2249 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2250 const DERItem
*rdnSetContent
) {
2251 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2252 &kCFTypeArrayCallBacks
);
2253 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2256 CFArrayRemoveAllValues(properties
);
2257 appendInvalidProperty(properties
, CFSTR("RDN"), rdnSetContent
);
2264 From rfc3739 - 3.1.2. Subject
2266 When parsing the subject here are some tips for a short name of the cert.
2267 Choice I: commonName
2268 Choice II: givenName
2269 Choice III: pseudonym
2271 The commonName attribute value SHALL, when present, contain a name
2272 of the subject. This MAY be in the subject's preferred
2273 presentation format, or a format preferred by the CA, or some
2274 other format. Pseudonyms, nicknames, and names with spelling
2275 other than defined by the registered name MAY be used. To
2276 understand the nature of the name presented in commonName,
2277 complying applications MAY have to examine present values of the
2278 givenName and surname attributes, or the pseudonym attribute.
2281 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2282 const DERItem
*x501NameContent
) {
2283 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2284 &kCFTypeArrayCallBacks
);
2285 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2288 CFArrayRemoveAllValues(properties
);
2289 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501NameContent
);
2295 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2296 const DERItem
*x501Name
) {
2297 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2298 &kCFTypeArrayCallBacks
);
2299 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2301 CFArrayRemoveAllValues(properties
);
2302 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501Name
);
2308 static void appendIntegerProperty(CFMutableArrayRef properties
,
2309 CFStringRef label
, const DERItem
*integer
) {
2310 CFStringRef string
= copyIntegerContentDescription(
2311 CFGetAllocator(properties
), integer
);
2312 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2316 static void appendBoolProperty(CFMutableArrayRef properties
,
2317 CFStringRef label
, bool boolean
) {
2318 appendPropertyP(properties
, kSecPropertyTypeString
,
2319 label
, boolean
? CFSTR("Yes") : CFSTR("No"));
2322 static void appendBooleanProperty(CFMutableArrayRef properties
,
2323 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2325 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2327 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2328 appendInvalidProperty(properties
, label
, boolean
);
2330 appendBoolProperty(properties
, label
, result
);
2334 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2335 CFStringRef label
, const DERItem
*bitStringContent
,
2336 const CFStringRef
*names
, CFIndex namesCount
) {
2337 DERSize len
= bitStringContent
->length
- 1;
2338 require_quiet(len
== 1 || len
== 2, badDER
);
2339 DERByte numUnusedBits
= bitStringContent
->data
[0];
2340 require_quiet(numUnusedBits
< 8, badDER
);
2341 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2342 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2343 uint_fast16_t value
= bitStringContent
->data
[1];
2346 value
= (value
<< 8) + bitStringContent
->data
[2];
2352 bool didOne
= false;
2353 CFMutableStringRef string
=
2354 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2355 for (ix
= 0; ix
< bits
; ++ix
) {
2358 CFStringAppend(string
, CFSTR(", "));
2362 CFStringAppend(string
, names
[ix
]);
2366 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2370 appendInvalidProperty(properties
, label
, bitStringContent
);
2373 static void appendBitStringNames(CFMutableArrayRef properties
,
2374 CFStringRef label
, const DERItem
*bitString
,
2375 const CFStringRef
*names
, CFIndex namesCount
) {
2376 DERDecodedInfo bitStringContent
;
2377 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2378 require_noerr_quiet(drtn
, badDER
);
2379 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2380 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2384 appendInvalidProperty(properties
, label
, bitString
);
2388 typedef uint16_t SecKeyUsage
;
2390 #define kSecKeyUsageDigitalSignature 0x8000
2391 #define kSecKeyUsageNonRepudiation 0x4000
2392 #define kSecKeyUsageKeyEncipherment 0x2000
2393 #define kSecKeyUsageDataEncipherment 0x1000
2394 #define kSecKeyUsageKeyAgreement 0x0800
2395 #define kSecKeyUsageKeyCertSign 0x0400
2396 #define kSecKeyUsageCRLSign 0x0200
2397 #define kSecKeyUsageEncipherOnly 0x0100
2398 #define kSecKeyUsageDecipherOnly 0x0080
2401 KeyUsage ::= BIT STRING {
2402 digitalSignature (0),
2404 keyEncipherment (2),
2405 dataEncipherment (3),
2412 static void appendKeyUsage(CFMutableArrayRef properties
,
2413 const DERItem
*extnValue
) {
2414 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2415 extnValue
->data
[0] != ASN1_BIT_STRING
||
2416 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2417 extnValue
->data
[2] > 7) {
2418 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2421 CFMutableStringRef string
=
2422 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2423 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2424 if (extnValue
->length
== 5)
2425 usage
+= extnValue
->data
[4];
2426 secinfo("keyusage", "keyusage: %04X", usage
);
2427 static const CFStringRef usageNames
[] = {
2428 CFSTR("Digital Signature"),
2429 CFSTR("Non-Repudiation"),
2430 CFSTR("Key Encipherment"),
2431 CFSTR("Data Encipherment"),
2432 CFSTR("Key Agreement"),
2438 bool didOne
= false;
2439 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2440 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2441 for (ix
= 0; ix
< bits
; ++ix
) {
2444 CFStringAppend(string
, CFSTR(", "));
2448 /* @@@ Localize usageNames[ix]. */
2449 CFStringAppend(string
, usageNames
[ix
]);
2453 appendPropertyP(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2459 static void appendKeyUsage(CFMutableArrayRef properties
,
2460 const DERItem
*extnValue
) {
2461 static const CFStringRef usageNames
[] = {
2462 CFSTR("Digital Signature"),
2463 CFSTR("Non-Repudiation"),
2464 CFSTR("Key Encipherment"),
2465 CFSTR("Data Encipherment"),
2466 CFSTR("Key Agreement"),
2469 CFSTR("Encipher Only"),
2470 CFSTR("Decipher Only")
2472 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
2473 usageNames
, sizeof(usageNames
) / sizeof(*usageNames
));
2477 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2478 const DERItem
*extnValue
) {
2479 DERPrivateKeyUsagePeriod pkup
;
2480 DERReturn drtn
= DERParseSequence(extnValue
,
2481 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2482 &pkup
, sizeof(pkup
));
2483 require_noerr_quiet(drtn
, badDER
);
2484 if (pkup
.notBefore
.length
) {
2485 appendDateContentProperty(properties
, CFSTR("Not Valid Before"),
2486 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2488 if (pkup
.notAfter
.length
) {
2489 appendDateContentProperty(properties
, CFSTR("Not Valid After"),
2490 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2494 appendInvalidProperty(properties
, CFSTR("Private Key Usage Period"),
2498 static void appendStringContentProperty(CFMutableArrayRef properties
,
2499 CFStringRef label
, const DERItem
*stringContent
,
2500 CFStringEncoding encoding
) {
2501 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2502 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2504 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2507 appendInvalidProperty(properties
, label
, stringContent
);
2512 OtherName ::= SEQUENCE {
2513 type-id OBJECT IDENTIFIER,
2514 value [0] EXPLICIT ANY DEFINED BY type-id }
2516 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2517 const DERItem
*otherNameContent
) {
2519 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2520 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2522 require_noerr_quiet(drtn
, badDER
);
2523 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2524 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
,
2525 &on
.typeIdentifier
);
2526 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2528 appendPropertyP(properties
, kSecPropertyTypeString
, oid_string
,
2531 appendUnparsedProperty(properties
, oid_string
, &on
.value
);
2535 appendInvalidProperty(properties
, CFSTR("Other Name"), otherNameContent
);
2539 GeneralName ::= CHOICE {
2540 otherName [0] OtherName,
2541 rfc822Name [1] IA5String,
2542 dNSName [2] IA5String,
2543 x400Address [3] ORAddress,
2544 directoryName [4] Name,
2545 ediPartyName [5] EDIPartyName,
2546 uniformResourceIdentifier [6] IA5String,
2547 iPAddress [7] OCTET STRING,
2548 registeredID [8] OBJECT IDENTIFIER}
2550 EDIPartyName ::= SEQUENCE {
2551 nameAssigner [0] DirectoryString OPTIONAL,
2552 partyName [1] DirectoryString }
2554 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2555 DERTag tag
, const DERItem
*generalName
) {
2557 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2558 appendOtherNameContentProperty(properties
, generalName
);
2560 case ASN1_CONTEXT_SPECIFIC
| 1:
2562 appendStringContentProperty(properties
, CFSTR("Email Address"),
2563 generalName
, kCFStringEncodingASCII
);
2565 case ASN1_CONTEXT_SPECIFIC
| 2:
2567 appendStringContentProperty(properties
, CFSTR("DNS Name"), generalName
,
2568 kCFStringEncodingASCII
);
2570 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2571 appendUnparsedProperty(properties
, CFSTR("X.400 Address"),
2574 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2576 CFArrayRef directory_plist
=
2577 createPropertiesForX501Name(CFGetAllocator(properties
),
2579 appendPropertyP(properties
, kSecPropertyTypeSection
,
2580 CFSTR("Directory Name"), directory_plist
);
2581 CFRelease(directory_plist
);
2584 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2585 appendUnparsedProperty(properties
, CFSTR("EDI Party Name"),
2588 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2589 /* Technically I don't think this is valid, but there are certs out
2590 in the wild that use a constructed IA5String. In particular the
2591 VeriSign Time Stamping Authority CA.cer does this. */
2592 appendURLProperty(properties
, CFSTR("URI"), generalName
);
2594 case ASN1_CONTEXT_SPECIFIC
| 6:
2595 appendURLContentProperty(properties
, CFSTR("URI"), generalName
);
2597 case ASN1_CONTEXT_SPECIFIC
| 7:
2598 appendIPAddressContentProperty(properties
, CFSTR("IP Address"),
2601 case ASN1_CONTEXT_SPECIFIC
| 8:
2602 appendOIDProperty(properties
, CFSTR("Registered ID"), generalName
);
2612 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2613 const DERItem
*generalName
) {
2614 DERDecodedInfo generalNameContent
;
2615 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2616 require_noerr_quiet(drtn
, badDER
);
2617 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2618 &generalNameContent
.content
))
2621 appendInvalidProperty(properties
, CFSTR("General Name"), generalName
);
2626 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2628 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2629 const DERItem
*generalNamesContent
) {
2631 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2632 require_noerr_quiet(drtn
, badDER
);
2633 DERDecodedInfo generalNameContent
;
2634 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2636 if (!appendGeneralNameContentProperty(properties
,
2637 generalNameContent
.tag
, &generalNameContent
.content
)) {
2641 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2644 appendInvalidProperty(properties
, CFSTR("General Names"),
2645 generalNamesContent
);
2648 static void appendGeneralNames(CFMutableArrayRef properties
,
2649 const DERItem
*generalNames
) {
2650 DERDecodedInfo generalNamesContent
;
2651 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2652 require_noerr_quiet(drtn
, badDER
);
2653 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2655 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2658 appendInvalidProperty(properties
, CFSTR("General Names"), generalNames
);
2662 BasicConstraints ::= SEQUENCE {
2663 cA BOOLEAN DEFAULT FALSE,
2664 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
2666 static void appendBasicConstraints(CFMutableArrayRef properties
,
2667 const DERItem
*extnValue
) {
2668 DERBasicConstraints basicConstraints
;
2669 DERReturn drtn
= DERParseSequence(extnValue
,
2670 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
2671 &basicConstraints
, sizeof(basicConstraints
));
2672 require_noerr_quiet(drtn
, badDER
);
2674 appendBooleanProperty(properties
, CFSTR("Certificate Authority"),
2675 &basicConstraints
.cA
, false);
2677 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
2678 appendIntegerProperty(properties
, CFSTR("Path Length Constraint"),
2679 &basicConstraints
.pathLenConstraint
);
2683 appendInvalidProperty(properties
, CFSTR("Basic Constraints"), extnValue
);
2687 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
2689 DistributionPoint ::= SEQUENCE {
2690 distributionPoint [0] DistributionPointName OPTIONAL,
2691 reasons [1] ReasonFlags OPTIONAL,
2692 cRLIssuer [2] GeneralNames OPTIONAL }
2694 DistributionPointName ::= CHOICE {
2695 fullName [0] GeneralNames,
2696 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
2698 ReasonFlags ::= BIT STRING {
2702 affiliationChanged (3),
2704 cessationOfOperation (5),
2705 certificateHold (6),
2706 privilegeWithdrawn (7),
2709 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
2710 const DERItem
*extnValue
) {
2711 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2714 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
2715 require_noerr_quiet(drtn
, badDER
);
2716 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2717 DERDecodedInfo dpSeqContent
;
2718 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
2719 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2720 DERDistributionPoint dp
;
2721 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
2722 DERNumDistributionPointItemSpecs
,
2723 DERDistributionPointItemSpecs
,
2725 require_noerr_quiet(drtn
, badDER
);
2726 if (dp
.distributionPoint
.length
) {
2727 DERDecodedInfo distributionPointName
;
2728 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
2729 require_noerr_quiet(drtn
, badDER
);
2730 if (distributionPointName
.tag
==
2731 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
2733 appendGeneralNamesContent(properties
,
2734 &distributionPointName
.content
);
2735 } else if (distributionPointName
.tag
==
2736 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
2737 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
2739 appendPropertyP(properties
, kSecPropertyTypeSection
,
2740 CFSTR("Name Relative To CRL Issuer"), rdn_props
);
2741 CFRelease(rdn_props
);
2746 if (dp
.reasons
.length
) {
2747 static const CFStringRef reasonNames
[] = {
2749 CFSTR("Key Compromise"),
2750 CFSTR("CA Compromise"),
2751 CFSTR("Affiliation Changed"),
2752 CFSTR("Superseded"),
2753 CFSTR("Cessation Of Operation"),
2754 CFSTR("Certificate Hold"),
2755 CFSTR("Priviledge Withdrawn"),
2756 CFSTR("AA Compromise")
2758 appendBitStringContentNames(properties
, CFSTR("Reasons"),
2760 reasonNames
, sizeof(reasonNames
) / sizeof(*reasonNames
));
2762 if (dp
.cRLIssuer
.length
) {
2763 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
2764 &kCFTypeArrayCallBacks
);
2765 appendPropertyP(properties
, kSecPropertyTypeSection
,
2766 CFSTR("CRL Issuer"), crlIssuer
);
2767 CFRelease(crlIssuer
);
2768 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
2771 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2774 appendInvalidProperty(properties
, CFSTR("Crl Distribution Points"),
2778 /* Decode a sequence of integers into a comma separated list of ints. */
2779 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
2780 CFStringRef label
, const DERItem
*intSequenceContent
) {
2781 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2783 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
2784 require_noerr_quiet(drtn
, badDER
);
2785 DERDecodedInfo intContent
;
2786 CFMutableStringRef value
= NULL
;
2787 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
))
2789 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
2790 CFStringRef intDesc
= copyIntegerContentDescription(
2791 allocator
, &intContent
.content
);
2793 CFStringAppendFormat(value
, NULL
, CFSTR(", %@"), intDesc
);
2795 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
2799 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2801 appendPropertyP(properties
, kSecPropertyTypeString
,
2802 CFSTR("Notice Numbers"), value
);
2806 /* DROPTHOUGH if !value. */
2808 appendInvalidProperty(properties
, label
, intSequenceContent
);
2811 static void appendCertificatePolicies(CFMutableArrayRef properties
,
2812 const DERItem
*extnValue
) {
2813 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2816 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
2817 require_noerr_quiet(drtn
, badDER
);
2818 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2819 DERDecodedInfo piContent
;
2821 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
2822 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2823 DERPolicyInformation pi
;
2824 drtn
= DERParseSequenceContent(&piContent
.content
,
2825 DERNumPolicyInformationItemSpecs
,
2826 DERPolicyInformationItemSpecs
,
2828 require_noerr_quiet(drtn
, badDER
);
2829 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2830 CFSTR("Policy Identifier #%d"), pin
++);
2831 appendOIDProperty(properties
, piLabel
, &pi
.policyIdentifier
);
2833 if (pi
.policyQualifiers
.length
== 0)
2837 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
2838 require_noerr_quiet(drtn
, badDER
);
2839 DERDecodedInfo pqContent
;
2841 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
2842 DERPolicyQualifierInfo pqi
;
2843 drtn
= DERParseSequenceContent(&pqContent
.content
,
2844 DERNumPolicyQualifierInfoItemSpecs
,
2845 DERPolicyQualifierInfoItemSpecs
,
2847 require_noerr_quiet(drtn
, badDER
);
2848 DERDecodedInfo qualifierContent
;
2849 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
2850 require_noerr_quiet(drtn
, badDER
);
2851 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2852 CFSTR("Policy Qualifier #%d"), pqn
++);
2853 appendOIDProperty(properties
, pqLabel
, &pqi
.policyQualifierID
);
2855 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
2856 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
2857 appendURLContentProperty(properties
,
2859 &qualifierContent
.content
);
2860 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
2861 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2863 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
2864 DERNumUserNoticeItemSpecs
,
2865 DERUserNoticeItemSpecs
,
2867 require_noerr_quiet(drtn
, badDER
);
2868 if (un
.noticeRef
.length
) {
2869 DERNoticeReference nr
;
2870 drtn
= DERParseSequenceContent(&un
.noticeRef
,
2871 DERNumNoticeReferenceItemSpecs
,
2872 DERNoticeReferenceItemSpecs
,
2874 require_noerr_quiet(drtn
, badDER
);
2875 appendDERThingProperty(properties
,
2876 CFSTR("Organization"),
2878 appendIntegerSequenceContent(properties
,
2879 CFSTR("Notice Numbers"), &nr
.noticeNumbers
);
2881 if (un
.explicitText
.length
) {
2882 appendDERThingProperty(properties
, CFSTR("Explicit Text"),
2886 appendUnparsedProperty(properties
, CFSTR("Qualifier"),
2891 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2894 appendInvalidProperty(properties
, CFSTR("Certificate Policies"),
2898 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
2899 const DERItem
*extnValue
) {
2901 DERDecodedInfo keyIdentifier
;
2902 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
2903 require_noerr_quiet(drtn
, badDER
);
2904 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
2905 appendDataProperty(properties
, CFSTR("Key Identifier"),
2906 &keyIdentifier
.content
);
2910 appendInvalidProperty(properties
, CFSTR("Invalid Subject Key Identifier"),
2915 AuthorityKeyIdentifier ::= SEQUENCE {
2916 keyIdentifier [0] KeyIdentifier OPTIONAL,
2917 authorityCertIssuer [1] GeneralNames OPTIONAL,
2918 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
2919 -- authorityCertIssuer and authorityCertSerialNumber MUST both
2920 -- be present or both be absent
2922 KeyIdentifier ::= OCTET STRING
2924 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
2925 const DERItem
*extnValue
) {
2926 DERAuthorityKeyIdentifier akid
;
2928 drtn
= DERParseSequence(extnValue
,
2929 DERNumAuthorityKeyIdentifierItemSpecs
,
2930 DERAuthorityKeyIdentifierItemSpecs
,
2931 &akid
, sizeof(akid
));
2932 require_noerr_quiet(drtn
, badDER
);
2933 if (akid
.keyIdentifier
.length
) {
2934 appendDataProperty(properties
, CFSTR("Key Identifier"),
2935 &akid
.keyIdentifier
);
2937 if (akid
.authorityCertIssuer
.length
||
2938 akid
.authorityCertSerialNumber
.length
) {
2939 require_quiet(akid
.authorityCertIssuer
.length
&&
2940 akid
.authorityCertSerialNumber
.length
, badDER
);
2941 /* Perhaps put in a subsection called Authority Certificate Issuer. */
2942 appendGeneralNamesContent(properties
,
2943 &akid
.authorityCertIssuer
);
2944 appendIntegerProperty(properties
,
2945 CFSTR("Authority Certificate Serial Number"),
2946 &akid
.authorityCertSerialNumber
);
2951 appendInvalidProperty(properties
, CFSTR("Authority Key Identifier"),
2956 PolicyConstraints ::= SEQUENCE {
2957 requireExplicitPolicy [0] SkipCerts OPTIONAL,
2958 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
2960 SkipCerts ::= INTEGER (0..MAX)
2962 static void appendPolicyConstraints(CFMutableArrayRef properties
,
2963 const DERItem
*extnValue
) {
2964 DERPolicyConstraints pc
;
2966 drtn
= DERParseSequence(extnValue
,
2967 DERNumPolicyConstraintsItemSpecs
,
2968 DERPolicyConstraintsItemSpecs
,
2970 require_noerr_quiet(drtn
, badDER
);
2971 if (pc
.requireExplicitPolicy
.length
) {
2972 appendIntegerProperty(properties
,
2973 CFSTR("Require Explicit Policy"), &pc
.requireExplicitPolicy
);
2975 if (pc
.inhibitPolicyMapping
.length
) {
2976 appendIntegerProperty(properties
,
2977 CFSTR("Inhibit Policy Mapping"), &pc
.inhibitPolicyMapping
);
2983 appendInvalidProperty(properties
, CFSTR("Policy Constraints"), extnValue
);
2987 extendedKeyUsage EXTENSION ::= {
2988 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
2989 IDENTIFIED BY id-ce-extKeyUsage }
2991 KeyPurposeId ::= OBJECT IDENTIFIER
2993 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
2994 const DERItem
*extnValue
) {
2997 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
2998 require_noerr_quiet(drtn
, badDER
);
2999 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3000 DERDecodedInfo currDecoded
;
3001 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3002 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3003 appendOIDProperty(properties
, CFSTR("Purpose"),
3004 &currDecoded
.content
);
3006 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3009 appendInvalidProperty(properties
, CFSTR("Extended Key Usage"), extnValue
);
3013 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3015 AuthorityInfoAccessSyntax ::=
3016 SEQUENCE SIZE (1..MAX) OF AccessDescription
3018 AccessDescription ::= SEQUENCE {
3019 accessMethod OBJECT IDENTIFIER,
3020 accessLocation GeneralName }
3022 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3024 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3026 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3028 static void appendInfoAccess(CFMutableArrayRef properties
,
3029 const DERItem
*extnValue
) {
3032 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3033 require_noerr_quiet(drtn
, badDER
);
3034 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3035 DERDecodedInfo adContent
;
3036 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3037 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3038 DERAccessDescription ad
;
3039 drtn
= DERParseSequenceContent(&adContent
.content
,
3040 DERNumAccessDescriptionItemSpecs
,
3041 DERAccessDescriptionItemSpecs
,
3043 require_noerr_quiet(drtn
, badDER
);
3044 appendOIDProperty(properties
, CFSTR("Access Method"),
3046 //CFSTR("Access Location");
3047 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3049 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3052 appendInvalidProperty(properties
, CFSTR("Authority Information Access"),
3056 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3057 const DERItem
*extnValue
) {
3058 static const CFStringRef certTypes
[] = {
3059 CFSTR("SSL client"),
3060 CFSTR("SSL server"),
3062 CFSTR("Object Signing"),
3066 CFSTR("Object Signing CA")
3068 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
3069 certTypes
, sizeof(certTypes
) / sizeof(*certTypes
));
3073 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3074 const DERItem
*extnValue
) {
3078 * The list of Qualified Cert Statement statementIds we understand, even though
3079 * we don't actually do anything with them; if these are found in a Qualified
3080 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3082 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3084 /* id-qcs := { id-pkix 11 } */
3085 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3086 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3087 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3088 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3089 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3090 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3092 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3094 static void appendQCCertStatements(CFMutableArrayRef properties
,
3095 const DERItem
*extnValue
) {
3100 static bool appendPrintableDERSequenceP(CFMutableArrayRef properties
,
3101 CFStringRef label
, const DERItem
*sequence
) {
3104 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3105 require_noerr_quiet(drtn
, badSequence
);
3106 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3107 DERDecodedInfo currDecoded
;
3108 bool appendedSomething
= false;
3109 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3110 switch (currDecoded
.tag
)
3113 case ASN1_SEQUENCE
: // 16
3114 case ASN1_SET
: // 17
3115 // skip constructed object lengths
3118 case ASN1_UTF8_STRING
: // 12
3119 case ASN1_NUMERIC_STRING
: // 18
3120 case ASN1_PRINTABLE_STRING
: // 19
3121 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3122 case ASN1_VIDEOTEX_STRING
: // 21
3123 case ASN1_IA5_STRING
: // 22
3124 case ASN1_GRAPHIC_STRING
: // 25
3125 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3126 case ASN1_GENERAL_STRING
: // 27
3127 case ASN1_UNIVERSAL_STRING
: // 28
3129 CFStringRef string
=
3130 copyDERThingContentDescription(CFGetAllocator(properties
),
3131 currDecoded
.tag
, &currDecoded
.content
, false);
3132 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3134 appendPropertyP(properties
, kSecPropertyTypeString
, label
,
3137 appendedSomething
= true;
3144 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3145 return appendedSomething
;
3150 static void appendExtension(CFMutableArrayRef parent
,
3151 const SecCertificateExtension
*extn
) {
3152 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3153 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3154 &kCFTypeArrayCallBacks
);
3156 *extnID
= &extn
->extnID
,
3157 *extnValue
= &extn
->extnValue
;
3159 appendBoolProperty(properties
, CFSTR("Critical"), extn
->critical
);
3162 bool handled
= true;
3163 /* Extensions that we know how to handle ourselves... */
3164 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3165 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3167 switch (extnID
->data
[extnID
->length
- 1]) {
3168 case 14: /* SubjectKeyIdentifier id-ce 14 */
3169 appendSubjectKeyIdentifier(properties
, extnValue
);
3171 case 15: /* KeyUsage id-ce 15 */
3172 appendKeyUsage(properties
, extnValue
);
3174 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3175 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3177 case 17: /* SubjectAltName id-ce 17 */
3178 case 18: /* IssuerAltName id-ce 18 */
3179 appendGeneralNames(properties
, extnValue
);
3181 case 19: /* BasicConstraints id-ce 19 */
3182 appendBasicConstraints(properties
, extnValue
);
3184 case 30: /* NameConstraints id-ce 30 */
3187 case 31: /* CRLDistributionPoints id-ce 31 */
3188 appendCrlDistributionPoints(properties
, extnValue
);
3190 case 32: /* CertificatePolicies id-ce 32 */
3191 appendCertificatePolicies(properties
, extnValue
);
3193 case 33: /* PolicyMappings id-ce 33 */
3196 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3197 appendAuthorityKeyIdentifier(properties
, extnValue
);
3199 case 36: /* PolicyConstraints id-ce 36 */
3200 appendPolicyConstraints(properties
, extnValue
);
3202 case 37: /* ExtKeyUsage id-ce 37 */
3203 appendExtendedKeyUsage(properties
, extnValue
);
3205 case 46: /* FreshestCRL id-ce 46 */
3208 case 54: /* InhibitAnyPolicy id-ce 54 */
3215 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3216 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3218 switch (extnID
->data
[extnID
->length
- 1]) {
3219 case 1: /* AuthorityInfoAccess id-pe 1 */
3220 appendInfoAccess(properties
, extnValue
);
3222 case 3: /* QCStatements id-pe 3 */
3225 case 11: /* SubjectInfoAccess id-pe 11 */
3226 appendInfoAccess(properties
, extnValue
);
3232 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3233 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3234 appendNetscapeCertType(properties
, extnValue
);
3240 /* Try to parse and display printable string(s). */
3241 if (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3242 /* Nothing to do here appendPrintableDERSequenceP did the work. */
3244 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3245 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3249 /* Extensions that we know how to handle ourselves... */
3250 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3251 appendSubjectKeyIdentifier(properties
, extnValue
);
3252 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3253 appendKeyUsage(properties
, extnValue
);
3254 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3255 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3256 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3257 appendGeneralNames(properties
, extnValue
);
3258 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3259 appendGeneralNames(properties
, extnValue
);
3260 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3261 appendBasicConstraints(properties
, extnValue
);
3262 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3263 appendCrlDistributionPoints(properties
, extnValue
);
3264 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3265 appendCertificatePolicies(properties
, extnValue
);
3266 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3267 appendAuthorityKeyIdentifier(properties
, extnValue
);
3268 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3269 appendPolicyConstraints(properties
, extnValue
);
3270 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3271 appendExtendedKeyUsage(properties
, extnValue
);
3272 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3273 appendInfoAccess(properties
, extnValue
);
3274 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3275 appendInfoAccess(properties
, extnValue
);
3276 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3277 appendNetscapeCertType(properties
, extnValue
);
3279 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3280 appendEntrustVersInfo(properties
, extnValue
);
3283 /* Try to parse and display printable string(s). */
3284 if (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3285 /* Nothing to do here appendPrintableDERSequenceP did the work. */
3287 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3288 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3291 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
, extnID
);
3292 appendPropertyP(parent
, kSecPropertyTypeSection
, oid_string
, properties
);
3293 CFRelease(oid_string
);
3294 CFRelease(properties
);
3297 /* Different types of summary types from least desired to most desired. */
3300 kSummaryTypePrintable
,
3301 kSummaryTypeOrganizationName
,
3302 kSummaryTypeOrganizationalUnitName
,
3303 kSummaryTypeCommonName
,
3307 enum SummaryType type
;
3308 CFStringRef summary
;
3309 CFStringRef description
;
3312 static OSStatus
obtainSummaryFromX501Name(void *context
,
3313 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3314 struct Summary
*summary
= (struct Summary
*)context
;
3315 enum SummaryType stype
= kSummaryTypeNone
;
3316 CFStringRef string
= NULL
;
3317 if (DEROidCompare(type
, &oidCommonName
)) {
3318 /* We skip Common Names that have generic values. */
3319 const char tfm
[] = "Thawte Freemail Member";
3320 if ((value
->length
== sizeof(tfm
) + 1) &&
3321 !memcmp(value
->data
+ 2, tfm
, sizeof(tfm
) - 1)) {
3322 return errSecSuccess
;
3324 stype
= kSummaryTypeCommonName
;
3325 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3326 stype
= kSummaryTypeOrganizationalUnitName
;
3327 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3328 stype
= kSummaryTypeOrganizationName
;
3329 } else if (DEROidCompare(type
, &oidDescription
)) {
3330 if (!summary
->description
) {
3331 summary
->description
= string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3334 stype
= kSummaryTypePrintable
;
3336 stype
= kSummaryTypePrintable
;
3339 /* Use the first field we encounter of the highest priority type. */
3340 if (summary
->type
< stype
) {
3342 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3346 CFReleaseSafe(summary
->summary
);
3347 summary
->summary
= string
;
3348 summary
->type
= stype
;
3351 CFReleaseSafe(string
);
3354 return errSecSuccess
;
3357 CFStringRef
SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate
) {
3358 struct Summary summary
= {};
3359 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3360 /* If we found a description and a common name we change the summary to
3361 CommonName (Description). */
3362 if (summary
.description
) {
3363 if (summary
.type
== kSummaryTypeCommonName
) {
3364 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3365 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3366 CFRelease(summary
.summary
);
3367 summary
.summary
= newSummary
;
3369 CFRelease(summary
.description
);
3372 if (!summary
.summary
) {
3373 /* If we didn't find a suitable printable string in the subject at all, we try
3374 the first email address in the certificate instead. */
3375 CFArrayRef names
= SecCertificateCopyRFC822NamesP(certificate
);
3377 /* If we didn't find any email addresses in the certificate, we try finding
3378 a DNS name instead. */
3379 names
= SecCertificateCopyDNSNamesP(certificate
);
3382 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3383 CFRetain(summary
.summary
);
3388 return summary
.summary
;
3391 CFStringRef
SecCertificateCopyIssuerSummaryP(SecCertificateRefP certificate
) {
3392 struct Summary summary
= {};
3393 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3394 /* If we found a description and a common name we change the summary to
3395 CommonName (Description). */
3396 if (summary
.description
) {
3397 if (summary
.type
== kSummaryTypeCommonName
) {
3398 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3399 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3400 CFRelease(summary
.summary
);
3401 summary
.summary
= newSummary
;
3403 CFRelease(summary
.description
);
3406 return summary
.summary
;
3409 /* Return the earliest date on which all certificates in this chain are still
3411 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3412 SecCertificateRefP certificate
) {
3413 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3415 while (certificate
->_parent
) {
3416 certificate
= certificate
->_parent
;
3417 if (earliest
> certificate
->_notAfter
)
3418 earliest
= certificate
->_notAfter
;
3425 /* Return the latest date on which all certificates in this chain will be
3427 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3428 SecCertificateRefP certificate
) {
3429 CFAbsoluteTime latest
= certificate
->_notBefore
;
3431 while (certificate
->_parent
) {
3432 certificate
= certificate
->_parent
;
3433 if (latest
< certificate
->_notBefore
)
3434 latest
= certificate
->_notBefore
;
3441 bool SecCertificateIsValidP(SecCertificateRefP certificate
,
3442 CFAbsoluteTime verifyTime
) {
3444 return certificate
->_notBefore
<= verifyTime
&&
3445 verifyTime
<= certificate
->_notAfter
;
3448 CFIndex
SecCertificateVersionP(SecCertificateRefP certificate
) {
3449 return certificate
->_version
+ 1;
3452 CFAbsoluteTime
SecCertificateNotValidBeforeP(SecCertificateRefP certificate
) {
3453 return certificate
->_notBefore
;
3456 CFAbsoluteTime
SecCertificateNotValidAfterP(SecCertificateRefP certificate
) {
3457 return certificate
->_notAfter
;
3460 CFMutableArrayRef
SecCertificateCopySummaryPropertiesP(
3461 SecCertificateRefP certificate
, CFAbsoluteTime verifyTime
) {
3462 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3463 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3464 &kCFTypeArrayCallBacks
);
3466 /* First we put the subject summary name. */
3467 CFStringRef ssummary
= SecCertificateCopySubjectSummaryP(certificate
);
3469 appendPropertyP(summary
, kSecPropertyTypeTitle
,
3471 CFRelease(ssummary
);
3474 CFStringRef isummary
= CFSTR("Issuer Summary");
3475 appendPropertyP(summary
, kSecPropertyTypeString
,
3476 CFSTR("Issued By"), isummary
);
3477 CFRelease(isummary
);
3480 /* Let see if this certificate is currently valid. */
3482 CFAbsoluteTime when
;
3483 CFStringRef message
;
3485 if (verifyTime
> certificate
->_notAfter
) {
3486 label
= CFSTR("Expired");
3487 when
= certificate
->_notAfter
;
3488 ptype
= kSecPropertyTypeError
;
3489 message
= CFSTR("This certificate has expired");
3490 } else if (certificate
->_notBefore
> verifyTime
) {
3491 label
= CFSTR("Valid from");
3492 when
= certificate
->_notBefore
;
3493 ptype
= kSecPropertyTypeError
;
3494 message
= CFSTR("This certificate is not yet valid");
3496 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3497 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3498 if (verifyTime
> last
) {
3499 label
= CFSTR("Expired");
3501 ptype
= kSecPropertyTypeError
;
3502 message
= CFSTR("This certificate has an issuer that has expired");
3503 } else if (verifyTime
< first
) {
3504 label
= CFSTR("Valid from");
3506 ptype
= kSecPropertyTypeError
;
3507 message
= CFSTR("This certificate has an issuer that is not yet valid");
3509 label
= CFSTR("Expires");
3510 when
= certificate
->_notAfter
;
3511 ptype
= kSecPropertyTypeSuccess
;
3512 message
= CFSTR("This certificate is valid");
3516 appendDateProperty(summary
, label
, when
);
3517 appendPropertyP(summary
, ptype
, NULL
, message
);
3522 CFArrayRef
SecCertificateCopyPropertiesP(SecCertificateRefP certificate
) {
3523 if (!certificate
->_properties
) {
3524 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3525 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3526 &kCFTypeArrayCallBacks
);
3528 /* First we put the Subject Name in the property list. */
3529 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3530 &certificate
->_subject
);
3531 appendPropertyP(properties
, kSecPropertyTypeSection
,
3532 CFSTR("Subject Name"), subject_plist
);
3533 CFRelease(subject_plist
);
3536 /* Put Normalized subject in for testing. */
3537 if (certificate
->_normalizedSubject
) {
3538 DERItem nsubject
= {
3539 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
3540 CFDataGetLength(certificate
->_normalizedSubject
)
3542 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
3544 appendPropertyP(properties
, kSecPropertyTypeSection
,
3545 CFSTR("Normalized Subject Name"), nsubject_plist
);
3546 CFRelease(nsubject_plist
);
3550 /* Next we put the Issuer Name in the property list. */
3551 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3552 &certificate
->_issuer
);
3553 appendPropertyP(properties
, kSecPropertyTypeSection
,
3554 CFSTR("Issuer Name"), issuer_plist
);
3555 CFRelease(issuer_plist
);
3558 /* Certificate version/type. */
3559 bool isRoot
= false;
3560 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
3561 CFSTR("X.509 version %d %scertificate"),
3562 certificate
->_version
+ 1, isRoot
? "root " : "");
3563 appendPropertyP(properties
, kSecPropertyTypeString
,
3564 CFSTR("Certificate Type"), typeString
);
3565 CFRelease(typeString
);
3569 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
3570 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
3571 appendPropertyP(properties
, kSecPropertyTypeString
,
3572 CFSTR("Version"), versionString
);
3573 CFRelease(versionString
);
3576 if (certificate
->_serialNum
.length
) {
3577 appendIntegerProperty(properties
, CFSTR("Serial Number"),
3578 &certificate
->_serialNum
);
3581 /* Signature algorithm. */
3583 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3584 &certificate
->_sigAlg
);
3586 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3587 &certificate
->_tbsSigAlg
);
3590 /* Validity dates. */
3591 appendDateProperty(properties
, CFSTR("Not Valid Before"),
3592 certificate
->_notBefore
);
3593 appendDateProperty(properties
, CFSTR("Not Valid After"),
3594 certificate
->_notAfter
);
3596 if (certificate
->_subjectUniqueID
.length
) {
3597 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
3598 &certificate
->_subjectUniqueID
);
3600 if (certificate
->_issuerUniqueID
.length
) {
3601 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
3602 &certificate
->_issuerUniqueID
);
3605 /* Public key algorithm. */
3606 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
3607 &certificate
->_algId
);
3609 /* Consider breaking down an RSA public key into modulus and
3611 appendDataProperty(properties
, CFSTR("Public Key Data"),
3612 &certificate
->_pubKeyDER
);
3614 /* @@@ Key Usage. */
3616 appendDataProperty(properties
, CFSTR("Signature"),
3617 &certificate
->_signature
);
3620 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
3621 appendExtension(properties
, &certificate
->_extensions
[ix
]);
3624 /* @@@ Key Fingerprints. */
3626 certificate
->_properties
= properties
;
3629 CFRetain(certificate
->_properties
);
3630 return certificate
->_properties
;
3633 CFDataRef
SecCertificateCopySerialNumberP(
3634 SecCertificateRefP certificate
) {
3635 if (certificate
->_serialNumber
) {
3636 CFRetain(certificate
->_serialNumber
);
3638 return certificate
->_serialNumber
;
3642 * Accessor for normalized issuer content
3644 CFDataRef
SecCertificateGetNormalizedIssuerContentP(
3645 SecCertificateRefP certificate
) {
3646 return certificate
->_normalizedIssuer
;
3650 * Accessor for normalized subject content
3652 CFDataRef
SecCertificateGetNormalizedSubjectContentP(
3653 SecCertificateRefP certificate
) {
3654 return certificate
->_normalizedSubject
;
3658 * Returns DER-encoded normalized issuer sequence
3659 * for use with SecItemCopyMatching; caller must release
3661 CFDataRef
SecCertificateCopyNormalizedIssuerSequenceP(
3662 SecCertificateRefP certificate
) {
3663 if (!certificate
|| !certificate
->_normalizedIssuer
) {
3667 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
3668 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
3670 return SecDERItemCopySequenceP(&tmpdi
);
3674 * Returns DER-encoded normalized subject sequence
3675 * for use with SecItemCopyMatching; caller must release
3677 CFDataRef
SecCertificateCopyNormalizedSubjectSequenceP(
3678 SecCertificateRefP certificate
) {
3679 if (!certificate
|| !certificate
->_normalizedSubject
) {
3683 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
);
3684 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
3686 return SecDERItemCopySequenceP(&tmpdi
);
3689 /* Verify that certificate was signed by issuerKey. */
3690 OSStatus
SecCertificateIsSignedByP(SecCertificateRefP certificate
,
3691 SecKeyRefP issuerKey
) {
3692 /* Setup algId in SecAsn1AlgId format. */
3694 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3695 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3696 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3697 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3700 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3701 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3702 certificate
->_signature
.data
, certificate
->_signature
.length
);
3704 secinfo("verify", "signature verify failed: %d", status
);
3705 return errSecNotSigner
;
3709 return errSecSuccess
;
3713 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRefP certificate
,
3714 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3715 if (!signatureCheckOnly
) {
3716 /* It turns out we don't actually need to use normalized subject and
3717 issuer according to rfc2459. */
3719 /* If present we should check issuerID against the issuer subjectID. */
3721 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3722 then we should look for a SubjectKeyIdentifier in the issuer
3724 If we have a authorityCertSerialNumber we can use that for chaining.
3725 If we have a authorityCertIssuer we can use that? (or not) */
3727 /* Verify that this cert was issued by issuer. Do so by chaining
3728 either issuerID to subjectID or normalized issuer to normalized
3730 CFDataRef normalizedIssuer
=
3731 SecCertificateGetNormalizedIssuerContentP(certificate
);
3732 CFDataRef normalizedIssuerSubject
=
3733 SecCertificateGetNormalizedSubjectContentP(issuer
);
3734 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3735 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3736 return errSecIssuerMismatch
;
3739 /* Next verify that this cert was signed by issuer. */
3740 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3742 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3743 /* FIXME: We sould cache this (or at least the digest) until we find
3744 a suitable issuer. */
3745 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3746 CFIndex signedDataLength
;
3747 CertVerifyReturn crtn
;
3748 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3749 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3750 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3751 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3752 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3753 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3754 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3755 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3756 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3758 secinfo("verify", "unsupported algorithm");
3759 return errSecUnsupportedAlgorithm
;
3762 secinfo("verify", "*DigestInfo returned: %d", crtn
);
3763 /* FIXME: Do proper error code translation. */
3764 return errSecUnsupportedAlgorithm
;
3767 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3768 signedData
, signedDataLength
,
3769 certificate
->_signature
.data
, certificate
->_signature
.length
);
3771 secinfo("verify", "signature verify failed: %d", status
);
3772 return errSecNotSigner
;
3775 return errSecSuccess
;
3778 static OSStatus
_SecCertificateSetParent(SecCertificateRefP certificate
,
3779 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3781 if (certificate
->_parent
) {
3782 /* Setting a certificates issuer twice is only allowed if the new
3783 issuer is equal to the current one. */
3784 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3788 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3789 signatureCheckOnly
);
3791 OSStatus status
= errSecSuccess
;
3794 if (CFEqual(certificate
, issuer
)) {
3795 /* We don't retain ourselves cause that would be bad mojo,
3796 however we do record that we are properly self signed. */
3797 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3798 secinfo("cert", "set self as parent");
3799 return errSecSuccess
;
3803 certificate
->_parent
= issuer
;
3804 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3810 static bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate
) {
3811 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3812 certificate
->_isSelfSigned
=
3813 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3814 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3817 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3820 /* Return true iff we were able to set our own parent from one of the
3821 certificates in other_certificates, return false otherwise. If
3822 signatureCheckOnly is true, we can skip the subject == issuer or
3823 authorityKeyIdentifier tests. */
3824 static bool SecCertificateSetParentFrom(SecCertificateRefP certificate
,
3825 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3826 CFIndex count
= CFArrayGetCount(other_certificates
);
3828 for (ix
= 0; ix
< count
; ++ix
) {
3829 SecCertificateRefP candidate
= (SecCertificateRefP
)
3830 CFArrayGetValueAtIndex(other_certificates
, ix
);
3831 if (_SecCertificateSetParent(certificate
, candidate
,
3832 signatureCheckOnly
))
3838 /* Lookup the parent of certificate in the keychain and set it. */
3839 static bool SecCertificateFindParent(SecCertificateRefP certificate
) {
3840 /* FIXME: Search for things other than just subject of our issuer if we
3841 have a subjectID or authorityKeyIdentifier. */
3842 CFDataRef normalizedIssuer
=
3843 SecCertificateGetNormalizedIssuerContentP(certificate
);
3844 const void *keys
[] = {
3851 kSecClassCertificate
,
3856 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3857 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3859 OSStatus status
= SecItemCopyMatching(query
, &results
);
3862 secinfo("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3866 CFArrayRef certs
= (CFArrayRef
)results
;
3867 /* Since we already know the certificates we are providing as candidates
3868 have been checked for subject matching, we can ask
3869 SecCertificateSetParentFrom to skip everything except the signature
3871 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3876 OSStatus
SecCertificateCompleteChainP(SecCertificateRefP certificate
,
3877 CFArrayRef other_certificates
) {
3879 if (certificate
->_parent
== NULL
) {
3880 if (SecCertificateIsSelfSignedP(certificate
))
3881 return errSecSuccess
;
3882 if (!other_certificates
||
3883 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
3885 if (!SecCertificateFindParent(certificate
))
3886 return errSecIssuerNotFound
;
3889 certificate
= certificate
->_parent
;
3894 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
3895 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
3896 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
3897 if (gnType
== GNT_IPAddress
) {
3898 CFStringRef string
= copyIPAddressContentDescription(
3899 kCFAllocatorDefault
, generalName
);
3901 CFArrayAppendValue(ipAddresses
, string
);
3904 return errSecInvalidCertificate
;
3907 return errSecSuccess
;
3910 CFArrayRef
SecCertificateCopyIPAddressesP(SecCertificateRefP certificate
) {
3911 /* These can only exist in the subject alt name. */
3912 if (!certificate
->_subjectAltName
)
3915 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
3916 0, &kCFTypeArrayCallBacks
);
3917 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
3918 ipAddresses
, appendIPAddressesFromGeneralNames
);
3919 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
3920 CFRelease(ipAddresses
);
3926 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
3927 const DERItem
*generalName
) {
3928 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
3929 if (gnType
== GNT_DNSName
) {
3930 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
3931 generalName
->data
, generalName
->length
,
3932 kCFStringEncodingUTF8
, FALSE
);
3934 CFArrayAppendValue(dnsNames
, string
);
3937 return errSecInvalidCertificate
;
3940 return errSecSuccess
;
3943 /* Return true if the passed in string matches the
3944 Preferred name syntax from sections 2.3.1. in RFC 1035.
3945 With the added check that we disallow empty dns names.
3946 Also in order to support wildcard DNSNames we allow for the '*'
3947 character anywhere in a dns component where we currently allow
3950 <domain> ::= <subdomain> | " "
3952 <subdomain> ::= <label> | <subdomain> "." <label>
3954 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
3956 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
3958 <let-dig-hyp> ::= <let-dig> | "-"
3960 <let-dig> ::= <letter> | <digit>
3962 <letter> ::= any one of the 52 alphabetic characters A through Z in
3963 upper case and a through z in lower case
3965 <digit> ::= any one of the ten digits 0 through 9
3967 static bool isDNSName(CFStringRef string
) {
3968 CFStringInlineBuffer buf
;
3969 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
3970 /* From RFC 1035 2.3.4. Size limits:
3971 labels 63 octets or less
3972 names 255 octets or less */
3973 require_quiet(length
<= 255, notDNS
);
3974 CFRange range
= { 0, length
};
3975 CFStringInitInlineBuffer(string
, &buf
, range
);
3979 kDNSStateAfterAlpha
,
3980 kDNSStateAfterDigit
,
3982 } state
= kDNSStateInital
;
3984 bool nonAlpha
= false;
3985 for (ix
= 0; ix
< length
; ++ix
) {
3986 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
3989 require_quiet(labelLength
<= 64 &&
3990 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
3992 state
= kDNSStateAfterDot
;
3995 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
3997 state
= kDNSStateAfterAlpha
;
3998 } else if ('0' <= ch
&& ch
<= '9') {
4000 /* The requirement for labels to start with a letter was
4001 dropped so we don't check this anymore. */
4002 require_quiet(state
== kDNSStateAfterAlpha
||
4003 state
== kDNSStateAfterDigit
||
4004 state
== kDNSStateAfterDash
, notDNS
);
4006 state
= kDNSStateAfterDigit
;
4008 } else if (ch
== '-') {
4009 require_quiet(state
== kDNSStateAfterAlpha
||
4010 state
== kDNSStateAfterDigit
||
4011 state
== kDNSStateAfterDash
, notDNS
);
4012 state
= kDNSStateAfterDash
;
4019 /* We don't allow a dns name to end in a dot, and we require the
4020 final name component to only have alphanumeric chars. */
4021 require_quiet(!nonAlpha
&& labelLength
<= 63 &&
4022 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4030 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4031 const DERItem
*value
, CFIndex rdnIX
) {
4032 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4033 if (DEROidCompare(type
, &oidCommonName
)) {
4034 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4037 if (isDNSName(string
)) {
4038 /* We found a common name that is formatted like a valid
4040 CFArrayAppendValue(dnsNames
, string
);
4044 return errSecInvalidCertificate
;
4047 return errSecSuccess
;
4050 /* Not everything returned by this function is going to be a proper DNS name,
4051 we also return the certificates common name entries from the subject,
4052 assuming they look like dns names as specified in RFC 1035. */
4053 CFArrayRef
SecCertificateCopyDNSNamesP(SecCertificateRefP certificate
) {
4054 /* These can exist in the subject alt name or in the subject. */
4055 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4056 0, &kCFTypeArrayCallBacks
);
4057 OSStatus status
= errSecSuccess
;
4058 if (certificate
->_subjectAltName
) {
4059 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4060 dnsNames
, appendDNSNamesFromGeneralNames
);
4062 /* RFC 2818 section 3.1. Server Identity
4064 If a subjectAltName extension of type dNSName is present, that MUST
4065 be used as the identity. Otherwise, the (most specific) Common Name
4066 field in the Subject field of the certificate MUST be used. Although
4067 the use of the Common Name is existing practice, it is deprecated and
4068 Certification Authorities are encouraged to use the dNSName instead.
4071 This implies that if we found 1 or more DNSNames in the
4072 subjectAltName, we should not use the Common Name of the subject as
4075 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4076 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4077 appendDNSNamesFromX501Name
);
4079 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4080 CFRelease(dnsNames
);
4086 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4087 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4088 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4089 if (gnType
== GNT_RFC822Name
) {
4090 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4091 generalName
->data
, generalName
->length
,
4092 kCFStringEncodingASCII
, FALSE
);
4094 CFArrayAppendValue(dnsNames
, string
);
4097 return errSecInvalidCertificate
;
4100 return errSecSuccess
;
4103 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4104 const DERItem
*value
, CFIndex rdnIX
) {
4105 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4106 if (DEROidCompare(type
, &oidEmailAddress
)) {
4107 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4110 CFArrayAppendValue(dnsNames
, string
);
4113 return errSecInvalidCertificate
;
4116 return errSecSuccess
;
4119 CFArrayRef
SecCertificateCopyRFC822NamesP(SecCertificateRefP certificate
) {
4120 /* These can exist in the subject alt name or in the subject. */
4121 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4122 0, &kCFTypeArrayCallBacks
);
4123 OSStatus status
= errSecSuccess
;
4124 if (certificate
->_subjectAltName
) {
4125 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4126 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4129 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4130 appendRFC822NamesFromX501Name
);
4132 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4133 CFRelease(rfc822Names
);
4139 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4140 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4141 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4142 if (DEROidCompare(type
, &oidCommonName
)) {
4143 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4146 CFArrayAppendValue(commonNames
, string
);
4149 return errSecInvalidCertificate
;
4152 return errSecSuccess
;
4155 CFArrayRef
SecCertificateCopyCommonNamesP(SecCertificateRefP certificate
) {
4156 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4157 0, &kCFTypeArrayCallBacks
);
4159 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4160 appendCommonNamesFromX501Name
);
4161 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4162 CFRelease(commonNames
);
4168 static OSStatus
appendOrganizationFromX501Name(void *context
,
4169 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4170 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4171 if (DEROidCompare(type
, &oidOrganizationName
)) {
4172 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4175 CFArrayAppendValue(organization
, string
);
4178 return errSecInvalidCertificate
;
4181 return errSecSuccess
;
4184 CFArrayRef
SecCertificateCopyOrganizationP(SecCertificateRefP certificate
) {
4185 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4186 0, &kCFTypeArrayCallBacks
);
4188 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4189 appendOrganizationFromX501Name
);
4190 if (status
|| CFArrayGetCount(organization
) == 0) {
4191 CFRelease(organization
);
4192 organization
= NULL
;
4194 return organization
;
4197 const SecCEBasicConstraints
*
4198 SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate
) {
4199 if (certificate
->_basicConstraints
.present
)
4200 return &certificate
->_basicConstraints
;
4205 const SecCEPolicyConstraints
*
4206 SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate
) {
4207 if (certificate
->_policyConstraints
.present
)
4208 return &certificate
->_policyConstraints
;
4214 SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate
) {
4215 return certificate
->_policyMappings
;
4218 const SecCECertificatePolicies
*
4219 SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate
) {
4220 if (certificate
->_certificatePolicies
.present
)
4221 return &certificate
->_certificatePolicies
;
4227 SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate
) {
4228 return certificate
->_inhibitAnyPolicySkipCerts
;
4231 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4232 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4233 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4234 if (gnType
== GNT_OtherName
) {
4236 DERReturn drtn
= DERParseSequenceContent(generalName
,
4237 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4239 require_noerr_quiet(drtn
, badDER
);
4240 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4242 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4243 &on
.value
, true), badDER
);
4244 CFArrayAppendValue(ntPrincipalNames
, string
);
4248 return errSecSuccess
;
4251 return errSecInvalidCertificate
;
4255 CFArrayRef
SecCertificateCopyNTPrincipalNamesP(SecCertificateRefP certificate
) {
4256 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4257 0, &kCFTypeArrayCallBacks
);
4258 OSStatus status
= errSecSuccess
;
4259 if (certificate
->_subjectAltName
) {
4260 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4261 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4263 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4264 CFRelease(ntPrincipalNames
);
4265 ntPrincipalNames
= NULL
;
4267 return ntPrincipalNames
;
4270 static OSStatus
appendToRFC2253String(void *context
,
4271 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4272 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4276 ST stateOrProvinceName
4278 OU organizationalUnitName
4280 STREET streetAddress
4284 /* Prepend a + if this is not the first RDN in an RDN set.
4285 Otherwise prepend a , if this is not the first RDN. */
4287 CFStringAppend(string
, CFSTR("+"));
4288 else if (CFStringGetLength(string
)) {
4289 CFStringAppend(string
, CFSTR(","));
4292 CFStringRef label
, oid
= NULL
;
4293 /* @@@ Consider changing this to a dictionary lookup keyed by the
4294 decimal representation. */
4295 #if 0 // represent all labels as oids
4296 if (DEROidCompare(type
, &oidCommonName
)) {
4297 label
= CFSTR("CN");
4298 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4300 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4301 label
= CFSTR("ST");
4302 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4304 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4305 label
= CFSTR("OU");
4306 } else if (DEROidCompare(type
, &oidCountryName
)) {
4309 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4310 label
= CFSTR("STREET");
4311 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4312 label
= CFSTR("DC");
4313 } else if (DEROidCompare(type
, &oidUserID
)) {
4314 label
= CFSTR("UID");
4319 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4322 CFStringAppend(string
, label
);
4323 CFStringAppend(string
, CFSTR("="));
4324 CFStringRef raw
= NULL
;
4326 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4329 /* Append raw to string while escaping:
4330 a space or "#" character occurring at the beginning of the string
4331 a space character occurring at the end of the string
4332 one of the characters ",", "+", """, "\", "<", ">" or ";"
4334 CFStringInlineBuffer buffer
;
4335 CFIndex ix
, length
= CFStringGetLength(raw
);
4336 CFRange range
= { 0, length
};
4337 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4338 for (ix
= 0; ix
< length
; ++ix
) {
4339 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4341 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4342 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4343 ch
== '<' || ch
== '>' || ch
== ';' ||
4344 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4345 (ch
== '#' && ix
== 0)) {
4346 UniChar chars
[] = { '\\', ch
};
4347 CFStringAppendCharacters(string
, chars
, 2);
4349 CFStringAppendCharacters(string
, &ch
, 1);
4354 /* Append the value in hex. */
4355 CFStringAppend(string
, CFSTR("#"));
4357 for (ix
= 0; ix
< value
->length
; ++ix
)
4358 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4363 return errSecSuccess
;
4366 CFStringRef
SecCertificateCopySubjectStringP(SecCertificateRefP certificate
) {
4367 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4368 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4369 if (status
|| CFStringGetLength(string
) == 0) {
4376 static OSStatus
appendToCompanyNameString(void *context
,
4377 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4378 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4379 if (CFStringGetLength(string
) != 0)
4380 return errSecSuccess
;
4382 if (!DEROidCompare(type
, &oidOrganizationName
))
4383 return errSecSuccess
;
4386 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4388 return errSecSuccess
;
4389 CFStringAppend(string
, raw
);
4392 return errSecSuccess
;
4395 CFStringRef
SecCertificateCopyCompanyNameP(SecCertificateRefP certificate
) {
4396 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4397 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4398 appendToCompanyNameString
);
4399 if (status
|| CFStringGetLength(string
) == 0) {
4406 CFDataRef
SecDERItemCopySequenceP(DERItem
*content
) {
4407 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4408 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4409 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4411 CFDataSetLength(sequence
, sequence_length
);
4412 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4413 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
4414 require_noerr_quiet(DEREncodeLength(content
->length
,
4415 sequence_ptr
, &seq_len_length
), out
);
4416 sequence_ptr
+= seq_len_length
;
4417 memcpy(sequence_ptr
, content
->data
, content
->length
);
4420 CFReleaseSafe(sequence
);
4424 CFDataRef
SecCertificateCopyIssuerSequenceP(
4425 SecCertificateRefP certificate
) {
4426 return SecDERItemCopySequenceP(&certificate
->_issuer
);
4429 CFDataRef
SecCertificateCopySubjectSequenceP(
4430 SecCertificateRefP certificate
) {
4431 return SecDERItemCopySequenceP(&certificate
->_subject
);
4434 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithmP(
4435 SecCertificateRefP certificate
) {
4436 return &certificate
->_algId
;
4439 const DERItem
*SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate
) {
4440 return &certificate
->_pubKeyDER
;
4443 SecKeyRefP
SecCertificateCopyPublicKeyP(SecCertificateRefP certificate
) {
4444 SecKeyRefP publicKey
= NULL
;
4446 const DERAlgorithmId
*algId
=
4447 SecCertificateGetPublicKeyAlgorithmP(certificate
);
4448 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4449 if (DEROidCompare(&algId
->oid
, &oidRsa
)) {
4450 publicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
4451 keyData
->data
, keyData
->length
, kSecKeyEncodingPkcs1
);
4453 secinfo("cert", "Unsupported algorithm oid");
4460 CFDataRef
SecCertificateGetSHA1DigestP(SecCertificateRefP certificate
) {
4461 if (!certificate
->_sha1Digest
) {
4462 certificate
->_sha1Digest
=
4463 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4464 certificate
->_der
.data
, certificate
->_der
.length
);
4467 return certificate
->_sha1Digest
;
4470 CFDataRef
SecCertificateCopyIssuerSHA1DigestP(SecCertificateRefP certificate
) {
4471 CFDataRef digest
= NULL
;
4472 CFDataRef issuer
= SecCertificateCopyIssuerSequenceP(certificate
);
4474 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4475 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4481 CFDataRef
SecCertificateCopyPublicKeySHA1DigestP(SecCertificateRefP certificate
) {
4482 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4483 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4486 /* note: this function is exported with a non-P-suffix name.
4487 * since it doesn't accept or return a SecCertificateRefP type, this is OK for now.
4489 CFDataRef
SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator
,
4490 CFDataRef der_certificate
)
4492 CFDataRef result
= NULL
;
4493 SecCertificateRefP iosCertRef
= SecCertificateCreateWithDataP(allocator
, der_certificate
);
4494 if (NULL
== iosCertRef
)
4499 result
= SecCertificateCopyPublicKeySHA1DigestP(iosCertRef
);
4500 CFRelease(iosCertRef
);
4504 CFDataRef
SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate
) {
4505 if (!certificate
->_authorityKeyID
&&
4506 certificate
->_authorityKeyIdentifier
.length
) {
4507 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4508 certificate
->_authorityKeyIdentifier
.data
,
4509 certificate
->_authorityKeyIdentifier
.length
);
4512 return certificate
->_authorityKeyID
;
4515 CFDataRef
SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate
) {
4516 if (!certificate
->_subjectKeyID
&&
4517 certificate
->_subjectKeyIdentifier
.length
) {
4518 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4519 certificate
->_subjectKeyIdentifier
.data
,
4520 certificate
->_subjectKeyIdentifier
.length
);
4523 return certificate
->_subjectKeyID
;
4526 CFArrayRef
SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate
) {
4527 return certificate
->_crlDistributionPoints
;
4530 CFArrayRef
SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate
) {
4531 return certificate
->_ocspResponders
;
4534 CFArrayRef
SecCertificateGetCAIssuersP(SecCertificateRefP certificate
) {
4535 return certificate
->_caIssuers
;
4538 bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate
) {
4539 return certificate
->_subjectAltName
&&
4540 certificate
->_subjectAltName
->critical
;
4543 bool SecCertificateHasSubjectP(SecCertificateRefP certificate
) {
4544 /* Since the _subject field is the content of the subject and not the
4545 whole thing, we can simply check for a 0 length subject here. */
4546 return certificate
->_subject
.length
!= 0;
4549 bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate
) {
4550 return certificate
->_foundUnknownCriticalExtension
;
4553 /* Private API functions. */
4554 void SecCertificateShowP(SecCertificateRefP certificate
) {
4556 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4557 fprintf(stderr
, "\n");
4560 CFDictionaryRef
SecCertificateCopyAttributeDictionaryP(
4561 SecCertificateRefP certificate
) {
4562 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4563 CFNumberRef certificateType
, certificateEncoding
;
4564 CFStringRef label
, alias
;
4565 CFDataRef skid
, pubKeyDigest
, certData
;
4566 CFDictionaryRef dict
= NULL
;
4570 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4571 SInt32 ctv
= certificate
->_version
+ 1;
4572 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4573 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4574 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4575 certData
= SecCertificateCopyDataP(certificate
);
4576 skid
= SecCertificateGetSubjectKeyIDP(certificate
);
4577 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4578 certificate
->_pubKeyDER
.length
);
4580 /* We still need to figure out how to deal with multi valued attributes. */
4581 alias
= SecCertificateCopyRFC822NamesP(certificate
);
4582 label
= SecCertificateCopySubjectSummary(certificate
);
4588 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4589 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4590 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4592 DICT_ADDPAIR(kSecAttrLabel
, label
);
4594 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4595 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4596 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4597 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4599 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4600 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4601 DICT_ADDPAIR(kSecValueData
, certData
);
4602 dict
= DICT_CREATE(allocator
);
4604 CFReleaseSafe(label
);
4605 CFReleaseSafe(pubKeyDigest
);
4606 CFReleaseSafe(certData
);
4607 CFReleaseSafe(certificateEncoding
);
4608 CFReleaseSafe(certificateType
);
4613 SecCertificateRefP
SecCertificateCreateFromAttributeDictionaryP(
4614 CFDictionaryRef refAttributes
) {
4615 /* @@@ Support having an allocator in refAttributes. */
4616 CFAllocatorRef allocator
= NULL
;
4617 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4618 return SecCertificateCreateWithDataP(allocator
, data
);
4621 bool SecCertificateIsSelfSignedCAP(SecCertificateRefP certificate
) {
4622 bool result
= false;
4623 SecKeyRefP publicKey
;
4624 require(publicKey
= SecCertificateCopyPublicKeyP(certificate
), out
);
4625 CFDataRef normalizedIssuer
=
4626 SecCertificateGetNormalizedIssuerContentP(certificate
);
4627 CFDataRef normalizedSubject
=
4628 SecCertificateGetNormalizedSubjectContentP(certificate
);
4629 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4630 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4632 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyIDP(certificate
);
4633 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyIDP(certificate
);
4634 if (authorityKeyID
) {
4635 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4638 if (SecCertificateVersionP(certificate
) >= 3) {
4639 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraintsP(certificate
);
4640 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4641 require_noerr_quiet(SecCertificateIsSignedByP(certificate
, publicKey
), out
);
4646 CFReleaseSafe(publicKey
);
4650 SecKeyUsage
SecCertificateGetKeyUsageP(SecCertificateRefP certificate
) {
4651 return certificate
->_keyUsage
;
4654 CFArrayRef
SecCertificateCopyExtendedKeyUsageP(SecCertificateRefP certificate
)
4656 CFMutableArrayRef extended_key_usage_oids
=
4657 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4658 require_quiet(extended_key_usage_oids
, out
);
4660 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4661 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4662 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4663 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4666 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4667 require_noerr_quiet(drtn
, out
);
4668 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4669 DERDecodedInfo currDecoded
;
4671 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4672 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4673 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4674 currDecoded
.content
.data
, currDecoded
.content
.length
);
4676 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4680 require_quiet(drtn
== DR_EndOfSequence
, out
);
4681 return extended_key_usage_oids
;
4685 CFReleaseSafe(extended_key_usage_oids
);
4689 SecCertificateRefP
SecCertificateCreateWithPEMP(CFAllocatorRef allocator
,
4690 CFDataRef pem_certificate
)
4692 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
4693 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
4694 uint8_t *base64_data
= NULL
;
4695 SecCertificateRefP cert
= NULL
;
4696 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
4697 //const size_t length = CFDataGetLength(pem_certificate);
4698 char *begin
= strstr((const char *)data
, begin_cert
);
4699 char *end
= strstr((const char *)data
, end_cert
);
4702 begin
+= sizeof(begin_cert
) - 1;
4703 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
4704 if (base64_length
) {
4705 require_quiet(base64_data
= calloc(1, base64_length
), out
);
4706 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
4707 cert
= SecCertificateCreateWithBytesP(kCFAllocatorDefault
, base64_data
, base64_length
);
4714 static void convertCertificateToCFData(const void *value
, void *context
) {
4715 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4716 SecCertificateRefP certificate
= (SecCertificateRefP
)value
;
4717 CFDataRef data
= SecCertificateCopyDataP(certificate
);
4718 CFArrayAppendValue(result
, data
);
4722 /* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
4723 CFArrayRef
SecCertificateArrayCopyDataArrayP(CFArrayRef certificates
) {
4724 CFIndex count
= CFArrayGetCount(certificates
);
4725 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
4726 CFRange all_certs
= { 0, count
};
4727 CFArrayApplyFunction(certificates
, all_certs
, convertCertificateToCFData
, result
);
4731 /* AUDIT[securityd](done):
4732 value (ok) is an element in a caller provided array.
4734 static void convertCFDataToCertificate(const void *value
, void *context
) {
4735 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4736 CFDataRef data
= (CFDataRef
)value
;
4737 if (data
&& CFGetTypeID(data
) == CFDataGetTypeID()) {
4738 SecCertificateRefP certificate
= SecCertificateCreateWithDataP(kCFAllocatorDefault
, data
);
4740 CFArrayAppendValue(result
, certificate
);
4741 CFRelease(certificate
);
4746 /* AUDIT[securityd](done):
4747 certificates (ok) is a caller provided array, only its cf type has
4750 CFArrayRef
SecCertificateDataArrayCopyArrayP(CFArrayRef certificates
) {
4751 CFIndex count
= CFArrayGetCount(certificates
);
4752 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
4753 CFRange all_certs
= { 0, count
};
4754 CFArrayApplyFunction(certificates
, all_certs
, convertCFDataToCertificate
, result
);