2 * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 #include "SecCertificateInternalP.h"
31 #include <CommonCrypto/CommonDigest.h>
32 #include <CoreFoundation/CFRuntime.h>
33 #include <CoreFoundation/CFString.h>
34 #include <CoreFoundation/CFBundle.h>
35 #include <CoreFoundation/CFDictionary.h>
36 #include <CoreFoundation/CFNumber.h>
37 #include <CoreFoundation/CFTimeZone.h>
40 #include <AssertMacros.h>
41 #include <libDER/libDER.h>
42 #include <libDER/DER_CertCrl.h>
43 #include <libDER/DER_Encode.h>
44 #include <libDER/DER_Keys.h>
45 #include <libDER/asn1Types.h>
46 #include <libDER/oidsPriv.h>
48 #include "SecBasePriv.h"
50 #include "SecRSAKeyP.h"
51 #include "SecFrameworkP.h"
53 #include "SecItemPriv.h"
56 #include <libkern/OSByteOrder.h>
58 #include "SecInternalP.h"
59 #include "SecBase64P.h"
61 #include <security_utilities/debugging.h>
63 typedef struct SecCertificateExtension
{
67 } SecCertificateExtension
;
70 typedef struct KnownExtension
{
76 kSecSelfSignedUnknown
= 0,
82 struct __SecCertificate
{
85 DERItem _der
; /* Entire certificate in DER form. */
86 DERItem _tbs
; /* To Be Signed cert DER bytes. */
87 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
88 DERItem _signature
; /* The content of the sig bit string. */
91 DERItem _serialNum
; /* Integer. */
92 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
93 DERItem _issuer
; /* Sequence of RDN. */
94 CFAbsoluteTime _notBefore
;
95 CFAbsoluteTime _notAfter
;
96 DERItem _subject
; /* Sequence of RDN. */
97 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
98 DERItem _pubKeyDER
; /* contents of bit string */
99 DERItem _issuerUniqueID
; /* bit string, optional */
100 DERItem _subjectUniqueID
; /* bit string, optional */
103 /* Known extensions if the certificate contains them,
104 extnValue.length will be > 0. */
105 KnownExtension _authorityKeyID
;
107 /* This extension is used to uniquely identify a certificate from among
108 several that have the same subject name. If the extension is not
109 present, its value is calculated by performing a SHA-1 hash of the
110 certificate's DER encoded subjectPublicKeyInfo, as recommended by
112 KnownExtension _subjectKeyID
;
113 KnownExtension _keyUsage
;
114 KnownExtension _extendedKeyUsage
;
115 KnownExtension _basicConstraints
;
116 KnownExtension _netscapeCertType
;
117 KnownExtension _subjectAltName
;
118 KnownExtension _qualCertStatements
;
121 bool _foundUnknownCriticalExtension
;
123 /* Well known certificate extensions. */
124 SecCEBasicConstraints _basicConstraints
;
125 SecCEPolicyConstraints _policyConstraints
;
126 CFDictionaryRef _policyMappings
;
127 SecCECertificatePolicies _certificatePolicies
;
129 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
130 value of the SkipCerts field of the InhibitAnyPolicy extension
132 uint32_t _inhibitAnyPolicySkipCerts
;
134 /* If KeyUsage extension is not present this is 0, otherwise it's
135 the value of the extension. */
136 SecKeyUsage _keyUsage
;
138 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
139 Length = 0 if not present. */
140 DERItem _subjectKeyIdentifier
;
142 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
143 Length = 0 if not present. */
144 DERItem _authorityKeyIdentifier
;
145 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
146 _authorityKeyIdentifierSerialNumber have non zero length if present.
147 Both are either present or absent together. */
148 DERItem _authorityKeyIdentifierIssuer
;
149 DERItem _authorityKeyIdentifierSerialNumber
;
151 /* Subject alt name extension, if present. Not malloced, it's just a
152 pointer to an element in the _extensions array. */
153 const SecCertificateExtension
*_subjectAltName
;
155 /* Parsed extension values. */
157 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
158 CFMutableArrayRef _crlDistributionPoints
;
160 /* Array of CFURLRefs containing the URI values of accessLocations of each
161 id-ad-ocsp AccessDescription in the Authority Information Access
163 CFMutableArrayRef _ocspResponders
;
165 /* Array of CFURLRefs containing the URI values of accessLocations of each
166 id-ad-caIssuers AccessDescription in the Authority Information Access
168 CFMutableArrayRef _caIssuers
;
170 /* All other (non known) extensions. The _extensions array is malloced. */
171 CFIndex _extensionCount
;
172 SecCertificateExtension
*_extensions
;
174 /* Optional cached fields. */
177 CFArrayRef _properties
;
178 CFDataRef _serialNumber
;
179 CFDataRef _normalizedIssuer
;
180 CFDataRef _normalizedSubject
;
181 CFDataRef _authorityKeyID
;
182 CFDataRef _subjectKeyID
;
184 CFDataRef _sha1Digest
;
185 uint8_t _isSelfSigned
;
189 /* Public Constants for property list keys. */
190 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 SecCertificateCopyIssuerSummaryP(certificate
));
233 static void SecCertificateDestroy(CFTypeRef cf
) {
234 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
235 if (certificate
->_certificatePolicies
.policies
)
236 free(certificate
->_certificatePolicies
.policies
);
237 CFReleaseSafe(certificate
->_policyMappings
);
238 CFReleaseSafe(certificate
->_crlDistributionPoints
);
239 CFReleaseSafe(certificate
->_ocspResponders
);
240 CFReleaseSafe(certificate
->_caIssuers
);
241 if (certificate
->_extensions
) {
242 free(certificate
->_extensions
);
244 CFReleaseSafe(certificate
->_pubKey
);
245 CFReleaseSafe(certificate
->_der_data
);
246 CFReleaseSafe(certificate
->_properties
);
247 CFReleaseSafe(certificate
->_serialNumber
);
248 CFReleaseSafe(certificate
->_normalizedIssuer
);
249 CFReleaseSafe(certificate
->_normalizedSubject
);
250 CFReleaseSafe(certificate
->_authorityKeyID
);
251 CFReleaseSafe(certificate
->_subjectKeyID
);
252 CFReleaseSafe(certificate
->_sha1Digest
);
255 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
256 SecCertificateRefP cert1
= (SecCertificateRefP
)cf1
;
257 SecCertificateRefP cert2
= (SecCertificateRefP
)cf2
;
260 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
262 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
265 /* Hash of the certificate is der length + signature length + last 4 bytes
267 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
268 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
269 DERSize der_length
= certificate
->_der
.length
;
270 DERSize sig_length
= certificate
->_signature
.length
;
271 DERSize ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
272 CFHashCode hashCode
= 0;
273 for (; ix
< sig_length
; ++ix
)
274 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
276 return (hashCode
+ der_length
+ sig_length
);
281 /************************************************************************/
282 /************************* General Name Parsing *************************/
283 /************************************************************************/
285 typedef OSStatus (*parseGeneralNameCallback
)(void *context
,
286 SecCEGeneralNameType type
, const DERItem
*value
);
290 GeneralName ::= CHOICE {
291 otherName [0] OtherName,
292 rfc822Name [1] IA5String,
293 dNSName [2] IA5String,
294 x400Address [3] ORAddress,
295 directoryName [4] Name,
296 ediPartyName [5] EDIPartyName,
297 uniformResourceIdentifier [6] IA5String,
298 iPAddress [7] OCTET STRING,
299 registeredID [8] OBJECT IDENTIFIER}
301 OtherName ::= SEQUENCE {
302 type-id OBJECT IDENTIFIER,
303 value [0] EXPLICIT ANY DEFINED BY type-id }
305 EDIPartyName ::= SEQUENCE {
306 nameAssigner [0] DirectoryString OPTIONAL,
307 partyName [1] DirectoryString }
309 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
310 const DERItem
*generalNameContent
,
311 void *context
, parseGeneralNameCallback callback
) {
313 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
314 return callback(context
, GNT_OtherName
, generalNameContent
);
315 case ASN1_CONTEXT_SPECIFIC
| 1:
316 return callback(context
, GNT_RFC822Name
, generalNameContent
);
317 case ASN1_CONTEXT_SPECIFIC
| 2:
318 return callback(context
, GNT_DNSName
, generalNameContent
);
319 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
320 return callback(context
, GNT_X400Address
, generalNameContent
);
321 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
322 return callback(context
, GNT_DirectoryName
, generalNameContent
);
323 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
324 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
325 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
327 /* Technically I don't think this is valid, but there are certs out
328 in the wild that use a constructed IA5String. In particular the
329 VeriSign Time Stamping Authority CA.cer does this. */
330 DERDecodedInfo uriContent
;
331 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
332 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
333 return callback(context
, GNT_URI
, &uriContent
.content
);
335 case ASN1_CONTEXT_SPECIFIC
| 6:
336 return callback(context
, GNT_URI
, generalNameContent
);
337 case ASN1_CONTEXT_SPECIFIC
| 7:
338 return callback(context
, GNT_IPAddress
, generalNameContent
);
339 case ASN1_CONTEXT_SPECIFIC
| 8:
340 return callback(context
, GNT_RegisteredID
, generalNameContent
);
345 return errSecInvalidCertificate
;
348 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
349 void *context
, parseGeneralNameCallback callback
) {
351 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
352 require_noerr_quiet(drtn
, badDER
);
353 DERDecodedInfo generalNameContent
;
354 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
356 OSStatus status
= parseGeneralNameContentProperty(
357 generalNameContent
.tag
, &generalNameContent
.content
, context
,
362 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
363 return errSecSuccess
;
366 return errSecInvalidCertificate
;
369 static OSStatus
parseGeneralNames(const DERItem
*generalNames
, void *context
,
370 parseGeneralNameCallback callback
) {
371 DERDecodedInfo generalNamesContent
;
372 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
373 require_noerr_quiet(drtn
, badDER
);
374 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
375 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
378 return errSecInvalidCertificate
;
384 GeneralName ::= CHOICE {
385 otherName [0] OtherName,
386 rfc822Name [1] IA5String,
387 dNSName [2] IA5String,
388 x400Address [3] ORAddress,
389 directoryName [4] Name,
390 ediPartyName [5] EDIPartyName,
391 uniformResourceIdentifier [6] IA5String,
392 iPAddress [7] OCTET STRING,
393 registeredID [8] OBJECT IDENTIFIER}
395 EDIPartyName ::= SEQUENCE {
396 nameAssigner [0] DirectoryString OPTIONAL,
397 partyName [1] DirectoryString }
399 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
400 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
402 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
403 generalName
->nameType
= GNT_OtherName
;
404 generalName
->berEncoded
= true;
405 generalName
->name
= *generalNameContent
;
407 case ASN1_CONTEXT_SPECIFIC
| 1:
409 generalName
->nameType
= GNT_RFC822Name
;
410 generalName
->berEncoded
= false;
411 generalName
->name
= *generalNameContent
;
413 case ASN1_CONTEXT_SPECIFIC
| 2:
415 generalName
->nameType
= GNT_DNSName
;
416 generalName
->berEncoded
= false;
417 generalName
->name
= *generalNameContent
;
419 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
420 generalName
->nameType
= GNT_X400Address
;
421 generalName
->berEncoded
= true;
422 generalName
->name
= *generalNameContent
;
424 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
425 generalName
->nameType
= GNT_DirectoryName
;
426 generalName
->berEncoded
= true;
427 generalName
->name
= *generalNameContent
;
429 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
430 generalName
->nameType
= GNT_EdiPartyName
;
431 generalName
->berEncoded
= true;
432 generalName
->name
= *generalNameContent
;
434 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
436 /* Technically I don't think this is valid, but there are certs out
437 in the wild that use a constructed IA5String. In particular the
438 VeriSign Time Stamping Authority CA.cer does this. */
439 DERDecodedInfo decoded
;
440 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
441 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
442 generalName
->nameType
= GNT_URI
;
443 generalName
->berEncoded
= false;
444 generalName
->name
= decoded
.content
;
447 case ASN1_CONTEXT_SPECIFIC
| 6:
448 generalName
->nameType
= GNT_URI
;
449 generalName
->berEncoded
= false;
450 generalName
->name
= *generalNameContent
;
452 case ASN1_CONTEXT_SPECIFIC
| 7:
453 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
454 8 octects, addr/mask for ipv6 it's 32. */
455 generalName
->nameType
= GNT_IPAddress
;
456 generalName
->berEncoded
= false;
457 generalName
->name
= *generalNameContent
;
459 case ASN1_CONTEXT_SPECIFIC
| 8:
460 /* name is the content of an OID. */
461 generalName
->nameType
= GNT_RegisteredID
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= *generalNameContent
;
469 return errSecSuccess
;
471 return errSecInvalidCertificate
;
475 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
477 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
478 CFIndex
*count
, SecCEGeneralName
**name
) {
479 SecCEGeneralName
*generalNames
= NULL
;
481 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
482 require_noerr_quiet(drtn
, badDER
);
483 DERDecodedInfo generalNameContent
;
484 CFIndex generalNamesCount
= 0;
485 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
489 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
491 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
493 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
495 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
497 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
498 &generalNameContent
.content
, &generalNames
[ix
])) {
503 *count
= generalNamesCount
;
504 *name
= generalNames
;
505 return errSecSuccess
;
510 return errSecInvalidCertificate
;
513 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
514 CFIndex
*count
, SecCEGeneralName
**name
) {
515 DERDecodedInfo generalNamesContent
;
516 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
517 require_noerr_quiet(drtn
, badDER
);
518 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
520 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
521 return errSecSuccess
;
523 return errSecInvalidCertificate
;
527 /************************************************************************/
528 /************************** X.509 Name Parsing **************************/
529 /************************************************************************/
531 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
532 const DERItem
*value
, CFIndex rdnIX
);
534 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
535 parseX501NameCallback callback
) {
537 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
538 require_noerr_quiet(drtn
, badDER
);
539 DERDecodedInfo atvContent
;
541 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
542 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
543 DERAttributeTypeAndValue atv
;
544 drtn
= DERParseSequenceContent(&atvContent
.content
,
545 DERNumAttributeTypeAndValueItemSpecs
,
546 DERAttributeTypeAndValueItemSpecs
,
548 require_noerr_quiet(drtn
, badDER
);
549 require_quiet(atv
.type
.length
!= 0, badDER
);
550 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
554 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
556 return errSecSuccess
;
558 return errSecInvalidCertificate
;
561 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
562 parseX501NameCallback callback
) {
564 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
565 require_noerr_quiet(drtn
, badDER
);
566 DERDecodedInfo currDecoded
;
567 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
568 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
569 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
574 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
576 return errSecSuccess
;
579 return errSecInvalidCertificate
;
582 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
583 parseX501NameCallback callback
) {
584 DERDecodedInfo x501NameContent
;
585 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
586 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
587 return errSecInvalidCertificate
;
589 return parseX501NameContent(&x501NameContent
.content
, context
,
594 /************************************************************************/
595 /********************** Extension Parsing Routines **********************/
596 /************************************************************************/
598 static void SecCEPSubjectKeyIdentifier(SecCertificateRefP certificate
,
599 const SecCertificateExtension
*extn
) {
600 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("cert", "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("cert", "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
= (uint32_t)policy_count
;
738 certificate
->_certificatePolicies
.policies
= policies
;
743 certificate
->_certificatePolicies
.present
= false;
744 secdebug("cert", "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("cert", "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("cert", "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("cert", "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("cert", "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("cert", "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
, (int)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 - it was saved in full DER form */
1407 DERSubjPubKeyInfo pubKeyInfo
;
1408 drtn
= DERParseSequence(&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"), (unsigned long)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 %02lX"), (unsigned long)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 appendPropertyP(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 (int)length
, bytes
, (int)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 appendPropertyP(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 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1972 static void appendDateProperty(CFMutableArrayRef properties
,
1973 CFStringRef label
, CFAbsoluteTime absTime
) {
1974 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1975 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1979 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
1980 CFStringRef label
, const DERItem
*ip
) {
1982 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
1984 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(alg_props
, kSecPropertyTypeString
,
2036 CFSTR("Parameters"), CFSTR("none"));
2038 appendUnparsedProperty(alg_props
, CFSTR("Parameters"),
2039 &algorithm
->params
);
2042 appendPropertyP(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
, (int)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 appendPropertyP(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 appendPropertyP(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
);
2233 return errSecSuccess
;
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 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2306 static void appendBoolProperty(CFMutableArrayRef properties
,
2307 CFStringRef label
, bool boolean
) {
2308 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPrintableDERSequenceP(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 appendPropertyP(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 handled
= 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 (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3233 /* Nothing to do here appendPrintableDERSequenceP 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 (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3276 /* Nothing to do here appendPrintableDERSequenceP 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 appendPropertyP(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)) {
3313 return errSecSuccess
;
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
);
3345 return errSecSuccess
;
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
= SecCertificateCopyRFC822NamesP(certificate
);
3368 /* If we didn't find any email addresses in the certificate, we try finding
3369 a DNS name instead. */
3370 names
= SecCertificateCopyDNSNamesP(certificate
);
3373 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3374 CFRetain(summary
.summary
);
3379 return summary
.summary
;
3382 CFStringRef
SecCertificateCopyIssuerSummaryP(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 SecCertificateIsValidP(SecCertificateRefP certificate
,
3433 CFAbsoluteTime verifyTime
) {
3435 return certificate
->_notBefore
<= verifyTime
&&
3436 verifyTime
<= certificate
->_notAfter
;
3439 CFIndex
SecCertificateVersionP(SecCertificateRefP certificate
) {
3440 return certificate
->_version
+ 1;
3443 CFAbsoluteTime
SecCertificateNotValidBeforeP(SecCertificateRefP certificate
) {
3444 return certificate
->_notBefore
;
3447 CFAbsoluteTime
SecCertificateNotValidAfterP(SecCertificateRefP certificate
) {
3448 return certificate
->_notAfter
;
3451 CFMutableArrayRef
SecCertificateCopySummaryPropertiesP(
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 appendPropertyP(summary
, kSecPropertyTypeTitle
,
3462 CFRelease(ssummary
);
3465 CFStringRef isummary
= CFSTR("Issuer Summary");
3466 appendPropertyP(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 appendPropertyP(summary
, ptype
, NULL
, message
);
3513 CFArrayRef
SecCertificateCopyPropertiesP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(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 appendPropertyP(properties
, kSecPropertyTypeString
,
3555 CFSTR("Certificate Type"), typeString
);
3556 CFRelease(typeString
);
3560 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
3561 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
3562 appendPropertyP(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
;
3633 * Accessor for normalized issuer content
3635 CFDataRef
SecCertificateGetNormalizedIssuerContentP(
3636 SecCertificateRefP certificate
) {
3637 return certificate
->_normalizedIssuer
;
3641 * Accessor for normalized subject content
3643 CFDataRef
SecCertificateGetNormalizedSubjectContentP(
3644 SecCertificateRefP certificate
) {
3645 return certificate
->_normalizedSubject
;
3649 * Returns DER-encoded normalized issuer sequence
3650 * for use with SecItemCopyMatching; caller must release
3652 CFDataRef
SecCertificateCopyNormalizedIssuerSequenceP(
3653 SecCertificateRefP certificate
) {
3655 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
3656 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
3658 return SecDERItemCopySequenceP(&tmpdi
);
3662 * Returns DER-encoded normalized subject sequence
3663 * for use with SecItemCopyMatching; caller must release
3665 CFDataRef
SecCertificateCopyNormalizedSubjectSequenceP(
3666 SecCertificateRefP certificate
) {
3668 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
);
3669 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
3671 return SecDERItemCopySequenceP(&tmpdi
);
3674 /* Verify that certificate was signed by issuerKey. */
3675 OSStatus
SecCertificateIsSignedByP(SecCertificateRefP certificate
,
3676 SecKeyRefP issuerKey
) {
3677 /* Setup algId in SecAsn1AlgId format. */
3679 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3680 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3681 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3682 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3684 #warning implementation empty
3686 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3687 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3688 certificate
->_signature
.data
, certificate
->_signature
.length
);
3690 secdebug("verify", "signature verify failed: %d", status
);
3691 return errSecNotSigner
;
3695 return errSecSuccess
;
3699 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRefP certificate
,
3700 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3701 if (!signatureCheckOnly
) {
3702 /* It turns out we don't actually need to use normalized subject and
3703 issuer according to rfc2459. */
3705 /* If present we should check issuerID against the issuer subjectID. */
3707 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3708 then we should look for a SubjectKeyIdentifier in the issuer
3710 If we have a authorityCertSerialNumber we can use that for chaining.
3711 If we have a authorityCertIssuer we can use that? (or not) */
3713 /* Verify that this cert was issued by issuer. Do so by chaining
3714 either issuerID to subjectID or normalized issuer to normalized
3716 CFDataRef normalizedIssuer
=
3717 SecCertificateGetNormalizedIssuerContentP(certificate
);
3718 CFDataRef normalizedIssuerSubject
=
3719 SecCertificateGetNormalizedSubjectContentP(issuer
);
3720 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3721 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3722 return errSecIssuerMismatch
;
3725 /* Next verify that this cert was signed by issuer. */
3726 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3728 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3729 /* FIXME: We sould cache this (or at least the digest) until we find
3730 a suitable issuer. */
3731 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3732 CFIndex signedDataLength
;
3733 CertVerifyReturn crtn
;
3734 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3735 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3736 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3737 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3738 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3739 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3740 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3741 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3742 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3744 secdebug("verify", "unsupported algorithm");
3745 return errSecUnsupportedAlgorithm
;
3748 secdebug("verify", "*DigestInfo returned: %d", crtn
);
3749 /* FIXME: Do proper error code translation. */
3750 return errSecUnsupportedAlgorithm
;
3753 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3754 signedData
, signedDataLength
,
3755 certificate
->_signature
.data
, certificate
->_signature
.length
);
3757 secdebug("verify", "signature verify failed: %d", status
);
3758 return errSecNotSigner
;
3761 return errSecSuccess
;
3764 static OSStatus
_SecCertificateSetParent(SecCertificateRefP certificate
,
3765 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3767 if (certificate
->_parent
) {
3768 /* Setting a certificates issuer twice is only allowed if the new
3769 issuer is equal to the current one. */
3770 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3774 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3775 signatureCheckOnly
);
3777 OSStatus status
= errSecSuccess
;
3780 if (CFEqual(certificate
, issuer
)) {
3781 /* We don't retain ourselves cause that would be bad mojo,
3782 however we do record that we are properly self signed. */
3783 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3784 secdebug("cert", "set self as parent");
3785 return errSecSuccess
;
3789 certificate
->_parent
= issuer
;
3790 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3796 static bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate
) {
3797 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3798 certificate
->_isSelfSigned
=
3799 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3800 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3803 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3806 /* Return true iff we were able to set our own parent from one of the
3807 certificates in other_certificates, return false otherwise. If
3808 signatureCheckOnly is true, we can skip the subject == issuer or
3809 authorityKeyIdentifier tests. */
3810 static bool SecCertificateSetParentFrom(SecCertificateRefP certificate
,
3811 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3812 CFIndex count
= CFArrayGetCount(other_certificates
);
3814 for (ix
= 0; ix
< count
; ++ix
) {
3815 SecCertificateRefP candidate
= (SecCertificateRefP
)
3816 CFArrayGetValueAtIndex(other_certificates
, ix
);
3817 if (_SecCertificateSetParent(certificate
, candidate
,
3818 signatureCheckOnly
))
3824 /* Lookup the parent of certificate in the keychain and set it. */
3825 static bool SecCertificateFindParent(SecCertificateRefP certificate
) {
3826 /* FIXME: Search for things other than just subject of our issuer if we
3827 have a subjectID or authorityKeyIdentifier. */
3828 CFDataRef normalizedIssuer
=
3829 SecCertificateGetNormalizedIssuerContentP(certificate
);
3830 const void *keys
[] = {
3837 kSecClassCertificate
,
3842 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3843 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3845 OSStatus status
= SecItemCopyMatching(query
, &results
);
3848 secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3852 CFArrayRef certs
= (CFArrayRef
)results
;
3853 /* Since we already know the certificates we are providing as candidates
3854 have been checked for subject matching, we can ask
3855 SecCertificateSetParentFrom to skip everything except the signature
3857 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3862 OSStatus
SecCertificateCompleteChainP(SecCertificateRefP certificate
,
3863 CFArrayRef other_certificates
) {
3865 if (certificate
->_parent
== NULL
) {
3866 if (SecCertificateIsSelfSignedP(certificate
))
3867 return errSecSuccess
;
3868 if (!other_certificates
||
3869 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
3871 if (!SecCertificateFindParent(certificate
))
3872 return errSecIssuerNotFound
;
3875 certificate
= certificate
->_parent
;
3880 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
3881 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
3882 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
3883 if (gnType
== GNT_IPAddress
) {
3884 CFStringRef string
= copyIPAddressContentDescription(
3885 kCFAllocatorDefault
, generalName
);
3887 CFArrayAppendValue(ipAddresses
, string
);
3890 return errSecInvalidCertificate
;
3893 return errSecSuccess
;
3896 CFArrayRef
SecCertificateCopyIPAddressesP(SecCertificateRefP certificate
) {
3897 /* These can only exist in the subject alt name. */
3898 if (!certificate
->_subjectAltName
)
3901 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
3902 0, &kCFTypeArrayCallBacks
);
3903 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
3904 ipAddresses
, appendIPAddressesFromGeneralNames
);
3905 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
3906 CFRelease(ipAddresses
);
3912 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
3913 const DERItem
*generalName
) {
3914 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
3915 if (gnType
== GNT_DNSName
) {
3916 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
3917 generalName
->data
, generalName
->length
,
3918 kCFStringEncodingUTF8
, FALSE
);
3920 CFArrayAppendValue(dnsNames
, string
);
3923 return errSecInvalidCertificate
;
3926 return errSecSuccess
;
3929 /* Return true if the passed in string matches the
3930 Preferred name syntax from sections 2.3.1. in RFC 1035.
3931 With the added check that we disallow empty dns names.
3932 Also in order to support wildcard DNSNames we allow for the '*'
3933 character anywhere in a dns component where we currently allow
3936 <domain> ::= <subdomain> | " "
3938 <subdomain> ::= <label> | <subdomain> "." <label>
3940 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
3942 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
3944 <let-dig-hyp> ::= <let-dig> | "-"
3946 <let-dig> ::= <letter> | <digit>
3948 <letter> ::= any one of the 52 alphabetic characters A through Z in
3949 upper case and a through z in lower case
3951 <digit> ::= any one of the ten digits 0 through 9
3953 static bool isDNSName(CFStringRef string
) {
3954 CFStringInlineBuffer buf
;
3955 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
3956 /* From RFC 1035 2.3.4. Size limits:
3957 labels 63 octets or less
3958 names 255 octets or less */
3959 require_quiet(length
<= 255, notDNS
);
3960 CFRange range
= { 0, length
};
3961 CFStringInitInlineBuffer(string
, &buf
, range
);
3965 kDNSStateAfterAlpha
,
3966 kDNSStateAfterDigit
,
3968 } state
= kDNSStateInital
;
3970 bool nonAlpha
= false;
3971 for (ix
= 0; ix
< length
; ++ix
) {
3972 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
3975 require_quiet(labelLength
<= 64 &&
3976 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
3978 state
= kDNSStateAfterDot
;
3981 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
3983 state
= kDNSStateAfterAlpha
;
3984 } else if ('0' <= ch
&& ch
<= '9') {
3986 /* The requirement for labels to start with a letter was
3987 dropped so we don't check this anymore. */
3988 require_quiet(state
== kDNSStateAfterAlpha
||
3989 state
== kDNSStateAfterDigit
||
3990 state
== kDNSStateAfterDash
, notDNS
);
3992 state
= kDNSStateAfterDigit
;
3994 } else if (ch
== '-') {
3995 require_quiet(state
== kDNSStateAfterAlpha
||
3996 state
== kDNSStateAfterDigit
||
3997 state
== kDNSStateAfterDash
, notDNS
);
3998 state
= kDNSStateAfterDash
;
4005 /* We don't allow a dns name to end in a dot, and we require the
4006 final name component to only have alphanumeric chars. */
4007 require_quiet(!nonAlpha
&& labelLength
<= 63 &&
4008 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4016 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4017 const DERItem
*value
, CFIndex rdnIX
) {
4018 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4019 if (DEROidCompare(type
, &oidCommonName
)) {
4020 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4023 if (isDNSName(string
)) {
4024 /* We found a common name that is formatted like a valid
4026 CFArrayAppendValue(dnsNames
, string
);
4030 return errSecInvalidCertificate
;
4033 return errSecSuccess
;
4036 /* Not everything returned by this function is going to be a proper DNS name,
4037 we also return the certificates common name entries from the subject,
4038 assuming they look like dns names as specified in RFC 1035. */
4039 CFArrayRef
SecCertificateCopyDNSNamesP(SecCertificateRefP certificate
) {
4040 /* These can exist in the subject alt name or in the subject. */
4041 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4042 0, &kCFTypeArrayCallBacks
);
4043 OSStatus status
= errSecSuccess
;
4044 if (certificate
->_subjectAltName
) {
4045 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4046 dnsNames
, appendDNSNamesFromGeneralNames
);
4048 /* RFC 2818 section 3.1. Server Identity
4050 If a subjectAltName extension of type dNSName is present, that MUST
4051 be used as the identity. Otherwise, the (most specific) Common Name
4052 field in the Subject field of the certificate MUST be used. Although
4053 the use of the Common Name is existing practice, it is deprecated and
4054 Certification Authorities are encouraged to use the dNSName instead.
4057 This implies that if we found 1 or more DNSNames in the
4058 subjectAltName, we should not use the Common Name of the subject as
4061 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4062 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4063 appendDNSNamesFromX501Name
);
4065 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4066 CFRelease(dnsNames
);
4072 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4073 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4074 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4075 if (gnType
== GNT_RFC822Name
) {
4076 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4077 generalName
->data
, generalName
->length
,
4078 kCFStringEncodingASCII
, FALSE
);
4080 CFArrayAppendValue(dnsNames
, string
);
4083 return errSecInvalidCertificate
;
4086 return errSecSuccess
;
4089 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4090 const DERItem
*value
, CFIndex rdnIX
) {
4091 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4092 if (DEROidCompare(type
, &oidEmailAddress
)) {
4093 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4096 CFArrayAppendValue(dnsNames
, string
);
4099 return errSecInvalidCertificate
;
4102 return errSecSuccess
;
4105 CFArrayRef
SecCertificateCopyRFC822NamesP(SecCertificateRefP certificate
) {
4106 /* These can exist in the subject alt name or in the subject. */
4107 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4108 0, &kCFTypeArrayCallBacks
);
4109 OSStatus status
= errSecSuccess
;
4110 if (certificate
->_subjectAltName
) {
4111 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4112 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4115 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4116 appendRFC822NamesFromX501Name
);
4118 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4119 CFRelease(rfc822Names
);
4125 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4126 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4127 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4128 if (DEROidCompare(type
, &oidCommonName
)) {
4129 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4132 CFArrayAppendValue(commonNames
, string
);
4135 return errSecInvalidCertificate
;
4138 return errSecSuccess
;
4141 CFArrayRef
SecCertificateCopyCommonNamesP(SecCertificateRefP certificate
) {
4142 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4143 0, &kCFTypeArrayCallBacks
);
4145 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4146 appendCommonNamesFromX501Name
);
4147 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4148 CFRelease(commonNames
);
4154 static OSStatus
appendOrganizationFromX501Name(void *context
,
4155 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4156 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4157 if (DEROidCompare(type
, &oidOrganizationName
)) {
4158 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4161 CFArrayAppendValue(organization
, string
);
4164 return errSecInvalidCertificate
;
4167 return errSecSuccess
;
4170 CFArrayRef
SecCertificateCopyOrganizationP(SecCertificateRefP certificate
) {
4171 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4172 0, &kCFTypeArrayCallBacks
);
4174 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4175 appendOrganizationFromX501Name
);
4176 if (status
|| CFArrayGetCount(organization
) == 0) {
4177 CFRelease(organization
);
4178 organization
= NULL
;
4180 return organization
;
4183 const SecCEBasicConstraints
*
4184 SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate
) {
4185 if (certificate
->_basicConstraints
.present
)
4186 return &certificate
->_basicConstraints
;
4191 const SecCEPolicyConstraints
*
4192 SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate
) {
4193 if (certificate
->_policyConstraints
.present
)
4194 return &certificate
->_policyConstraints
;
4200 SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate
) {
4201 return certificate
->_policyMappings
;
4204 const SecCECertificatePolicies
*
4205 SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate
) {
4206 if (certificate
->_certificatePolicies
.present
)
4207 return &certificate
->_certificatePolicies
;
4213 SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate
) {
4214 return certificate
->_inhibitAnyPolicySkipCerts
;
4217 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4218 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4219 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4220 if (gnType
== GNT_OtherName
) {
4222 DERReturn drtn
= DERParseSequenceContent(generalName
,
4223 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4225 require_noerr_quiet(drtn
, badDER
);
4226 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4228 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4229 &on
.value
, true), badDER
);
4230 CFArrayAppendValue(ntPrincipalNames
, string
);
4234 return errSecSuccess
;
4237 return errSecInvalidCertificate
;
4241 CFArrayRef
SecCertificateCopyNTPrincipalNamesP(SecCertificateRefP certificate
) {
4242 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4243 0, &kCFTypeArrayCallBacks
);
4244 OSStatus status
= errSecSuccess
;
4245 if (certificate
->_subjectAltName
) {
4246 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4247 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4249 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4250 CFRelease(ntPrincipalNames
);
4251 ntPrincipalNames
= NULL
;
4253 return ntPrincipalNames
;
4256 static OSStatus
appendToRFC2253String(void *context
,
4257 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4258 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4262 ST stateOrProvinceName
4264 OU organizationalUnitName
4266 STREET streetAddress
4270 /* Prepend a + if this is not the first RDN in an RDN set.
4271 Otherwise prepend a , if this is not the first RDN. */
4273 CFStringAppend(string
, CFSTR("+"));
4274 else if (CFStringGetLength(string
)) {
4275 CFStringAppend(string
, CFSTR(","));
4278 CFStringRef label
, oid
= NULL
;
4279 /* @@@ Consider changing this to a dictionary lookup keyed by the
4280 decimal representation. */
4281 #if 0 // represent all labels as oids
4282 if (DEROidCompare(type
, &oidCommonName
)) {
4283 label
= CFSTR("CN");
4284 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4286 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4287 label
= CFSTR("ST");
4288 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4290 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4291 label
= CFSTR("OU");
4292 } else if (DEROidCompare(type
, &oidCountryName
)) {
4295 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4296 label
= CFSTR("STREET");
4297 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4298 label
= CFSTR("DC");
4299 } else if (DEROidCompare(type
, &oidUserID
)) {
4300 label
= CFSTR("UID");
4305 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4308 CFStringAppend(string
, label
);
4309 CFStringAppend(string
, CFSTR("="));
4310 CFStringRef raw
= NULL
;
4312 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4315 /* Append raw to string while escaping:
4316 a space or "#" character occurring at the beginning of the string
4317 a space character occurring at the end of the string
4318 one of the characters ",", "+", """, "\", "<", ">" or ";"
4320 CFStringInlineBuffer buffer
;
4321 CFIndex ix
, length
= CFStringGetLength(raw
);
4322 CFRange range
= { 0, length
};
4323 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4324 for (ix
= 0; ix
< length
; ++ix
) {
4325 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4327 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4328 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4329 ch
== '<' || ch
== '>' || ch
== ';' ||
4330 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4331 (ch
== '#' && ix
== 0)) {
4332 UniChar chars
[] = { '\\', ch
};
4333 CFStringAppendCharacters(string
, chars
, 2);
4335 CFStringAppendCharacters(string
, &ch
, 1);
4340 /* Append the value in hex. */
4341 CFStringAppend(string
, CFSTR("#"));
4343 for (ix
= 0; ix
< value
->length
; ++ix
)
4344 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4349 return errSecSuccess
;
4352 CFStringRef
SecCertificateCopySubjectStringP(SecCertificateRefP certificate
) {
4353 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4354 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4355 if (status
|| CFStringGetLength(string
) == 0) {
4362 static OSStatus
appendToCompanyNameString(void *context
,
4363 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4364 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4365 if (CFStringGetLength(string
) != 0)
4366 return errSecSuccess
;
4368 if (!DEROidCompare(type
, &oidOrganizationName
))
4369 return errSecSuccess
;
4372 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4374 return errSecSuccess
;
4375 CFStringAppend(string
, raw
);
4378 return errSecSuccess
;
4381 CFStringRef
SecCertificateCopyCompanyNameP(SecCertificateRefP certificate
) {
4382 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4383 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4384 appendToCompanyNameString
);
4385 if (status
|| CFStringGetLength(string
) == 0) {
4392 CFDataRef
SecDERItemCopySequenceP(DERItem
*content
) {
4393 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4394 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4395 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4397 CFDataSetLength(sequence
, sequence_length
);
4398 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4399 *sequence_ptr
++ = 0x30; /* ASN1_CONSTR_SEQUENCE */
4400 require_noerr_quiet(DEREncodeLength(content
->length
,
4401 sequence_ptr
, &seq_len_length
), out
);
4402 sequence_ptr
+= seq_len_length
;
4403 memcpy(sequence_ptr
, content
->data
, content
->length
);
4406 CFReleaseSafe(sequence
);
4410 CFDataRef
SecCertificateCopyIssuerSequenceP(
4411 SecCertificateRefP certificate
) {
4412 return SecDERItemCopySequenceP(&certificate
->_issuer
);
4415 CFDataRef
SecCertificateCopySubjectSequenceP(
4416 SecCertificateRefP certificate
) {
4417 return SecDERItemCopySequenceP(&certificate
->_subject
);
4420 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithmP(
4421 SecCertificateRefP certificate
) {
4422 return &certificate
->_algId
;
4425 const DERItem
*SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate
) {
4426 return &certificate
->_pubKeyDER
;
4429 SecKeyRefP
SecCertificateCopyPublicKeyP(SecCertificateRefP certificate
) {
4430 SecKeyRefP publicKey
= NULL
;
4431 #warning implementation empty
4433 const DERAlgorithmId
*algId
=
4434 SecCertificateGetPublicKeyAlgorithmP(certificate
);
4435 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4436 if (DEROidCompare(&algId
->oid
, &oidRsa
)) {
4437 publicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
4438 keyData
->data
, keyData
->length
, kSecKeyEncodingPkcs1
);
4440 secdebug("cert", "Unsupported algorithm oid");
4447 CFDataRef
SecCertificateGetSHA1DigestP(SecCertificateRefP certificate
) {
4448 if (!certificate
->_sha1Digest
) {
4449 certificate
->_sha1Digest
=
4450 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4451 certificate
->_der
.data
, certificate
->_der
.length
);
4454 return certificate
->_sha1Digest
;
4457 CFDataRef
SecCertificateCopyIssuerSHA1DigestP(SecCertificateRefP certificate
) {
4458 CFDataRef digest
= NULL
;
4459 CFDataRef issuer
= SecCertificateCopyIssuerSequenceP(certificate
);
4461 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4462 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4468 CFDataRef
SecCertificateCopyPublicKeySHA1DigestP(SecCertificateRefP certificate
) {
4469 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4470 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4473 /* note: this function is exported with a non-P-suffix name.
4474 * since it doesn't accept or return a SecCertificateRefP type, this is OK for now.
4476 CFDataRef
SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator
,
4477 CFDataRef der_certificate
)
4479 CFDataRef result
= NULL
;
4480 SecCertificateRefP iosCertRef
= SecCertificateCreateWithDataP(allocator
, der_certificate
);
4481 if (NULL
== iosCertRef
)
4486 result
= SecCertificateCopyPublicKeySHA1DigestP(iosCertRef
);
4487 CFRelease(iosCertRef
);
4491 CFDataRef
SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate
) {
4492 if (!certificate
->_authorityKeyID
&&
4493 certificate
->_authorityKeyIdentifier
.length
) {
4494 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4495 certificate
->_authorityKeyIdentifier
.data
,
4496 certificate
->_authorityKeyIdentifier
.length
);
4499 return certificate
->_authorityKeyID
;
4502 CFDataRef
SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate
) {
4503 if (!certificate
->_subjectKeyID
&&
4504 certificate
->_subjectKeyIdentifier
.length
) {
4505 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4506 certificate
->_subjectKeyIdentifier
.data
,
4507 certificate
->_subjectKeyIdentifier
.length
);
4510 return certificate
->_subjectKeyID
;
4513 CFArrayRef
SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate
) {
4514 return certificate
->_crlDistributionPoints
;
4517 CFArrayRef
SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate
) {
4518 return certificate
->_ocspResponders
;
4521 CFArrayRef
SecCertificateGetCAIssuersP(SecCertificateRefP certificate
) {
4522 return certificate
->_caIssuers
;
4525 bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate
) {
4526 return certificate
->_subjectAltName
&&
4527 certificate
->_subjectAltName
->critical
;
4530 bool SecCertificateHasSubjectP(SecCertificateRefP certificate
) {
4531 /* Since the _subject field is the content of the subject and not the
4532 whole thing, we can simply check for a 0 length subject here. */
4533 return certificate
->_subject
.length
!= 0;
4536 bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate
) {
4537 return certificate
->_foundUnknownCriticalExtension
;
4540 /* Private API functions. */
4541 void SecCertificateShowP(SecCertificateRefP certificate
) {
4543 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4544 fprintf(stderr
, "\n");
4547 CFDictionaryRef
SecCertificateCopyAttributeDictionaryP(
4548 SecCertificateRefP certificate
) {
4549 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4550 CFNumberRef certificateType
, certificateEncoding
;
4551 CFStringRef label
, alias
;
4552 CFDataRef skid
, pubKeyDigest
, certData
;
4553 CFDictionaryRef dict
= NULL
;
4557 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4558 SInt32 ctv
= certificate
->_version
+ 1;
4559 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4560 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4561 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4562 certData
= SecCertificateCopyDataP(certificate
);
4563 skid
= SecCertificateGetSubjectKeyIDP(certificate
);
4564 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4565 certificate
->_pubKeyDER
.length
);
4567 /* We still need to figure out how to deal with multi valued attributes. */
4568 alias
= SecCertificateCopyRFC822NamesP(certificate
);
4569 label
= SecCertificateCopySubjectSummary(certificate
);
4575 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4576 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4577 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4579 DICT_ADDPAIR(kSecAttrLabel
, label
);
4581 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4582 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4583 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4584 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4586 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4587 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4588 DICT_ADDPAIR(kSecValueData
, certData
);
4589 dict
= DICT_CREATE(allocator
);
4591 CFReleaseSafe(label
);
4592 CFReleaseSafe(pubKeyDigest
);
4593 CFReleaseSafe(certData
);
4594 CFReleaseSafe(certificateEncoding
);
4595 CFReleaseSafe(certificateType
);
4600 SecCertificateRefP
SecCertificateCreateFromAttributeDictionaryP(
4601 CFDictionaryRef refAttributes
) {
4602 /* @@@ Support having an allocator in refAttributes. */
4603 CFAllocatorRef allocator
= NULL
;
4604 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4605 return SecCertificateCreateWithDataP(allocator
, data
);
4608 bool SecCertificateIsSelfSignedCAP(SecCertificateRefP certificate
) {
4609 bool result
= false;
4610 SecKeyRefP publicKey
;
4611 require(publicKey
= SecCertificateCopyPublicKeyP(certificate
), out
);
4612 CFDataRef normalizedIssuer
=
4613 SecCertificateGetNormalizedIssuerContentP(certificate
);
4614 CFDataRef normalizedSubject
=
4615 SecCertificateGetNormalizedSubjectContentP(certificate
);
4616 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4617 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4619 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyIDP(certificate
);
4620 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyIDP(certificate
);
4621 if (authorityKeyID
) {
4622 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4625 if (SecCertificateVersionP(certificate
) >= 3) {
4626 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraintsP(certificate
);
4627 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4628 require_noerr_quiet(SecCertificateIsSignedByP(certificate
, publicKey
), out
);
4633 CFReleaseSafe(publicKey
);
4637 SecKeyUsage
SecCertificateGetKeyUsageP(SecCertificateRefP certificate
) {
4638 return certificate
->_keyUsage
;
4641 CFArrayRef
SecCertificateCopyExtendedKeyUsageP(SecCertificateRefP certificate
)
4643 CFMutableArrayRef extended_key_usage_oids
=
4644 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4645 require_quiet(extended_key_usage_oids
, out
);
4647 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4648 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4649 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4650 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4653 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4654 require_noerr_quiet(drtn
, out
);
4655 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4656 DERDecodedInfo currDecoded
;
4658 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4659 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4660 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4661 currDecoded
.content
.data
, currDecoded
.content
.length
);
4663 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4667 require_quiet(drtn
== DR_EndOfSequence
, out
);
4668 return extended_key_usage_oids
;
4672 CFReleaseSafe(extended_key_usage_oids
);
4676 SecCertificateRefP
SecCertificateCreateWithPEMP(CFAllocatorRef allocator
,
4677 CFDataRef pem_certificate
)
4679 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
4680 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
4681 uint8_t *base64_data
= NULL
;
4682 SecCertificateRefP cert
= NULL
;
4683 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
4684 //const size_t length = CFDataGetLength(pem_certificate);
4685 char *begin
= strstr((const char *)data
, begin_cert
);
4686 char *end
= strstr((const char *)data
, end_cert
);
4689 begin
+= sizeof(begin_cert
) - 1;
4690 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
4691 if (base64_length
) {
4692 require_quiet(base64_data
= calloc(1, base64_length
), out
);
4693 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
4694 cert
= SecCertificateCreateWithBytesP(kCFAllocatorDefault
, base64_data
, base64_length
);
4701 static void convertCertificateToCFData(const void *value
, void *context
) {
4702 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4703 SecCertificateRefP certificate
= (SecCertificateRefP
)value
;
4704 CFDataRef data
= SecCertificateCopyDataP(certificate
);
4705 CFArrayAppendValue(result
, data
);
4709 /* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
4710 CFArrayRef
SecCertificateArrayCopyDataArrayP(CFArrayRef certificates
) {
4711 CFIndex count
= CFArrayGetCount(certificates
);
4712 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
4713 CFRange all_certs
= { 0, count
};
4714 CFArrayApplyFunction(certificates
, all_certs
, convertCertificateToCFData
, result
);
4718 /* AUDIT[securityd](done):
4719 value (ok) is an element in a caller provided array.
4721 static void convertCFDataToCertificate(const void *value
, void *context
) {
4722 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4723 CFDataRef data
= (CFDataRef
)value
;
4724 if (data
&& CFGetTypeID(data
) == CFDataGetTypeID()) {
4725 SecCertificateRefP certificate
= SecCertificateCreateWithDataP(kCFAllocatorDefault
, data
);
4727 CFArrayAppendValue(result
, certificate
);
4728 CFRelease(certificate
);
4733 /* AUDIT[securityd](done):
4734 certificates (ok) is a caller provided array, only its cf type has
4737 CFArrayRef
SecCertificateDataArrayCopyArrayP(CFArrayRef certificates
) {
4738 CFIndex count
= CFArrayGetCount(certificates
);
4739 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
4740 CFRange all_certs
= { 0, count
};
4741 CFArrayApplyFunction(certificates
, all_certs
, convertCFDataToCertificate
, result
);