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"
56 #include "debuggingP.h"
58 #include <libkern/OSByteOrder.h>
60 #include "SecInternalP.h"
61 #include "SecBase64P.h"
63 typedef struct SecCertificateExtension
{
67 } SecCertificateExtension
;
70 typedef struct KnownExtension
{
76 kSecSelfSignedUnknown
= 0,
82 struct __SecCertificate
{
85 DERItem _der
; /* Entire certificate in DER form. */
86 DERItem _tbs
; /* To Be Signed cert DER bytes. */
87 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
88 DERItem _signature
; /* The content of the sig bit string. */
91 DERItem _serialNum
; /* Integer. */
92 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
93 DERItem _issuer
; /* Sequence of RDN. */
94 CFAbsoluteTime _notBefore
;
95 CFAbsoluteTime _notAfter
;
96 DERItem _subject
; /* Sequence of RDN. */
97 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
98 DERItem _pubKeyDER
; /* contents of bit string */
99 DERItem _issuerUniqueID
; /* bit string, optional */
100 DERItem _subjectUniqueID
; /* bit string, optional */
103 /* Known extensions if the certificate contains them,
104 extnValue.length will be > 0. */
105 KnownExtension _authorityKeyID
;
107 /* This extension is used to uniquely identify a certificate from among
108 several that have the same subject name. If the extension is not
109 present, its value is calculated by performing a SHA-1 hash of the
110 certificate's DER encoded subjectPublicKeyInfo, as recommended by
112 KnownExtension _subjectKeyID
;
113 KnownExtension _keyUsage
;
114 KnownExtension _extendedKeyUsage
;
115 KnownExtension _basicConstraints
;
116 KnownExtension _netscapeCertType
;
117 KnownExtension _subjectAltName
;
118 KnownExtension _qualCertStatements
;
121 bool _foundUnknownCriticalExtension
;
123 /* Well known certificate extensions. */
124 SecCEBasicConstraints _basicConstraints
;
125 SecCEPolicyConstraints _policyConstraints
;
126 CFDictionaryRef _policyMappings
;
127 SecCECertificatePolicies _certificatePolicies
;
129 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
130 value of the SkipCerts field of the InhibitAnyPolicy extension
132 uint32_t _inhibitAnyPolicySkipCerts
;
134 /* If KeyUsage extension is not present this is 0, otherwise it's
135 the value of the extension. */
136 SecKeyUsage _keyUsage
;
138 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
139 Length = 0 if not present. */
140 DERItem _subjectKeyIdentifier
;
142 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
143 Length = 0 if not present. */
144 DERItem _authorityKeyIdentifier
;
145 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
146 _authorityKeyIdentifierSerialNumber have non zero length if present.
147 Both are either present or absent together. */
148 DERItem _authorityKeyIdentifierIssuer
;
149 DERItem _authorityKeyIdentifierSerialNumber
;
151 /* Subject alt name extension, if present. Not malloced, it's just a
152 pointer to an element in the _extensions array. */
153 const SecCertificateExtension
*_subjectAltName
;
155 /* Parsed extension values. */
157 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
158 CFMutableArrayRef _crlDistributionPoints
;
160 /* Array of CFURLRefs containing the URI values of accessLocations of each
161 id-ad-ocsp AccessDescription in the Authority Information Access
163 CFMutableArrayRef _ocspResponders
;
165 /* Array of CFURLRefs containing the URI values of accessLocations of each
166 id-ad-caIssuers AccessDescription in the Authority Information Access
168 CFMutableArrayRef _caIssuers
;
170 /* All other (non known) extensions. The _extensions array is malloced. */
171 CFIndex _extensionCount
;
172 SecCertificateExtension
*_extensions
;
174 /* Optional cached fields. */
177 CFArrayRef _properties
;
178 CFDataRef _serialNumber
;
179 CFDataRef _normalizedIssuer
;
180 CFDataRef _normalizedSubject
;
181 CFDataRef _authorityKeyID
;
182 CFDataRef _subjectKeyID
;
184 CFDataRef _sha1Digest
;
185 uint8_t _isSelfSigned
;
189 /* Public Constants for property list keys. */
190 CFStringRef kSecPropertyKeyType
= CFSTR("type");
191 CFStringRef kSecPropertyKeyLabel
= CFSTR("label");
192 CFStringRef kSecPropertyKeyLocalizedLabel
= CFSTR("localized label");
193 CFStringRef kSecPropertyKeyValue
= CFSTR("value");
195 /* Public Constants for property list values. */
196 CFStringRef kSecPropertyTypeWarning
= CFSTR("warning");
197 CFStringRef kSecPropertyTypeError
= CFSTR("error");
198 CFStringRef kSecPropertyTypeSuccess
= CFSTR("success");
199 CFStringRef kSecPropertyTypeTitle
= CFSTR("title");
200 CFStringRef kSecPropertyTypeSection
= CFSTR("section");
201 CFStringRef kSecPropertyTypeData
= CFSTR("data");
202 CFStringRef kSecPropertyTypeString
= CFSTR("string");
203 CFStringRef kSecPropertyTypeURL
= CFSTR("url");
204 CFStringRef kSecPropertyTypeDate
= CFSTR("date");
206 /* Extension parsing routine. */
207 typedef void (*SecCertificateExtensionParser
)(SecCertificateRefP certificate
,
208 const SecCertificateExtension
*extn
);
210 /* CFRuntime regsitration data. */
211 static pthread_once_t kSecCertificateRegisterClass
= PTHREAD_ONCE_INIT
;
212 static CFTypeID kSecCertificateTypeID
= _kCFRuntimeNotATypeID
;
214 /* Mapping from extension OIDs (as a DERItem *) to
215 SecCertificateExtensionParser extension parsing routines. */
216 static CFDictionaryRef gExtensionParsers
;
218 /* Forward declartions of static functions. */
219 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
);
220 static void SecCertificateDestroy(CFTypeRef cf
);
221 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
222 CFAbsoluteTime
*absTime
);
224 /* Static functions. */
225 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
) {
226 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
227 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
228 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
,
229 SecCertificateCopySubjectSummaryP(certificate
),
230 SecCertificateCopyIssuerSummary(certificate
));
233 static void SecCertificateDestroy(CFTypeRef cf
) {
234 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
235 if (certificate
->_certificatePolicies
.policies
)
236 free(certificate
->_certificatePolicies
.policies
);
237 CFReleaseSafe(certificate
->_policyMappings
);
238 CFReleaseSafe(certificate
->_crlDistributionPoints
);
239 CFReleaseSafe(certificate
->_ocspResponders
);
240 CFReleaseSafe(certificate
->_caIssuers
);
241 if (certificate
->_extensions
) {
242 free(certificate
->_extensions
);
244 CFReleaseSafe(certificate
->_pubKey
);
245 CFReleaseSafe(certificate
->_der_data
);
246 CFReleaseSafe(certificate
->_properties
);
247 CFReleaseSafe(certificate
->_serialNumber
);
248 CFReleaseSafe(certificate
->_normalizedIssuer
);
249 CFReleaseSafe(certificate
->_normalizedSubject
);
250 CFReleaseSafe(certificate
->_authorityKeyID
);
251 CFReleaseSafe(certificate
->_subjectKeyID
);
252 CFReleaseSafe(certificate
->_sha1Digest
);
255 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
256 SecCertificateRefP cert1
= (SecCertificateRefP
)cf1
;
257 SecCertificateRefP cert2
= (SecCertificateRefP
)cf2
;
260 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
262 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
265 /* Hash of the certificate is der length + signature length + last 4 bytes
267 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
268 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
269 uint32_t der_length
= certificate
->_der
.length
;
270 uint32_t sig_length
= certificate
->_signature
.length
;
271 uint32_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
272 CFHashCode hashCode
= 0;
273 for (; ix
< sig_length
; ++ix
)
274 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
276 return (hashCode
+ der_length
+ sig_length
);
281 /************************************************************************/
282 /************************* General Name Parsing *************************/
283 /************************************************************************/
285 typedef OSStatus (*parseGeneralNameCallback
)(void *context
,
286 SecCEGeneralNameType type
, const DERItem
*value
);
290 GeneralName ::= CHOICE {
291 otherName [0] OtherName,
292 rfc822Name [1] IA5String,
293 dNSName [2] IA5String,
294 x400Address [3] ORAddress,
295 directoryName [4] Name,
296 ediPartyName [5] EDIPartyName,
297 uniformResourceIdentifier [6] IA5String,
298 iPAddress [7] OCTET STRING,
299 registeredID [8] OBJECT IDENTIFIER}
301 OtherName ::= SEQUENCE {
302 type-id OBJECT IDENTIFIER,
303 value [0] EXPLICIT ANY DEFINED BY type-id }
305 EDIPartyName ::= SEQUENCE {
306 nameAssigner [0] DirectoryString OPTIONAL,
307 partyName [1] DirectoryString }
309 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
310 const DERItem
*generalNameContent
,
311 void *context
, parseGeneralNameCallback callback
) {
313 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
314 return callback(context
, GNT_OtherName
, generalNameContent
);
315 case ASN1_CONTEXT_SPECIFIC
| 1:
316 return callback(context
, GNT_RFC822Name
, generalNameContent
);
317 case ASN1_CONTEXT_SPECIFIC
| 2:
318 return callback(context
, GNT_DNSName
, generalNameContent
);
319 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
320 return callback(context
, GNT_X400Address
, generalNameContent
);
321 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
322 return callback(context
, GNT_DirectoryName
, generalNameContent
);
323 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
324 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
325 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
327 /* Technically I don't think this is valid, but there are certs out
328 in the wild that use a constructed IA5String. In particular the
329 VeriSign Time Stamping Authority CA.cer does this. */
330 DERDecodedInfo uriContent
;
331 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
332 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
333 return callback(context
, GNT_URI
, &uriContent
.content
);
335 case ASN1_CONTEXT_SPECIFIC
| 6:
336 return callback(context
, GNT_URI
, generalNameContent
);
337 case ASN1_CONTEXT_SPECIFIC
| 7:
338 return callback(context
, GNT_IPAddress
, generalNameContent
);
339 case ASN1_CONTEXT_SPECIFIC
| 8:
340 return callback(context
, GNT_RegisteredID
, generalNameContent
);
345 return errSecInvalidCertificate
;
348 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
349 void *context
, parseGeneralNameCallback callback
) {
351 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
352 require_noerr_quiet(drtn
, badDER
);
353 DERDecodedInfo generalNameContent
;
354 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
356 OSStatus status
= parseGeneralNameContentProperty(
357 generalNameContent
.tag
, &generalNameContent
.content
, context
,
362 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
366 return errSecInvalidCertificate
;
369 static OSStatus
parseGeneralNames(const DERItem
*generalNames
, void *context
,
370 parseGeneralNameCallback callback
) {
371 DERDecodedInfo generalNamesContent
;
372 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
373 require_noerr_quiet(drtn
, badDER
);
374 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
375 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
378 return errSecInvalidCertificate
;
384 GeneralName ::= CHOICE {
385 otherName [0] OtherName,
386 rfc822Name [1] IA5String,
387 dNSName [2] IA5String,
388 x400Address [3] ORAddress,
389 directoryName [4] Name,
390 ediPartyName [5] EDIPartyName,
391 uniformResourceIdentifier [6] IA5String,
392 iPAddress [7] OCTET STRING,
393 registeredID [8] OBJECT IDENTIFIER}
395 EDIPartyName ::= SEQUENCE {
396 nameAssigner [0] DirectoryString OPTIONAL,
397 partyName [1] DirectoryString }
399 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
400 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
402 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
403 generalName
->nameType
= GNT_OtherName
;
404 generalName
->berEncoded
= true;
405 generalName
->name
= *generalNameContent
;
407 case ASN1_CONTEXT_SPECIFIC
| 1:
409 generalName
->nameType
= GNT_RFC822Name
;
410 generalName
->berEncoded
= false;
411 generalName
->name
= *generalNameContent
;
413 case ASN1_CONTEXT_SPECIFIC
| 2:
415 generalName
->nameType
= GNT_DNSName
;
416 generalName
->berEncoded
= false;
417 generalName
->name
= *generalNameContent
;
419 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
420 generalName
->nameType
= GNT_X400Address
;
421 generalName
->berEncoded
= true;
422 generalName
->name
= *generalNameContent
;
424 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
425 generalName
->nameType
= GNT_DirectoryName
;
426 generalName
->berEncoded
= true;
427 generalName
->name
= *generalNameContent
;
429 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
430 generalName
->nameType
= GNT_EdiPartyName
;
431 generalName
->berEncoded
= true;
432 generalName
->name
= *generalNameContent
;
434 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
436 /* Technically I don't think this is valid, but there are certs out
437 in the wild that use a constructed IA5String. In particular the
438 VeriSign Time Stamping Authority CA.cer does this. */
439 DERDecodedInfo decoded
;
440 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
441 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
442 generalName
->nameType
= GNT_URI
;
443 generalName
->berEncoded
= false;
444 generalName
->name
= decoded
.content
;
447 case ASN1_CONTEXT_SPECIFIC
| 6:
448 generalName
->nameType
= GNT_URI
;
449 generalName
->berEncoded
= false;
450 generalName
->name
= *generalNameContent
;
452 case ASN1_CONTEXT_SPECIFIC
| 7:
453 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
454 8 octects, addr/mask for ipv6 it's 32. */
455 generalName
->nameType
= GNT_IPAddress
;
456 generalName
->berEncoded
= false;
457 generalName
->name
= *generalNameContent
;
459 case ASN1_CONTEXT_SPECIFIC
| 8:
460 /* name is the content of an OID. */
461 generalName
->nameType
= GNT_RegisteredID
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= *generalNameContent
;
471 return errSecInvalidCertificate
;
475 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
477 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
478 CFIndex
*count
, SecCEGeneralName
**name
) {
479 SecCEGeneralName
*generalNames
= NULL
;
481 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
482 require_noerr_quiet(drtn
, badDER
);
483 DERDecodedInfo generalNameContent
;
484 CFIndex generalNamesCount
= 0;
485 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
489 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
491 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
493 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
495 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
497 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
498 &generalNameContent
.content
, &generalNames
[ix
])) {
503 *count
= generalNamesCount
;
504 *name
= generalNames
;
510 return errSecInvalidCertificate
;
513 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
514 CFIndex
*count
, SecCEGeneralName
**name
) {
515 DERDecodedInfo generalNamesContent
;
516 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
517 require_noerr_quiet(drtn
, badDER
);
518 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
520 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
523 return errSecInvalidCertificate
;
527 /************************************************************************/
528 /************************** X.509 Name Parsing **************************/
529 /************************************************************************/
531 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
532 const DERItem
*value
, CFIndex rdnIX
);
534 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
535 parseX501NameCallback callback
) {
537 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
538 require_noerr_quiet(drtn
, badDER
);
539 DERDecodedInfo atvContent
;
541 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
542 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
543 DERAttributeTypeAndValue atv
;
544 drtn
= DERParseSequenceContent(&atvContent
.content
,
545 DERNumAttributeTypeAndValueItemSpecs
,
546 DERAttributeTypeAndValueItemSpecs
,
548 require_noerr_quiet(drtn
, badDER
);
549 require_quiet(atv
.type
.length
!= 0, badDER
);
550 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
554 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
558 return errSecInvalidCertificate
;
561 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
562 parseX501NameCallback callback
) {
564 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
565 require_noerr_quiet(drtn
, badDER
);
566 DERDecodedInfo currDecoded
;
567 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
568 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
569 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
574 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
579 return errSecInvalidCertificate
;
582 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
583 parseX501NameCallback callback
) {
584 DERDecodedInfo x501NameContent
;
585 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
586 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
587 return errSecInvalidCertificate
;
589 return parseX501NameContent(&x501NameContent
.content
, context
,
594 /************************************************************************/
595 /********************** Extension Parsing Routines **********************/
596 /************************************************************************/
598 static void SecCEPSubjectKeyIdentifier(SecCertificateRefP certificate
,
599 const SecCertificateExtension
*extn
) {
600 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
601 DERDecodedInfo keyIdentifier
;
602 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
603 require_noerr_quiet(drtn
, badDER
);
604 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
605 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
609 secdebug(NULL
, "Invalid SubjectKeyIdentifier Extension");
612 static void SecCEPKeyUsage(SecCertificateRefP certificate
,
613 const SecCertificateExtension
*extn
) {
614 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
615 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
616 DERDecodedInfo bitStringContent
;
617 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
618 require_noerr_quiet(drtn
, badDER
);
619 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
620 DERSize len
= bitStringContent
.content
.length
- 1;
621 require_quiet(len
== 1 || len
== 2, badDER
);
622 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
623 require_quiet(numUnusedBits
< 8, badDER
);
624 /* Flip the bits in the bit string so the first bit in the lsb. */
625 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
626 uint_fast16_t value
= bitStringContent
.content
.data
[1];
629 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
635 for (ix
= 0; ix
< bits
; ++ix
) {
641 certificate
->_keyUsage
= keyUsage
;
644 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
647 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRefP certificate
,
648 const SecCertificateExtension
*extn
) {
649 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
652 static void SecCEPSubjectAltName(SecCertificateRefP certificate
,
653 const SecCertificateExtension
*extn
) {
654 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
655 certificate
->_subjectAltName
= extn
;
658 static void SecCEPIssuerAltName(SecCertificateRefP certificate
,
659 const SecCertificateExtension
*extn
) {
660 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
663 static void SecCEPBasicConstraints(SecCertificateRefP certificate
,
664 const SecCertificateExtension
*extn
) {
665 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
666 DERBasicConstraints basicConstraints
;
667 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
668 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
669 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
670 require_noerr_quiet(DERParseBoolean(&basicConstraints
.cA
, false,
671 &certificate
->_basicConstraints
.isCA
), badDER
);
672 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
673 require_noerr_quiet(DERParseInteger(
674 &basicConstraints
.pathLenConstraint
,
675 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
676 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
678 certificate
->_basicConstraints
.present
= true;
679 certificate
->_basicConstraints
.critical
= extn
->critical
;
682 certificate
->_basicConstraints
.present
= false;
683 secdebug(NULL
, "Invalid BasicConstraints Extension");
686 static void SecCEPCrlDistributionPoints(SecCertificateRefP certificate
,
687 const SecCertificateExtension
*extn
) {
688 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
692 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
694 PolicyInformation ::= SEQUENCE {
695 policyIdentifier CertPolicyId,
696 policyQualifiers SEQUENCE SIZE (1..MAX) OF
697 PolicyQualifierInfo OPTIONAL }
699 CertPolicyId ::= OBJECT IDENTIFIER
701 PolicyQualifierInfo ::= SEQUENCE {
702 policyQualifierId PolicyQualifierId,
703 qualifier ANY DEFINED BY policyQualifierId }
705 static void SecCEPCertificatePolicies(SecCertificateRefP certificate
,
706 const SecCertificateExtension
*extn
) {
707 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
710 SecCEPolicyInformation
*policies
= NULL
;
711 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
712 require_noerr_quiet(drtn
, badDER
);
713 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
714 DERDecodedInfo piContent
;
715 DERSize policy_count
= 0;
716 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
717 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
720 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
721 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
723 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
724 DERSize policy_ix
= 0;
725 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
726 DERPolicyInformation pi
;
727 drtn
= DERParseSequenceContent(&piContent
.content
,
728 DERNumPolicyInformationItemSpecs
,
729 DERPolicyInformationItemSpecs
,
731 require_noerr_quiet(drtn
, badDER
);
732 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
733 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
735 certificate
->_certificatePolicies
.present
= true;
736 certificate
->_certificatePolicies
.critical
= extn
->critical
;
737 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
738 certificate
->_certificatePolicies
.policies
= policies
;
743 certificate
->_certificatePolicies
.present
= false;
744 secdebug(NULL
, "Invalid CertificatePolicies Extension");
748 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
750 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
751 issuerDomainPolicy CertPolicyId,
752 subjectDomainPolicy CertPolicyId }
755 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
756 const SecCertificateExtension
*extn
) {
757 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
760 SecCEPolicyMapping
*mappings
= NULL
;
761 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
762 require_noerr_quiet(drtn
, badDER
);
763 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
764 DERDecodedInfo pmContent
;
765 DERSize mapping_count
= 0;
766 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
767 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
770 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
772 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
773 DERSize mapping_ix
= 0;
774 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
776 drtn
= DERParseSequenceContent(&pmContent
.content
,
777 DERNumPolicyMappingItemSpecs
,
778 DERPolicyMappingItemSpecs
,
780 require_noerr_quiet(drtn
, badDER
);
781 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
782 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
784 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
785 certificate
->_policyMappings
.present
= true;
786 certificate
->_policyMappings
.critical
= extn
->critical
;
787 certificate
->_policyMappings
.numMappings
= mapping_count
;
788 certificate
->_policyMappings
.mappings
= mappings
;
793 CFReleaseSafe(mappings
);
794 certificate
->_policyMappings
.present
= false;
795 secdebug(NULL
, "Invalid CertificatePolicies Extension");
798 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
799 const SecCertificateExtension
*extn
) {
800 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
803 CFMutableDictionaryRef mappings
= NULL
;
804 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
805 require_noerr_quiet(drtn
, badDER
);
806 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
807 DERDecodedInfo pmContent
;
808 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
809 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
811 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
812 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
814 drtn
= DERParseSequenceContent(&pmContent
.content
,
815 DERNumPolicyMappingItemSpecs
,
816 DERPolicyMappingItemSpecs
,
818 require_noerr_quiet(drtn
, badDER
);
820 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
821 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
822 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
823 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
824 CFMutableArrayRef sdps
=
825 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
827 CFArrayAppendValue(sdps
, sdp
);
829 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
830 &kCFTypeArrayCallBacks
), badDER
);
831 CFDictionarySetValue(mappings
, idp
, sdps
);
835 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
836 certificate
->_policyMappings
= mappings
;
839 CFReleaseSafe(mappings
);
840 certificate
->_policyMappings
= NULL
;
841 secdebug(NULL
, "Invalid CertificatePolicies Extension");
846 AuthorityKeyIdentifier ::= SEQUENCE {
847 keyIdentifier [0] KeyIdentifier OPTIONAL,
848 authorityCertIssuer [1] GeneralNames OPTIONAL,
849 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
850 -- authorityCertIssuer and authorityCertSerialNumber MUST both
851 -- be present or both be absent
853 KeyIdentifier ::= OCTET STRING
855 static void SecCEPAuthorityKeyIdentifier(SecCertificateRefP certificate
,
856 const SecCertificateExtension
*extn
) {
857 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
858 DERAuthorityKeyIdentifier akid
;
860 drtn
= DERParseSequence(&extn
->extnValue
,
861 DERNumAuthorityKeyIdentifierItemSpecs
,
862 DERAuthorityKeyIdentifierItemSpecs
,
863 &akid
, sizeof(akid
));
864 require_noerr_quiet(drtn
, badDER
);
865 if (akid
.keyIdentifier
.length
) {
866 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
868 if (akid
.authorityCertIssuer
.length
||
869 akid
.authorityCertSerialNumber
.length
) {
870 require_quiet(akid
.authorityCertIssuer
.length
&&
871 akid
.authorityCertSerialNumber
.length
, badDER
);
872 /* Perhaps put in a subsection called Authority Certificate Issuer. */
873 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
874 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
879 secdebug(NULL
, "Invalid AuthorityKeyIdentifier Extension");
882 static void SecCEPPolicyConstraints(SecCertificateRefP certificate
,
883 const SecCertificateExtension
*extn
) {
884 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
885 DERPolicyConstraints pc
;
887 drtn
= DERParseSequence(&extn
->extnValue
,
888 DERNumPolicyConstraintsItemSpecs
,
889 DERPolicyConstraintsItemSpecs
,
891 require_noerr_quiet(drtn
, badDER
);
892 if (pc
.requireExplicitPolicy
.length
) {
893 require_noerr_quiet(DERParseInteger(
894 &pc
.requireExplicitPolicy
,
895 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
896 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
898 if (pc
.inhibitPolicyMapping
.length
) {
899 require_noerr_quiet(DERParseInteger(
900 &pc
.inhibitPolicyMapping
,
901 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
902 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
905 certificate
->_policyConstraints
.present
= true;
906 certificate
->_policyConstraints
.critical
= extn
->critical
;
910 certificate
->_policyConstraints
.present
= false;
911 secdebug(NULL
, "Invalid PolicyConstraints Extension");
914 static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate
,
915 const SecCertificateExtension
*extn
) {
916 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
920 InhibitAnyPolicy ::= SkipCerts
922 SkipCerts ::= INTEGER (0..MAX)
924 static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate
,
925 const SecCertificateExtension
*extn
) {
926 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
927 require_noerr_quiet(DERParseInteger(
929 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
932 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
933 secdebug(NULL
, "Invalid InhibitAnyPolicy Extension");
937 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
939 AuthorityInfoAccessSyntax ::=
940 SEQUENCE SIZE (1..MAX) OF AccessDescription
942 AccessDescription ::= SEQUENCE {
943 accessMethod OBJECT IDENTIFIER,
944 accessLocation GeneralName }
946 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
948 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
950 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
952 static void SecCEPAuthorityInfoAccess(SecCertificateRefP certificate
,
953 const SecCertificateExtension
*extn
) {
954 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
957 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
958 require_noerr_quiet(drtn
, badDER
);
959 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
960 DERDecodedInfo adContent
;
961 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
962 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
963 DERAccessDescription ad
;
964 drtn
= DERParseSequenceContent(&adContent
.content
,
965 DERNumAccessDescriptionItemSpecs
,
966 DERAccessDescriptionItemSpecs
,
968 require_noerr_quiet(drtn
, badDER
);
969 CFMutableArrayRef
*urls
;
970 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
971 urls
= &certificate
->_ocspResponders
;
972 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
973 urls
= &certificate
->_caIssuers
;
977 DERDecodedInfo generalNameContent
;
978 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
979 require_noerr_quiet(drtn
, badDER
);
980 switch (generalNameContent
.tag
) {
982 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
983 /* Technically I don't think this is valid, but there are certs out
984 in the wild that use a constructed IA5String. In particular the
985 VeriSign Time Stamping Authority CA.cer does this. */
987 case ASN1_CONTEXT_SPECIFIC
| 6:
989 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
990 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
991 kCFStringEncodingASCII
, NULL
);
994 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
995 CFArrayAppendValue(*urls
, url
);
1001 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02x v: %.*s",
1002 generalNameContent
.tag
, generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1007 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1010 secdebug("cert", "failed to parse Authority Information Access extension");
1013 static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate
,
1014 const SecCertificateExtension
*extn
) {
1015 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1018 static void SecCEPNetscapeCertType(SecCertificateRefP certificate
,
1019 const SecCertificateExtension
*extn
) {
1020 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1023 static void SecCEPEntrustVersInfo(SecCertificateRefP certificate
,
1024 const SecCertificateExtension
*extn
) {
1025 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1028 /* Dictionary key callback for comparing to DERItems. */
1029 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1030 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1033 /* Dictionary key callback calculating the hash of a DERItem. */
1034 static CFHashCode
SecDERItemHash(const void *value
) {
1035 const DERItem
*derItem
= (const DERItem
*)value
;
1036 CFHashCode hash
= derItem
->length
;
1037 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1038 for (; ix
< derItem
->length
; ++ix
) {
1039 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1045 /* Dictionary key callbacks using the above 2 functions. */
1046 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1050 NULL
, /* copyDescription */
1051 SecDERItemEqual
, /* equal */
1052 SecDERItemHash
/* hash */
1055 static void SecCertificateRegisterClass(void) {
1056 static const CFRuntimeClass kSecCertificateClass
= {
1058 "SecCertificate", /* class name */
1061 SecCertificateDestroy
, /* dealloc */
1062 SecCertificateEqual
, /* equal */
1063 SecCertificateHash
, /* hash */
1064 NULL
, /* copyFormattingDesc */
1065 SecCertificateDescribe
/* copyDebugDesc */
1068 kSecCertificateTypeID
= _CFRuntimeRegisterClass(&kSecCertificateClass
);
1070 /* Build a dictionary that maps from extension OIDs to callback functions
1071 which can parse the extension of the type given. */
1072 static const void *extnOIDs
[] = {
1073 &oidSubjectKeyIdentifier
,
1075 &oidPrivateKeyUsagePeriod
,
1078 &oidBasicConstraints
,
1079 &oidCrlDistributionPoints
,
1080 &oidCertificatePolicies
,
1082 &oidAuthorityKeyIdentifier
,
1083 &oidPolicyConstraints
,
1084 &oidExtendedKeyUsage
,
1085 &oidInhibitAnyPolicy
,
1086 &oidAuthorityInfoAccess
,
1087 &oidSubjectInfoAccess
,
1088 &oidNetscapeCertType
,
1091 static const void *extnParsers
[] = {
1092 SecCEPSubjectKeyIdentifier
,
1094 SecCEPPrivateKeyUsagePeriod
,
1095 SecCEPSubjectAltName
,
1096 SecCEPIssuerAltName
,
1097 SecCEPBasicConstraints
,
1098 SecCEPCrlDistributionPoints
,
1099 SecCEPCertificatePolicies
,
1100 SecCEPPolicyMappings
,
1101 SecCEPAuthorityKeyIdentifier
,
1102 SecCEPPolicyConstraints
,
1103 SecCEPExtendedKeyUsage
,
1104 SecCEPInhibitAnyPolicy
,
1105 SecCEPAuthorityInfoAccess
,
1106 SecCEPSubjectInfoAccess
,
1107 SecCEPNetscapeCertType
,
1108 SecCEPEntrustVersInfo
1110 gExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1111 extnParsers
, sizeof(extnOIDs
) / sizeof(*extnOIDs
),
1112 &SecDERItemKeyCallBacks
, NULL
);
1115 /* Given the contents of an X.501 Name return the contents of a normalized
1117 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1118 const DERItem
*x501name
) {
1119 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1120 CFIndex length
= x501name
->length
;
1121 CFDataSetLength(result
, length
);
1122 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1125 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1127 require_noerr_quiet(drtn
, badDER
);
1130 /* Always points to last rdn tag. */
1131 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1132 /* Offset relative to base of current rdn set tag. */
1133 CFIndex rdnTagLocation
= 0;
1134 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1135 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1136 /* We don't allow empty RDNs. */
1137 require_quiet(rdn
.content
.length
!= 0, badDER
);
1138 /* Length of the tag and length of the current rdn. */
1139 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1140 CFIndex rdnContentLength
= rdn
.content
.length
;
1141 /* Copy the tag and length of the RDN. */
1142 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1145 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1147 /* Always points to tag of current atv sequence. */
1148 const DERByte
*atvTag
= atvSeq
.nextItem
;
1149 /* Offset relative to base of current atv sequence tag. */
1150 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1151 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1152 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1153 /* Length of the tag and length of the current atv. */
1154 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1155 CFIndex atvContentLength
= atv
.content
.length
;
1156 /* Copy the tag and length of the atv and the atv itself. */
1157 memcpy(base
+ atvTagLocation
, atvTag
,
1158 atvTLLength
+ atv
.content
.length
);
1160 /* Now decode the atv sequence. */
1161 DERAttributeTypeAndValue atvPair
;
1162 drtn
= DERParseSequenceContent(&atv
.content
,
1163 DERNumAttributeTypeAndValueItemSpecs
,
1164 DERAttributeTypeAndValueItemSpecs
,
1165 &atvPair
, sizeof(atvPair
));
1166 require_noerr_quiet(drtn
, badDER
);
1167 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1168 DERDecodedInfo value
;
1169 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1170 require_noerr_quiet(drtn
, badDER
);
1172 /* (c) attribute values in PrintableString are not case sensitive
1173 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1175 (d) attribute values in PrintableString are compared after
1176 removing leading and trailing white space and converting internal
1177 substrings of one or more consecutive white space characters to a
1179 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1180 /* Offset relative to base of current value tag. */
1181 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1182 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1183 CFIndex valueContentLength
= value
.content
.length
;
1185 /* Now copy all the bytes, but convert to upper case while
1186 doing so and convert multiple whitespace chars into a
1188 bool lastWasBlank
= false;
1189 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1190 CFIndex valueCurrentLocation
= valueLocation
;
1192 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1193 UInt8 ch
= value
.content
.data
[ix
];
1198 /* Don't insert a space for first character
1200 if (valueCurrentLocation
> valueLocation
) {
1201 base
[valueCurrentLocation
++] = ' ';
1203 lastWasBlank
= true;
1206 lastWasBlank
= false;
1207 if ('a' <= ch
&& ch
<= 'z') {
1208 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1210 base
[valueCurrentLocation
++] = ch
;
1214 /* Finally if lastWasBlank remove the trailing space. */
1215 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1216 valueCurrentLocation
--;
1218 /* Adjust content length to normalized length. */
1219 valueContentLength
= valueCurrentLocation
- valueLocation
;
1221 /* Number of bytes by which the length should be shorted. */
1222 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1223 if (lengthDiff
== 0) {
1224 /* Easy case no need to adjust lengths. */
1226 /* Hard work we need to go back and fix up length fields
1228 1) The value itself.
1229 2) The ATV Sequence containing type/value
1230 3) The RDN Set containing one or more atv pairs.
1234 /* Step 1 fix up length of value. */
1235 /* Length of value tag and length minus the tag. */
1236 DERSize newValueTLLength
= valueTLLength
- 1;
1237 drtn
= DEREncodeLength(valueContentLength
,
1238 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1239 /* Add the length of the tag back in. */
1241 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1243 /* The size of the length field changed, let's slide
1244 the value back by valueLLDiff bytes. */
1245 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1246 base
+ valueTagLocation
+ valueTLLength
,
1247 valueContentLength
);
1248 /* The length diff for the enclosing object. */
1249 lengthDiff
+= valueLLDiff
;
1252 /* Step 2 fix up length of the enclosing ATV Sequence. */
1253 atvContentLength
-= lengthDiff
;
1254 DERSize newATVTLLength
= atvTLLength
- 1;
1255 drtn
= DEREncodeLength(atvContentLength
,
1256 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1257 /* Add the length of the tag back in. */
1259 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1261 /* The size of the length field changed, let's slide
1262 the value back by valueLLDiff bytes. */
1263 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1264 base
+ atvTagLocation
+ atvTLLength
,
1266 /* The length diff for the enclosing object. */
1267 lengthDiff
+= atvLLDiff
;
1268 atvTLLength
= newATVTLLength
;
1271 /* Step 3 fix up length of enclosing RDN Set. */
1272 rdnContentLength
-= lengthDiff
;
1273 DERSize newRDNTLLength
= rdnTLLength
- 1;
1274 drtn
= DEREncodeLength(rdnContentLength
,
1275 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1276 /* Add the length of the tag back in. */
1278 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1280 /* The size of the length field changed, let's slide
1281 the value back by valueLLDiff bytes. */
1282 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1283 base
+ rdnTagLocation
+ rdnTLLength
,
1285 /* The length diff for the enclosing object. */
1286 lengthDiff
+= rdnLLDiff
;
1287 rdnTLLength
= newRDNTLLength
;
1289 /* Adjust the locations that might have changed due to
1291 atvTagLocation
-= rdnLLDiff
;
1295 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1296 atvTag
= atvSeq
.nextItem
;
1298 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1299 rdnTag
= rdnSeq
.nextItem
;
1301 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1302 /* Truncate the result to the proper length. */
1303 CFDataSetLength(result
, rdnTagLocation
);
1312 /* AUDIT[securityd]:
1313 certificate->_der is a caller provided data of any length (might be 0).
1315 Top level certificate decode.
1317 static bool SecCertificateParse(SecCertificateRefP certificate
)
1322 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1324 /* top level decode */
1325 DERSignedCertCrl signedCert
;
1326 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1327 DERSignedCertCrlItemSpecs
, &signedCert
,
1328 sizeof(signedCert
));
1329 require_noerr_quiet(drtn
, badCert
);
1330 /* Store tbs since we need to digest it for verification later on. */
1331 certificate
->_tbs
= signedCert
.tbs
;
1333 /* decode the TBSCert - it was saved in full DER form */
1335 drtn
= DERParseSequence(&signedCert
.tbs
,
1336 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1337 &tbsCert
, sizeof(tbsCert
));
1338 require_noerr_quiet(drtn
, badCert
);
1340 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1341 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1342 of the params field. */
1343 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1344 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1345 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1346 require_noerr_quiet(drtn
, badCert
);
1348 /* The contents of signedCert.sig is a bit string whose contents
1349 are the signature itself. */
1350 DERByte numUnusedBits
;
1351 drtn
= DERParseBitString(&signedCert
.sig
,
1352 &certificate
->_signature
, &numUnusedBits
);
1353 require_noerr_quiet(drtn
, badCert
);
1355 /* Now decode the tbsCert. */
1357 /* First we turn the optional version into an int. */
1358 if (tbsCert
.version
.length
) {
1359 DERDecodedInfo decoded
;
1360 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1361 require_noerr_quiet(drtn
, badCert
);
1362 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1363 require_quiet(decoded
.content
.length
== 1, badCert
);
1364 certificate
->_version
= decoded
.content
.data
[0];
1365 require_quiet(certificate
->_version
> 0, badCert
);
1366 require_quiet(certificate
->_version
< 3, badCert
);
1368 certificate
->_version
= 0;
1371 /* The serial number is in the tbsCert.serialNum - it was saved in
1372 INTEGER form without the tag and length. */
1373 certificate
->_serialNum
= tbsCert
.serialNum
;
1374 certificate
->_serialNumber
= CFDataCreate(allocator
,
1375 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1377 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1378 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1379 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1380 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1381 require_noerr_quiet(drtn
, badCert
);
1383 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1384 and length fields. */
1385 certificate
->_issuer
= tbsCert
.issuer
;
1386 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1389 /* sequence we're given: decode the tbsCerts Validity sequence. */
1390 DERValidity validity
;
1391 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1392 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1393 &validity
, sizeof(validity
));
1394 require_noerr_quiet(drtn
, badCert
);
1395 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1396 &certificate
->_notBefore
), badCert
);
1397 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1398 &certificate
->_notAfter
), badCert
);
1400 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1401 and length fields. */
1402 certificate
->_subject
= tbsCert
.subject
;
1403 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1406 /* sequence we're given: encoded DERSubjPubKeyInfo */
1407 DERSubjPubKeyInfo pubKeyInfo
;
1408 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1409 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1410 &pubKeyInfo
, sizeof(pubKeyInfo
));
1411 require_noerr_quiet(drtn
, badCert
);
1413 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1414 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1415 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1416 &certificate
->_algId
, sizeof(certificate
->_algId
));
1417 require_noerr_quiet(drtn
, badCert
);
1419 /* Now we can figure out the key's algorithm id and params based on
1420 certificate->_algId.oid. */
1422 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1423 are a PKCS1 format RSA key. */
1424 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1425 &certificate
->_pubKeyDER
, &numUnusedBits
);
1426 require_noerr_quiet(drtn
, badCert
);
1428 /* The contents of tbsCert.issuerID is a bit string. */
1429 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1431 /* The contents of tbsCert.subjectID is a bit string. */
1432 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1435 if (tbsCert
.extensions
.length
) {
1436 CFIndex extensionCount
= 0;
1439 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1441 require_noerr_quiet(drtn
, badCert
);
1442 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1443 DERDecodedInfo currDecoded
;
1444 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1446 /* ! = MUST recognize ? = SHOULD recognize
1449 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1450 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1451 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1452 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1453 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1454 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1455 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1456 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1458 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1459 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1460 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1461 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1462 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1463 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1464 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1465 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1467 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1468 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1473 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1475 /* Put some upper limit on the number of extentions allowed. */
1476 require_quiet(extensionCount
< 10000, badCert
);
1477 certificate
->_extensionCount
= extensionCount
;
1478 certificate
->_extensions
=
1479 malloc(sizeof(SecCertificateExtension
) * extensionCount
);
1482 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1483 require_noerr_quiet(drtn
, badCert
);
1484 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1485 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1486 require_quiet(drtn
== DR_Success
||
1487 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1488 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1490 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1491 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1492 &extn
, sizeof(extn
));
1493 require_noerr_quiet(drtn
, badCert
);
1494 /* Copy stuff into certificate->extensions[ix]. */
1495 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1496 require_noerr_quiet(drtn
= DERParseBoolean(&extn
.critical
, false,
1497 &certificate
->_extensions
[ix
].critical
), badCert
);
1498 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1500 SecCertificateExtensionParser parser
=
1501 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1502 gExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1504 /* Invoke the parser. */
1505 parser(certificate
, &certificate
->_extensions
[ix
]);
1506 } else if (certificate
->_extensions
[ix
].critical
) {
1507 secdebug("cert", "Found unknown critical extension");
1508 certificate
->_foundUnknownCriticalExtension
= true;
1510 secdebug("cert", "Found unknown non critical extension");
1522 /* Public API functions. */
1523 CFTypeID
SecCertificateGetTypeIDP(void) {
1524 pthread_once(&kSecCertificateRegisterClass
, SecCertificateRegisterClass
);
1525 return kSecCertificateTypeID
;
1528 SecCertificateRefP
SecCertificateCreateWithBytesP(CFAllocatorRef allocator
,
1529 const UInt8
*der_bytes
, CFIndex der_length
) {
1532 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1533 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1534 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1536 memset((char*)result
+ sizeof(result
->_base
), 0,
1537 sizeof(*result
) - sizeof(result
->_base
));
1538 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1539 result
->_der
.length
= der_length
;
1540 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1541 if (!SecCertificateParse(result
)) {
1549 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1550 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1551 const UInt8
*der_bytes
, CFIndex der_length
);
1553 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1554 const UInt8
*der_bytes
, CFIndex der_length
) {
1555 return SecCertificateCreateWithBytesP(allocator
, der_bytes
, der_length
);
1557 /* @@@ End of placeholder. */
1559 /* AUDIT[securityd](done):
1560 der_certificate is a caller provided data of any length (might be 0), only
1561 its cf type has been checked.
1563 SecCertificateRefP
SecCertificateCreateWithDataP(CFAllocatorRef allocator
,
1564 CFDataRef der_certificate
) {
1565 check(der_certificate
);
1566 CFIndex size
= sizeof(struct __SecCertificate
);
1567 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1568 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1570 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1571 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1572 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1573 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1574 if (!SecCertificateParse(result
)) {
1582 CFDataRef
SecCertificateCopyDataP(SecCertificateRefP certificate
) {
1585 if (certificate
->_der_data
) {
1586 CFRetain(certificate
->_der_data
);
1587 result
= certificate
->_der_data
;
1589 result
= CFDataCreate(CFGetAllocator(certificate
),
1590 certificate
->_der
.data
, certificate
->_der
.length
);
1592 /* FIXME: If we wish to cache result we need to lock the certificate.
1593 Also this create 2 copies of the certificate data which is somewhat
1596 certificate
->_der_data
= result
;
1603 CFIndex
SecCertificateGetLengthP(SecCertificateRefP certificate
) {
1604 return certificate
->_der
.length
;
1607 const UInt8
*SecCertificateGetBytePtrP(SecCertificateRefP certificate
) {
1608 return certificate
->_der
.data
;
1611 /* From rfc3280 - Appendix B. ASN.1 Notes
1613 Object Identifiers (OIDs) are used throughout this specification to
1614 identify certificate policies, public key and signature algorithms,
1615 certificate extensions, etc. There is no maximum size for OIDs.
1616 This specification mandates support for OIDs which have arc elements
1617 with values that are less than 2^28, that is, they MUST be between 0
1618 and 268,435,455, inclusive. This allows each arc element to be
1619 represented within a single 32 bit word. Implementations MUST also
1620 support OIDs where the length of the dotted decimal (see [RFC 2252],
1621 section 4.1) string representation can be up to 100 bytes
1622 (inclusive). Implementations MUST be able to handle OIDs with up to
1623 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1624 contain OIDs that exceed these requirements. Likewise, CRL issuers
1625 SHOULD NOT issue CRLs which contain OIDs that exceed these
1629 /* Oids longer than this are considered invalid. */
1630 #define MAX_OID_SIZE 32
1632 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1633 const DERItem
*oid
) {
1635 if (oid
->length
== 0) {
1636 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1637 CFSTR("SecCertificate"));
1639 if (oid
->length
> MAX_OID_SIZE
) {
1640 return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"),
1641 CFSTR("SecCertificate"));
1644 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1646 // The first two levels are encoded into one byte, since the root level
1647 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1648 // y may be > 39, so we have to add special-case handling for this.
1649 uint32_t x
= oid
->data
[0] / 40;
1650 uint32_t y
= oid
->data
[0] % 40;
1653 // Handle special case for large y if x = 2
1657 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1660 for (x
= 1; x
< oid
->length
; ++x
)
1662 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1663 /* @@@ value may not span more than 4 bytes. */
1664 /* A max number of 20 values is allowed. */
1665 if (!(oid
->data
[x
] & 0x80))
1667 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), value
);
1674 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1675 const DERItem
*oid
) {
1676 if (oid
->length
== 0) {
1677 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1678 CFSTR("SecCertificate"));
1681 /* Build the key we use to lookup the localized OID description. */
1682 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1683 oid
->length
* 3 + 5);
1684 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02X"), oid
->length
);
1686 for (ix
= 0; ix
< oid
->length
; ++ix
)
1687 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1689 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1690 if (CFEqual(oidKey
, name
)) {
1692 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1699 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1700 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1701 have a length of exactly 4 or 16 octects. */
1702 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1703 const DERItem
*ip
) {
1704 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1705 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1706 16 octects addr, or 32 octects addr/mask. */
1707 CFStringRef value
= NULL
;
1708 if (ip
->length
== 4) {
1709 value
= CFStringCreateWithFormat(allocator
, NULL
,
1710 CFSTR("%u.%u.%u.%u"),
1711 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1712 } else if (ip
->length
== 16) {
1713 value
= CFStringCreateWithFormat(allocator
, NULL
,
1714 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1715 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1716 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
1717 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
1718 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
1719 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
1726 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
1727 const DERItem
*oid
) {
1728 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1729 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
1730 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
1731 CFSTR("%@ (%@)"), name
, decimal
);
1738 void appendProperty(CFMutableArrayRef properties
,
1739 CFStringRef propertyType
, CFStringRef label
, CFTypeRef value
) {
1740 CFDictionaryRef property
;
1742 CFStringRef localizedLabel
= SecFrameworkCopyLocalizedString(label
,
1743 CFSTR("SecCertificate"));
1744 const void *all_keys
[4];
1745 all_keys
[0] = kSecPropertyKeyType
;
1746 all_keys
[1] = kSecPropertyKeyLabel
;
1747 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
1748 all_keys
[3] = kSecPropertyKeyValue
;
1749 const void *property_values
[] = {
1755 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1756 all_keys
, property_values
, value
? 4 : 3,
1757 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1758 CFRelease(localizedLabel
);
1760 const void *nolabel_keys
[2];
1761 nolabel_keys
[0] = kSecPropertyKeyType
;
1762 nolabel_keys
[1] = kSecPropertyKeyValue
;
1763 const void *property_values
[] = {
1767 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1768 nolabel_keys
, property_values
, 2,
1769 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1772 CFArrayAppendValue(properties
, property
);
1773 CFRelease(property
);
1777 #define UTC_TIME_NOSEC_ZULU_LEN 11
1779 #define UTC_TIME_ZULU_LEN 13
1780 /* YYMMDDhhmmssThhmm */
1781 #define UTC_TIME_LOCALIZED_LEN 17
1782 /* YYYYMMDDhhmmssZ */
1783 #define GENERALIZED_TIME_ZULU_LEN 15
1784 /* YYYYMMDDhhmmssThhmm */
1785 #define GENERALIZED_TIME_LOCALIZED_LEN 19
1787 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
1789 static inline SInt32
parseDecimalPair(const DERByte
**p
) {
1790 const DERByte
*cp
= *p
;
1792 return 10 * (cp
[0] - '0') + cp
[1] - '0';
1795 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1796 true if the date was valid and properly decoded, also return the result in
1797 absTime. Return false otherwise. */
1798 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
1804 bool isUtcLength
= false;
1805 bool isLocalized
= false;
1806 bool noSeconds
= false;
1808 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
1812 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
1815 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
1817 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
1820 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
1823 default: /* unknown format */
1827 /* Make sure the der tag fits the thing inside it. */
1828 if (tag
== ASN1_UTC_TIME
) {
1831 } else if (tag
== ASN1_GENERALIZED_TIME
) {
1838 const DERByte
*cp
= bytes
;
1839 /* Check that all characters are digits, except if localized the timezone
1840 indicator or if not localized the 'Z' at the end. */
1842 for (ix
= 0; ix
< length
; ++ix
) {
1843 if (!(isdigit(cp
[ix
]))) {
1844 if ((isLocalized
&& ix
== length
- 5 &&
1845 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
1846 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
1853 /* Initialize the fields in a gregorian date struct. */
1854 CFGregorianDate gdate
;
1856 SInt32 year
= parseDecimalPair(&cp
);
1858 /* 0 <= year < 50 : assume century 21 */
1859 gdate
.year
= 2000 + year
;
1860 } else if (year
< 70) {
1861 /* 50 <= year < 70 : illegal per PKIX */
1864 /* 70 < year <= 99 : assume century 20 */
1865 gdate
.year
= 1900 + year
;
1868 gdate
.year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
1870 gdate
.month
= parseDecimalPair(&cp
);
1871 gdate
.day
= parseDecimalPair(&cp
);
1872 gdate
.hour
= parseDecimalPair(&cp
);
1873 gdate
.minute
= parseDecimalPair(&cp
);
1877 gdate
.second
= parseDecimalPair(&cp
);
1880 CFTimeInterval timeZoneOffset
= 0;
1882 /* ZONE INDICATOR */
1883 SInt32 multiplier
= *cp
++ == '+' ? 60 : -60;
1884 timeZoneOffset
= multiplier
*
1885 (parseDecimalPair(&cp
) + 60 * parseDecimalPair(&cp
));
1890 secdebug("dateparse",
1891 "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f",
1892 length
, bytes
, gdate
.year
, gdate
.month
,
1893 gdate
.day
, gdate
.hour
, gdate
.minute
, gdate
.second
,
1894 timeZoneOffset
/ 60);
1896 if (!CFGregorianDateIsValid(gdate
, kCFGregorianAllUnits
))
1898 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
,
1902 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(gdate
, timeZone
);
1903 CFRelease(timeZone
);
1907 static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
1908 CFAbsoluteTime
*pabsTime
) {
1909 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
1911 if (absTime
== NULL_TIME
)
1914 *pabsTime
= absTime
;
1918 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1919 true if the date was valid and properly decoded, also return the result in
1920 absTime. Return false otherwise. */
1921 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
1922 CFAbsoluteTime
*absTime
) {
1925 if (dateChoice
->length
== 0)
1928 DERDecodedInfo decoded
;
1929 if (DERDecodeItem(dateChoice
, &decoded
))
1932 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
1936 static void appendDataProperty(CFMutableArrayRef properties
,
1937 CFStringRef label
, const DERItem
*der_data
) {
1938 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
1939 der_data
->data
, der_data
->length
);
1940 appendProperty(properties
, kSecPropertyTypeData
, label
, data
);
1944 static void appendUnparsedProperty(CFMutableArrayRef properties
,
1945 CFStringRef label
, const DERItem
*der_data
) {
1946 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1947 NULL
, CFSTR("Unparsed %@"), label
);
1948 appendDataProperty(properties
, newLabel
, der_data
);
1949 CFRelease(newLabel
);
1952 static void appendInvalidProperty(CFMutableArrayRef properties
,
1953 CFStringRef label
, const DERItem
*der_data
) {
1954 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1955 NULL
, CFSTR("Invalid %@"), label
);
1956 appendDataProperty(properties
, newLabel
, der_data
);
1957 CFRelease(newLabel
);
1960 static void appendDateContentProperty(CFMutableArrayRef properties
,
1961 CFStringRef label
, DERTag tag
, const DERItem
*dateContent
) {
1962 CFAbsoluteTime absTime
;
1963 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
1964 /* Date decode failure insert hex bytes instead. */
1965 return appendInvalidProperty(properties
, label
, dateContent
);
1967 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1968 appendProperty(properties
, kSecPropertyTypeDate
, label
, date
);
1972 static void appendDateProperty(CFMutableArrayRef properties
,
1973 CFStringRef label
, CFAbsoluteTime absTime
) {
1974 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1975 appendProperty(properties
, kSecPropertyTypeDate
, label
, date
);
1979 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
1980 CFStringRef label
, const DERItem
*ip
) {
1982 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
1984 appendProperty(properties
, kSecPropertyTypeString
, label
, value
);
1987 appendUnparsedProperty(properties
, label
, ip
);
1991 static void appendURLContentProperty(CFMutableArrayRef properties
,
1992 CFStringRef label
, const DERItem
*urlContent
) {
1993 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
1994 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
1996 appendProperty(properties
, kSecPropertyTypeURL
, label
, url
);
1999 appendInvalidProperty(properties
, label
, urlContent
);
2003 static void appendURLProperty(CFMutableArrayRef properties
,
2004 CFStringRef label
, const DERItem
*url
) {
2005 DERDecodedInfo decoded
;
2008 drtn
= DERDecodeItem(url
, &decoded
);
2009 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2010 appendInvalidProperty(properties
, label
, url
);
2012 appendURLContentProperty(properties
, label
, &decoded
.content
);
2016 static void appendOIDProperty(CFMutableArrayRef properties
,
2017 CFStringRef label
, const DERItem
*oid
) {
2018 CFStringRef oid_string
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2020 appendProperty(properties
, kSecPropertyTypeString
, label
, oid_string
);
2021 CFRelease(oid_string
);
2024 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2025 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2026 CFMutableArrayRef alg_props
=
2027 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2028 &kCFTypeArrayCallBacks
);
2029 appendOIDProperty(alg_props
, CFSTR("Algorithm"), &algorithm
->oid
);
2030 if (algorithm
->params
.length
) {
2031 if (algorithm
->params
.length
== 2 &&
2032 algorithm
->params
.data
[0] == ASN1_NULL
&&
2033 algorithm
->params
.data
[1] == 0) {
2034 /* @@@ Localize <NULL> or perhaps skip it? */
2035 appendProperty(alg_props
, kSecPropertyTypeString
,
2036 CFSTR("Parameters"), CFSTR("none"));
2038 appendUnparsedProperty(alg_props
, CFSTR("Parameters"),
2039 &algorithm
->params
);
2042 appendProperty(properties
, kSecPropertyTypeSection
, label
, alg_props
);
2043 CFRelease(alg_props
);
2046 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2047 const DERItem
*blob
) {
2048 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2049 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2050 blob
->length
* 3 - 1);
2051 for (ix
= 0; ix
< length
; ++ix
)
2053 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2055 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2060 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2061 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2062 CFStringRef blobFormat
= SecFrameworkCopyLocalizedString(
2063 CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate")
2064 /*, "format string for encoded field data (e.g. Sequence; 128 bytes; "
2065 "data = 00 00 ...)" */);
2066 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2067 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2068 blobFormat
, blobType
, blob
->length
, quanta
, hex
);
2070 CFRelease(blobFormat
);
2075 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2076 const DERItem
*string
, CFStringEncoding encoding
,
2077 bool printableOnly
) {
2078 /* Strip potential bogus trailing zero from printable strings. */
2079 DERSize length
= string
->length
;
2080 if (length
&& string
->data
[length
- 1] == 0) {
2081 /* Don't mess with the length of UTF16 strings though. */
2082 if (encoding
!= kCFStringEncodingUTF16
)
2085 /* A zero length string isn't considered printable. */
2086 if (!length
&& printableOnly
)
2089 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2090 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2091 passing false makes it treat it as native endian by default. */
2092 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2093 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2097 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2100 /* From rfc3280 - Appendix B. ASN.1 Notes
2102 CAs MUST force the serialNumber to be a non-negative integer, that
2103 is, the sign bit in the DER encoding of the INTEGER value MUST be
2104 zero - this can be done by adding a leading (leftmost) `00'H octet if
2105 necessary. This removes a potential ambiguity in mapping between a
2106 string of octets and an integer value.
2108 As noted in section 4.1.2.2, serial numbers can be expected to
2109 contain long integers. Certificate users MUST be able to handle
2110 serialNumber values up to 20 octets in length. Conformant CAs MUST
2111 NOT use serialNumber values longer than 20 octets.
2114 /* Return the given numeric data as a string: decimal up to 64 bits,
2116 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2117 const DERItem
*integer
) {
2119 CFIndex ix
, length
= integer
->length
;
2121 if (length
== 0 || length
> 8)
2122 return copyHexDescription(allocator
, integer
);
2124 for(ix
= 0; ix
< length
; ++ix
) {
2126 value
+= integer
->data
[ix
];
2129 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2132 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2133 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2137 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2138 case ASN1_PRINTABLE_STRING
:
2139 case ASN1_IA5_STRING
:
2140 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2141 case ASN1_UTF8_STRING
:
2142 case ASN1_GENERAL_STRING
:
2143 case ASN1_UNIVERSAL_STRING
:
2144 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2145 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2146 case ASN1_VIDEOTEX_STRING
: // 21
2147 case ASN1_VISIBLE_STRING
: // 26
2148 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2149 case ASN1_BMP_STRING
: // 30
2150 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2151 case ASN1_OCTET_STRING
:
2152 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Byte string"), CFSTR("bytes"),
2154 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2155 case ASN1_BIT_STRING
:
2156 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Bit string"), CFSTR("bits"),
2158 case (DERByte
)ASN1_CONSTR_SEQUENCE
:
2159 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Sequence"), CFSTR("bytes"),
2161 case (DERByte
)ASN1_CONSTR_SET
:
2162 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Set"), CFSTR("bytes"),
2164 case ASN1_OBJECT_ID
:
2165 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2168 /* "format string for undisplayed field data with a given DER tag" */
2169 return printableOnly
? NULL
: CFStringCreateWithFormat(allocator
, NULL
,
2170 CFSTR("not displayed (tag = %d; length %d)"),
2171 tag
, derThing
->length
);
2175 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2176 const DERItem
*derThing
, bool printableOnly
) {
2177 DERDecodedInfo decoded
;
2180 drtn
= DERDecodeItem(derThing
, &decoded
);
2182 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2184 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2185 &decoded
.content
, false);
2189 static void appendDERThingProperty(CFMutableArrayRef properties
,
2190 CFStringRef label
, const DERItem
*derThing
) {
2191 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2193 appendProperty(properties
, kSecPropertyTypeString
, label
, value
);
2197 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2198 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2199 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2201 /* If there is more than one value pair we create a subsection for the
2202 second pair, and append things to the subsection for subsequent
2204 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2205 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2207 /* Since this is the second rdn pair for a given rdn, we setup a
2208 new subsection for this rdn. We remove the first property
2209 from the properties array and make it the first element in the
2210 subsection instead. */
2211 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2212 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2213 CFArrayAppendValue(rdn_props
, lastValue
);
2214 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2215 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, rdn_props
);
2216 properties
= rdn_props
;
2218 /* Since this is the third or later rdn pair we have already
2219 created a subsection in the top level properties array. Instead
2220 of appending to that directly we append to the array inside the
2222 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2223 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2227 /* Finally we append the new rdn value to the property array. */
2228 CFStringRef label
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2231 appendDERThingProperty(properties
, label
, rdnValue
);
2235 return errSecInvalidCertificate
;
2239 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2240 const DERItem
*rdnSetContent
) {
2241 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2242 &kCFTypeArrayCallBacks
);
2243 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2246 CFArrayRemoveAllValues(properties
);
2247 appendInvalidProperty(properties
, CFSTR("RDN"), rdnSetContent
);
2254 From rfc3739 - 3.1.2. Subject
2256 When parsing the subject here are some tips for a short name of the cert.
2257 Choice I: commonName
2258 Choice II: givenName
2259 Choice III: pseudonym
2261 The commonName attribute value SHALL, when present, contain a name
2262 of the subject. This MAY be in the subject's preferred
2263 presentation format, or a format preferred by the CA, or some
2264 other format. Pseudonyms, nicknames, and names with spelling
2265 other than defined by the registered name MAY be used. To
2266 understand the nature of the name presented in commonName,
2267 complying applications MAY have to examine present values of the
2268 givenName and surname attributes, or the pseudonym attribute.
2271 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2272 const DERItem
*x501NameContent
) {
2273 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2274 &kCFTypeArrayCallBacks
);
2275 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2278 CFArrayRemoveAllValues(properties
);
2279 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501NameContent
);
2285 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2286 const DERItem
*x501Name
) {
2287 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2288 &kCFTypeArrayCallBacks
);
2289 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2291 CFArrayRemoveAllValues(properties
);
2292 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501Name
);
2298 static void appendIntegerProperty(CFMutableArrayRef properties
,
2299 CFStringRef label
, const DERItem
*integer
) {
2300 CFStringRef string
= copyIntegerContentDescription(
2301 CFGetAllocator(properties
), integer
);
2302 appendProperty(properties
, kSecPropertyTypeString
, label
, string
);
2306 static void appendBoolProperty(CFMutableArrayRef properties
,
2307 CFStringRef label
, bool boolean
) {
2308 appendProperty(properties
, kSecPropertyTypeString
,
2309 label
, boolean
? CFSTR("Yes") : CFSTR("No"));
2312 static void appendBooleanProperty(CFMutableArrayRef properties
,
2313 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2315 DERReturn drtn
= DERParseBoolean(boolean
, defaultValue
, &result
);
2317 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2318 appendInvalidProperty(properties
, label
, boolean
);
2320 appendBoolProperty(properties
, label
, result
);
2324 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2325 CFStringRef label
, const DERItem
*bitStringContent
,
2326 const CFStringRef
*names
, CFIndex namesCount
) {
2327 DERSize len
= bitStringContent
->length
- 1;
2328 require_quiet(len
== 1 || len
== 2, badDER
);
2329 DERByte numUnusedBits
= bitStringContent
->data
[0];
2330 require_quiet(numUnusedBits
< 8, badDER
);
2331 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2332 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2333 uint_fast16_t value
= bitStringContent
->data
[1];
2336 value
= (value
<< 8) + bitStringContent
->data
[2];
2342 bool didOne
= false;
2343 CFMutableStringRef string
=
2344 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2345 for (ix
= 0; ix
< bits
; ++ix
) {
2348 CFStringAppend(string
, CFSTR(", "));
2352 CFStringAppend(string
, names
[ix
]);
2356 appendProperty(properties
, kSecPropertyTypeString
, label
, string
);
2360 appendInvalidProperty(properties
, label
, bitStringContent
);
2363 static void appendBitStringNames(CFMutableArrayRef properties
,
2364 CFStringRef label
, const DERItem
*bitString
,
2365 const CFStringRef
*names
, CFIndex namesCount
) {
2366 DERDecodedInfo bitStringContent
;
2367 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2368 require_noerr_quiet(drtn
, badDER
);
2369 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2370 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2374 appendInvalidProperty(properties
, label
, bitString
);
2378 typedef uint16_t SecKeyUsage
;
2380 #define kSecKeyUsageDigitalSignature 0x8000
2381 #define kSecKeyUsageNonRepudiation 0x4000
2382 #define kSecKeyUsageKeyEncipherment 0x2000
2383 #define kSecKeyUsageDataEncipherment 0x1000
2384 #define kSecKeyUsageKeyAgreement 0x0800
2385 #define kSecKeyUsageKeyCertSign 0x0400
2386 #define kSecKeyUsageCRLSign 0x0200
2387 #define kSecKeyUsageEncipherOnly 0x0100
2388 #define kSecKeyUsageDecipherOnly 0x0080
2391 KeyUsage ::= BIT STRING {
2392 digitalSignature (0),
2394 keyEncipherment (2),
2395 dataEncipherment (3),
2402 static void appendKeyUsage(CFMutableArrayRef properties
,
2403 const DERItem
*extnValue
) {
2404 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2405 extnValue
->data
[0] != ASN1_BIT_STRING
||
2406 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2407 extnValue
->data
[2] > 7) {
2408 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2411 CFMutableStringRef string
=
2412 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2413 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2414 if (extnValue
->length
== 5)
2415 usage
+= extnValue
->data
[4];
2416 secdebug("keyusage", "keyusage: %04X", usage
);
2417 static const CFStringRef usageNames
[] = {
2418 CFSTR("Digital Signature"),
2419 CFSTR("Non-Repudiation"),
2420 CFSTR("Key Encipherment"),
2421 CFSTR("Data Encipherment"),
2422 CFSTR("Key Agreement"),
2428 bool didOne
= false;
2429 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2430 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2431 for (ix
= 0; ix
< bits
; ++ix
) {
2434 CFStringAppend(string
, CFSTR(", "));
2438 /* @@@ Localize usageNames[ix]. */
2439 CFStringAppend(string
, usageNames
[ix
]);
2443 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2449 static void appendKeyUsage(CFMutableArrayRef properties
,
2450 const DERItem
*extnValue
) {
2451 static const CFStringRef usageNames
[] = {
2452 CFSTR("Digital Signature"),
2453 CFSTR("Non-Repudiation"),
2454 CFSTR("Key Encipherment"),
2455 CFSTR("Data Encipherment"),
2456 CFSTR("Key Agreement"),
2459 CFSTR("Encipher Only"),
2460 CFSTR("Decipher Only")
2462 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
2463 usageNames
, sizeof(usageNames
) / sizeof(*usageNames
));
2467 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2468 const DERItem
*extnValue
) {
2469 DERPrivateKeyUsagePeriod pkup
;
2470 DERReturn drtn
= DERParseSequence(extnValue
,
2471 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2472 &pkup
, sizeof(pkup
));
2473 require_noerr_quiet(drtn
, badDER
);
2474 if (pkup
.notBefore
.length
) {
2475 appendDateContentProperty(properties
, CFSTR("Not Valid Before"),
2476 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2478 if (pkup
.notAfter
.length
) {
2479 appendDateContentProperty(properties
, CFSTR("Not Valid After"),
2480 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2484 appendInvalidProperty(properties
, CFSTR("Private Key Usage Period"),
2488 static void appendStringContentProperty(CFMutableArrayRef properties
,
2489 CFStringRef label
, const DERItem
*stringContent
,
2490 CFStringEncoding encoding
) {
2491 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2492 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2494 appendProperty(properties
, kSecPropertyTypeString
, label
, string
);
2497 appendInvalidProperty(properties
, label
, stringContent
);
2502 OtherName ::= SEQUENCE {
2503 type-id OBJECT IDENTIFIER,
2504 value [0] EXPLICIT ANY DEFINED BY type-id }
2506 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2507 const DERItem
*otherNameContent
) {
2509 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2510 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2512 require_noerr_quiet(drtn
, badDER
);
2513 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2514 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
,
2515 &on
.typeIdentifier
);
2516 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2518 appendProperty(properties
, kSecPropertyTypeString
, oid_string
,
2521 appendUnparsedProperty(properties
, oid_string
, &on
.value
);
2525 appendInvalidProperty(properties
, CFSTR("Other Name"), otherNameContent
);
2529 GeneralName ::= CHOICE {
2530 otherName [0] OtherName,
2531 rfc822Name [1] IA5String,
2532 dNSName [2] IA5String,
2533 x400Address [3] ORAddress,
2534 directoryName [4] Name,
2535 ediPartyName [5] EDIPartyName,
2536 uniformResourceIdentifier [6] IA5String,
2537 iPAddress [7] OCTET STRING,
2538 registeredID [8] OBJECT IDENTIFIER}
2540 EDIPartyName ::= SEQUENCE {
2541 nameAssigner [0] DirectoryString OPTIONAL,
2542 partyName [1] DirectoryString }
2544 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2545 DERTag tag
, const DERItem
*generalName
) {
2547 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2548 appendOtherNameContentProperty(properties
, generalName
);
2550 case ASN1_CONTEXT_SPECIFIC
| 1:
2552 appendStringContentProperty(properties
, CFSTR("Email Address"),
2553 generalName
, kCFStringEncodingASCII
);
2555 case ASN1_CONTEXT_SPECIFIC
| 2:
2557 appendStringContentProperty(properties
, CFSTR("DNS Name"), generalName
,
2558 kCFStringEncodingASCII
);
2560 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2561 appendUnparsedProperty(properties
, CFSTR("X.400 Address"),
2564 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2566 CFArrayRef directory_plist
=
2567 createPropertiesForX501Name(CFGetAllocator(properties
),
2569 appendProperty(properties
, kSecPropertyTypeSection
,
2570 CFSTR("Directory Name"), directory_plist
);
2571 CFRelease(directory_plist
);
2574 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2575 appendUnparsedProperty(properties
, CFSTR("EDI Party Name"),
2578 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2579 /* Technically I don't think this is valid, but there are certs out
2580 in the wild that use a constructed IA5String. In particular the
2581 VeriSign Time Stamping Authority CA.cer does this. */
2582 appendURLProperty(properties
, CFSTR("URI"), generalName
);
2584 case ASN1_CONTEXT_SPECIFIC
| 6:
2585 appendURLContentProperty(properties
, CFSTR("URI"), generalName
);
2587 case ASN1_CONTEXT_SPECIFIC
| 7:
2588 appendIPAddressContentProperty(properties
, CFSTR("IP Address"),
2591 case ASN1_CONTEXT_SPECIFIC
| 8:
2592 appendOIDProperty(properties
, CFSTR("Registered ID"), generalName
);
2603 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2604 const DERItem
*generalName
) {
2605 DERDecodedInfo generalNameContent
;
2606 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2607 require_noerr_quiet(drtn
, badDER
);
2608 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2609 &generalNameContent
.content
))
2612 appendInvalidProperty(properties
, CFSTR("General Name"), generalName
);
2617 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2619 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2620 const DERItem
*generalNamesContent
) {
2622 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2623 require_noerr_quiet(drtn
, badDER
);
2624 DERDecodedInfo generalNameContent
;
2625 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2627 if (!appendGeneralNameContentProperty(properties
,
2628 generalNameContent
.tag
, &generalNameContent
.content
)) {
2632 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2635 appendInvalidProperty(properties
, CFSTR("General Names"),
2636 generalNamesContent
);
2639 static void appendGeneralNames(CFMutableArrayRef properties
,
2640 const DERItem
*generalNames
) {
2641 DERDecodedInfo generalNamesContent
;
2642 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2643 require_noerr_quiet(drtn
, badDER
);
2644 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2646 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2649 appendInvalidProperty(properties
, CFSTR("General Names"), generalNames
);
2653 BasicConstraints ::= SEQUENCE {
2654 cA BOOLEAN DEFAULT FALSE,
2655 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
2657 static void appendBasicConstraints(CFMutableArrayRef properties
,
2658 const DERItem
*extnValue
) {
2659 DERBasicConstraints basicConstraints
;
2660 DERReturn drtn
= DERParseSequence(extnValue
,
2661 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
2662 &basicConstraints
, sizeof(basicConstraints
));
2663 require_noerr_quiet(drtn
, badDER
);
2665 appendBooleanProperty(properties
, CFSTR("Certificate Authority"),
2666 &basicConstraints
.cA
, false);
2668 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
2669 appendIntegerProperty(properties
, CFSTR("Path Length Constraint"),
2670 &basicConstraints
.pathLenConstraint
);
2674 appendInvalidProperty(properties
, CFSTR("Basic Constraints"), extnValue
);
2678 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
2680 DistributionPoint ::= SEQUENCE {
2681 distributionPoint [0] DistributionPointName OPTIONAL,
2682 reasons [1] ReasonFlags OPTIONAL,
2683 cRLIssuer [2] GeneralNames OPTIONAL }
2685 DistributionPointName ::= CHOICE {
2686 fullName [0] GeneralNames,
2687 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
2689 ReasonFlags ::= BIT STRING {
2693 affiliationChanged (3),
2695 cessationOfOperation (5),
2696 certificateHold (6),
2697 privilegeWithdrawn (7),
2700 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
2701 const DERItem
*extnValue
) {
2702 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2705 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
2706 require_noerr_quiet(drtn
, badDER
);
2707 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2708 DERDecodedInfo dpSeqContent
;
2709 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
2710 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2711 DERDistributionPoint dp
;
2712 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
2713 DERNumDistributionPointItemSpecs
,
2714 DERDistributionPointItemSpecs
,
2716 require_noerr_quiet(drtn
, badDER
);
2717 if (dp
.distributionPoint
.length
) {
2718 DERDecodedInfo distributionPointName
;
2719 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
2720 require_noerr_quiet(drtn
, badDER
);
2721 if (distributionPointName
.tag
==
2722 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
2724 appendGeneralNamesContent(properties
,
2725 &distributionPointName
.content
);
2726 } else if (distributionPointName
.tag
==
2727 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
2728 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
2730 appendProperty(properties
, kSecPropertyTypeSection
,
2731 CFSTR("Name Relative To CRL Issuer"), rdn_props
);
2732 CFRelease(rdn_props
);
2737 if (dp
.reasons
.length
) {
2738 static const CFStringRef reasonNames
[] = {
2740 CFSTR("Key Compromise"),
2741 CFSTR("CA Compromise"),
2742 CFSTR("Affiliation Changed"),
2743 CFSTR("Superseded"),
2744 CFSTR("Cessation Of Operation"),
2745 CFSTR("Certificate Hold"),
2746 CFSTR("Priviledge Withdrawn"),
2747 CFSTR("AA Compromise")
2749 appendBitStringContentNames(properties
, CFSTR("Reasons"),
2751 reasonNames
, sizeof(reasonNames
) / sizeof(*reasonNames
));
2753 if (dp
.cRLIssuer
.length
) {
2754 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
2755 &kCFTypeArrayCallBacks
);
2756 appendProperty(properties
, kSecPropertyTypeSection
,
2757 CFSTR("CRL Issuer"), crlIssuer
);
2758 CFRelease(crlIssuer
);
2759 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
2762 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2765 appendInvalidProperty(properties
, CFSTR("Crl Distribution Points"),
2769 /* Decode a sequence of integers into a comma separated list of ints. */
2770 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
2771 CFStringRef label
, const DERItem
*intSequenceContent
) {
2772 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2774 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
2775 require_noerr_quiet(drtn
, badDER
);
2776 DERDecodedInfo intContent
;
2777 CFMutableStringRef value
= NULL
;
2778 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
))
2780 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
2781 CFStringRef intDesc
= copyIntegerContentDescription(
2782 allocator
, &intContent
.content
);
2784 CFStringAppendFormat(value
, NULL
, CFSTR(", %@"), intDesc
);
2786 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
2790 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2792 appendProperty(properties
, kSecPropertyTypeString
,
2793 CFSTR("Notice Numbers"), value
);
2797 /* DROPTHOUGH if !value. */
2799 appendInvalidProperty(properties
, label
, intSequenceContent
);
2802 static void appendCertificatePolicies(CFMutableArrayRef properties
,
2803 const DERItem
*extnValue
) {
2804 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2807 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
2808 require_noerr_quiet(drtn
, badDER
);
2809 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2810 DERDecodedInfo piContent
;
2812 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
2813 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2814 DERPolicyInformation pi
;
2815 drtn
= DERParseSequenceContent(&piContent
.content
,
2816 DERNumPolicyInformationItemSpecs
,
2817 DERPolicyInformationItemSpecs
,
2819 require_noerr_quiet(drtn
, badDER
);
2820 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2821 CFSTR("Policy Identifier #%d"), pin
++);
2822 appendOIDProperty(properties
, piLabel
, &pi
.policyIdentifier
);
2824 if (pi
.policyQualifiers
.length
== 0)
2828 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
2829 require_noerr_quiet(drtn
, badDER
);
2830 DERDecodedInfo pqContent
;
2832 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
2833 DERPolicyQualifierInfo pqi
;
2834 drtn
= DERParseSequenceContent(&pqContent
.content
,
2835 DERNumPolicyQualifierInfoItemSpecs
,
2836 DERPolicyQualifierInfoItemSpecs
,
2838 require_noerr_quiet(drtn
, badDER
);
2839 DERDecodedInfo qualifierContent
;
2840 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
2841 require_noerr_quiet(drtn
, badDER
);
2842 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2843 CFSTR("Policy Qualifier #%d"), pqn
++);
2844 appendOIDProperty(properties
, pqLabel
, &pqi
.policyQualifierID
);
2846 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
2847 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
2848 appendURLContentProperty(properties
,
2850 &qualifierContent
.content
);
2851 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
2852 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2854 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
2855 DERNumUserNoticeItemSpecs
,
2856 DERUserNoticeItemSpecs
,
2858 require_noerr_quiet(drtn
, badDER
);
2859 if (un
.noticeRef
.length
) {
2860 DERNoticeReference nr
;
2861 drtn
= DERParseSequenceContent(&un
.noticeRef
,
2862 DERNumNoticeReferenceItemSpecs
,
2863 DERNoticeReferenceItemSpecs
,
2865 require_noerr_quiet(drtn
, badDER
);
2866 appendDERThingProperty(properties
,
2867 CFSTR("Organization"),
2869 appendIntegerSequenceContent(properties
,
2870 CFSTR("Notice Numbers"), &nr
.noticeNumbers
);
2872 if (un
.explicitText
.length
) {
2873 appendDERThingProperty(properties
, CFSTR("Explicit Text"),
2877 appendUnparsedProperty(properties
, CFSTR("Qualifier"),
2882 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2885 appendInvalidProperty(properties
, CFSTR("Certificate Policies"),
2889 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
2890 const DERItem
*extnValue
) {
2892 DERDecodedInfo keyIdentifier
;
2893 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
2894 require_noerr_quiet(drtn
, badDER
);
2895 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
2896 appendDataProperty(properties
, CFSTR("Key Identifier"),
2897 &keyIdentifier
.content
);
2901 appendInvalidProperty(properties
, CFSTR("Invalid Subject Key Identifier"),
2906 AuthorityKeyIdentifier ::= SEQUENCE {
2907 keyIdentifier [0] KeyIdentifier OPTIONAL,
2908 authorityCertIssuer [1] GeneralNames OPTIONAL,
2909 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
2910 -- authorityCertIssuer and authorityCertSerialNumber MUST both
2911 -- be present or both be absent
2913 KeyIdentifier ::= OCTET STRING
2915 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
2916 const DERItem
*extnValue
) {
2917 DERAuthorityKeyIdentifier akid
;
2919 drtn
= DERParseSequence(extnValue
,
2920 DERNumAuthorityKeyIdentifierItemSpecs
,
2921 DERAuthorityKeyIdentifierItemSpecs
,
2922 &akid
, sizeof(akid
));
2923 require_noerr_quiet(drtn
, badDER
);
2924 if (akid
.keyIdentifier
.length
) {
2925 appendDataProperty(properties
, CFSTR("Key Identifier"),
2926 &akid
.keyIdentifier
);
2928 if (akid
.authorityCertIssuer
.length
||
2929 akid
.authorityCertSerialNumber
.length
) {
2930 require_quiet(akid
.authorityCertIssuer
.length
&&
2931 akid
.authorityCertSerialNumber
.length
, badDER
);
2932 /* Perhaps put in a subsection called Authority Certificate Issuer. */
2933 appendGeneralNamesContent(properties
,
2934 &akid
.authorityCertIssuer
);
2935 appendIntegerProperty(properties
,
2936 CFSTR("Authority Certificate Serial Number"),
2937 &akid
.authorityCertSerialNumber
);
2942 appendInvalidProperty(properties
, CFSTR("Authority Key Identifier"),
2947 PolicyConstraints ::= SEQUENCE {
2948 requireExplicitPolicy [0] SkipCerts OPTIONAL,
2949 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
2951 SkipCerts ::= INTEGER (0..MAX)
2953 static void appendPolicyConstraints(CFMutableArrayRef properties
,
2954 const DERItem
*extnValue
) {
2955 DERPolicyConstraints pc
;
2957 drtn
= DERParseSequence(extnValue
,
2958 DERNumPolicyConstraintsItemSpecs
,
2959 DERPolicyConstraintsItemSpecs
,
2961 require_noerr_quiet(drtn
, badDER
);
2962 if (pc
.requireExplicitPolicy
.length
) {
2963 appendIntegerProperty(properties
,
2964 CFSTR("Require Explicit Policy"), &pc
.requireExplicitPolicy
);
2966 if (pc
.inhibitPolicyMapping
.length
) {
2967 appendIntegerProperty(properties
,
2968 CFSTR("Inhibit Policy Mapping"), &pc
.inhibitPolicyMapping
);
2974 appendInvalidProperty(properties
, CFSTR("Policy Constraints"), extnValue
);
2978 extendedKeyUsage EXTENSION ::= {
2979 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
2980 IDENTIFIED BY id-ce-extKeyUsage }
2982 KeyPurposeId ::= OBJECT IDENTIFIER
2984 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
2985 const DERItem
*extnValue
) {
2988 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
2989 require_noerr_quiet(drtn
, badDER
);
2990 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2991 DERDecodedInfo currDecoded
;
2992 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
2993 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
2994 appendOIDProperty(properties
, CFSTR("Purpose"),
2995 &currDecoded
.content
);
2997 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3000 appendInvalidProperty(properties
, CFSTR("Extended Key Usage"), extnValue
);
3004 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3006 AuthorityInfoAccessSyntax ::=
3007 SEQUENCE SIZE (1..MAX) OF AccessDescription
3009 AccessDescription ::= SEQUENCE {
3010 accessMethod OBJECT IDENTIFIER,
3011 accessLocation GeneralName }
3013 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3015 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3017 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3019 static void appendInfoAccess(CFMutableArrayRef properties
,
3020 const DERItem
*extnValue
) {
3023 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3024 require_noerr_quiet(drtn
, badDER
);
3025 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3026 DERDecodedInfo adContent
;
3027 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3028 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3029 DERAccessDescription ad
;
3030 drtn
= DERParseSequenceContent(&adContent
.content
,
3031 DERNumAccessDescriptionItemSpecs
,
3032 DERAccessDescriptionItemSpecs
,
3034 require_noerr_quiet(drtn
, badDER
);
3035 appendOIDProperty(properties
, CFSTR("Access Method"),
3037 //CFSTR("Access Location");
3038 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3040 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3043 appendInvalidProperty(properties
, CFSTR("Authority Information Access"),
3047 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3048 const DERItem
*extnValue
) {
3049 static const CFStringRef certTypes
[] = {
3050 CFSTR("SSL client"),
3051 CFSTR("SSL server"),
3053 CFSTR("Object Signing"),
3057 CFSTR("Object Signing CA")
3059 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
3060 certTypes
, sizeof(certTypes
) / sizeof(*certTypes
));
3064 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3065 const DERItem
*extnValue
) {
3069 * The list of Qualified Cert Statement statementIds we understand, even though
3070 * we don't actually do anything with them; if these are found in a Qualified
3071 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3073 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3075 /* id-qcs := { id-pkix 11 } */
3076 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3077 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3078 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3079 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3080 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3081 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3083 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3085 static void appendQCCertStatements(CFMutableArrayRef properties
,
3086 const DERItem
*extnValue
) {
3091 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3092 CFStringRef label
, const DERItem
*sequence
) {
3095 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3096 require_noerr_quiet(drtn
, badSequence
);
3097 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3098 DERDecodedInfo currDecoded
;
3099 bool appendedSomething
= false;
3100 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3101 switch (currDecoded
.tag
)
3104 case ASN1_SEQUENCE
: // 16
3105 case ASN1_SET
: // 17
3106 // skip constructed object lengths
3109 case ASN1_UTF8_STRING
: // 12
3110 case ASN1_NUMERIC_STRING
: // 18
3111 case ASN1_PRINTABLE_STRING
: // 19
3112 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3113 case ASN1_VIDEOTEX_STRING
: // 21
3114 case ASN1_IA5_STRING
: // 22
3115 case ASN1_GRAPHIC_STRING
: // 25
3116 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3117 case ASN1_GENERAL_STRING
: // 27
3118 case ASN1_UNIVERSAL_STRING
: // 28
3120 CFStringRef string
=
3121 copyDERThingContentDescription(CFGetAllocator(properties
),
3122 currDecoded
.tag
, &currDecoded
.content
, false);
3123 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3125 appendProperty(properties
, kSecPropertyTypeString
, label
,
3128 appendedSomething
= true;
3135 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3136 return appendedSomething
;
3141 static void appendExtension(CFMutableArrayRef parent
,
3142 const SecCertificateExtension
*extn
) {
3143 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3144 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3145 &kCFTypeArrayCallBacks
);
3147 *extnID
= &extn
->extnID
,
3148 *extnValue
= &extn
->extnValue
;
3150 appendBoolProperty(properties
, CFSTR("Critical"), extn
->critical
);
3153 bool handeled
= true;
3154 /* Extensions that we know how to handle ourselves... */
3155 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3156 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3158 switch (extnID
->data
[extnID
->length
- 1]) {
3159 case 14: /* SubjectKeyIdentifier id-ce 14 */
3160 appendSubjectKeyIdentifier(properties
, extnValue
);
3162 case 15: /* KeyUsage id-ce 15 */
3163 appendKeyUsage(properties
, extnValue
);
3165 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3166 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3168 case 17: /* SubjectAltName id-ce 17 */
3169 case 18: /* IssuerAltName id-ce 18 */
3170 appendGeneralNames(properties
, extnValue
);
3172 case 19: /* BasicConstraints id-ce 19 */
3173 appendBasicConstraints(properties
, extnValue
);
3175 case 30: /* NameConstraints id-ce 30 */
3178 case 31: /* CRLDistributionPoints id-ce 31 */
3179 appendCrlDistributionPoints(properties
, extnValue
);
3181 case 32: /* CertificatePolicies id-ce 32 */
3182 appendCertificatePolicies(properties
, extnValue
);
3184 case 33: /* PolicyMappings id-ce 33 */
3187 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3188 appendAuthorityKeyIdentifier(properties
, extnValue
);
3190 case 36: /* PolicyConstraints id-ce 36 */
3191 appendPolicyConstraints(properties
, extnValue
);
3193 case 37: /* ExtKeyUsage id-ce 37 */
3194 appendExtendedKeyUsage(properties
, extnValue
);
3196 case 46: /* FreshestCRL id-ce 46 */
3199 case 54: /* InhibitAnyPolicy id-ce 54 */
3206 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3207 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3209 switch (extnID
->data
[extnID
->length
- 1]) {
3210 case 1: /* AuthorityInfoAccess id-pe 1 */
3211 appendInfoAccess(properties
, extnValue
);
3213 case 3: /* QCStatements id-pe 3 */
3216 case 11: /* SubjectInfoAccess id-pe 11 */
3217 appendInfoAccess(properties
, extnValue
);
3223 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3224 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3225 appendNetscapeCertType(properties
, extnValue
);
3231 /* Try to parse and display printable string(s). */
3232 if (appendPrintableDERSequence(properties
, CFSTR("Data"), extnValue
)) {
3233 /* Nothing to do here appendPrintableDERSequence did the work. */
3235 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3236 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3240 /* Extensions that we know how to handle ourselves... */
3241 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3242 appendSubjectKeyIdentifier(properties
, extnValue
);
3243 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3244 appendKeyUsage(properties
, extnValue
);
3245 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3246 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3247 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3248 appendGeneralNames(properties
, extnValue
);
3249 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3250 appendGeneralNames(properties
, extnValue
);
3251 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3252 appendBasicConstraints(properties
, extnValue
);
3253 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3254 appendCrlDistributionPoints(properties
, extnValue
);
3255 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3256 appendCertificatePolicies(properties
, extnValue
);
3257 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3258 appendAuthorityKeyIdentifier(properties
, extnValue
);
3259 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3260 appendPolicyConstraints(properties
, extnValue
);
3261 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3262 appendExtendedKeyUsage(properties
, extnValue
);
3263 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3264 appendInfoAccess(properties
, extnValue
);
3265 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3266 appendInfoAccess(properties
, extnValue
);
3267 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3268 appendNetscapeCertType(properties
, extnValue
);
3270 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3271 appendEntrustVersInfo(properties
, extnValue
);
3274 /* Try to parse and display printable string(s). */
3275 if (appendPrintableDERSequence(properties
, CFSTR("Data"), extnValue
)) {
3276 /* Nothing to do here appendPrintableDERSequence did the work. */
3278 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3279 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3282 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
, extnID
);
3283 appendProperty(parent
, kSecPropertyTypeSection
, oid_string
, properties
);
3284 CFRelease(oid_string
);
3285 CFRelease(properties
);
3288 /* Different types of summary types from least desired to most desired. */
3291 kSummaryTypePrintable
,
3292 kSummaryTypeOrganizationName
,
3293 kSummaryTypeOrganizationalUnitName
,
3294 kSummaryTypeCommonName
,
3298 enum SummaryType type
;
3299 CFStringRef summary
;
3300 CFStringRef description
;
3303 static OSStatus
obtainSummaryFromX501Name(void *context
,
3304 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3305 struct Summary
*summary
= (struct Summary
*)context
;
3306 enum SummaryType stype
= kSummaryTypeNone
;
3307 CFStringRef string
= NULL
;
3308 if (DEROidCompare(type
, &oidCommonName
)) {
3309 /* We skip Common Names that have generic values. */
3310 const char tfm
[] = "Thawte Freemail Member";
3311 if ((value
->length
== sizeof(tfm
) + 1) &&
3312 !memcmp(value
->data
+ 2, tfm
, sizeof(tfm
) - 1)) {
3315 stype
= kSummaryTypeCommonName
;
3316 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3317 stype
= kSummaryTypeOrganizationalUnitName
;
3318 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3319 stype
= kSummaryTypeOrganizationName
;
3320 } else if (DEROidCompare(type
, &oidDescription
)) {
3321 if (!summary
->description
) {
3322 summary
->description
= string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3325 stype
= kSummaryTypePrintable
;
3327 stype
= kSummaryTypePrintable
;
3330 /* Use the first field we encounter of the highest priority type. */
3331 if (summary
->type
< stype
) {
3333 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3337 CFReleaseSafe(summary
->summary
);
3338 summary
->summary
= string
;
3339 summary
->type
= stype
;
3342 CFReleaseSafe(string
);
3348 CFStringRef
SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate
) {
3349 struct Summary summary
= {};
3350 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3351 /* If we found a description and a common name we change the summary to
3352 CommonName (Description). */
3353 if (summary
.description
) {
3354 if (summary
.type
== kSummaryTypeCommonName
) {
3355 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3356 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3357 CFRelease(summary
.summary
);
3358 summary
.summary
= newSummary
;
3360 CFRelease(summary
.description
);
3363 if (!summary
.summary
) {
3364 /* If we didn't find a suitable printable string in the subject at all, we try
3365 the first email address in the certificate instead. */
3366 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3368 /* If we didn't find any email addresses in the certificate, we try finding
3369 a DNS name instead. */
3370 names
= SecCertificateCopyDNSNames(certificate
);
3373 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3374 CFRetain(summary
.summary
);
3379 return summary
.summary
;
3382 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRefP certificate
) {
3383 struct Summary summary
= {};
3384 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3385 /* If we found a description and a common name we change the summary to
3386 CommonName (Description). */
3387 if (summary
.description
) {
3388 if (summary
.type
== kSummaryTypeCommonName
) {
3389 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3390 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3391 CFRelease(summary
.summary
);
3392 summary
.summary
= newSummary
;
3394 CFRelease(summary
.description
);
3397 return summary
.summary
;
3400 /* Return the earliest date on which all certificates in this chain are still
3402 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3403 SecCertificateRefP certificate
) {
3404 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3406 while (certificate
->_parent
) {
3407 certificate
= certificate
->_parent
;
3408 if (earliest
> certificate
->_notAfter
)
3409 earliest
= certificate
->_notAfter
;
3416 /* Return the latest date on which all certificates in this chain will be
3418 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3419 SecCertificateRefP certificate
) {
3420 CFAbsoluteTime latest
= certificate
->_notBefore
;
3422 while (certificate
->_parent
) {
3423 certificate
= certificate
->_parent
;
3424 if (latest
< certificate
->_notBefore
)
3425 latest
= certificate
->_notBefore
;
3432 bool SecCertificateIsValid(SecCertificateRefP certificate
,
3433 CFAbsoluteTime verifyTime
) {
3435 return certificate
->_notBefore
<= verifyTime
&&
3436 verifyTime
<= certificate
->_notAfter
;
3439 CFIndex
SecCertificateVersion(SecCertificateRefP certificate
) {
3440 return certificate
->_version
+ 1;
3443 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRefP certificate
) {
3444 return certificate
->_notBefore
;
3447 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRefP certificate
) {
3448 return certificate
->_notAfter
;
3451 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3452 SecCertificateRefP certificate
, CFAbsoluteTime verifyTime
) {
3453 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3454 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3455 &kCFTypeArrayCallBacks
);
3457 /* First we put the subject summary name. */
3458 CFStringRef ssummary
= SecCertificateCopySubjectSummaryP(certificate
);
3460 appendProperty(summary
, kSecPropertyTypeTitle
,
3462 CFRelease(ssummary
);
3465 CFStringRef isummary
= CFSTR("Issuer Summary");
3466 appendProperty(summary
, kSecPropertyTypeString
,
3467 CFSTR("Issued By"), isummary
);
3468 CFRelease(isummary
);
3471 /* Let see if this certificate is currently valid. */
3473 CFAbsoluteTime when
;
3474 CFStringRef message
;
3476 if (verifyTime
> certificate
->_notAfter
) {
3477 label
= CFSTR("Expired");
3478 when
= certificate
->_notAfter
;
3479 ptype
= kSecPropertyTypeError
;
3480 message
= CFSTR("This certificate has expired");
3481 } else if (certificate
->_notBefore
> verifyTime
) {
3482 label
= CFSTR("Valid from");
3483 when
= certificate
->_notBefore
;
3484 ptype
= kSecPropertyTypeError
;
3485 message
= CFSTR("This certificate is not yet valid");
3487 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3488 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3489 if (verifyTime
> last
) {
3490 label
= CFSTR("Expired");
3492 ptype
= kSecPropertyTypeError
;
3493 message
= CFSTR("This certificate has an issuer that has expired");
3494 } else if (verifyTime
< first
) {
3495 label
= CFSTR("Valid from");
3497 ptype
= kSecPropertyTypeError
;
3498 message
= CFSTR("This certificate has an issuer that is not yet valid");
3500 label
= CFSTR("Expires");
3501 when
= certificate
->_notAfter
;
3502 ptype
= kSecPropertyTypeSuccess
;
3503 message
= CFSTR("This certificate is valid");
3507 appendDateProperty(summary
, label
, when
);
3508 appendProperty(summary
, ptype
, NULL
, message
);
3513 CFArrayRef
SecCertificateCopyProperties(SecCertificateRefP certificate
) {
3514 if (!certificate
->_properties
) {
3515 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3516 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3517 &kCFTypeArrayCallBacks
);
3519 /* First we put the Subject Name in the property list. */
3520 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3521 &certificate
->_subject
);
3522 appendProperty(properties
, kSecPropertyTypeSection
,
3523 CFSTR("Subject Name"), subject_plist
);
3524 CFRelease(subject_plist
);
3527 /* Put Normalized subject in for testing. */
3528 if (certificate
->_normalizedSubject
) {
3529 DERItem nsubject
= {
3530 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
3531 CFDataGetLength(certificate
->_normalizedSubject
)
3533 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
3535 appendProperty(properties
, kSecPropertyTypeSection
,
3536 CFSTR("Normalized Subject Name"), nsubject_plist
);
3537 CFRelease(nsubject_plist
);
3541 /* Next we put the Issuer Name in the property list. */
3542 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3543 &certificate
->_issuer
);
3544 appendProperty(properties
, kSecPropertyTypeSection
,
3545 CFSTR("Issuer Name"), issuer_plist
);
3546 CFRelease(issuer_plist
);
3549 /* Certificate version/type. */
3550 bool isRoot
= false;
3551 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
3552 CFSTR("X.509 version %d %scertificate"),
3553 certificate
->_version
+ 1, isRoot
? "root " : "");
3554 appendProperty(properties
, kSecPropertyTypeString
,
3555 CFSTR("Certificate Type"), typeString
);
3556 CFRelease(typeString
);
3560 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
3561 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
3562 appendProperty(properties
, kSecPropertyTypeString
,
3563 CFSTR("Version"), versionString
);
3564 CFRelease(versionString
);
3567 if (certificate
->_serialNum
.length
) {
3568 appendIntegerProperty(properties
, CFSTR("Serial Number"),
3569 &certificate
->_serialNum
);
3572 /* Signature algorithm. */
3574 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3575 &certificate
->_sigAlg
);
3577 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3578 &certificate
->_tbsSigAlg
);
3581 /* Validity dates. */
3582 appendDateProperty(properties
, CFSTR("Not Valid Before"),
3583 certificate
->_notBefore
);
3584 appendDateProperty(properties
, CFSTR("Not Valid After"),
3585 certificate
->_notAfter
);
3587 if (certificate
->_subjectUniqueID
.length
) {
3588 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
3589 &certificate
->_subjectUniqueID
);
3591 if (certificate
->_issuerUniqueID
.length
) {
3592 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
3593 &certificate
->_issuerUniqueID
);
3596 /* Public key algorithm. */
3597 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
3598 &certificate
->_algId
);
3600 /* Consider breaking down an RSA public key into modulus and
3602 appendDataProperty(properties
, CFSTR("Public Key Data"),
3603 &certificate
->_pubKeyDER
);
3605 /* @@@ Key Usage. */
3607 appendDataProperty(properties
, CFSTR("Signature"),
3608 &certificate
->_signature
);
3611 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
3612 appendExtension(properties
, &certificate
->_extensions
[ix
]);
3615 /* @@@ Key Fingerprints. */
3617 certificate
->_properties
= properties
;
3620 CFRetain(certificate
->_properties
);
3621 return certificate
->_properties
;
3624 CFDataRef
SecCertificateCopySerialNumberP(
3625 SecCertificateRefP certificate
) {
3626 if (certificate
->_serialNumber
) {
3627 CFRetain(certificate
->_serialNumber
);
3629 return certificate
->_serialNumber
;
3632 CFDataRef
SecCertificateGetNormalizedIssuerContent(
3633 SecCertificateRefP certificate
) {
3634 return certificate
->_normalizedIssuer
;
3637 CFDataRef
SecCertificateGetNormalizedSubjectContent(
3638 SecCertificateRefP certificate
) {
3640 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
);
3641 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
3643 return SecDERItemCopySequence(&tmpdi
);
3646 CFDataRef
SecCertificateGetNormalizedIssuer(
3647 SecCertificateRefP certificate
) {
3649 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
3650 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
3652 return SecDERItemCopySequence(&tmpdi
);
3655 CFDataRef
SecCertificateGetNormalizedSubject(
3656 SecCertificateRefP certificate
) {
3657 return certificate
->_normalizedSubject
;
3660 /* Verify that certificate was signed by issuerKey. */
3661 OSStatus
SecCertificateIsSignedByP(SecCertificateRefP certificate
,
3662 SecKeyRefP issuerKey
) {
3663 /* Setup algId in SecAsn1AlgId format. */
3665 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3666 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3667 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3668 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3670 #warning implementation empty
3672 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3673 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3674 certificate
->_signature
.data
, certificate
->_signature
.length
);
3676 secdebug("verify", "signature verify failed: %d", status
);
3677 return errSecNotSigner
;
3685 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRefP certificate
,
3686 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3687 if (!signatureCheckOnly
) {
3688 /* It turns out we don't actually need to use normalized subject and
3689 issuer according to rfc2459. */
3691 /* If present we should check issuerID against the issuer subjectID. */
3693 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3694 then we should look for a SubjectKeyIdentifier in the issuer
3696 If we have a authorityCertSerialNumber we can use that for chaining.
3697 If we have a authorityCertIssuer we can use that? (or not) */
3699 /* Verify that this cert was issued by issuer. Do so by chaining
3700 either issuerID to subjectID or normalized issuer to normalized
3702 CFDataRef normalizedIssuer
=
3703 SecCertificateGetNormalizedIssuerContent(certificate
);
3704 CFDataRef normalizedIssuerSubject
=
3705 SecCertificateGetNormalizedSubjectContent(issuer
);
3706 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3707 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3708 return errSecIssuerMismatch
;
3711 /* Next verify that this cert was signed by issuer. */
3712 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3714 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3715 /* FIXME: We sould cache this (or at least the digest) until we find
3716 a suitable issuer. */
3717 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3718 CFIndex signedDataLength
;
3719 CertVerifyReturn crtn
;
3720 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3721 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3722 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3723 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3724 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3725 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3726 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3727 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3728 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3730 secdebug("verify", "unsupported algorithm");
3731 return errSecUnsupportedAlgorithm
;
3734 secdebug("verify", "*DigestInfo returned: %d", crtn
);
3735 /* FIXME: Do proper error code translation. */
3736 return errSecUnsupportedAlgorithm
;
3739 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3740 signedData
, signedDataLength
,
3741 certificate
->_signature
.data
, certificate
->_signature
.length
);
3743 secdebug("verify", "signature verify failed: %d", status
);
3744 return errSecNotSigner
;
3750 static OSStatus
_SecCertificateSetParent(SecCertificateRefP certificate
,
3751 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3753 if (certificate
->_parent
) {
3754 /* Setting a certificates issuer twice is only allowed if the new
3755 issuer is equal to the current one. */
3756 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3760 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3761 signatureCheckOnly
);
3763 OSStatus status
= noErr
;
3766 if (CFEqual(certificate
, issuer
)) {
3767 /* We don't retain ourselves cause that would be bad mojo,
3768 however we do record that we are properly self signed. */
3769 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3770 secdebug("cert", "set self as parent");
3775 certificate
->_parent
= issuer
;
3776 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3782 static bool SecCertificateIsSelfSigned(SecCertificateRefP certificate
) {
3783 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3784 certificate
->_isSelfSigned
=
3785 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3786 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3789 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3792 /* Return true iff we were able to set our own parent from one of the
3793 certificates in other_certificates, return false otherwise. If
3794 signatureCheckOnly is true, we can skip the subject == issuer or
3795 authorityKeyIdentifier tests. */
3796 static bool SecCertificateSetParentFrom(SecCertificateRefP certificate
,
3797 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3798 CFIndex count
= CFArrayGetCount(other_certificates
);
3800 for (ix
= 0; ix
< count
; ++ix
) {
3801 SecCertificateRefP candidate
= (SecCertificateRefP
)
3802 CFArrayGetValueAtIndex(other_certificates
, ix
);
3803 if (_SecCertificateSetParent(certificate
, candidate
,
3804 signatureCheckOnly
))
3810 /* Lookup the parent of certificate in the keychain and set it. */
3811 static bool SecCertificateFindParent(SecCertificateRefP certificate
) {
3812 /* FIXME: Search for things other than just subject of our issuer if we
3813 have a subjectID or authorityKeyIdentifier. */
3814 CFDataRef normalizedIssuer
=
3815 SecCertificateGetNormalizedIssuerContent(certificate
);
3816 const void *keys
[] = {
3823 kSecClassCertificate
,
3828 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3829 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3831 OSStatus status
= SecItemCopyMatching(query
, &results
);
3834 secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3838 CFArrayRef certs
= (CFArrayRef
)results
;
3839 /* Since we already know the certificates we are providing as candidates
3840 have been checked for subject matching, we can ask
3841 SecCertificateSetParentFrom to skip everything except the signature
3843 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3848 OSStatus
SecCertificateCompleteChain(SecCertificateRefP certificate
,
3849 CFArrayRef other_certificates
) {
3851 if (certificate
->_parent
== NULL
) {
3852 if (SecCertificateIsSelfSigned(certificate
))
3854 if (!other_certificates
||
3855 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
3857 if (!SecCertificateFindParent(certificate
))
3858 return errSecIssuerNotFound
;
3861 certificate
= certificate
->_parent
;
3866 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
3867 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
3868 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
3869 if (gnType
== GNT_IPAddress
) {
3870 CFStringRef string
= copyIPAddressContentDescription(
3871 kCFAllocatorDefault
, generalName
);
3873 CFArrayAppendValue(ipAddresses
, string
);
3876 return errSecInvalidCertificate
;
3882 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRefP certificate
) {
3883 /* These can only exist in the subject alt name. */
3884 if (!certificate
->_subjectAltName
)
3887 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
3888 0, &kCFTypeArrayCallBacks
);
3889 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
3890 ipAddresses
, appendIPAddressesFromGeneralNames
);
3891 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
3892 CFRelease(ipAddresses
);
3898 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
3899 const DERItem
*generalName
) {
3900 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
3901 if (gnType
== GNT_DNSName
) {
3902 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
3903 generalName
->data
, generalName
->length
,
3904 kCFStringEncodingUTF8
, FALSE
);
3906 CFArrayAppendValue(dnsNames
, string
);
3909 return errSecInvalidCertificate
;
3915 /* Return true if the passed in string matches the
3916 Preferred name syntax from sections 2.3.1. in RFC 1035.
3917 With the added check that we disallow empty dns names.
3918 Also in order to support wildcard DNSNames we allow for the '*'
3919 character anywhere in a dns component where we currently allow
3922 <domain> ::= <subdomain> | " "
3924 <subdomain> ::= <label> | <subdomain> "." <label>
3926 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
3928 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
3930 <let-dig-hyp> ::= <let-dig> | "-"
3932 <let-dig> ::= <letter> | <digit>
3934 <letter> ::= any one of the 52 alphabetic characters A through Z in
3935 upper case and a through z in lower case
3937 <digit> ::= any one of the ten digits 0 through 9
3939 static bool isDNSName(CFStringRef string
) {
3940 CFStringInlineBuffer buf
;
3941 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
3942 /* From RFC 1035 2.3.4. Size limits:
3943 labels 63 octets or less
3944 names 255 octets or less */
3945 require_quiet(length
<= 255, notDNS
);
3946 CFRange range
= { 0, length
};
3947 CFStringInitInlineBuffer(string
, &buf
, range
);
3951 kDNSStateAfterAlpha
,
3952 kDNSStateAfterDigit
,
3954 } state
= kDNSStateInital
;
3956 bool nonAlpha
= false;
3957 for (ix
= 0; ix
< length
; ++ix
) {
3958 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
3961 require_quiet(labelLength
<= 64 &&
3962 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
3964 state
= kDNSStateAfterDot
;
3967 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
3969 state
= kDNSStateAfterAlpha
;
3970 } else if ('0' <= ch
&& ch
<= '9') {
3972 /* The requirement for labels to start with a letter was
3973 dropped so we don't check this anymore. */
3974 require_quiet(state
== kDNSStateAfterAlpha
||
3975 state
== kDNSStateAfterDigit
||
3976 state
== kDNSStateAfterDash
, notDNS
);
3978 state
= kDNSStateAfterDigit
;
3980 } else if (ch
== '-') {
3981 require_quiet(state
== kDNSStateAfterAlpha
||
3982 state
== kDNSStateAfterDigit
||
3983 state
== kDNSStateAfterDash
, notDNS
);
3984 state
= kDNSStateAfterDash
;
3991 /* We don't allow a dns name to end in a dot, and we require the
3992 final name component to only have alphanumeric chars. */
3993 require_quiet(!nonAlpha
&& labelLength
<= 63 &&
3994 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4002 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4003 const DERItem
*value
, CFIndex rdnIX
) {
4004 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4005 if (DEROidCompare(type
, &oidCommonName
)) {
4006 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4009 if (isDNSName(string
)) {
4010 /* We found a common name that is formatted like a valid
4012 CFArrayAppendValue(dnsNames
, string
);
4016 return errSecInvalidCertificate
;
4022 /* Not everything returned by this function is going to be a proper DNS name,
4023 we also return the certificates common name entries from the subject,
4024 assuming they look like dns names as specified in RFC 1035. */
4025 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRefP certificate
) {
4026 /* These can exist in the subject alt name or in the subject. */
4027 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4028 0, &kCFTypeArrayCallBacks
);
4029 OSStatus status
= noErr
;
4030 if (certificate
->_subjectAltName
) {
4031 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4032 dnsNames
, appendDNSNamesFromGeneralNames
);
4034 /* RFC 2818 section 3.1. Server Identity
4036 If a subjectAltName extension of type dNSName is present, that MUST
4037 be used as the identity. Otherwise, the (most specific) Common Name
4038 field in the Subject field of the certificate MUST be used. Although
4039 the use of the Common Name is existing practice, it is deprecated and
4040 Certification Authorities are encouraged to use the dNSName instead.
4043 This implies that if we found 1 or more DNSNames in the
4044 subjectAltName, we should not use the Common Name of the subject as
4047 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4048 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4049 appendDNSNamesFromX501Name
);
4051 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4052 CFRelease(dnsNames
);
4058 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4059 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4060 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4061 if (gnType
== GNT_RFC822Name
) {
4062 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4063 generalName
->data
, generalName
->length
,
4064 kCFStringEncodingASCII
, FALSE
);
4066 CFArrayAppendValue(dnsNames
, string
);
4069 return errSecInvalidCertificate
;
4075 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4076 const DERItem
*value
, CFIndex rdnIX
) {
4077 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4078 if (DEROidCompare(type
, &oidEmailAddress
)) {
4079 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4082 CFArrayAppendValue(dnsNames
, string
);
4085 return errSecInvalidCertificate
;
4091 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRefP certificate
) {
4092 /* These can exist in the subject alt name or in the subject. */
4093 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4094 0, &kCFTypeArrayCallBacks
);
4095 OSStatus status
= noErr
;
4096 if (certificate
->_subjectAltName
) {
4097 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4098 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4101 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4102 appendRFC822NamesFromX501Name
);
4104 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4105 CFRelease(rfc822Names
);
4111 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4112 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4113 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4114 if (DEROidCompare(type
, &oidCommonName
)) {
4115 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4118 CFArrayAppendValue(commonNames
, string
);
4121 return errSecInvalidCertificate
;
4127 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRefP certificate
) {
4128 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4129 0, &kCFTypeArrayCallBacks
);
4131 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4132 appendCommonNamesFromX501Name
);
4133 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4134 CFRelease(commonNames
);
4140 static OSStatus
appendOrganizationFromX501Name(void *context
,
4141 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4142 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4143 if (DEROidCompare(type
, &oidOrganizationName
)) {
4144 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4147 CFArrayAppendValue(organization
, string
);
4150 return errSecInvalidCertificate
;
4156 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRefP certificate
) {
4157 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4158 0, &kCFTypeArrayCallBacks
);
4160 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4161 appendOrganizationFromX501Name
);
4162 if (status
|| CFArrayGetCount(organization
) == 0) {
4163 CFRelease(organization
);
4164 organization
= NULL
;
4166 return organization
;
4169 const SecCEBasicConstraints
*
4170 SecCertificateGetBasicConstraints(SecCertificateRefP certificate
) {
4171 if (certificate
->_basicConstraints
.present
)
4172 return &certificate
->_basicConstraints
;
4177 const SecCEPolicyConstraints
*
4178 SecCertificateGetPolicyConstraints(SecCertificateRefP certificate
) {
4179 if (certificate
->_policyConstraints
.present
)
4180 return &certificate
->_policyConstraints
;
4186 SecCertificateGetPolicyMappings(SecCertificateRefP certificate
) {
4187 return certificate
->_policyMappings
;
4190 const SecCECertificatePolicies
*
4191 SecCertificateGetCertificatePolicies(SecCertificateRefP certificate
) {
4192 if (certificate
->_certificatePolicies
.present
)
4193 return &certificate
->_certificatePolicies
;
4199 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRefP certificate
) {
4200 return certificate
->_inhibitAnyPolicySkipCerts
;
4203 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4204 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4205 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4206 if (gnType
== GNT_OtherName
) {
4208 DERReturn drtn
= DERParseSequenceContent(generalName
,
4209 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4211 require_noerr_quiet(drtn
, badDER
);
4212 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4214 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4215 &on
.value
, true), badDER
);
4216 CFArrayAppendValue(ntPrincipalNames
, string
);
4223 return errSecInvalidCertificate
;
4227 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRefP certificate
) {
4228 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4229 0, &kCFTypeArrayCallBacks
);
4230 OSStatus status
= noErr
;
4231 if (certificate
->_subjectAltName
) {
4232 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4233 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4235 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4236 CFRelease(ntPrincipalNames
);
4237 ntPrincipalNames
= NULL
;
4239 return ntPrincipalNames
;
4242 static OSStatus
appendToRFC2253String(void *context
,
4243 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4244 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4248 ST stateOrProvinceName
4250 OU organizationalUnitName
4252 STREET streetAddress
4256 /* Prepend a + if this is not the first RDN in an RDN set.
4257 Otherwise prepend a , if this is not the first RDN. */
4259 CFStringAppend(string
, CFSTR("+"));
4260 else if (CFStringGetLength(string
)) {
4261 CFStringAppend(string
, CFSTR(","));
4264 CFStringRef label
, oid
= NULL
;
4265 /* @@@ Consider changing this to a dictionary lookup keyed by the
4266 decimal representation. */
4267 #if 0 // represent all labels as oids
4268 if (DEROidCompare(type
, &oidCommonName
)) {
4269 label
= CFSTR("CN");
4270 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4272 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4273 label
= CFSTR("ST");
4274 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4276 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4277 label
= CFSTR("OU");
4278 } else if (DEROidCompare(type
, &oidCountryName
)) {
4281 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4282 label
= CFSTR("STREET");
4283 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4284 label
= CFSTR("DC");
4285 } else if (DEROidCompare(type
, &oidUserID
)) {
4286 label
= CFSTR("UID");
4291 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4294 CFStringAppend(string
, label
);
4295 CFStringAppend(string
, CFSTR("="));
4296 CFStringRef raw
= NULL
;
4298 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4301 /* Append raw to string while escaping:
4302 a space or "#" character occurring at the beginning of the string
4303 a space character occurring at the end of the string
4304 one of the characters ",", "+", """, "\", "<", ">" or ";"
4306 CFStringInlineBuffer buffer
;
4307 CFIndex ix
, length
= CFStringGetLength(raw
);
4308 CFRange range
= { 0, length
};
4309 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4310 for (ix
= 0; ix
< length
; ++ix
) {
4311 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4313 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4314 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4315 ch
== '<' || ch
== '>' || ch
== ';' ||
4316 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4317 (ch
== '#' && ix
== 0)) {
4318 UniChar chars
[] = { '\\', ch
};
4319 CFStringAppendCharacters(string
, chars
, 2);
4321 CFStringAppendCharacters(string
, &ch
, 1);
4326 /* Append the value in hex. */
4327 CFStringAppend(string
, CFSTR("#"));
4329 for (ix
= 0; ix
< value
->length
; ++ix
)
4330 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4338 CFStringRef
SecCertificateCopySubjectString(SecCertificateRefP certificate
) {
4339 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4340 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4341 if (status
|| CFStringGetLength(string
) == 0) {
4348 static OSStatus
appendToCompanyNameString(void *context
,
4349 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4350 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4351 if (CFStringGetLength(string
) != 0)
4354 if (!DEROidCompare(type
, &oidOrganizationName
))
4358 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4361 CFStringAppend(string
, raw
);
4367 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRefP certificate
) {
4368 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4369 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4370 appendToCompanyNameString
);
4371 if (status
|| CFStringGetLength(string
) == 0) {
4378 CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4379 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4380 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4381 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4383 CFDataSetLength(sequence
, sequence_length
);
4384 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4385 *sequence_ptr
++ = 0x30; /* ASN1_CONSTR_SEQUENCE */
4386 require_noerr_quiet(DEREncodeLength(content
->length
,
4387 sequence_ptr
, &seq_len_length
), out
);
4388 sequence_ptr
+= seq_len_length
;
4389 memcpy(sequence_ptr
, content
->data
, content
->length
);
4392 CFReleaseSafe(sequence
);
4396 CFDataRef
SecCertificateCopyIssuerSequence(
4397 SecCertificateRefP certificate
) {
4398 return SecDERItemCopySequence(&certificate
->_issuer
);
4401 CFDataRef
SecCertificateCopySubjectSequence(
4402 SecCertificateRefP certificate
) {
4403 return SecDERItemCopySequence(&certificate
->_subject
);
4406 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4407 SecCertificateRefP certificate
) {
4408 return &certificate
->_algId
;
4411 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRefP certificate
) {
4412 return &certificate
->_pubKeyDER
;
4415 SecKeyRefP
SecCertificateCopyPublicKeyP(SecCertificateRefP certificate
) {
4416 SecKeyRefP publicKey
= NULL
;
4417 #warning implementation empty
4419 const DERAlgorithmId
*algId
=
4420 SecCertificateGetPublicKeyAlgorithm(certificate
);
4421 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4422 if (DEROidCompare(&algId
->oid
, &oidRsa
)) {
4423 publicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
4424 keyData
->data
, keyData
->length
, kSecKeyEncodingPkcs1
);
4426 secdebug(NULL
, "Unsupported algorithm oid");
4433 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRefP certificate
) {
4434 if (!certificate
->_sha1Digest
) {
4435 certificate
->_sha1Digest
=
4436 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4437 certificate
->_der
.data
, certificate
->_der
.length
);
4440 return certificate
->_sha1Digest
;
4443 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRefP certificate
) {
4444 CFDataRef digest
= NULL
;
4445 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
4447 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4448 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4454 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRefP certificate
) {
4455 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4456 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4459 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRefP certificate
) {
4460 if (!certificate
->_authorityKeyID
&&
4461 certificate
->_authorityKeyIdentifier
.length
) {
4462 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4463 certificate
->_authorityKeyIdentifier
.data
,
4464 certificate
->_authorityKeyIdentifier
.length
);
4467 return certificate
->_authorityKeyID
;
4470 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRefP certificate
) {
4471 if (!certificate
->_subjectKeyID
&&
4472 certificate
->_subjectKeyIdentifier
.length
) {
4473 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4474 certificate
->_subjectKeyIdentifier
.data
,
4475 certificate
->_subjectKeyIdentifier
.length
);
4478 return certificate
->_subjectKeyID
;
4481 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRefP certificate
) {
4482 return certificate
->_crlDistributionPoints
;
4485 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRefP certificate
) {
4486 return certificate
->_ocspResponders
;
4489 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRefP certificate
) {
4490 return certificate
->_caIssuers
;
4493 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRefP certificate
) {
4494 return certificate
->_subjectAltName
&&
4495 certificate
->_subjectAltName
->critical
;
4498 bool SecCertificateHasSubject(SecCertificateRefP certificate
) {
4499 /* Since the _subject field is the content of the subject and not the
4500 whole thing, we can simply check for a 0 length subject here. */
4501 return certificate
->_subject
.length
!= 0;
4504 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRefP certificate
) {
4505 return certificate
->_foundUnknownCriticalExtension
;
4508 /* Private API functions. */
4509 void SecCertificateShow(SecCertificateRefP certificate
) {
4511 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4512 fprintf(stderr
, "\n");
4515 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
4516 SecCertificateRefP certificate
) {
4517 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4518 CFNumberRef certificateType
, certificateEncoding
;
4519 CFStringRef label
, alias
;
4520 CFDataRef skid
, pubKeyDigest
, certData
;
4521 CFDictionaryRef dict
= NULL
;
4525 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4526 SInt32 ctv
= certificate
->_version
+ 1;
4527 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4528 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4529 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4530 certData
= SecCertificateCopyDataP(certificate
);
4531 skid
= SecCertificateGetSubjectKeyID(certificate
);
4532 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4533 certificate
->_pubKeyDER
.length
);
4535 /* We still need to figure out how to deal with multi valued attributes. */
4536 alias
= SecCertificateCopyRFC822Names(certificate
);
4537 label
= SecCertificateCopySubjectSummary(certificate
);
4543 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4544 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4545 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4547 DICT_ADDPAIR(kSecAttrLabel
, label
);
4549 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4550 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4551 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4552 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4554 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4555 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4556 DICT_ADDPAIR(kSecValueData
, certData
);
4557 dict
= DICT_CREATE(allocator
);
4559 CFReleaseSafe(label
);
4560 CFReleaseSafe(pubKeyDigest
);
4561 CFReleaseSafe(certData
);
4562 CFReleaseSafe(certificateEncoding
);
4563 CFReleaseSafe(certificateType
);
4568 SecCertificateRefP
SecCertificateCreateFromAttributeDictionary(
4569 CFDictionaryRef refAttributes
) {
4570 /* @@@ Support having an allocator in refAttributes. */
4571 CFAllocatorRef allocator
= NULL
;
4572 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4573 return SecCertificateCreateWithDataP(allocator
, data
);
4576 bool SecCertificateIsSelfSignedCA(SecCertificateRefP certificate
) {
4577 bool result
= false;
4578 SecKeyRefP publicKey
;
4579 require(publicKey
= SecCertificateCopyPublicKeyP(certificate
), out
);
4580 CFDataRef normalizedIssuer
=
4581 SecCertificateGetNormalizedIssuerContent(certificate
);
4582 CFDataRef normalizedSubject
=
4583 SecCertificateGetNormalizedSubjectContent(certificate
);
4584 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4585 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4587 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
4588 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
4589 if (authorityKeyID
) {
4590 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4593 if (SecCertificateVersion(certificate
) >= 3) {
4594 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
4595 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4596 require_noerr_quiet(SecCertificateIsSignedByP(certificate
, publicKey
), out
);
4601 CFReleaseSafe(publicKey
);
4605 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRefP certificate
) {
4606 return certificate
->_keyUsage
;
4609 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRefP certificate
)
4611 CFMutableArrayRef extended_key_usage_oids
=
4612 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4613 require_quiet(extended_key_usage_oids
, out
);
4615 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4616 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4617 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4618 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4621 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4622 require_noerr_quiet(drtn
, out
);
4623 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4624 DERDecodedInfo currDecoded
;
4626 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4627 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4628 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4629 currDecoded
.content
.data
, currDecoded
.content
.length
);
4631 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4635 require_quiet(drtn
== DR_EndOfSequence
, out
);
4636 return extended_key_usage_oids
;
4640 CFReleaseSafe(extended_key_usage_oids
);
4644 SecCertificateRefP
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
4645 CFDataRef pem_certificate
)
4647 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
4648 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
4649 uint8_t *base64_data
= NULL
;
4650 SecCertificateRefP cert
= NULL
;
4651 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
4652 //const size_t length = CFDataGetLength(pem_certificate);
4653 char *begin
= strstr((const char *)data
, begin_cert
);
4654 char *end
= strstr((const char *)data
, end_cert
);
4657 begin
+= sizeof(begin_cert
) - 1;
4658 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
4659 if (base64_length
) {
4660 require_quiet(base64_data
= calloc(1, base64_length
), out
);
4661 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
4662 cert
= SecCertificateCreateWithBytesP(kCFAllocatorDefault
, base64_data
, base64_length
);
4669 static void convertCertificateToCFData(const void *value
, void *context
) {
4670 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4671 SecCertificateRefP certificate
= (SecCertificateRefP
)value
;
4672 CFDataRef data
= SecCertificateCopyDataP(certificate
);
4673 CFArrayAppendValue(result
, data
);
4677 /* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
4678 CFArrayRef
SecCertificateArrayCopyDataArray(CFArrayRef certificates
) {
4679 CFIndex count
= CFArrayGetCount(certificates
);
4680 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
4681 CFRange all_certs
= { 0, count
};
4682 CFArrayApplyFunction(certificates
, all_certs
, convertCertificateToCFData
, result
);
4686 /* AUDIT[securityd](done):
4687 value (ok) is an element in a caller provided array.
4689 static void convertCFDataToCertificate(const void *value
, void *context
) {
4690 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4691 CFDataRef data
= (CFDataRef
)value
;
4692 if (data
&& CFGetTypeID(data
) == CFDataGetTypeID()) {
4693 SecCertificateRefP certificate
= SecCertificateCreateWithDataP(kCFAllocatorDefault
, data
);
4695 CFArrayAppendValue(result
, certificate
);
4696 CFRelease(certificate
);
4701 /* AUDIT[securityd](done):
4702 certificates (ok) is a caller provided array, only its cf type has
4705 CFArrayRef
SecCertificateDataArrayCopyArray(CFArrayRef certificates
) {
4706 CFIndex count
= CFArrayGetCount(certificates
);
4707 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
4708 CFRange all_certs
= { 0, count
};
4709 CFArrayApplyFunction(certificates
, all_certs
, convertCFDataToCertificate
, result
);