2 * Copyright (c) 2006-2010 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 <Security/SecCertificateInternal.h>
30 #include "SecCertificateInternalP.h"
32 #include <CommonCrypto/CommonDigest.h>
33 #include <CoreFoundation/CFRuntime.h>
34 #include <CoreFoundation/CFString.h>
35 #include <CoreFoundation/CFBundle.h>
36 #include <CoreFoundation/CFDictionary.h>
37 #include <CoreFoundation/CFNumber.h>
38 #include <CoreFoundation/CFTimeZone.h>
41 #include <AssertMacros.h>
42 #include <libDER/libDER.h>
43 #include <libDER/DER_CertCrl.h>
44 #include <libDER/DER_Encode.h>
45 #include <libDER/DER_Keys.h>
46 #include <libDER/asn1Types.h>
47 #include <libDER/oids.h>
49 #include "SecBasePriv.h"
51 #include "SecRSAKeyP.h"
52 #include "SecFrameworkP.h"
54 #include "SecItemPriv.h"
57 #include <libkern/OSByteOrder.h>
59 #include "SecInternalP.h"
60 #include "SecBase64P.h"
62 #include <security_utilities/debugging.h>
64 typedef struct SecCertificateExtension
{
68 } SecCertificateExtension
;
71 typedef struct KnownExtension
{
77 kSecSelfSignedUnknown
= 0,
83 struct __SecCertificate
{
86 DERItem _der
; /* Entire certificate in DER form. */
87 DERItem _tbs
; /* To Be Signed cert DER bytes. */
88 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
89 DERItem _signature
; /* The content of the sig bit string. */
92 DERItem _serialNum
; /* Integer. */
93 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
94 DERItem _issuer
; /* Sequence of RDN. */
95 CFAbsoluteTime _notBefore
;
96 CFAbsoluteTime _notAfter
;
97 DERItem _subject
; /* Sequence of RDN. */
98 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
99 DERItem _pubKeyDER
; /* contents of bit string */
100 DERItem _issuerUniqueID
; /* bit string, optional */
101 DERItem _subjectUniqueID
; /* bit string, optional */
104 /* Known extensions if the certificate contains them,
105 extnValue.length will be > 0. */
106 KnownExtension _authorityKeyID
;
108 /* This extension is used to uniquely identify a certificate from among
109 several that have the same subject name. If the extension is not
110 present, its value is calculated by performing a SHA-1 hash of the
111 certificate's DER encoded subjectPublicKeyInfo, as recommended by
113 KnownExtension _subjectKeyID
;
114 KnownExtension _keyUsage
;
115 KnownExtension _extendedKeyUsage
;
116 KnownExtension _basicConstraints
;
117 KnownExtension _netscapeCertType
;
118 KnownExtension _subjectAltName
;
119 KnownExtension _qualCertStatements
;
122 bool _foundUnknownCriticalExtension
;
124 /* Well known certificate extensions. */
125 SecCEBasicConstraints _basicConstraints
;
126 SecCEPolicyConstraints _policyConstraints
;
127 CFDictionaryRef _policyMappings
;
128 SecCECertificatePolicies _certificatePolicies
;
130 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
131 value of the SkipCerts field of the InhibitAnyPolicy extension
133 uint32_t _inhibitAnyPolicySkipCerts
;
135 /* If KeyUsage extension is not present this is 0, otherwise it's
136 the value of the extension. */
137 SecKeyUsage _keyUsage
;
139 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
140 Length = 0 if not present. */
141 DERItem _subjectKeyIdentifier
;
143 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
144 Length = 0 if not present. */
145 DERItem _authorityKeyIdentifier
;
146 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
147 _authorityKeyIdentifierSerialNumber have non zero length if present.
148 Both are either present or absent together. */
149 DERItem _authorityKeyIdentifierIssuer
;
150 DERItem _authorityKeyIdentifierSerialNumber
;
152 /* Subject alt name extension, if present. Not malloced, it's just a
153 pointer to an element in the _extensions array. */
154 const SecCertificateExtension
*_subjectAltName
;
156 /* Parsed extension values. */
158 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
159 CFMutableArrayRef _crlDistributionPoints
;
161 /* Array of CFURLRefs containing the URI values of accessLocations of each
162 id-ad-ocsp AccessDescription in the Authority Information Access
164 CFMutableArrayRef _ocspResponders
;
166 /* Array of CFURLRefs containing the URI values of accessLocations of each
167 id-ad-caIssuers AccessDescription in the Authority Information Access
169 CFMutableArrayRef _caIssuers
;
171 /* All other (non known) extensions. The _extensions array is malloced. */
172 CFIndex _extensionCount
;
173 SecCertificateExtension
*_extensions
;
175 /* Optional cached fields. */
178 CFArrayRef _properties
;
179 CFDataRef _serialNumber
;
180 CFDataRef _normalizedIssuer
;
181 CFDataRef _normalizedSubject
;
182 CFDataRef _authorityKeyID
;
183 CFDataRef _subjectKeyID
;
185 CFDataRef _sha1Digest
;
186 uint8_t _isSelfSigned
;
190 /* Public Constants for property list keys. */
191 CFStringRef kSecPropertyKeyType
= CFSTR("type");
192 CFStringRef kSecPropertyKeyLabel
= CFSTR("label");
193 CFStringRef kSecPropertyKeyLocalizedLabel
= CFSTR("localized label");
194 CFStringRef kSecPropertyKeyValue
= CFSTR("value");
196 /* Public Constants for property list values. */
197 CFStringRef kSecPropertyTypeWarning
= CFSTR("warning");
198 CFStringRef kSecPropertyTypeError
= CFSTR("error");
199 CFStringRef kSecPropertyTypeSuccess
= CFSTR("success");
200 CFStringRef kSecPropertyTypeTitle
= CFSTR("title");
201 CFStringRef kSecPropertyTypeSection
= CFSTR("section");
202 CFStringRef kSecPropertyTypeData
= CFSTR("data");
203 CFStringRef kSecPropertyTypeString
= CFSTR("string");
204 CFStringRef kSecPropertyTypeURL
= CFSTR("url");
205 CFStringRef kSecPropertyTypeDate
= CFSTR("date");
207 /* Extension parsing routine. */
208 typedef void (*SecCertificateExtensionParser
)(SecCertificateRefP certificate
,
209 const SecCertificateExtension
*extn
);
211 /* CFRuntime regsitration data. */
212 static pthread_once_t kSecCertificateRegisterClass
= PTHREAD_ONCE_INIT
;
213 static CFTypeID kSecCertificateTypeID
= _kCFRuntimeNotATypeID
;
215 /* Mapping from extension OIDs (as a DERItem *) to
216 SecCertificateExtensionParser extension parsing routines. */
217 static CFDictionaryRef gExtensionParsers
;
219 /* Forward declartions of static functions. */
220 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
);
221 static void SecCertificateDestroy(CFTypeRef cf
);
222 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
223 CFAbsoluteTime
*absTime
);
225 /* Static functions. */
226 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
) {
227 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
228 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
229 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
,
230 SecCertificateCopySubjectSummaryP(certificate
),
231 SecCertificateCopyIssuerSummary(certificate
));
234 static void SecCertificateDestroy(CFTypeRef cf
) {
235 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
236 if (certificate
->_certificatePolicies
.policies
)
237 free(certificate
->_certificatePolicies
.policies
);
238 CFReleaseSafe(certificate
->_policyMappings
);
239 CFReleaseSafe(certificate
->_crlDistributionPoints
);
240 CFReleaseSafe(certificate
->_ocspResponders
);
241 CFReleaseSafe(certificate
->_caIssuers
);
242 if (certificate
->_extensions
) {
243 free(certificate
->_extensions
);
245 CFReleaseSafe(certificate
->_pubKey
);
246 CFReleaseSafe(certificate
->_der_data
);
247 CFReleaseSafe(certificate
->_properties
);
248 CFReleaseSafe(certificate
->_serialNumber
);
249 CFReleaseSafe(certificate
->_normalizedIssuer
);
250 CFReleaseSafe(certificate
->_normalizedSubject
);
251 CFReleaseSafe(certificate
->_authorityKeyID
);
252 CFReleaseSafe(certificate
->_subjectKeyID
);
253 CFReleaseSafe(certificate
->_sha1Digest
);
256 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
257 SecCertificateRefP cert1
= (SecCertificateRefP
)cf1
;
258 SecCertificateRefP cert2
= (SecCertificateRefP
)cf2
;
261 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
263 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
266 /* Hash of the certificate is der length + signature length + last 4 bytes
268 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
269 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
270 DERSize der_length
= certificate
->_der
.length
;
271 DERSize sig_length
= certificate
->_signature
.length
;
272 DERSize ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
273 CFHashCode hashCode
= 0;
274 for (; ix
< sig_length
; ++ix
)
275 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
277 return (hashCode
+ der_length
+ sig_length
);
282 /************************************************************************/
283 /************************* General Name Parsing *************************/
284 /************************************************************************/
286 typedef OSStatus (*parseGeneralNameCallback
)(void *context
,
287 SecCEGeneralNameType type
, const DERItem
*value
);
291 GeneralName ::= CHOICE {
292 otherName [0] OtherName,
293 rfc822Name [1] IA5String,
294 dNSName [2] IA5String,
295 x400Address [3] ORAddress,
296 directoryName [4] Name,
297 ediPartyName [5] EDIPartyName,
298 uniformResourceIdentifier [6] IA5String,
299 iPAddress [7] OCTET STRING,
300 registeredID [8] OBJECT IDENTIFIER}
302 OtherName ::= SEQUENCE {
303 type-id OBJECT IDENTIFIER,
304 value [0] EXPLICIT ANY DEFINED BY type-id }
306 EDIPartyName ::= SEQUENCE {
307 nameAssigner [0] DirectoryString OPTIONAL,
308 partyName [1] DirectoryString }
310 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
311 const DERItem
*generalNameContent
,
312 void *context
, parseGeneralNameCallback callback
) {
314 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
315 return callback(context
, GNT_OtherName
, generalNameContent
);
316 case ASN1_CONTEXT_SPECIFIC
| 1:
317 return callback(context
, GNT_RFC822Name
, generalNameContent
);
318 case ASN1_CONTEXT_SPECIFIC
| 2:
319 return callback(context
, GNT_DNSName
, generalNameContent
);
320 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
321 return callback(context
, GNT_X400Address
, generalNameContent
);
322 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
323 return callback(context
, GNT_DirectoryName
, generalNameContent
);
324 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
325 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
326 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
328 /* Technically I don't think this is valid, but there are certs out
329 in the wild that use a constructed IA5String. In particular the
330 VeriSign Time Stamping Authority CA.cer does this. */
331 DERDecodedInfo uriContent
;
332 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
333 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
334 return callback(context
, GNT_URI
, &uriContent
.content
);
336 case ASN1_CONTEXT_SPECIFIC
| 6:
337 return callback(context
, GNT_URI
, generalNameContent
);
338 case ASN1_CONTEXT_SPECIFIC
| 7:
339 return callback(context
, GNT_IPAddress
, generalNameContent
);
340 case ASN1_CONTEXT_SPECIFIC
| 8:
341 return callback(context
, GNT_RegisteredID
, generalNameContent
);
346 return errSecInvalidCertificate
;
349 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
350 void *context
, parseGeneralNameCallback callback
) {
352 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
353 require_noerr_quiet(drtn
, badDER
);
354 DERDecodedInfo generalNameContent
;
355 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
357 OSStatus status
= parseGeneralNameContentProperty(
358 generalNameContent
.tag
, &generalNameContent
.content
, context
,
363 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
364 return errSecSuccess
;
367 return errSecInvalidCertificate
;
370 static OSStatus
parseGeneralNames(const DERItem
*generalNames
, void *context
,
371 parseGeneralNameCallback callback
) {
372 DERDecodedInfo generalNamesContent
;
373 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
374 require_noerr_quiet(drtn
, badDER
);
375 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
376 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
379 return errSecInvalidCertificate
;
385 GeneralName ::= CHOICE {
386 otherName [0] OtherName,
387 rfc822Name [1] IA5String,
388 dNSName [2] IA5String,
389 x400Address [3] ORAddress,
390 directoryName [4] Name,
391 ediPartyName [5] EDIPartyName,
392 uniformResourceIdentifier [6] IA5String,
393 iPAddress [7] OCTET STRING,
394 registeredID [8] OBJECT IDENTIFIER}
396 EDIPartyName ::= SEQUENCE {
397 nameAssigner [0] DirectoryString OPTIONAL,
398 partyName [1] DirectoryString }
400 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
401 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
403 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
404 generalName
->nameType
= GNT_OtherName
;
405 generalName
->berEncoded
= true;
406 generalName
->name
= *generalNameContent
;
408 case ASN1_CONTEXT_SPECIFIC
| 1:
410 generalName
->nameType
= GNT_RFC822Name
;
411 generalName
->berEncoded
= false;
412 generalName
->name
= *generalNameContent
;
414 case ASN1_CONTEXT_SPECIFIC
| 2:
416 generalName
->nameType
= GNT_DNSName
;
417 generalName
->berEncoded
= false;
418 generalName
->name
= *generalNameContent
;
420 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
421 generalName
->nameType
= GNT_X400Address
;
422 generalName
->berEncoded
= true;
423 generalName
->name
= *generalNameContent
;
425 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
426 generalName
->nameType
= GNT_DirectoryName
;
427 generalName
->berEncoded
= true;
428 generalName
->name
= *generalNameContent
;
430 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
431 generalName
->nameType
= GNT_EdiPartyName
;
432 generalName
->berEncoded
= true;
433 generalName
->name
= *generalNameContent
;
435 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
437 /* Technically I don't think this is valid, but there are certs out
438 in the wild that use a constructed IA5String. In particular the
439 VeriSign Time Stamping Authority CA.cer does this. */
440 DERDecodedInfo decoded
;
441 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
442 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
443 generalName
->nameType
= GNT_URI
;
444 generalName
->berEncoded
= false;
445 generalName
->name
= decoded
.content
;
448 case ASN1_CONTEXT_SPECIFIC
| 6:
449 generalName
->nameType
= GNT_URI
;
450 generalName
->berEncoded
= false;
451 generalName
->name
= *generalNameContent
;
453 case ASN1_CONTEXT_SPECIFIC
| 7:
454 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
455 8 octects, addr/mask for ipv6 it's 32. */
456 generalName
->nameType
= GNT_IPAddress
;
457 generalName
->berEncoded
= false;
458 generalName
->name
= *generalNameContent
;
460 case ASN1_CONTEXT_SPECIFIC
| 8:
461 /* name is the content of an OID. */
462 generalName
->nameType
= GNT_RegisteredID
;
463 generalName
->berEncoded
= false;
464 generalName
->name
= *generalNameContent
;
470 return errSecSuccess
;
472 return errSecInvalidCertificate
;
476 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
478 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
479 CFIndex
*count
, SecCEGeneralName
**name
) {
480 SecCEGeneralName
*generalNames
= NULL
;
482 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
483 require_noerr_quiet(drtn
, badDER
);
484 DERDecodedInfo generalNameContent
;
485 CFIndex generalNamesCount
= 0;
486 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
490 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
492 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
494 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
496 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
498 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
499 &generalNameContent
.content
, &generalNames
[ix
])) {
504 *count
= generalNamesCount
;
505 *name
= generalNames
;
506 return errSecSuccess
;
511 return errSecInvalidCertificate
;
514 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
515 CFIndex
*count
, SecCEGeneralName
**name
) {
516 DERDecodedInfo generalNamesContent
;
517 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
518 require_noerr_quiet(drtn
, badDER
);
519 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
521 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
522 return errSecSuccess
;
524 return errSecInvalidCertificate
;
528 /************************************************************************/
529 /************************** X.509 Name Parsing **************************/
530 /************************************************************************/
532 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
533 const DERItem
*value
, CFIndex rdnIX
);
535 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
536 parseX501NameCallback callback
) {
538 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
539 require_noerr_quiet(drtn
, badDER
);
540 DERDecodedInfo atvContent
;
542 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
543 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
544 DERAttributeTypeAndValue atv
;
545 drtn
= DERParseSequenceContent(&atvContent
.content
,
546 DERNumAttributeTypeAndValueItemSpecs
,
547 DERAttributeTypeAndValueItemSpecs
,
549 require_noerr_quiet(drtn
, badDER
);
550 require_quiet(atv
.type
.length
!= 0, badDER
);
551 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
555 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
557 return errSecSuccess
;
559 return errSecInvalidCertificate
;
562 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
563 parseX501NameCallback callback
) {
565 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
566 require_noerr_quiet(drtn
, badDER
);
567 DERDecodedInfo currDecoded
;
568 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
569 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
570 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
575 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
577 return errSecSuccess
;
580 return errSecInvalidCertificate
;
583 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
584 parseX501NameCallback callback
) {
585 DERDecodedInfo x501NameContent
;
586 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
587 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
588 return errSecInvalidCertificate
;
590 return parseX501NameContent(&x501NameContent
.content
, context
,
595 /************************************************************************/
596 /********************** Extension Parsing Routines **********************/
597 /************************************************************************/
599 static void SecCEPSubjectKeyIdentifier(SecCertificateRefP certificate
,
600 const SecCertificateExtension
*extn
) {
601 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
602 DERDecodedInfo keyIdentifier
;
603 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
604 require_noerr_quiet(drtn
, badDER
);
605 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
606 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
610 secdebug("cert", "Invalid SubjectKeyIdentifier Extension");
613 static void SecCEPKeyUsage(SecCertificateRefP certificate
,
614 const SecCertificateExtension
*extn
) {
615 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
616 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
617 DERDecodedInfo bitStringContent
;
618 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
619 require_noerr_quiet(drtn
, badDER
);
620 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
621 DERSize len
= bitStringContent
.content
.length
- 1;
622 require_quiet(len
== 1 || len
== 2, badDER
);
623 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
624 require_quiet(numUnusedBits
< 8, badDER
);
625 /* Flip the bits in the bit string so the first bit in the lsb. */
626 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
627 uint_fast16_t value
= bitStringContent
.content
.data
[1];
630 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
636 for (ix
= 0; ix
< bits
; ++ix
) {
642 certificate
->_keyUsage
= keyUsage
;
645 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
648 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRefP certificate
,
649 const SecCertificateExtension
*extn
) {
650 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
653 static void SecCEPSubjectAltName(SecCertificateRefP certificate
,
654 const SecCertificateExtension
*extn
) {
655 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
656 certificate
->_subjectAltName
= extn
;
659 static void SecCEPIssuerAltName(SecCertificateRefP certificate
,
660 const SecCertificateExtension
*extn
) {
661 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
664 static void SecCEPBasicConstraints(SecCertificateRefP certificate
,
665 const SecCertificateExtension
*extn
) {
666 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
667 DERBasicConstraints basicConstraints
;
668 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
669 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
670 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
671 require_noerr_quiet(DERParseBoolean(&basicConstraints
.cA
, false,
672 &certificate
->_basicConstraints
.isCA
), badDER
);
673 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
674 require_noerr_quiet(DERParseInteger(
675 &basicConstraints
.pathLenConstraint
,
676 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
677 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
679 certificate
->_basicConstraints
.present
= true;
680 certificate
->_basicConstraints
.critical
= extn
->critical
;
683 certificate
->_basicConstraints
.present
= false;
684 secdebug("cert", "Invalid BasicConstraints Extension");
687 static void SecCEPCrlDistributionPoints(SecCertificateRefP certificate
,
688 const SecCertificateExtension
*extn
) {
689 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
693 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
695 PolicyInformation ::= SEQUENCE {
696 policyIdentifier CertPolicyId,
697 policyQualifiers SEQUENCE SIZE (1..MAX) OF
698 PolicyQualifierInfo OPTIONAL }
700 CertPolicyId ::= OBJECT IDENTIFIER
702 PolicyQualifierInfo ::= SEQUENCE {
703 policyQualifierId PolicyQualifierId,
704 qualifier ANY DEFINED BY policyQualifierId }
706 static void SecCEPCertificatePolicies(SecCertificateRefP certificate
,
707 const SecCertificateExtension
*extn
) {
708 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
711 SecCEPolicyInformation
*policies
= NULL
;
712 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
713 require_noerr_quiet(drtn
, badDER
);
714 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
715 DERDecodedInfo piContent
;
716 DERSize policy_count
= 0;
717 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
718 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
721 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
722 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
724 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
725 DERSize policy_ix
= 0;
726 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
727 DERPolicyInformation pi
;
728 drtn
= DERParseSequenceContent(&piContent
.content
,
729 DERNumPolicyInformationItemSpecs
,
730 DERPolicyInformationItemSpecs
,
732 require_noerr_quiet(drtn
, badDER
);
733 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
734 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
736 certificate
->_certificatePolicies
.present
= true;
737 certificate
->_certificatePolicies
.critical
= extn
->critical
;
738 certificate
->_certificatePolicies
.numPolicies
= (uint32_t)policy_count
;
739 certificate
->_certificatePolicies
.policies
= policies
;
744 certificate
->_certificatePolicies
.present
= false;
745 secdebug("cert", "Invalid CertificatePolicies Extension");
749 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
751 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
752 issuerDomainPolicy CertPolicyId,
753 subjectDomainPolicy CertPolicyId }
756 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
757 const SecCertificateExtension
*extn
) {
758 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
761 SecCEPolicyMapping
*mappings
= NULL
;
762 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
763 require_noerr_quiet(drtn
, badDER
);
764 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
765 DERDecodedInfo pmContent
;
766 DERSize mapping_count
= 0;
767 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
768 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
771 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
773 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
774 DERSize mapping_ix
= 0;
775 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
777 drtn
= DERParseSequenceContent(&pmContent
.content
,
778 DERNumPolicyMappingItemSpecs
,
779 DERPolicyMappingItemSpecs
,
781 require_noerr_quiet(drtn
, badDER
);
782 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
783 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
785 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
786 certificate
->_policyMappings
.present
= true;
787 certificate
->_policyMappings
.critical
= extn
->critical
;
788 certificate
->_policyMappings
.numMappings
= mapping_count
;
789 certificate
->_policyMappings
.mappings
= mappings
;
794 CFReleaseSafe(mappings
);
795 certificate
->_policyMappings
.present
= false;
796 secdebug("cert", "Invalid CertificatePolicies Extension");
799 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
800 const SecCertificateExtension
*extn
) {
801 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
804 CFMutableDictionaryRef mappings
= NULL
;
805 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
806 require_noerr_quiet(drtn
, badDER
);
807 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
808 DERDecodedInfo pmContent
;
809 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
810 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
812 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
813 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
815 drtn
= DERParseSequenceContent(&pmContent
.content
,
816 DERNumPolicyMappingItemSpecs
,
817 DERPolicyMappingItemSpecs
,
819 require_noerr_quiet(drtn
, badDER
);
821 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
822 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
823 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
824 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
825 CFMutableArrayRef sdps
=
826 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
828 CFArrayAppendValue(sdps
, sdp
);
830 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
831 &kCFTypeArrayCallBacks
), badDER
);
832 CFDictionarySetValue(mappings
, idp
, sdps
);
836 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
837 certificate
->_policyMappings
= mappings
;
840 CFReleaseSafe(mappings
);
841 certificate
->_policyMappings
= NULL
;
842 secdebug("cert", "Invalid CertificatePolicies Extension");
847 AuthorityKeyIdentifier ::= SEQUENCE {
848 keyIdentifier [0] KeyIdentifier OPTIONAL,
849 authorityCertIssuer [1] GeneralNames OPTIONAL,
850 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
851 -- authorityCertIssuer and authorityCertSerialNumber MUST both
852 -- be present or both be absent
854 KeyIdentifier ::= OCTET STRING
856 static void SecCEPAuthorityKeyIdentifier(SecCertificateRefP certificate
,
857 const SecCertificateExtension
*extn
) {
858 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
859 DERAuthorityKeyIdentifier akid
;
861 drtn
= DERParseSequence(&extn
->extnValue
,
862 DERNumAuthorityKeyIdentifierItemSpecs
,
863 DERAuthorityKeyIdentifierItemSpecs
,
864 &akid
, sizeof(akid
));
865 require_noerr_quiet(drtn
, badDER
);
866 if (akid
.keyIdentifier
.length
) {
867 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
869 if (akid
.authorityCertIssuer
.length
||
870 akid
.authorityCertSerialNumber
.length
) {
871 require_quiet(akid
.authorityCertIssuer
.length
&&
872 akid
.authorityCertSerialNumber
.length
, badDER
);
873 /* Perhaps put in a subsection called Authority Certificate Issuer. */
874 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
875 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
880 secdebug("cert", "Invalid AuthorityKeyIdentifier Extension");
883 static void SecCEPPolicyConstraints(SecCertificateRefP certificate
,
884 const SecCertificateExtension
*extn
) {
885 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
886 DERPolicyConstraints pc
;
888 drtn
= DERParseSequence(&extn
->extnValue
,
889 DERNumPolicyConstraintsItemSpecs
,
890 DERPolicyConstraintsItemSpecs
,
892 require_noerr_quiet(drtn
, badDER
);
893 if (pc
.requireExplicitPolicy
.length
) {
894 require_noerr_quiet(DERParseInteger(
895 &pc
.requireExplicitPolicy
,
896 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
897 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
899 if (pc
.inhibitPolicyMapping
.length
) {
900 require_noerr_quiet(DERParseInteger(
901 &pc
.inhibitPolicyMapping
,
902 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
903 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
906 certificate
->_policyConstraints
.present
= true;
907 certificate
->_policyConstraints
.critical
= extn
->critical
;
911 certificate
->_policyConstraints
.present
= false;
912 secdebug("cert", "Invalid PolicyConstraints Extension");
915 static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate
,
916 const SecCertificateExtension
*extn
) {
917 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
921 InhibitAnyPolicy ::= SkipCerts
923 SkipCerts ::= INTEGER (0..MAX)
925 static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate
,
926 const SecCertificateExtension
*extn
) {
927 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
928 require_noerr_quiet(DERParseInteger(
930 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
933 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
934 secdebug("cert", "Invalid InhibitAnyPolicy Extension");
938 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
940 AuthorityInfoAccessSyntax ::=
941 SEQUENCE SIZE (1..MAX) OF AccessDescription
943 AccessDescription ::= SEQUENCE {
944 accessMethod OBJECT IDENTIFIER,
945 accessLocation GeneralName }
947 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
949 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
951 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
953 static void SecCEPAuthorityInfoAccess(SecCertificateRefP certificate
,
954 const SecCertificateExtension
*extn
) {
955 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
958 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
959 require_noerr_quiet(drtn
, badDER
);
960 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
961 DERDecodedInfo adContent
;
962 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
963 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
964 DERAccessDescription ad
;
965 drtn
= DERParseSequenceContent(&adContent
.content
,
966 DERNumAccessDescriptionItemSpecs
,
967 DERAccessDescriptionItemSpecs
,
969 require_noerr_quiet(drtn
, badDER
);
970 CFMutableArrayRef
*urls
;
971 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
972 urls
= &certificate
->_ocspResponders
;
973 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
974 urls
= &certificate
->_caIssuers
;
978 DERDecodedInfo generalNameContent
;
979 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
980 require_noerr_quiet(drtn
, badDER
);
981 switch (generalNameContent
.tag
) {
983 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
984 /* Technically I don't think this is valid, but there are certs out
985 in the wild that use a constructed IA5String. In particular the
986 VeriSign Time Stamping Authority CA.cer does this. */
988 case ASN1_CONTEXT_SPECIFIC
| 6:
990 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
991 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
992 kCFStringEncodingASCII
, NULL
);
995 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
996 CFArrayAppendValue(*urls
, url
);
1002 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02x v: %.*s",
1003 generalNameContent
.tag
, (int)generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1008 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1011 secdebug("cert", "failed to parse Authority Information Access extension");
1014 static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate
,
1015 const SecCertificateExtension
*extn
) {
1016 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1019 static void SecCEPNetscapeCertType(SecCertificateRefP certificate
,
1020 const SecCertificateExtension
*extn
) {
1021 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1024 static void SecCEPEntrustVersInfo(SecCertificateRefP certificate
,
1025 const SecCertificateExtension
*extn
) {
1026 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1029 /* Dictionary key callback for comparing to DERItems. */
1030 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1031 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1034 /* Dictionary key callback calculating the hash of a DERItem. */
1035 static CFHashCode
SecDERItemHash(const void *value
) {
1036 const DERItem
*derItem
= (const DERItem
*)value
;
1037 CFHashCode hash
= derItem
->length
;
1038 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1039 for (; ix
< derItem
->length
; ++ix
) {
1040 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1046 /* Dictionary key callbacks using the above 2 functions. */
1047 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1051 NULL
, /* copyDescription */
1052 SecDERItemEqual
, /* equal */
1053 SecDERItemHash
/* hash */
1056 static void SecCertificateRegisterClass(void) {
1057 static const CFRuntimeClass kSecCertificateClass
= {
1059 "SecCertificate", /* class name */
1062 SecCertificateDestroy
, /* dealloc */
1063 SecCertificateEqual
, /* equal */
1064 SecCertificateHash
, /* hash */
1065 NULL
, /* copyFormattingDesc */
1066 SecCertificateDescribe
/* copyDebugDesc */
1069 kSecCertificateTypeID
= _CFRuntimeRegisterClass(&kSecCertificateClass
);
1071 /* Build a dictionary that maps from extension OIDs to callback functions
1072 which can parse the extension of the type given. */
1073 static const void *extnOIDs
[] = {
1074 &oidSubjectKeyIdentifier
,
1076 &oidPrivateKeyUsagePeriod
,
1079 &oidBasicConstraints
,
1080 &oidCrlDistributionPoints
,
1081 &oidCertificatePolicies
,
1083 &oidAuthorityKeyIdentifier
,
1084 &oidPolicyConstraints
,
1085 &oidExtendedKeyUsage
,
1086 &oidInhibitAnyPolicy
,
1087 &oidAuthorityInfoAccess
,
1088 &oidSubjectInfoAccess
,
1089 &oidNetscapeCertType
,
1092 static const void *extnParsers
[] = {
1093 SecCEPSubjectKeyIdentifier
,
1095 SecCEPPrivateKeyUsagePeriod
,
1096 SecCEPSubjectAltName
,
1097 SecCEPIssuerAltName
,
1098 SecCEPBasicConstraints
,
1099 SecCEPCrlDistributionPoints
,
1100 SecCEPCertificatePolicies
,
1101 SecCEPPolicyMappings
,
1102 SecCEPAuthorityKeyIdentifier
,
1103 SecCEPPolicyConstraints
,
1104 SecCEPExtendedKeyUsage
,
1105 SecCEPInhibitAnyPolicy
,
1106 SecCEPAuthorityInfoAccess
,
1107 SecCEPSubjectInfoAccess
,
1108 SecCEPNetscapeCertType
,
1109 SecCEPEntrustVersInfo
1111 gExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1112 extnParsers
, sizeof(extnOIDs
) / sizeof(*extnOIDs
),
1113 &SecDERItemKeyCallBacks
, NULL
);
1116 /* Given the contents of an X.501 Name return the contents of a normalized
1118 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1119 const DERItem
*x501name
) {
1120 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1121 CFIndex length
= x501name
->length
;
1122 CFDataSetLength(result
, length
);
1123 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1126 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1128 require_noerr_quiet(drtn
, badDER
);
1131 /* Always points to last rdn tag. */
1132 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1133 /* Offset relative to base of current rdn set tag. */
1134 CFIndex rdnTagLocation
= 0;
1135 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1136 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1137 /* We don't allow empty RDNs. */
1138 require_quiet(rdn
.content
.length
!= 0, badDER
);
1139 /* Length of the tag and length of the current rdn. */
1140 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1141 CFIndex rdnContentLength
= rdn
.content
.length
;
1142 /* Copy the tag and length of the RDN. */
1143 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1146 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1148 /* Always points to tag of current atv sequence. */
1149 const DERByte
*atvTag
= atvSeq
.nextItem
;
1150 /* Offset relative to base of current atv sequence tag. */
1151 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1152 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1153 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1154 /* Length of the tag and length of the current atv. */
1155 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1156 CFIndex atvContentLength
= atv
.content
.length
;
1157 /* Copy the tag and length of the atv and the atv itself. */
1158 memcpy(base
+ atvTagLocation
, atvTag
,
1159 atvTLLength
+ atv
.content
.length
);
1161 /* Now decode the atv sequence. */
1162 DERAttributeTypeAndValue atvPair
;
1163 drtn
= DERParseSequenceContent(&atv
.content
,
1164 DERNumAttributeTypeAndValueItemSpecs
,
1165 DERAttributeTypeAndValueItemSpecs
,
1166 &atvPair
, sizeof(atvPair
));
1167 require_noerr_quiet(drtn
, badDER
);
1168 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1169 DERDecodedInfo value
;
1170 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1171 require_noerr_quiet(drtn
, badDER
);
1173 /* (c) attribute values in PrintableString are not case sensitive
1174 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1176 (d) attribute values in PrintableString are compared after
1177 removing leading and trailing white space and converting internal
1178 substrings of one or more consecutive white space characters to a
1180 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1181 /* Offset relative to base of current value tag. */
1182 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1183 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1184 CFIndex valueContentLength
= value
.content
.length
;
1186 /* Now copy all the bytes, but convert to upper case while
1187 doing so and convert multiple whitespace chars into a
1189 bool lastWasBlank
= false;
1190 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1191 CFIndex valueCurrentLocation
= valueLocation
;
1193 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1194 UInt8 ch
= value
.content
.data
[ix
];
1199 /* Don't insert a space for first character
1201 if (valueCurrentLocation
> valueLocation
) {
1202 base
[valueCurrentLocation
++] = ' ';
1204 lastWasBlank
= true;
1207 lastWasBlank
= false;
1208 if ('a' <= ch
&& ch
<= 'z') {
1209 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1211 base
[valueCurrentLocation
++] = ch
;
1215 /* Finally if lastWasBlank remove the trailing space. */
1216 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1217 valueCurrentLocation
--;
1219 /* Adjust content length to normalized length. */
1220 valueContentLength
= valueCurrentLocation
- valueLocation
;
1222 /* Number of bytes by which the length should be shorted. */
1223 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1224 if (lengthDiff
== 0) {
1225 /* Easy case no need to adjust lengths. */
1227 /* Hard work we need to go back and fix up length fields
1229 1) The value itself.
1230 2) The ATV Sequence containing type/value
1231 3) The RDN Set containing one or more atv pairs.
1235 /* Step 1 fix up length of value. */
1236 /* Length of value tag and length minus the tag. */
1237 DERSize newValueTLLength
= valueTLLength
- 1;
1238 drtn
= DEREncodeLength(valueContentLength
,
1239 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1240 /* Add the length of the tag back in. */
1242 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1244 /* The size of the length field changed, let's slide
1245 the value back by valueLLDiff bytes. */
1246 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1247 base
+ valueTagLocation
+ valueTLLength
,
1248 valueContentLength
);
1249 /* The length diff for the enclosing object. */
1250 lengthDiff
+= valueLLDiff
;
1253 /* Step 2 fix up length of the enclosing ATV Sequence. */
1254 atvContentLength
-= lengthDiff
;
1255 DERSize newATVTLLength
= atvTLLength
- 1;
1256 drtn
= DEREncodeLength(atvContentLength
,
1257 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1258 /* Add the length of the tag back in. */
1260 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1262 /* The size of the length field changed, let's slide
1263 the value back by valueLLDiff bytes. */
1264 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1265 base
+ atvTagLocation
+ atvTLLength
,
1267 /* The length diff for the enclosing object. */
1268 lengthDiff
+= atvLLDiff
;
1269 atvTLLength
= newATVTLLength
;
1272 /* Step 3 fix up length of enclosing RDN Set. */
1273 rdnContentLength
-= lengthDiff
;
1274 DERSize newRDNTLLength
= rdnTLLength
- 1;
1275 drtn
= DEREncodeLength(rdnContentLength
,
1276 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1277 /* Add the length of the tag back in. */
1279 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1281 /* The size of the length field changed, let's slide
1282 the value back by valueLLDiff bytes. */
1283 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1284 base
+ rdnTagLocation
+ rdnTLLength
,
1286 /* The length diff for the enclosing object. */
1287 lengthDiff
+= rdnLLDiff
;
1288 rdnTLLength
= newRDNTLLength
;
1290 /* Adjust the locations that might have changed due to
1292 atvTagLocation
-= rdnLLDiff
;
1296 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1297 atvTag
= atvSeq
.nextItem
;
1299 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1300 rdnTag
= rdnSeq
.nextItem
;
1302 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1303 /* Truncate the result to the proper length. */
1304 CFDataSetLength(result
, rdnTagLocation
);
1313 /* AUDIT[securityd]:
1314 certificate->_der is a caller provided data of any length (might be 0).
1316 Top level certificate decode.
1318 static bool SecCertificateParse(SecCertificateRefP certificate
)
1323 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1325 /* top level decode */
1326 DERSignedCertCrl signedCert
;
1327 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1328 DERSignedCertCrlItemSpecs
, &signedCert
,
1329 sizeof(signedCert
));
1330 require_noerr_quiet(drtn
, badCert
);
1331 /* Store tbs since we need to digest it for verification later on. */
1332 certificate
->_tbs
= signedCert
.tbs
;
1334 /* decode the TBSCert - it was saved in full DER form */
1336 drtn
= DERParseSequence(&signedCert
.tbs
,
1337 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1338 &tbsCert
, sizeof(tbsCert
));
1339 require_noerr_quiet(drtn
, badCert
);
1341 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1342 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1343 of the params field. */
1344 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1345 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1346 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1347 require_noerr_quiet(drtn
, badCert
);
1349 /* The contents of signedCert.sig is a bit string whose contents
1350 are the signature itself. */
1351 DERByte numUnusedBits
;
1352 drtn
= DERParseBitString(&signedCert
.sig
,
1353 &certificate
->_signature
, &numUnusedBits
);
1354 require_noerr_quiet(drtn
, badCert
);
1356 /* Now decode the tbsCert. */
1358 /* First we turn the optional version into an int. */
1359 if (tbsCert
.version
.length
) {
1360 DERDecodedInfo decoded
;
1361 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1362 require_noerr_quiet(drtn
, badCert
);
1363 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1364 require_quiet(decoded
.content
.length
== 1, badCert
);
1365 certificate
->_version
= decoded
.content
.data
[0];
1366 require_quiet(certificate
->_version
> 0, badCert
);
1367 require_quiet(certificate
->_version
< 3, badCert
);
1369 certificate
->_version
= 0;
1372 /* The serial number is in the tbsCert.serialNum - it was saved in
1373 INTEGER form without the tag and length. */
1374 certificate
->_serialNum
= tbsCert
.serialNum
;
1375 certificate
->_serialNumber
= CFDataCreate(allocator
,
1376 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1378 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1379 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1380 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1381 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1382 require_noerr_quiet(drtn
, badCert
);
1384 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1385 and length fields. */
1386 certificate
->_issuer
= tbsCert
.issuer
;
1387 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1390 /* sequence we're given: decode the tbsCerts Validity sequence. */
1391 DERValidity validity
;
1392 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1393 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1394 &validity
, sizeof(validity
));
1395 require_noerr_quiet(drtn
, badCert
);
1396 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1397 &certificate
->_notBefore
), badCert
);
1398 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1399 &certificate
->_notAfter
), badCert
);
1401 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1402 and length fields. */
1403 certificate
->_subject
= tbsCert
.subject
;
1404 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1407 /* sequence we're given: encoded DERSubjPubKeyInfo */
1408 DERSubjPubKeyInfo pubKeyInfo
;
1409 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1410 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1411 &pubKeyInfo
, sizeof(pubKeyInfo
));
1412 require_noerr_quiet(drtn
, badCert
);
1414 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1415 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1416 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1417 &certificate
->_algId
, sizeof(certificate
->_algId
));
1418 require_noerr_quiet(drtn
, badCert
);
1420 /* Now we can figure out the key's algorithm id and params based on
1421 certificate->_algId.oid. */
1423 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1424 are a PKCS1 format RSA key. */
1425 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1426 &certificate
->_pubKeyDER
, &numUnusedBits
);
1427 require_noerr_quiet(drtn
, badCert
);
1429 /* The contents of tbsCert.issuerID is a bit string. */
1430 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1432 /* The contents of tbsCert.subjectID is a bit string. */
1433 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1436 if (tbsCert
.extensions
.length
) {
1437 CFIndex extensionCount
= 0;
1440 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1442 require_noerr_quiet(drtn
, badCert
);
1443 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1444 DERDecodedInfo currDecoded
;
1445 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1447 /* ! = MUST recognize ? = SHOULD recognize
1450 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1451 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1452 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1453 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1454 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1455 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1456 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1457 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1459 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1460 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1461 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1462 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1463 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1464 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1465 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1466 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1468 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1469 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1474 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1476 /* Put some upper limit on the number of extentions allowed. */
1477 require_quiet(extensionCount
< 10000, badCert
);
1478 certificate
->_extensionCount
= extensionCount
;
1479 certificate
->_extensions
=
1480 malloc(sizeof(SecCertificateExtension
) * extensionCount
);
1483 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1484 require_noerr_quiet(drtn
, badCert
);
1485 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1486 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1487 require_quiet(drtn
== DR_Success
||
1488 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1489 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1491 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1492 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1493 &extn
, sizeof(extn
));
1494 require_noerr_quiet(drtn
, badCert
);
1495 /* Copy stuff into certificate->extensions[ix]. */
1496 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1497 require_noerr_quiet(drtn
= DERParseBoolean(&extn
.critical
, false,
1498 &certificate
->_extensions
[ix
].critical
), badCert
);
1499 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1501 SecCertificateExtensionParser parser
=
1502 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1503 gExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1505 /* Invoke the parser. */
1506 parser(certificate
, &certificate
->_extensions
[ix
]);
1507 } else if (certificate
->_extensions
[ix
].critical
) {
1508 secdebug("cert", "Found unknown critical extension");
1509 certificate
->_foundUnknownCriticalExtension
= true;
1511 secdebug("cert", "Found unknown non critical extension");
1523 /* Public API functions. */
1524 CFTypeID
SecCertificateGetTypeIDP(void) {
1525 pthread_once(&kSecCertificateRegisterClass
, SecCertificateRegisterClass
);
1526 return kSecCertificateTypeID
;
1529 SecCertificateRefP
SecCertificateCreateWithBytesP(CFAllocatorRef allocator
,
1530 const UInt8
*der_bytes
, CFIndex der_length
) {
1533 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1534 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1535 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1537 memset((char*)result
+ sizeof(result
->_base
), 0,
1538 sizeof(*result
) - sizeof(result
->_base
));
1539 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1540 result
->_der
.length
= der_length
;
1541 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1542 if (!SecCertificateParse(result
)) {
1550 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1551 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1552 const UInt8
*der_bytes
, CFIndex der_length
);
1554 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1555 const UInt8
*der_bytes
, CFIndex der_length
) {
1556 return SecCertificateCreateWithBytesP(allocator
, der_bytes
, der_length
);
1558 /* @@@ End of placeholder. */
1560 /* AUDIT[securityd](done):
1561 der_certificate is a caller provided data of any length (might be 0), only
1562 its cf type has been checked.
1564 SecCertificateRefP
SecCertificateCreateWithDataP(CFAllocatorRef allocator
,
1565 CFDataRef der_certificate
) {
1566 check(der_certificate
);
1567 CFIndex size
= sizeof(struct __SecCertificate
);
1568 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1569 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1571 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1572 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1573 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1574 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1575 if (!SecCertificateParse(result
)) {
1583 CFDataRef
SecCertificateCopyDataP(SecCertificateRefP certificate
) {
1586 if (certificate
->_der_data
) {
1587 CFRetain(certificate
->_der_data
);
1588 result
= certificate
->_der_data
;
1590 result
= CFDataCreate(CFGetAllocator(certificate
),
1591 certificate
->_der
.data
, certificate
->_der
.length
);
1593 /* FIXME: If we wish to cache result we need to lock the certificate.
1594 Also this create 2 copies of the certificate data which is somewhat
1597 certificate
->_der_data
= result
;
1604 CFIndex
SecCertificateGetLengthP(SecCertificateRefP certificate
) {
1605 return certificate
->_der
.length
;
1608 const UInt8
*SecCertificateGetBytePtrP(SecCertificateRefP certificate
) {
1609 return certificate
->_der
.data
;
1612 /* From rfc3280 - Appendix B. ASN.1 Notes
1614 Object Identifiers (OIDs) are used throughout this specification to
1615 identify certificate policies, public key and signature algorithms,
1616 certificate extensions, etc. There is no maximum size for OIDs.
1617 This specification mandates support for OIDs which have arc elements
1618 with values that are less than 2^28, that is, they MUST be between 0
1619 and 268,435,455, inclusive. This allows each arc element to be
1620 represented within a single 32 bit word. Implementations MUST also
1621 support OIDs where the length of the dotted decimal (see [RFC 2252],
1622 section 4.1) string representation can be up to 100 bytes
1623 (inclusive). Implementations MUST be able to handle OIDs with up to
1624 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1625 contain OIDs that exceed these requirements. Likewise, CRL issuers
1626 SHOULD NOT issue CRLs which contain OIDs that exceed these
1630 /* Oids longer than this are considered invalid. */
1631 #define MAX_OID_SIZE 32
1633 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1634 const DERItem
*oid
) {
1636 if (oid
->length
== 0) {
1637 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1638 CFSTR("SecCertificate"));
1640 if (oid
->length
> MAX_OID_SIZE
) {
1641 return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"),
1642 CFSTR("SecCertificate"));
1645 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1647 // The first two levels are encoded into one byte, since the root level
1648 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1649 // y may be > 39, so we have to add special-case handling for this.
1650 uint32_t x
= oid
->data
[0] / 40;
1651 uint32_t y
= oid
->data
[0] % 40;
1654 // Handle special case for large y if x = 2
1658 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1661 for (x
= 1; x
< oid
->length
; ++x
)
1663 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1664 /* @@@ value may not span more than 4 bytes. */
1665 /* A max number of 20 values is allowed. */
1666 if (!(oid
->data
[x
] & 0x80))
1668 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), (unsigned long)value
);
1675 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1676 const DERItem
*oid
) {
1677 if (oid
->length
== 0) {
1678 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1679 CFSTR("SecCertificate"));
1682 /* Build the key we use to lookup the localized OID description. */
1683 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1684 oid
->length
* 3 + 5);
1685 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), (unsigned long)oid
->length
);
1687 for (ix
= 0; ix
< oid
->length
; ++ix
)
1688 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1690 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1691 if (CFEqual(oidKey
, name
)) {
1693 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1700 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1701 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1702 have a length of exactly 4 or 16 octects. */
1703 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1704 const DERItem
*ip
) {
1705 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1706 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1707 16 octects addr, or 32 octects addr/mask. */
1708 CFStringRef value
= NULL
;
1709 if (ip
->length
== 4) {
1710 value
= CFStringCreateWithFormat(allocator
, NULL
,
1711 CFSTR("%u.%u.%u.%u"),
1712 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1713 } else if (ip
->length
== 16) {
1714 value
= CFStringCreateWithFormat(allocator
, NULL
,
1715 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1716 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1717 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
1718 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
1719 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
1720 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
1727 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
1728 const DERItem
*oid
) {
1729 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1730 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
1731 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
1732 CFSTR("%@ (%@)"), name
, decimal
);
1739 void appendProperty(CFMutableArrayRef properties
,
1740 CFStringRef propertyType
, CFStringRef label
, CFTypeRef value
) {
1741 CFDictionaryRef property
;
1743 CFStringRef localizedLabel
= SecFrameworkCopyLocalizedString(label
,
1744 CFSTR("SecCertificate"));
1745 const void *all_keys
[4];
1746 all_keys
[0] = kSecPropertyKeyType
;
1747 all_keys
[1] = kSecPropertyKeyLabel
;
1748 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
1749 all_keys
[3] = kSecPropertyKeyValue
;
1750 const void *property_values
[] = {
1756 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1757 all_keys
, property_values
, value
? 4 : 3,
1758 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1759 CFRelease(localizedLabel
);
1761 const void *nolabel_keys
[2];
1762 nolabel_keys
[0] = kSecPropertyKeyType
;
1763 nolabel_keys
[1] = kSecPropertyKeyValue
;
1764 const void *property_values
[] = {
1768 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1769 nolabel_keys
, property_values
, 2,
1770 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1773 CFArrayAppendValue(properties
, property
);
1774 CFRelease(property
);
1778 #define UTC_TIME_NOSEC_ZULU_LEN 11
1780 #define UTC_TIME_ZULU_LEN 13
1781 /* YYMMDDhhmmssThhmm */
1782 #define UTC_TIME_LOCALIZED_LEN 17
1783 /* YYYYMMDDhhmmssZ */
1784 #define GENERALIZED_TIME_ZULU_LEN 15
1785 /* YYYYMMDDhhmmssThhmm */
1786 #define GENERALIZED_TIME_LOCALIZED_LEN 19
1788 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
1790 static inline SInt32
parseDecimalPair(const DERByte
**p
) {
1791 const DERByte
*cp
= *p
;
1793 return 10 * (cp
[0] - '0') + cp
[1] - '0';
1796 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1797 true if the date was valid and properly decoded, also return the result in
1798 absTime. Return false otherwise. */
1799 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
1805 bool isUtcLength
= false;
1806 bool isLocalized
= false;
1807 bool noSeconds
= false;
1809 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
1813 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
1816 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
1818 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
1821 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
1824 default: /* unknown format */
1828 /* Make sure the der tag fits the thing inside it. */
1829 if (tag
== ASN1_UTC_TIME
) {
1832 } else if (tag
== ASN1_GENERALIZED_TIME
) {
1839 const DERByte
*cp
= bytes
;
1840 /* Check that all characters are digits, except if localized the timezone
1841 indicator or if not localized the 'Z' at the end. */
1843 for (ix
= 0; ix
< length
; ++ix
) {
1844 if (!(isdigit(cp
[ix
]))) {
1845 if ((isLocalized
&& ix
== length
- 5 &&
1846 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
1847 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
1854 /* Initialize the fields in a gregorian date struct. */
1855 CFGregorianDate gdate
;
1857 SInt32 year
= parseDecimalPair(&cp
);
1859 /* 0 <= year < 50 : assume century 21 */
1860 gdate
.year
= 2000 + year
;
1861 } else if (year
< 70) {
1862 /* 50 <= year < 70 : illegal per PKIX */
1865 /* 70 < year <= 99 : assume century 20 */
1866 gdate
.year
= 1900 + year
;
1869 gdate
.year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
1871 gdate
.month
= parseDecimalPair(&cp
);
1872 gdate
.day
= parseDecimalPair(&cp
);
1873 gdate
.hour
= parseDecimalPair(&cp
);
1874 gdate
.minute
= parseDecimalPair(&cp
);
1878 gdate
.second
= parseDecimalPair(&cp
);
1881 CFTimeInterval timeZoneOffset
= 0;
1883 /* ZONE INDICATOR */
1884 SInt32 multiplier
= *cp
++ == '+' ? 60 : -60;
1885 timeZoneOffset
= multiplier
*
1886 (parseDecimalPair(&cp
) + 60 * parseDecimalPair(&cp
));
1891 secdebug("dateparse",
1892 "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f",
1893 (int)length
, bytes
, (int)gdate
.year
, gdate
.month
,
1894 gdate
.day
, gdate
.hour
, gdate
.minute
, gdate
.second
,
1895 timeZoneOffset
/ 60);
1897 if (!CFGregorianDateIsValid(gdate
, kCFGregorianAllUnits
))
1899 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
,
1903 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(gdate
, timeZone
);
1904 CFRelease(timeZone
);
1908 static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
1909 CFAbsoluteTime
*pabsTime
) {
1910 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
1912 if (absTime
== NULL_TIME
)
1915 *pabsTime
= absTime
;
1919 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1920 true if the date was valid and properly decoded, also return the result in
1921 absTime. Return false otherwise. */
1922 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
1923 CFAbsoluteTime
*absTime
) {
1926 if (dateChoice
->length
== 0)
1929 DERDecodedInfo decoded
;
1930 if (DERDecodeItem(dateChoice
, &decoded
))
1933 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
1937 static void appendDataProperty(CFMutableArrayRef properties
,
1938 CFStringRef label
, const DERItem
*der_data
) {
1939 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
1940 der_data
->data
, der_data
->length
);
1941 appendProperty(properties
, kSecPropertyTypeData
, label
, data
);
1945 static void appendUnparsedProperty(CFMutableArrayRef properties
,
1946 CFStringRef label
, const DERItem
*der_data
) {
1947 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1948 NULL
, CFSTR("Unparsed %@"), label
);
1949 appendDataProperty(properties
, newLabel
, der_data
);
1950 CFRelease(newLabel
);
1953 static void appendInvalidProperty(CFMutableArrayRef properties
,
1954 CFStringRef label
, const DERItem
*der_data
) {
1955 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1956 NULL
, CFSTR("Invalid %@"), label
);
1957 appendDataProperty(properties
, newLabel
, der_data
);
1958 CFRelease(newLabel
);
1961 static void appendDateContentProperty(CFMutableArrayRef properties
,
1962 CFStringRef label
, DERTag tag
, const DERItem
*dateContent
) {
1963 CFAbsoluteTime absTime
;
1964 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
1965 /* Date decode failure insert hex bytes instead. */
1966 return appendInvalidProperty(properties
, label
, dateContent
);
1968 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1969 appendProperty(properties
, kSecPropertyTypeDate
, label
, date
);
1973 static void appendDateProperty(CFMutableArrayRef properties
,
1974 CFStringRef label
, CFAbsoluteTime absTime
) {
1975 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1976 appendProperty(properties
, kSecPropertyTypeDate
, label
, date
);
1980 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
1981 CFStringRef label
, const DERItem
*ip
) {
1983 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
1985 appendProperty(properties
, kSecPropertyTypeString
, label
, value
);
1988 appendUnparsedProperty(properties
, label
, ip
);
1992 static void appendURLContentProperty(CFMutableArrayRef properties
,
1993 CFStringRef label
, const DERItem
*urlContent
) {
1994 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
1995 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
1997 appendProperty(properties
, kSecPropertyTypeURL
, label
, url
);
2000 appendInvalidProperty(properties
, label
, urlContent
);
2004 static void appendURLProperty(CFMutableArrayRef properties
,
2005 CFStringRef label
, const DERItem
*url
) {
2006 DERDecodedInfo decoded
;
2009 drtn
= DERDecodeItem(url
, &decoded
);
2010 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2011 appendInvalidProperty(properties
, label
, url
);
2013 appendURLContentProperty(properties
, label
, &decoded
.content
);
2017 static void appendOIDProperty(CFMutableArrayRef properties
,
2018 CFStringRef label
, const DERItem
*oid
) {
2019 CFStringRef oid_string
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2021 appendProperty(properties
, kSecPropertyTypeString
, label
, oid_string
);
2022 CFRelease(oid_string
);
2025 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2026 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2027 CFMutableArrayRef alg_props
=
2028 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2029 &kCFTypeArrayCallBacks
);
2030 appendOIDProperty(alg_props
, CFSTR("Algorithm"), &algorithm
->oid
);
2031 if (algorithm
->params
.length
) {
2032 if (algorithm
->params
.length
== 2 &&
2033 algorithm
->params
.data
[0] == ASN1_NULL
&&
2034 algorithm
->params
.data
[1] == 0) {
2035 /* @@@ Localize <NULL> or perhaps skip it? */
2036 appendProperty(alg_props
, kSecPropertyTypeString
,
2037 CFSTR("Parameters"), CFSTR("none"));
2039 appendUnparsedProperty(alg_props
, CFSTR("Parameters"),
2040 &algorithm
->params
);
2043 appendProperty(properties
, kSecPropertyTypeSection
, label
, alg_props
);
2044 CFRelease(alg_props
);
2047 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2048 const DERItem
*blob
) {
2049 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2050 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2051 blob
->length
* 3 - 1);
2052 for (ix
= 0; ix
< length
; ++ix
)
2054 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2056 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2061 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2062 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2063 CFStringRef blobFormat
= SecFrameworkCopyLocalizedString(
2064 CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate")
2065 /*, "format string for encoded field data (e.g. Sequence; 128 bytes; "
2066 "data = 00 00 ...)" */);
2067 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2068 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2069 blobFormat
, blobType
, blob
->length
, quanta
, hex
);
2071 CFRelease(blobFormat
);
2076 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2077 const DERItem
*string
, CFStringEncoding encoding
,
2078 bool printableOnly
) {
2079 /* Strip potential bogus trailing zero from printable strings. */
2080 DERSize length
= string
->length
;
2081 if (length
&& string
->data
[length
- 1] == 0) {
2082 /* Don't mess with the length of UTF16 strings though. */
2083 if (encoding
!= kCFStringEncodingUTF16
)
2086 /* A zero length string isn't considered printable. */
2087 if (!length
&& printableOnly
)
2090 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2091 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2092 passing false makes it treat it as native endian by default. */
2093 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2094 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2098 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2101 /* From rfc3280 - Appendix B. ASN.1 Notes
2103 CAs MUST force the serialNumber to be a non-negative integer, that
2104 is, the sign bit in the DER encoding of the INTEGER value MUST be
2105 zero - this can be done by adding a leading (leftmost) `00'H octet if
2106 necessary. This removes a potential ambiguity in mapping between a
2107 string of octets and an integer value.
2109 As noted in section 4.1.2.2, serial numbers can be expected to
2110 contain long integers. Certificate users MUST be able to handle
2111 serialNumber values up to 20 octets in length. Conformant CAs MUST
2112 NOT use serialNumber values longer than 20 octets.
2115 /* Return the given numeric data as a string: decimal up to 64 bits,
2117 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2118 const DERItem
*integer
) {
2120 CFIndex ix
, length
= integer
->length
;
2122 if (length
== 0 || length
> 8)
2123 return copyHexDescription(allocator
, integer
);
2125 for(ix
= 0; ix
< length
; ++ix
) {
2127 value
+= integer
->data
[ix
];
2130 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2133 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2134 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2138 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2139 case ASN1_PRINTABLE_STRING
:
2140 case ASN1_IA5_STRING
:
2141 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2142 case ASN1_UTF8_STRING
:
2143 case ASN1_GENERAL_STRING
:
2144 case ASN1_UNIVERSAL_STRING
:
2145 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2146 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2147 case ASN1_VIDEOTEX_STRING
: // 21
2148 case ASN1_VISIBLE_STRING
: // 26
2149 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2150 case ASN1_BMP_STRING
: // 30
2151 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2152 case ASN1_OCTET_STRING
:
2153 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Byte string"), CFSTR("bytes"),
2155 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2156 case ASN1_BIT_STRING
:
2157 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Bit string"), CFSTR("bits"),
2159 case (DERByte
)ASN1_CONSTR_SEQUENCE
:
2160 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Sequence"), CFSTR("bytes"),
2162 case (DERByte
)ASN1_CONSTR_SET
:
2163 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Set"), CFSTR("bytes"),
2165 case ASN1_OBJECT_ID
:
2166 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2169 /* "format string for undisplayed field data with a given DER tag" */
2170 return printableOnly
? NULL
: CFStringCreateWithFormat(allocator
, NULL
,
2171 CFSTR("not displayed (tag = %d; length %d)"),
2172 tag
, (int)derThing
->length
);
2176 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2177 const DERItem
*derThing
, bool printableOnly
) {
2178 DERDecodedInfo decoded
;
2181 drtn
= DERDecodeItem(derThing
, &decoded
);
2183 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2185 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2186 &decoded
.content
, false);
2190 static void appendDERThingProperty(CFMutableArrayRef properties
,
2191 CFStringRef label
, const DERItem
*derThing
) {
2192 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2194 appendProperty(properties
, kSecPropertyTypeString
, label
, value
);
2198 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2199 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2200 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2202 /* If there is more than one value pair we create a subsection for the
2203 second pair, and append things to the subsection for subsequent
2205 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2206 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2208 /* Since this is the second rdn pair for a given rdn, we setup a
2209 new subsection for this rdn. We remove the first property
2210 from the properties array and make it the first element in the
2211 subsection instead. */
2212 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2213 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2214 CFArrayAppendValue(rdn_props
, lastValue
);
2215 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2216 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, rdn_props
);
2217 properties
= rdn_props
;
2219 /* Since this is the third or later rdn pair we have already
2220 created a subsection in the top level properties array. Instead
2221 of appending to that directly we append to the array inside the
2223 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2224 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2228 /* Finally we append the new rdn value to the property array. */
2229 CFStringRef label
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2232 appendDERThingProperty(properties
, label
, rdnValue
);
2234 return errSecSuccess
;
2236 return errSecInvalidCertificate
;
2240 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2241 const DERItem
*rdnSetContent
) {
2242 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2243 &kCFTypeArrayCallBacks
);
2244 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2247 CFArrayRemoveAllValues(properties
);
2248 appendInvalidProperty(properties
, CFSTR("RDN"), rdnSetContent
);
2255 From rfc3739 - 3.1.2. Subject
2257 When parsing the subject here are some tips for a short name of the cert.
2258 Choice I: commonName
2259 Choice II: givenName
2260 Choice III: pseudonym
2262 The commonName attribute value SHALL, when present, contain a name
2263 of the subject. This MAY be in the subject's preferred
2264 presentation format, or a format preferred by the CA, or some
2265 other format. Pseudonyms, nicknames, and names with spelling
2266 other than defined by the registered name MAY be used. To
2267 understand the nature of the name presented in commonName,
2268 complying applications MAY have to examine present values of the
2269 givenName and surname attributes, or the pseudonym attribute.
2272 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2273 const DERItem
*x501NameContent
) {
2274 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2275 &kCFTypeArrayCallBacks
);
2276 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2279 CFArrayRemoveAllValues(properties
);
2280 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501NameContent
);
2286 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2287 const DERItem
*x501Name
) {
2288 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2289 &kCFTypeArrayCallBacks
);
2290 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2292 CFArrayRemoveAllValues(properties
);
2293 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501Name
);
2299 static void appendIntegerProperty(CFMutableArrayRef properties
,
2300 CFStringRef label
, const DERItem
*integer
) {
2301 CFStringRef string
= copyIntegerContentDescription(
2302 CFGetAllocator(properties
), integer
);
2303 appendProperty(properties
, kSecPropertyTypeString
, label
, string
);
2307 static void appendBoolProperty(CFMutableArrayRef properties
,
2308 CFStringRef label
, bool boolean
) {
2309 appendProperty(properties
, kSecPropertyTypeString
,
2310 label
, boolean
? CFSTR("Yes") : CFSTR("No"));
2313 static void appendBooleanProperty(CFMutableArrayRef properties
,
2314 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2316 DERReturn drtn
= DERParseBoolean(boolean
, defaultValue
, &result
);
2318 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2319 appendInvalidProperty(properties
, label
, boolean
);
2321 appendBoolProperty(properties
, label
, result
);
2325 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2326 CFStringRef label
, const DERItem
*bitStringContent
,
2327 const CFStringRef
*names
, CFIndex namesCount
) {
2328 DERSize len
= bitStringContent
->length
- 1;
2329 require_quiet(len
== 1 || len
== 2, badDER
);
2330 DERByte numUnusedBits
= bitStringContent
->data
[0];
2331 require_quiet(numUnusedBits
< 8, badDER
);
2332 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2333 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2334 uint_fast16_t value
= bitStringContent
->data
[1];
2337 value
= (value
<< 8) + bitStringContent
->data
[2];
2343 bool didOne
= false;
2344 CFMutableStringRef string
=
2345 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2346 for (ix
= 0; ix
< bits
; ++ix
) {
2349 CFStringAppend(string
, CFSTR(", "));
2353 CFStringAppend(string
, names
[ix
]);
2357 appendProperty(properties
, kSecPropertyTypeString
, label
, string
);
2361 appendInvalidProperty(properties
, label
, bitStringContent
);
2364 static void appendBitStringNames(CFMutableArrayRef properties
,
2365 CFStringRef label
, const DERItem
*bitString
,
2366 const CFStringRef
*names
, CFIndex namesCount
) {
2367 DERDecodedInfo bitStringContent
;
2368 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2369 require_noerr_quiet(drtn
, badDER
);
2370 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2371 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2375 appendInvalidProperty(properties
, label
, bitString
);
2379 typedef uint16_t SecKeyUsage
;
2381 #define kSecKeyUsageDigitalSignature 0x8000
2382 #define kSecKeyUsageNonRepudiation 0x4000
2383 #define kSecKeyUsageKeyEncipherment 0x2000
2384 #define kSecKeyUsageDataEncipherment 0x1000
2385 #define kSecKeyUsageKeyAgreement 0x0800
2386 #define kSecKeyUsageKeyCertSign 0x0400
2387 #define kSecKeyUsageCRLSign 0x0200
2388 #define kSecKeyUsageEncipherOnly 0x0100
2389 #define kSecKeyUsageDecipherOnly 0x0080
2392 KeyUsage ::= BIT STRING {
2393 digitalSignature (0),
2395 keyEncipherment (2),
2396 dataEncipherment (3),
2403 static void appendKeyUsage(CFMutableArrayRef properties
,
2404 const DERItem
*extnValue
) {
2405 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2406 extnValue
->data
[0] != ASN1_BIT_STRING
||
2407 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2408 extnValue
->data
[2] > 7) {
2409 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2412 CFMutableStringRef string
=
2413 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2414 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2415 if (extnValue
->length
== 5)
2416 usage
+= extnValue
->data
[4];
2417 secdebug("keyusage", "keyusage: %04X", usage
);
2418 static const CFStringRef usageNames
[] = {
2419 CFSTR("Digital Signature"),
2420 CFSTR("Non-Repudiation"),
2421 CFSTR("Key Encipherment"),
2422 CFSTR("Data Encipherment"),
2423 CFSTR("Key Agreement"),
2429 bool didOne
= false;
2430 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2431 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2432 for (ix
= 0; ix
< bits
; ++ix
) {
2435 CFStringAppend(string
, CFSTR(", "));
2439 /* @@@ Localize usageNames[ix]. */
2440 CFStringAppend(string
, usageNames
[ix
]);
2444 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2450 static void appendKeyUsage(CFMutableArrayRef properties
,
2451 const DERItem
*extnValue
) {
2452 static const CFStringRef usageNames
[] = {
2453 CFSTR("Digital Signature"),
2454 CFSTR("Non-Repudiation"),
2455 CFSTR("Key Encipherment"),
2456 CFSTR("Data Encipherment"),
2457 CFSTR("Key Agreement"),
2460 CFSTR("Encipher Only"),
2461 CFSTR("Decipher Only")
2463 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
2464 usageNames
, sizeof(usageNames
) / sizeof(*usageNames
));
2468 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2469 const DERItem
*extnValue
) {
2470 DERPrivateKeyUsagePeriod pkup
;
2471 DERReturn drtn
= DERParseSequence(extnValue
,
2472 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2473 &pkup
, sizeof(pkup
));
2474 require_noerr_quiet(drtn
, badDER
);
2475 if (pkup
.notBefore
.length
) {
2476 appendDateContentProperty(properties
, CFSTR("Not Valid Before"),
2477 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2479 if (pkup
.notAfter
.length
) {
2480 appendDateContentProperty(properties
, CFSTR("Not Valid After"),
2481 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2485 appendInvalidProperty(properties
, CFSTR("Private Key Usage Period"),
2489 static void appendStringContentProperty(CFMutableArrayRef properties
,
2490 CFStringRef label
, const DERItem
*stringContent
,
2491 CFStringEncoding encoding
) {
2492 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2493 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2495 appendProperty(properties
, kSecPropertyTypeString
, label
, string
);
2498 appendInvalidProperty(properties
, label
, stringContent
);
2503 OtherName ::= SEQUENCE {
2504 type-id OBJECT IDENTIFIER,
2505 value [0] EXPLICIT ANY DEFINED BY type-id }
2507 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2508 const DERItem
*otherNameContent
) {
2510 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2511 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2513 require_noerr_quiet(drtn
, badDER
);
2514 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2515 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
,
2516 &on
.typeIdentifier
);
2517 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2519 appendProperty(properties
, kSecPropertyTypeString
, oid_string
,
2522 appendUnparsedProperty(properties
, oid_string
, &on
.value
);
2526 appendInvalidProperty(properties
, CFSTR("Other Name"), otherNameContent
);
2530 GeneralName ::= CHOICE {
2531 otherName [0] OtherName,
2532 rfc822Name [1] IA5String,
2533 dNSName [2] IA5String,
2534 x400Address [3] ORAddress,
2535 directoryName [4] Name,
2536 ediPartyName [5] EDIPartyName,
2537 uniformResourceIdentifier [6] IA5String,
2538 iPAddress [7] OCTET STRING,
2539 registeredID [8] OBJECT IDENTIFIER}
2541 EDIPartyName ::= SEQUENCE {
2542 nameAssigner [0] DirectoryString OPTIONAL,
2543 partyName [1] DirectoryString }
2545 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2546 DERTag tag
, const DERItem
*generalName
) {
2548 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2549 appendOtherNameContentProperty(properties
, generalName
);
2551 case ASN1_CONTEXT_SPECIFIC
| 1:
2553 appendStringContentProperty(properties
, CFSTR("Email Address"),
2554 generalName
, kCFStringEncodingASCII
);
2556 case ASN1_CONTEXT_SPECIFIC
| 2:
2558 appendStringContentProperty(properties
, CFSTR("DNS Name"), generalName
,
2559 kCFStringEncodingASCII
);
2561 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2562 appendUnparsedProperty(properties
, CFSTR("X.400 Address"),
2565 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2567 CFArrayRef directory_plist
=
2568 createPropertiesForX501Name(CFGetAllocator(properties
),
2570 appendProperty(properties
, kSecPropertyTypeSection
,
2571 CFSTR("Directory Name"), directory_plist
);
2572 CFRelease(directory_plist
);
2575 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2576 appendUnparsedProperty(properties
, CFSTR("EDI Party Name"),
2579 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2580 /* Technically I don't think this is valid, but there are certs out
2581 in the wild that use a constructed IA5String. In particular the
2582 VeriSign Time Stamping Authority CA.cer does this. */
2583 appendURLProperty(properties
, CFSTR("URI"), generalName
);
2585 case ASN1_CONTEXT_SPECIFIC
| 6:
2586 appendURLContentProperty(properties
, CFSTR("URI"), generalName
);
2588 case ASN1_CONTEXT_SPECIFIC
| 7:
2589 appendIPAddressContentProperty(properties
, CFSTR("IP Address"),
2592 case ASN1_CONTEXT_SPECIFIC
| 8:
2593 appendOIDProperty(properties
, CFSTR("Registered ID"), generalName
);
2604 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2605 const DERItem
*generalName
) {
2606 DERDecodedInfo generalNameContent
;
2607 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2608 require_noerr_quiet(drtn
, badDER
);
2609 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2610 &generalNameContent
.content
))
2613 appendInvalidProperty(properties
, CFSTR("General Name"), generalName
);
2618 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2620 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2621 const DERItem
*generalNamesContent
) {
2623 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2624 require_noerr_quiet(drtn
, badDER
);
2625 DERDecodedInfo generalNameContent
;
2626 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2628 if (!appendGeneralNameContentProperty(properties
,
2629 generalNameContent
.tag
, &generalNameContent
.content
)) {
2633 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2636 appendInvalidProperty(properties
, CFSTR("General Names"),
2637 generalNamesContent
);
2640 static void appendGeneralNames(CFMutableArrayRef properties
,
2641 const DERItem
*generalNames
) {
2642 DERDecodedInfo generalNamesContent
;
2643 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2644 require_noerr_quiet(drtn
, badDER
);
2645 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2647 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2650 appendInvalidProperty(properties
, CFSTR("General Names"), generalNames
);
2654 BasicConstraints ::= SEQUENCE {
2655 cA BOOLEAN DEFAULT FALSE,
2656 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
2658 static void appendBasicConstraints(CFMutableArrayRef properties
,
2659 const DERItem
*extnValue
) {
2660 DERBasicConstraints basicConstraints
;
2661 DERReturn drtn
= DERParseSequence(extnValue
,
2662 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
2663 &basicConstraints
, sizeof(basicConstraints
));
2664 require_noerr_quiet(drtn
, badDER
);
2666 appendBooleanProperty(properties
, CFSTR("Certificate Authority"),
2667 &basicConstraints
.cA
, false);
2669 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
2670 appendIntegerProperty(properties
, CFSTR("Path Length Constraint"),
2671 &basicConstraints
.pathLenConstraint
);
2675 appendInvalidProperty(properties
, CFSTR("Basic Constraints"), extnValue
);
2679 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
2681 DistributionPoint ::= SEQUENCE {
2682 distributionPoint [0] DistributionPointName OPTIONAL,
2683 reasons [1] ReasonFlags OPTIONAL,
2684 cRLIssuer [2] GeneralNames OPTIONAL }
2686 DistributionPointName ::= CHOICE {
2687 fullName [0] GeneralNames,
2688 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
2690 ReasonFlags ::= BIT STRING {
2694 affiliationChanged (3),
2696 cessationOfOperation (5),
2697 certificateHold (6),
2698 privilegeWithdrawn (7),
2701 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
2702 const DERItem
*extnValue
) {
2703 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2706 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
2707 require_noerr_quiet(drtn
, badDER
);
2708 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2709 DERDecodedInfo dpSeqContent
;
2710 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
2711 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2712 DERDistributionPoint dp
;
2713 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
2714 DERNumDistributionPointItemSpecs
,
2715 DERDistributionPointItemSpecs
,
2717 require_noerr_quiet(drtn
, badDER
);
2718 if (dp
.distributionPoint
.length
) {
2719 DERDecodedInfo distributionPointName
;
2720 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
2721 require_noerr_quiet(drtn
, badDER
);
2722 if (distributionPointName
.tag
==
2723 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
2725 appendGeneralNamesContent(properties
,
2726 &distributionPointName
.content
);
2727 } else if (distributionPointName
.tag
==
2728 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
2729 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
2731 appendProperty(properties
, kSecPropertyTypeSection
,
2732 CFSTR("Name Relative To CRL Issuer"), rdn_props
);
2733 CFRelease(rdn_props
);
2738 if (dp
.reasons
.length
) {
2739 static const CFStringRef reasonNames
[] = {
2741 CFSTR("Key Compromise"),
2742 CFSTR("CA Compromise"),
2743 CFSTR("Affiliation Changed"),
2744 CFSTR("Superseded"),
2745 CFSTR("Cessation Of Operation"),
2746 CFSTR("Certificate Hold"),
2747 CFSTR("Priviledge Withdrawn"),
2748 CFSTR("AA Compromise")
2750 appendBitStringContentNames(properties
, CFSTR("Reasons"),
2752 reasonNames
, sizeof(reasonNames
) / sizeof(*reasonNames
));
2754 if (dp
.cRLIssuer
.length
) {
2755 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
2756 &kCFTypeArrayCallBacks
);
2757 appendProperty(properties
, kSecPropertyTypeSection
,
2758 CFSTR("CRL Issuer"), crlIssuer
);
2759 CFRelease(crlIssuer
);
2760 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
2763 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2766 appendInvalidProperty(properties
, CFSTR("Crl Distribution Points"),
2770 /* Decode a sequence of integers into a comma separated list of ints. */
2771 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
2772 CFStringRef label
, const DERItem
*intSequenceContent
) {
2773 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2775 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
2776 require_noerr_quiet(drtn
, badDER
);
2777 DERDecodedInfo intContent
;
2778 CFMutableStringRef value
= NULL
;
2779 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
))
2781 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
2782 CFStringRef intDesc
= copyIntegerContentDescription(
2783 allocator
, &intContent
.content
);
2785 CFStringAppendFormat(value
, NULL
, CFSTR(", %@"), intDesc
);
2787 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
2791 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2793 appendProperty(properties
, kSecPropertyTypeString
,
2794 CFSTR("Notice Numbers"), value
);
2798 /* DROPTHOUGH if !value. */
2800 appendInvalidProperty(properties
, label
, intSequenceContent
);
2803 static void appendCertificatePolicies(CFMutableArrayRef properties
,
2804 const DERItem
*extnValue
) {
2805 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2808 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
2809 require_noerr_quiet(drtn
, badDER
);
2810 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2811 DERDecodedInfo piContent
;
2813 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
2814 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2815 DERPolicyInformation pi
;
2816 drtn
= DERParseSequenceContent(&piContent
.content
,
2817 DERNumPolicyInformationItemSpecs
,
2818 DERPolicyInformationItemSpecs
,
2820 require_noerr_quiet(drtn
, badDER
);
2821 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2822 CFSTR("Policy Identifier #%d"), pin
++);
2823 appendOIDProperty(properties
, piLabel
, &pi
.policyIdentifier
);
2825 if (pi
.policyQualifiers
.length
== 0)
2829 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
2830 require_noerr_quiet(drtn
, badDER
);
2831 DERDecodedInfo pqContent
;
2833 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
2834 DERPolicyQualifierInfo pqi
;
2835 drtn
= DERParseSequenceContent(&pqContent
.content
,
2836 DERNumPolicyQualifierInfoItemSpecs
,
2837 DERPolicyQualifierInfoItemSpecs
,
2839 require_noerr_quiet(drtn
, badDER
);
2840 DERDecodedInfo qualifierContent
;
2841 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
2842 require_noerr_quiet(drtn
, badDER
);
2843 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2844 CFSTR("Policy Qualifier #%d"), pqn
++);
2845 appendOIDProperty(properties
, pqLabel
, &pqi
.policyQualifierID
);
2847 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
2848 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
2849 appendURLContentProperty(properties
,
2851 &qualifierContent
.content
);
2852 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
2853 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2855 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
2856 DERNumUserNoticeItemSpecs
,
2857 DERUserNoticeItemSpecs
,
2859 require_noerr_quiet(drtn
, badDER
);
2860 if (un
.noticeRef
.length
) {
2861 DERNoticeReference nr
;
2862 drtn
= DERParseSequenceContent(&un
.noticeRef
,
2863 DERNumNoticeReferenceItemSpecs
,
2864 DERNoticeReferenceItemSpecs
,
2866 require_noerr_quiet(drtn
, badDER
);
2867 appendDERThingProperty(properties
,
2868 CFSTR("Organization"),
2870 appendIntegerSequenceContent(properties
,
2871 CFSTR("Notice Numbers"), &nr
.noticeNumbers
);
2873 if (un
.explicitText
.length
) {
2874 appendDERThingProperty(properties
, CFSTR("Explicit Text"),
2878 appendUnparsedProperty(properties
, CFSTR("Qualifier"),
2883 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2886 appendInvalidProperty(properties
, CFSTR("Certificate Policies"),
2890 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
2891 const DERItem
*extnValue
) {
2893 DERDecodedInfo keyIdentifier
;
2894 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
2895 require_noerr_quiet(drtn
, badDER
);
2896 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
2897 appendDataProperty(properties
, CFSTR("Key Identifier"),
2898 &keyIdentifier
.content
);
2902 appendInvalidProperty(properties
, CFSTR("Invalid Subject Key Identifier"),
2907 AuthorityKeyIdentifier ::= SEQUENCE {
2908 keyIdentifier [0] KeyIdentifier OPTIONAL,
2909 authorityCertIssuer [1] GeneralNames OPTIONAL,
2910 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
2911 -- authorityCertIssuer and authorityCertSerialNumber MUST both
2912 -- be present or both be absent
2914 KeyIdentifier ::= OCTET STRING
2916 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
2917 const DERItem
*extnValue
) {
2918 DERAuthorityKeyIdentifier akid
;
2920 drtn
= DERParseSequence(extnValue
,
2921 DERNumAuthorityKeyIdentifierItemSpecs
,
2922 DERAuthorityKeyIdentifierItemSpecs
,
2923 &akid
, sizeof(akid
));
2924 require_noerr_quiet(drtn
, badDER
);
2925 if (akid
.keyIdentifier
.length
) {
2926 appendDataProperty(properties
, CFSTR("Key Identifier"),
2927 &akid
.keyIdentifier
);
2929 if (akid
.authorityCertIssuer
.length
||
2930 akid
.authorityCertSerialNumber
.length
) {
2931 require_quiet(akid
.authorityCertIssuer
.length
&&
2932 akid
.authorityCertSerialNumber
.length
, badDER
);
2933 /* Perhaps put in a subsection called Authority Certificate Issuer. */
2934 appendGeneralNamesContent(properties
,
2935 &akid
.authorityCertIssuer
);
2936 appendIntegerProperty(properties
,
2937 CFSTR("Authority Certificate Serial Number"),
2938 &akid
.authorityCertSerialNumber
);
2943 appendInvalidProperty(properties
, CFSTR("Authority Key Identifier"),
2948 PolicyConstraints ::= SEQUENCE {
2949 requireExplicitPolicy [0] SkipCerts OPTIONAL,
2950 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
2952 SkipCerts ::= INTEGER (0..MAX)
2954 static void appendPolicyConstraints(CFMutableArrayRef properties
,
2955 const DERItem
*extnValue
) {
2956 DERPolicyConstraints pc
;
2958 drtn
= DERParseSequence(extnValue
,
2959 DERNumPolicyConstraintsItemSpecs
,
2960 DERPolicyConstraintsItemSpecs
,
2962 require_noerr_quiet(drtn
, badDER
);
2963 if (pc
.requireExplicitPolicy
.length
) {
2964 appendIntegerProperty(properties
,
2965 CFSTR("Require Explicit Policy"), &pc
.requireExplicitPolicy
);
2967 if (pc
.inhibitPolicyMapping
.length
) {
2968 appendIntegerProperty(properties
,
2969 CFSTR("Inhibit Policy Mapping"), &pc
.inhibitPolicyMapping
);
2975 appendInvalidProperty(properties
, CFSTR("Policy Constraints"), extnValue
);
2979 extendedKeyUsage EXTENSION ::= {
2980 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
2981 IDENTIFIED BY id-ce-extKeyUsage }
2983 KeyPurposeId ::= OBJECT IDENTIFIER
2985 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
2986 const DERItem
*extnValue
) {
2989 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
2990 require_noerr_quiet(drtn
, badDER
);
2991 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2992 DERDecodedInfo currDecoded
;
2993 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
2994 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
2995 appendOIDProperty(properties
, CFSTR("Purpose"),
2996 &currDecoded
.content
);
2998 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3001 appendInvalidProperty(properties
, CFSTR("Extended Key Usage"), extnValue
);
3005 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3007 AuthorityInfoAccessSyntax ::=
3008 SEQUENCE SIZE (1..MAX) OF AccessDescription
3010 AccessDescription ::= SEQUENCE {
3011 accessMethod OBJECT IDENTIFIER,
3012 accessLocation GeneralName }
3014 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3016 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3018 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3020 static void appendInfoAccess(CFMutableArrayRef properties
,
3021 const DERItem
*extnValue
) {
3024 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3025 require_noerr_quiet(drtn
, badDER
);
3026 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3027 DERDecodedInfo adContent
;
3028 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3029 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3030 DERAccessDescription ad
;
3031 drtn
= DERParseSequenceContent(&adContent
.content
,
3032 DERNumAccessDescriptionItemSpecs
,
3033 DERAccessDescriptionItemSpecs
,
3035 require_noerr_quiet(drtn
, badDER
);
3036 appendOIDProperty(properties
, CFSTR("Access Method"),
3038 //CFSTR("Access Location");
3039 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3041 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3044 appendInvalidProperty(properties
, CFSTR("Authority Information Access"),
3048 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3049 const DERItem
*extnValue
) {
3050 static const CFStringRef certTypes
[] = {
3051 CFSTR("SSL client"),
3052 CFSTR("SSL server"),
3054 CFSTR("Object Signing"),
3058 CFSTR("Object Signing CA")
3060 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
3061 certTypes
, sizeof(certTypes
) / sizeof(*certTypes
));
3065 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3066 const DERItem
*extnValue
) {
3070 * The list of Qualified Cert Statement statementIds we understand, even though
3071 * we don't actually do anything with them; if these are found in a Qualified
3072 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3074 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3076 /* id-qcs := { id-pkix 11 } */
3077 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3078 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3079 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3080 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3081 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3082 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3084 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3086 static void appendQCCertStatements(CFMutableArrayRef properties
,
3087 const DERItem
*extnValue
) {
3092 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3093 CFStringRef label
, const DERItem
*sequence
) {
3096 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3097 require_noerr_quiet(drtn
, badSequence
);
3098 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3099 DERDecodedInfo currDecoded
;
3100 bool appendedSomething
= false;
3101 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3102 switch (currDecoded
.tag
)
3105 case ASN1_SEQUENCE
: // 16
3106 case ASN1_SET
: // 17
3107 // skip constructed object lengths
3110 case ASN1_UTF8_STRING
: // 12
3111 case ASN1_NUMERIC_STRING
: // 18
3112 case ASN1_PRINTABLE_STRING
: // 19
3113 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3114 case ASN1_VIDEOTEX_STRING
: // 21
3115 case ASN1_IA5_STRING
: // 22
3116 case ASN1_GRAPHIC_STRING
: // 25
3117 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3118 case ASN1_GENERAL_STRING
: // 27
3119 case ASN1_UNIVERSAL_STRING
: // 28
3121 CFStringRef string
=
3122 copyDERThingContentDescription(CFGetAllocator(properties
),
3123 currDecoded
.tag
, &currDecoded
.content
, false);
3124 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3126 appendProperty(properties
, kSecPropertyTypeString
, label
,
3129 appendedSomething
= true;
3136 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3137 return appendedSomething
;
3142 static void appendExtension(CFMutableArrayRef parent
,
3143 const SecCertificateExtension
*extn
) {
3144 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3145 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3146 &kCFTypeArrayCallBacks
);
3148 *extnID
= &extn
->extnID
,
3149 *extnValue
= &extn
->extnValue
;
3151 appendBoolProperty(properties
, CFSTR("Critical"), extn
->critical
);
3154 bool handeled
= true;
3155 /* Extensions that we know how to handle ourselves... */
3156 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3157 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3159 switch (extnID
->data
[extnID
->length
- 1]) {
3160 case 14: /* SubjectKeyIdentifier id-ce 14 */
3161 appendSubjectKeyIdentifier(properties
, extnValue
);
3163 case 15: /* KeyUsage id-ce 15 */
3164 appendKeyUsage(properties
, extnValue
);
3166 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3167 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3169 case 17: /* SubjectAltName id-ce 17 */
3170 case 18: /* IssuerAltName id-ce 18 */
3171 appendGeneralNames(properties
, extnValue
);
3173 case 19: /* BasicConstraints id-ce 19 */
3174 appendBasicConstraints(properties
, extnValue
);
3176 case 30: /* NameConstraints id-ce 30 */
3179 case 31: /* CRLDistributionPoints id-ce 31 */
3180 appendCrlDistributionPoints(properties
, extnValue
);
3182 case 32: /* CertificatePolicies id-ce 32 */
3183 appendCertificatePolicies(properties
, extnValue
);
3185 case 33: /* PolicyMappings id-ce 33 */
3188 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3189 appendAuthorityKeyIdentifier(properties
, extnValue
);
3191 case 36: /* PolicyConstraints id-ce 36 */
3192 appendPolicyConstraints(properties
, extnValue
);
3194 case 37: /* ExtKeyUsage id-ce 37 */
3195 appendExtendedKeyUsage(properties
, extnValue
);
3197 case 46: /* FreshestCRL id-ce 46 */
3200 case 54: /* InhibitAnyPolicy id-ce 54 */
3207 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3208 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3210 switch (extnID
->data
[extnID
->length
- 1]) {
3211 case 1: /* AuthorityInfoAccess id-pe 1 */
3212 appendInfoAccess(properties
, extnValue
);
3214 case 3: /* QCStatements id-pe 3 */
3217 case 11: /* SubjectInfoAccess id-pe 11 */
3218 appendInfoAccess(properties
, extnValue
);
3224 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3225 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3226 appendNetscapeCertType(properties
, extnValue
);
3232 /* Try to parse and display printable string(s). */
3233 if (appendPrintableDERSequence(properties
, CFSTR("Data"), extnValue
)) {
3234 /* Nothing to do here appendPrintableDERSequence did the work. */
3236 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3237 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3241 /* Extensions that we know how to handle ourselves... */
3242 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3243 appendSubjectKeyIdentifier(properties
, extnValue
);
3244 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3245 appendKeyUsage(properties
, extnValue
);
3246 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3247 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3248 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3249 appendGeneralNames(properties
, extnValue
);
3250 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3251 appendGeneralNames(properties
, extnValue
);
3252 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3253 appendBasicConstraints(properties
, extnValue
);
3254 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3255 appendCrlDistributionPoints(properties
, extnValue
);
3256 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3257 appendCertificatePolicies(properties
, extnValue
);
3258 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3259 appendAuthorityKeyIdentifier(properties
, extnValue
);
3260 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3261 appendPolicyConstraints(properties
, extnValue
);
3262 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3263 appendExtendedKeyUsage(properties
, extnValue
);
3264 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3265 appendInfoAccess(properties
, extnValue
);
3266 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3267 appendInfoAccess(properties
, extnValue
);
3268 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3269 appendNetscapeCertType(properties
, extnValue
);
3271 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3272 appendEntrustVersInfo(properties
, extnValue
);
3275 /* Try to parse and display printable string(s). */
3276 if (appendPrintableDERSequence(properties
, CFSTR("Data"), extnValue
)) {
3277 /* Nothing to do here appendPrintableDERSequence did the work. */
3279 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3280 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3283 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
, extnID
);
3284 appendProperty(parent
, kSecPropertyTypeSection
, oid_string
, properties
);
3285 CFRelease(oid_string
);
3286 CFRelease(properties
);
3289 /* Different types of summary types from least desired to most desired. */
3292 kSummaryTypePrintable
,
3293 kSummaryTypeOrganizationName
,
3294 kSummaryTypeOrganizationalUnitName
,
3295 kSummaryTypeCommonName
,
3299 enum SummaryType type
;
3300 CFStringRef summary
;
3301 CFStringRef description
;
3304 static OSStatus
obtainSummaryFromX501Name(void *context
,
3305 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3306 struct Summary
*summary
= (struct Summary
*)context
;
3307 enum SummaryType stype
= kSummaryTypeNone
;
3308 CFStringRef string
= NULL
;
3309 if (DEROidCompare(type
, &oidCommonName
)) {
3310 /* We skip Common Names that have generic values. */
3311 const char tfm
[] = "Thawte Freemail Member";
3312 if ((value
->length
== sizeof(tfm
) + 1) &&
3313 !memcmp(value
->data
+ 2, tfm
, sizeof(tfm
) - 1)) {
3314 return errSecSuccess
;
3316 stype
= kSummaryTypeCommonName
;
3317 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3318 stype
= kSummaryTypeOrganizationalUnitName
;
3319 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3320 stype
= kSummaryTypeOrganizationName
;
3321 } else if (DEROidCompare(type
, &oidDescription
)) {
3322 if (!summary
->description
) {
3323 summary
->description
= string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3326 stype
= kSummaryTypePrintable
;
3328 stype
= kSummaryTypePrintable
;
3331 /* Use the first field we encounter of the highest priority type. */
3332 if (summary
->type
< stype
) {
3334 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3338 CFReleaseSafe(summary
->summary
);
3339 summary
->summary
= string
;
3340 summary
->type
= stype
;
3343 CFReleaseSafe(string
);
3346 return errSecSuccess
;
3349 CFStringRef
SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate
) {
3350 struct Summary summary
= {};
3351 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3352 /* If we found a description and a common name we change the summary to
3353 CommonName (Description). */
3354 if (summary
.description
) {
3355 if (summary
.type
== kSummaryTypeCommonName
) {
3356 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3357 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3358 CFRelease(summary
.summary
);
3359 summary
.summary
= newSummary
;
3361 CFRelease(summary
.description
);
3364 if (!summary
.summary
) {
3365 /* If we didn't find a suitable printable string in the subject at all, we try
3366 the first email address in the certificate instead. */
3367 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3369 /* If we didn't find any email addresses in the certificate, we try finding
3370 a DNS name instead. */
3371 names
= SecCertificateCopyDNSNames(certificate
);
3374 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3375 CFRetain(summary
.summary
);
3380 return summary
.summary
;
3383 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRefP certificate
) {
3384 struct Summary summary
= {};
3385 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3386 /* If we found a description and a common name we change the summary to
3387 CommonName (Description). */
3388 if (summary
.description
) {
3389 if (summary
.type
== kSummaryTypeCommonName
) {
3390 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3391 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3392 CFRelease(summary
.summary
);
3393 summary
.summary
= newSummary
;
3395 CFRelease(summary
.description
);
3398 return summary
.summary
;
3401 /* Return the earliest date on which all certificates in this chain are still
3403 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3404 SecCertificateRefP certificate
) {
3405 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3407 while (certificate
->_parent
) {
3408 certificate
= certificate
->_parent
;
3409 if (earliest
> certificate
->_notAfter
)
3410 earliest
= certificate
->_notAfter
;
3417 /* Return the latest date on which all certificates in this chain will be
3419 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3420 SecCertificateRefP certificate
) {
3421 CFAbsoluteTime latest
= certificate
->_notBefore
;
3423 while (certificate
->_parent
) {
3424 certificate
= certificate
->_parent
;
3425 if (latest
< certificate
->_notBefore
)
3426 latest
= certificate
->_notBefore
;
3433 bool SecCertificateIsValidP(SecCertificateRefP certificate
,
3434 CFAbsoluteTime verifyTime
) {
3436 return certificate
->_notBefore
<= verifyTime
&&
3437 verifyTime
<= certificate
->_notAfter
;
3440 CFIndex
SecCertificateVersion(SecCertificateRefP certificate
) {
3441 return certificate
->_version
+ 1;
3444 CFAbsoluteTime
SecCertificateNotValidBeforeP(SecCertificateRefP certificate
) {
3445 return certificate
->_notBefore
;
3448 CFAbsoluteTime
SecCertificateNotValidAfterP(SecCertificateRefP certificate
) {
3449 return certificate
->_notAfter
;
3452 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3453 SecCertificateRefP certificate
, CFAbsoluteTime verifyTime
) {
3454 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3455 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3456 &kCFTypeArrayCallBacks
);
3458 /* First we put the subject summary name. */
3459 CFStringRef ssummary
= SecCertificateCopySubjectSummaryP(certificate
);
3461 appendProperty(summary
, kSecPropertyTypeTitle
,
3463 CFRelease(ssummary
);
3466 CFStringRef isummary
= CFSTR("Issuer Summary");
3467 appendProperty(summary
, kSecPropertyTypeString
,
3468 CFSTR("Issued By"), isummary
);
3469 CFRelease(isummary
);
3472 /* Let see if this certificate is currently valid. */
3474 CFAbsoluteTime when
;
3475 CFStringRef message
;
3477 if (verifyTime
> certificate
->_notAfter
) {
3478 label
= CFSTR("Expired");
3479 when
= certificate
->_notAfter
;
3480 ptype
= kSecPropertyTypeError
;
3481 message
= CFSTR("This certificate has expired");
3482 } else if (certificate
->_notBefore
> verifyTime
) {
3483 label
= CFSTR("Valid from");
3484 when
= certificate
->_notBefore
;
3485 ptype
= kSecPropertyTypeError
;
3486 message
= CFSTR("This certificate is not yet valid");
3488 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3489 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3490 if (verifyTime
> last
) {
3491 label
= CFSTR("Expired");
3493 ptype
= kSecPropertyTypeError
;
3494 message
= CFSTR("This certificate has an issuer that has expired");
3495 } else if (verifyTime
< first
) {
3496 label
= CFSTR("Valid from");
3498 ptype
= kSecPropertyTypeError
;
3499 message
= CFSTR("This certificate has an issuer that is not yet valid");
3501 label
= CFSTR("Expires");
3502 when
= certificate
->_notAfter
;
3503 ptype
= kSecPropertyTypeSuccess
;
3504 message
= CFSTR("This certificate is valid");
3508 appendDateProperty(summary
, label
, when
);
3509 appendProperty(summary
, ptype
, NULL
, message
);
3514 CFArrayRef
SecCertificateCopyProperties(SecCertificateRefP certificate
) {
3515 if (!certificate
->_properties
) {
3516 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3517 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3518 &kCFTypeArrayCallBacks
);
3520 /* First we put the Subject Name in the property list. */
3521 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3522 &certificate
->_subject
);
3523 appendProperty(properties
, kSecPropertyTypeSection
,
3524 CFSTR("Subject Name"), subject_plist
);
3525 CFRelease(subject_plist
);
3528 /* Put Normalized subject in for testing. */
3529 if (certificate
->_normalizedSubject
) {
3530 DERItem nsubject
= {
3531 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
3532 CFDataGetLength(certificate
->_normalizedSubject
)
3534 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
3536 appendProperty(properties
, kSecPropertyTypeSection
,
3537 CFSTR("Normalized Subject Name"), nsubject_plist
);
3538 CFRelease(nsubject_plist
);
3542 /* Next we put the Issuer Name in the property list. */
3543 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3544 &certificate
->_issuer
);
3545 appendProperty(properties
, kSecPropertyTypeSection
,
3546 CFSTR("Issuer Name"), issuer_plist
);
3547 CFRelease(issuer_plist
);
3550 /* Certificate version/type. */
3551 bool isRoot
= false;
3552 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
3553 CFSTR("X.509 version %d %scertificate"),
3554 certificate
->_version
+ 1, isRoot
? "root " : "");
3555 appendProperty(properties
, kSecPropertyTypeString
,
3556 CFSTR("Certificate Type"), typeString
);
3557 CFRelease(typeString
);
3561 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
3562 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
3563 appendProperty(properties
, kSecPropertyTypeString
,
3564 CFSTR("Version"), versionString
);
3565 CFRelease(versionString
);
3568 if (certificate
->_serialNum
.length
) {
3569 appendIntegerProperty(properties
, CFSTR("Serial Number"),
3570 &certificate
->_serialNum
);
3573 /* Signature algorithm. */
3575 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3576 &certificate
->_sigAlg
);
3578 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3579 &certificate
->_tbsSigAlg
);
3582 /* Validity dates. */
3583 appendDateProperty(properties
, CFSTR("Not Valid Before"),
3584 certificate
->_notBefore
);
3585 appendDateProperty(properties
, CFSTR("Not Valid After"),
3586 certificate
->_notAfter
);
3588 if (certificate
->_subjectUniqueID
.length
) {
3589 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
3590 &certificate
->_subjectUniqueID
);
3592 if (certificate
->_issuerUniqueID
.length
) {
3593 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
3594 &certificate
->_issuerUniqueID
);
3597 /* Public key algorithm. */
3598 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
3599 &certificate
->_algId
);
3601 /* Consider breaking down an RSA public key into modulus and
3603 appendDataProperty(properties
, CFSTR("Public Key Data"),
3604 &certificate
->_pubKeyDER
);
3606 /* @@@ Key Usage. */
3608 appendDataProperty(properties
, CFSTR("Signature"),
3609 &certificate
->_signature
);
3612 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
3613 appendExtension(properties
, &certificate
->_extensions
[ix
]);
3616 /* @@@ Key Fingerprints. */
3618 certificate
->_properties
= properties
;
3621 CFRetain(certificate
->_properties
);
3622 return certificate
->_properties
;
3625 CFDataRef
SecCertificateCopySerialNumberP(
3626 SecCertificateRefP certificate
) {
3627 if (certificate
->_serialNumber
) {
3628 CFRetain(certificate
->_serialNumber
);
3630 return certificate
->_serialNumber
;
3634 * Accessor for normalized issuer content
3636 CFDataRef
SecCertificateGetNormalizedIssuerContent(
3637 SecCertificateRefP certificate
) {
3638 return certificate
->_normalizedIssuer
;
3642 * Accessor for normalized subject content
3644 CFDataRef
SecCertificateGetNormalizedSubjectContent(
3645 SecCertificateRefP certificate
) {
3646 return certificate
->_normalizedSubject
;
3650 * Returns DER-encoded normalized issuer sequence
3651 * for use with SecItemCopyMatching; caller must release
3653 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(
3654 SecCertificateRefP certificate
) {
3656 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
3657 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
3659 return SecDERItemCopySequence(&tmpdi
);
3663 * Returns DER-encoded normalized subject sequence
3664 * for use with SecItemCopyMatching; caller must release
3666 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(
3667 SecCertificateRefP certificate
) {
3669 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
);
3670 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
3672 return SecDERItemCopySequence(&tmpdi
);
3675 /* Verify that certificate was signed by issuerKey. */
3677 OSStatus
SecCertificateIsSignedByP(SecCertificateRefP certificate
,
3678 SecKeyRefP issuerKey
) {
3679 /* Setup algId in SecAsn1AlgId format. */
3681 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3682 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3683 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3684 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3686 #warning implementation empty
3688 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3689 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3690 certificate
->_signature
.data
, certificate
->_signature
.length
);
3692 secdebug("verify", "signature verify failed: %d", status
);
3693 return errSecNotSigner
;
3697 return errSecSuccess
;
3701 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRefP certificate
,
3702 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3703 if (!signatureCheckOnly
) {
3704 /* It turns out we don't actually need to use normalized subject and
3705 issuer according to rfc2459. */
3707 /* If present we should check issuerID against the issuer subjectID. */
3709 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3710 then we should look for a SubjectKeyIdentifier in the issuer
3712 If we have a authorityCertSerialNumber we can use that for chaining.
3713 If we have a authorityCertIssuer we can use that? (or not) */
3715 /* Verify that this cert was issued by issuer. Do so by chaining
3716 either issuerID to subjectID or normalized issuer to normalized
3718 CFDataRef normalizedIssuer
=
3719 SecCertificateGetNormalizedIssuerContent(certificate
);
3720 CFDataRef normalizedIssuerSubject
=
3721 SecCertificateGetNormalizedSubjectContent(issuer
);
3722 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3723 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3724 return errSecIssuerMismatch
;
3727 /* Next verify that this cert was signed by issuer. */
3728 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3730 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3731 /* FIXME: We sould cache this (or at least the digest) until we find
3732 a suitable issuer. */
3733 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3734 CFIndex signedDataLength
;
3735 CertVerifyReturn crtn
;
3736 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3737 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3738 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3739 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3740 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3741 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3742 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3743 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3744 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3746 secdebug("verify", "unsupported algorithm");
3747 return errSecUnsupportedAlgorithm
;
3750 secdebug("verify", "*DigestInfo returned: %d", crtn
);
3751 /* FIXME: Do proper error code translation. */
3752 return errSecUnsupportedAlgorithm
;
3755 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3756 signedData
, signedDataLength
,
3757 certificate
->_signature
.data
, certificate
->_signature
.length
);
3759 secdebug("verify", "signature verify failed: %d", status
);
3760 return errSecNotSigner
;
3763 return errSecSuccess
;
3766 static OSStatus
_SecCertificateSetParent(SecCertificateRefP certificate
,
3767 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3769 if (certificate
->_parent
) {
3770 /* Setting a certificates issuer twice is only allowed if the new
3771 issuer is equal to the current one. */
3772 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3776 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3777 signatureCheckOnly
);
3779 OSStatus status
= errSecSuccess
;
3782 if (CFEqual(certificate
, issuer
)) {
3783 /* We don't retain ourselves cause that would be bad mojo,
3784 however we do record that we are properly self signed. */
3785 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3786 secdebug("cert", "set self as parent");
3787 return errSecSuccess
;
3791 certificate
->_parent
= issuer
;
3792 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3798 static bool SecCertificateIsSelfSigned(SecCertificateRefP certificate
) {
3799 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3800 certificate
->_isSelfSigned
=
3801 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3802 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3805 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3808 /* Return true iff we were able to set our own parent from one of the
3809 certificates in other_certificates, return false otherwise. If
3810 signatureCheckOnly is true, we can skip the subject == issuer or
3811 authorityKeyIdentifier tests. */
3812 static bool SecCertificateSetParentFrom(SecCertificateRefP certificate
,
3813 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3814 CFIndex count
= CFArrayGetCount(other_certificates
);
3816 for (ix
= 0; ix
< count
; ++ix
) {
3817 SecCertificateRefP candidate
= (SecCertificateRefP
)
3818 CFArrayGetValueAtIndex(other_certificates
, ix
);
3819 if (_SecCertificateSetParent(certificate
, candidate
,
3820 signatureCheckOnly
))
3826 /* Lookup the parent of certificate in the keychain and set it. */
3827 static bool SecCertificateFindParent(SecCertificateRefP certificate
) {
3828 /* FIXME: Search for things other than just subject of our issuer if we
3829 have a subjectID or authorityKeyIdentifier. */
3830 CFDataRef normalizedIssuer
=
3831 SecCertificateGetNormalizedIssuerContent(certificate
);
3832 const void *keys
[] = {
3839 kSecClassCertificate
,
3844 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3845 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3847 OSStatus status
= SecItemCopyMatching(query
, &results
);
3850 secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3854 CFArrayRef certs
= (CFArrayRef
)results
;
3855 /* Since we already know the certificates we are providing as candidates
3856 have been checked for subject matching, we can ask
3857 SecCertificateSetParentFrom to skip everything except the signature
3859 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3864 OSStatus
SecCertificateCompleteChain(SecCertificateRefP certificate
,
3865 CFArrayRef other_certificates
) {
3867 if (certificate
->_parent
== NULL
) {
3868 if (SecCertificateIsSelfSigned(certificate
))
3869 return errSecSuccess
;
3870 if (!other_certificates
||
3871 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
3873 if (!SecCertificateFindParent(certificate
))
3874 return errSecIssuerNotFound
;
3877 certificate
= certificate
->_parent
;
3882 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
3883 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
3884 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
3885 if (gnType
== GNT_IPAddress
) {
3886 CFStringRef string
= copyIPAddressContentDescription(
3887 kCFAllocatorDefault
, generalName
);
3889 CFArrayAppendValue(ipAddresses
, string
);
3892 return errSecInvalidCertificate
;
3895 return errSecSuccess
;
3898 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRefP certificate
) {
3899 /* These can only exist in the subject alt name. */
3900 if (!certificate
->_subjectAltName
)
3903 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
3904 0, &kCFTypeArrayCallBacks
);
3905 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
3906 ipAddresses
, appendIPAddressesFromGeneralNames
);
3907 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
3908 CFRelease(ipAddresses
);
3914 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
3915 const DERItem
*generalName
) {
3916 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
3917 if (gnType
== GNT_DNSName
) {
3918 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
3919 generalName
->data
, generalName
->length
,
3920 kCFStringEncodingUTF8
, FALSE
);
3922 CFArrayAppendValue(dnsNames
, string
);
3925 return errSecInvalidCertificate
;
3928 return errSecSuccess
;
3931 /* Return true if the passed in string matches the
3932 Preferred name syntax from sections 2.3.1. in RFC 1035.
3933 With the added check that we disallow empty dns names.
3934 Also in order to support wildcard DNSNames we allow for the '*'
3935 character anywhere in a dns component where we currently allow
3938 <domain> ::= <subdomain> | " "
3940 <subdomain> ::= <label> | <subdomain> "." <label>
3942 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
3944 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
3946 <let-dig-hyp> ::= <let-dig> | "-"
3948 <let-dig> ::= <letter> | <digit>
3950 <letter> ::= any one of the 52 alphabetic characters A through Z in
3951 upper case and a through z in lower case
3953 <digit> ::= any one of the ten digits 0 through 9
3955 static bool isDNSName(CFStringRef string
) {
3956 CFStringInlineBuffer buf
;
3957 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
3958 /* From RFC 1035 2.3.4. Size limits:
3959 labels 63 octets or less
3960 names 255 octets or less */
3961 require_quiet(length
<= 255, notDNS
);
3962 CFRange range
= { 0, length
};
3963 CFStringInitInlineBuffer(string
, &buf
, range
);
3967 kDNSStateAfterAlpha
,
3968 kDNSStateAfterDigit
,
3970 } state
= kDNSStateInital
;
3972 bool nonAlpha
= false;
3973 for (ix
= 0; ix
< length
; ++ix
) {
3974 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
3977 require_quiet(labelLength
<= 64 &&
3978 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
3980 state
= kDNSStateAfterDot
;
3983 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
3985 state
= kDNSStateAfterAlpha
;
3986 } else if ('0' <= ch
&& ch
<= '9') {
3988 /* The requirement for labels to start with a letter was
3989 dropped so we don't check this anymore. */
3990 require_quiet(state
== kDNSStateAfterAlpha
||
3991 state
== kDNSStateAfterDigit
||
3992 state
== kDNSStateAfterDash
, notDNS
);
3994 state
= kDNSStateAfterDigit
;
3996 } else if (ch
== '-') {
3997 require_quiet(state
== kDNSStateAfterAlpha
||
3998 state
== kDNSStateAfterDigit
||
3999 state
== kDNSStateAfterDash
, notDNS
);
4000 state
= kDNSStateAfterDash
;
4007 /* We don't allow a dns name to end in a dot, and we require the
4008 final name component to only have alphanumeric chars. */
4009 require_quiet(!nonAlpha
&& labelLength
<= 63 &&
4010 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4018 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4019 const DERItem
*value
, CFIndex rdnIX
) {
4020 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4021 if (DEROidCompare(type
, &oidCommonName
)) {
4022 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4025 if (isDNSName(string
)) {
4026 /* We found a common name that is formatted like a valid
4028 CFArrayAppendValue(dnsNames
, string
);
4032 return errSecInvalidCertificate
;
4035 return errSecSuccess
;
4038 /* Not everything returned by this function is going to be a proper DNS name,
4039 we also return the certificates common name entries from the subject,
4040 assuming they look like dns names as specified in RFC 1035. */
4041 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRefP certificate
) {
4042 /* These can exist in the subject alt name or in the subject. */
4043 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4044 0, &kCFTypeArrayCallBacks
);
4045 OSStatus status
= errSecSuccess
;
4046 if (certificate
->_subjectAltName
) {
4047 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4048 dnsNames
, appendDNSNamesFromGeneralNames
);
4050 /* RFC 2818 section 3.1. Server Identity
4052 If a subjectAltName extension of type dNSName is present, that MUST
4053 be used as the identity. Otherwise, the (most specific) Common Name
4054 field in the Subject field of the certificate MUST be used. Although
4055 the use of the Common Name is existing practice, it is deprecated and
4056 Certification Authorities are encouraged to use the dNSName instead.
4059 This implies that if we found 1 or more DNSNames in the
4060 subjectAltName, we should not use the Common Name of the subject as
4063 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4064 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4065 appendDNSNamesFromX501Name
);
4067 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4068 CFRelease(dnsNames
);
4074 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4075 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4076 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4077 if (gnType
== GNT_RFC822Name
) {
4078 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4079 generalName
->data
, generalName
->length
,
4080 kCFStringEncodingASCII
, FALSE
);
4082 CFArrayAppendValue(dnsNames
, string
);
4085 return errSecInvalidCertificate
;
4088 return errSecSuccess
;
4091 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4092 const DERItem
*value
, CFIndex rdnIX
) {
4093 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4094 if (DEROidCompare(type
, &oidEmailAddress
)) {
4095 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4098 CFArrayAppendValue(dnsNames
, string
);
4101 return errSecInvalidCertificate
;
4104 return errSecSuccess
;
4107 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRefP certificate
) {
4108 /* These can exist in the subject alt name or in the subject. */
4109 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4110 0, &kCFTypeArrayCallBacks
);
4111 OSStatus status
= errSecSuccess
;
4112 if (certificate
->_subjectAltName
) {
4113 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4114 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4117 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4118 appendRFC822NamesFromX501Name
);
4120 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4121 CFRelease(rfc822Names
);
4127 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4128 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4129 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4130 if (DEROidCompare(type
, &oidCommonName
)) {
4131 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4134 CFArrayAppendValue(commonNames
, string
);
4137 return errSecInvalidCertificate
;
4140 return errSecSuccess
;
4143 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRefP certificate
) {
4144 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4145 0, &kCFTypeArrayCallBacks
);
4147 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4148 appendCommonNamesFromX501Name
);
4149 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4150 CFRelease(commonNames
);
4156 static OSStatus
appendOrganizationFromX501Name(void *context
,
4157 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4158 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4159 if (DEROidCompare(type
, &oidOrganizationName
)) {
4160 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4163 CFArrayAppendValue(organization
, string
);
4166 return errSecInvalidCertificate
;
4169 return errSecSuccess
;
4172 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRefP certificate
) {
4173 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4174 0, &kCFTypeArrayCallBacks
);
4176 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4177 appendOrganizationFromX501Name
);
4178 if (status
|| CFArrayGetCount(organization
) == 0) {
4179 CFRelease(organization
);
4180 organization
= NULL
;
4182 return organization
;
4185 const SecCEBasicConstraints
*
4186 SecCertificateGetBasicConstraints(SecCertificateRefP certificate
) {
4187 if (certificate
->_basicConstraints
.present
)
4188 return &certificate
->_basicConstraints
;
4193 const SecCEPolicyConstraints
*
4194 SecCertificateGetPolicyConstraints(SecCertificateRefP certificate
) {
4195 if (certificate
->_policyConstraints
.present
)
4196 return &certificate
->_policyConstraints
;
4202 SecCertificateGetPolicyMappings(SecCertificateRefP certificate
) {
4203 return certificate
->_policyMappings
;
4206 const SecCECertificatePolicies
*
4207 SecCertificateGetCertificatePolicies(SecCertificateRefP certificate
) {
4208 if (certificate
->_certificatePolicies
.present
)
4209 return &certificate
->_certificatePolicies
;
4215 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRefP certificate
) {
4216 return certificate
->_inhibitAnyPolicySkipCerts
;
4219 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4220 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4221 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4222 if (gnType
== GNT_OtherName
) {
4224 DERReturn drtn
= DERParseSequenceContent(generalName
,
4225 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4227 require_noerr_quiet(drtn
, badDER
);
4228 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4230 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4231 &on
.value
, true), badDER
);
4232 CFArrayAppendValue(ntPrincipalNames
, string
);
4236 return errSecSuccess
;
4239 return errSecInvalidCertificate
;
4243 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRefP certificate
) {
4244 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4245 0, &kCFTypeArrayCallBacks
);
4246 OSStatus status
= errSecSuccess
;
4247 if (certificate
->_subjectAltName
) {
4248 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4249 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4251 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4252 CFRelease(ntPrincipalNames
);
4253 ntPrincipalNames
= NULL
;
4255 return ntPrincipalNames
;
4258 static OSStatus
appendToRFC2253String(void *context
,
4259 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4260 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4264 ST stateOrProvinceName
4266 OU organizationalUnitName
4268 STREET streetAddress
4272 /* Prepend a + if this is not the first RDN in an RDN set.
4273 Otherwise prepend a , if this is not the first RDN. */
4275 CFStringAppend(string
, CFSTR("+"));
4276 else if (CFStringGetLength(string
)) {
4277 CFStringAppend(string
, CFSTR(","));
4280 CFStringRef label
, oid
= NULL
;
4281 /* @@@ Consider changing this to a dictionary lookup keyed by the
4282 decimal representation. */
4283 #if 0 // represent all labels as oids
4284 if (DEROidCompare(type
, &oidCommonName
)) {
4285 label
= CFSTR("CN");
4286 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4288 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4289 label
= CFSTR("ST");
4290 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4292 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4293 label
= CFSTR("OU");
4294 } else if (DEROidCompare(type
, &oidCountryName
)) {
4297 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4298 label
= CFSTR("STREET");
4299 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4300 label
= CFSTR("DC");
4301 } else if (DEROidCompare(type
, &oidUserID
)) {
4302 label
= CFSTR("UID");
4307 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4310 CFStringAppend(string
, label
);
4311 CFStringAppend(string
, CFSTR("="));
4312 CFStringRef raw
= NULL
;
4314 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4317 /* Append raw to string while escaping:
4318 a space or "#" character occurring at the beginning of the string
4319 a space character occurring at the end of the string
4320 one of the characters ",", "+", """, "\", "<", ">" or ";"
4322 CFStringInlineBuffer buffer
;
4323 CFIndex ix
, length
= CFStringGetLength(raw
);
4324 CFRange range
= { 0, length
};
4325 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4326 for (ix
= 0; ix
< length
; ++ix
) {
4327 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4329 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4330 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4331 ch
== '<' || ch
== '>' || ch
== ';' ||
4332 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4333 (ch
== '#' && ix
== 0)) {
4334 UniChar chars
[] = { '\\', ch
};
4335 CFStringAppendCharacters(string
, chars
, 2);
4337 CFStringAppendCharacters(string
, &ch
, 1);
4342 /* Append the value in hex. */
4343 CFStringAppend(string
, CFSTR("#"));
4345 for (ix
= 0; ix
< value
->length
; ++ix
)
4346 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4351 return errSecSuccess
;
4354 CFStringRef
SecCertificateCopySubjectString(SecCertificateRefP certificate
) {
4355 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4356 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4357 if (status
|| CFStringGetLength(string
) == 0) {
4364 static OSStatus
appendToCompanyNameString(void *context
,
4365 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4366 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4367 if (CFStringGetLength(string
) != 0)
4368 return errSecSuccess
;
4370 if (!DEROidCompare(type
, &oidOrganizationName
))
4371 return errSecSuccess
;
4374 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4376 return errSecSuccess
;
4377 CFStringAppend(string
, raw
);
4380 return errSecSuccess
;
4383 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRefP certificate
) {
4384 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4385 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4386 appendToCompanyNameString
);
4387 if (status
|| CFStringGetLength(string
) == 0) {
4394 CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4395 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4396 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4397 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4399 CFDataSetLength(sequence
, sequence_length
);
4400 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4401 *sequence_ptr
++ = 0x30; /* ASN1_CONSTR_SEQUENCE */
4402 require_noerr_quiet(DEREncodeLength(content
->length
,
4403 sequence_ptr
, &seq_len_length
), out
);
4404 sequence_ptr
+= seq_len_length
;
4405 memcpy(sequence_ptr
, content
->data
, content
->length
);
4408 CFReleaseSafe(sequence
);
4412 CFDataRef
SecCertificateCopyIssuerSequenceP(
4413 SecCertificateRefP certificate
) {
4414 return SecDERItemCopySequence(&certificate
->_issuer
);
4417 CFDataRef
SecCertificateCopySubjectSequenceP(
4418 SecCertificateRefP certificate
) {
4419 return SecDERItemCopySequence(&certificate
->_subject
);
4422 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4423 SecCertificateRefP certificate
) {
4424 return &certificate
->_algId
;
4427 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRefP certificate
) {
4428 return &certificate
->_pubKeyDER
;
4431 SecKeyRefP
SecCertificateCopyPublicKeyP(SecCertificateRefP certificate
) {
4432 SecKeyRefP publicKey
= NULL
;
4433 #warning implementation empty
4435 const DERAlgorithmId
*algId
=
4436 SecCertificateGetPublicKeyAlgorithm(certificate
);
4437 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4438 if (DEROidCompare(&algId
->oid
, &oidRsa
)) {
4439 publicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
4440 keyData
->data
, keyData
->length
, kSecKeyEncodingPkcs1
);
4442 secdebug("cert", "Unsupported algorithm oid");
4449 CFDataRef
SecCertificateGetSHA1DigestP(SecCertificateRefP certificate
) {
4450 if (!certificate
->_sha1Digest
) {
4451 certificate
->_sha1Digest
=
4452 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4453 certificate
->_der
.data
, certificate
->_der
.length
);
4456 return certificate
->_sha1Digest
;
4459 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRefP certificate
) {
4460 CFDataRef digest
= NULL
;
4461 CFDataRef issuer
= SecCertificateCopyIssuerSequenceP(certificate
);
4463 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4464 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4470 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRefP certificate
) {
4471 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4472 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4475 CFDataRef
SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator
,
4476 CFDataRef der_certificate
)
4478 CFDataRef result
= NULL
;
4479 SecCertificateRefP iosCertRef
= SecCertificateCreateWithDataP(allocator
, der_certificate
);
4480 if (NULL
== iosCertRef
)
4485 result
= SecCertificateCopyPublicKeySHA1Digest(iosCertRef
);
4486 CFRelease(iosCertRef
);
4490 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRefP certificate
) {
4491 if (!certificate
->_authorityKeyID
&&
4492 certificate
->_authorityKeyIdentifier
.length
) {
4493 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4494 certificate
->_authorityKeyIdentifier
.data
,
4495 certificate
->_authorityKeyIdentifier
.length
);
4498 return certificate
->_authorityKeyID
;
4501 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRefP certificate
) {
4502 if (!certificate
->_subjectKeyID
&&
4503 certificate
->_subjectKeyIdentifier
.length
) {
4504 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4505 certificate
->_subjectKeyIdentifier
.data
,
4506 certificate
->_subjectKeyIdentifier
.length
);
4509 return certificate
->_subjectKeyID
;
4512 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRefP certificate
) {
4513 return certificate
->_crlDistributionPoints
;
4516 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRefP certificate
) {
4517 return certificate
->_ocspResponders
;
4520 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRefP certificate
) {
4521 return certificate
->_caIssuers
;
4524 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRefP certificate
) {
4525 return certificate
->_subjectAltName
&&
4526 certificate
->_subjectAltName
->critical
;
4529 bool SecCertificateHasSubject(SecCertificateRefP certificate
) {
4530 /* Since the _subject field is the content of the subject and not the
4531 whole thing, we can simply check for a 0 length subject here. */
4532 return certificate
->_subject
.length
!= 0;
4535 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRefP certificate
) {
4536 return certificate
->_foundUnknownCriticalExtension
;
4539 /* Private API functions. */
4540 void SecCertificateShow(SecCertificateRefP certificate
) {
4542 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4543 fprintf(stderr
, "\n");
4546 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
4547 SecCertificateRefP certificate
) {
4548 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4549 CFNumberRef certificateType
, certificateEncoding
;
4550 CFStringRef label
, alias
;
4551 CFDataRef skid
, pubKeyDigest
, certData
;
4552 CFDictionaryRef dict
= NULL
;
4556 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4557 SInt32 ctv
= certificate
->_version
+ 1;
4558 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4559 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4560 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4561 certData
= SecCertificateCopyDataP(certificate
);
4562 skid
= SecCertificateGetSubjectKeyID(certificate
);
4563 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4564 certificate
->_pubKeyDER
.length
);
4566 /* We still need to figure out how to deal with multi valued attributes. */
4567 alias
= SecCertificateCopyRFC822Names(certificate
);
4568 label
= SecCertificateCopySubjectSummary(certificate
);
4574 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4575 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4576 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4578 DICT_ADDPAIR(kSecAttrLabel
, label
);
4580 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4581 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4582 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4583 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4585 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4586 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4587 DICT_ADDPAIR(kSecValueData
, certData
);
4588 dict
= DICT_CREATE(allocator
);
4590 CFReleaseSafe(label
);
4591 CFReleaseSafe(pubKeyDigest
);
4592 CFReleaseSafe(certData
);
4593 CFReleaseSafe(certificateEncoding
);
4594 CFReleaseSafe(certificateType
);
4599 SecCertificateRefP
SecCertificateCreateFromAttributeDictionary(
4600 CFDictionaryRef refAttributes
) {
4601 /* @@@ Support having an allocator in refAttributes. */
4602 CFAllocatorRef allocator
= NULL
;
4603 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4604 return SecCertificateCreateWithDataP(allocator
, data
);
4607 bool SecCertificateIsSelfSignedCA(SecCertificateRefP certificate
) {
4608 bool result
= false;
4609 SecKeyRefP publicKey
;
4610 require(publicKey
= SecCertificateCopyPublicKeyP(certificate
), out
);
4611 CFDataRef normalizedIssuer
=
4612 SecCertificateGetNormalizedIssuerContent(certificate
);
4613 CFDataRef normalizedSubject
=
4614 SecCertificateGetNormalizedSubjectContent(certificate
);
4615 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4616 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4618 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
4619 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
4620 if (authorityKeyID
) {
4621 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4624 if (SecCertificateVersion(certificate
) >= 3) {
4625 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
4626 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4627 require_noerr_quiet(SecCertificateIsSignedByP(certificate
, publicKey
), out
);
4632 CFReleaseSafe(publicKey
);
4636 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRefP certificate
) {
4637 return certificate
->_keyUsage
;
4640 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRefP certificate
)
4642 CFMutableArrayRef extended_key_usage_oids
=
4643 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4644 require_quiet(extended_key_usage_oids
, out
);
4646 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4647 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4648 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4649 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4652 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4653 require_noerr_quiet(drtn
, out
);
4654 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4655 DERDecodedInfo currDecoded
;
4657 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4658 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4659 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4660 currDecoded
.content
.data
, currDecoded
.content
.length
);
4662 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4666 require_quiet(drtn
== DR_EndOfSequence
, out
);
4667 return extended_key_usage_oids
;
4671 CFReleaseSafe(extended_key_usage_oids
);
4675 SecCertificateRefP
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
4676 CFDataRef pem_certificate
)
4678 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
4679 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
4680 uint8_t *base64_data
= NULL
;
4681 SecCertificateRefP cert
= NULL
;
4682 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
4683 //const size_t length = CFDataGetLength(pem_certificate);
4684 char *begin
= strstr((const char *)data
, begin_cert
);
4685 char *end
= strstr((const char *)data
, end_cert
);
4688 begin
+= sizeof(begin_cert
) - 1;
4689 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
4690 if (base64_length
) {
4691 require_quiet(base64_data
= calloc(1, base64_length
), out
);
4692 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
4693 cert
= SecCertificateCreateWithBytesP(kCFAllocatorDefault
, base64_data
, base64_length
);
4700 static void convertCertificateToCFData(const void *value
, void *context
) {
4701 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4702 SecCertificateRefP certificate
= (SecCertificateRefP
)value
;
4703 CFDataRef data
= SecCertificateCopyDataP(certificate
);
4704 CFArrayAppendValue(result
, data
);
4708 /* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
4709 CFArrayRef
SecCertificateArrayCopyDataArray(CFArrayRef certificates
) {
4710 CFIndex count
= CFArrayGetCount(certificates
);
4711 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
4712 CFRange all_certs
= { 0, count
};
4713 CFArrayApplyFunction(certificates
, all_certs
, convertCertificateToCFData
, result
);
4717 /* AUDIT[securityd](done):
4718 value (ok) is an element in a caller provided array.
4720 static void convertCFDataToCertificate(const void *value
, void *context
) {
4721 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4722 CFDataRef data
= (CFDataRef
)value
;
4723 if (data
&& CFGetTypeID(data
) == CFDataGetTypeID()) {
4724 SecCertificateRefP certificate
= SecCertificateCreateWithDataP(kCFAllocatorDefault
, data
);
4726 CFArrayAppendValue(result
, certificate
);
4727 CFRelease(certificate
);
4732 /* AUDIT[securityd](done):
4733 certificates (ok) is a caller provided array, only its cf type has
4736 CFArrayRef
SecCertificateDataArrayCopyArray(CFArrayRef certificates
) {
4737 CFIndex count
= CFArrayGetCount(certificates
);
4738 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
4739 CFRange all_certs
= { 0, count
};
4740 CFArrayApplyFunction(certificates
, all_certs
, convertCFDataToCertificate
, result
);