2 * Copyright (c) 2002-2007 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@
27 #include <security_keychain/Certificate.h>
28 #include <security_cdsa_utilities/Schema.h>
29 #include <Security/oidscert.h>
30 #include <Security/oidsattr.h>
31 #include <Security/SecCertificate.h>
32 #include <Security/SecCertificatePriv.h>
33 #include <security_cdsa_client/cspclient.h>
34 #include <security_keychain/KeyItem.h>
35 #include <security_keychain/KCCursor.h>
37 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
38 //#include "CLFieldsCommon.h"
41 using namespace KeychainCore
;
44 Certificate::clForType(CSSM_CERT_TYPE type
)
46 return CL(gGuidAppleX509CL
);
49 Certificate::Certificate(const CSSM_DATA
&data
, CSSM_CERT_TYPE type
, CSSM_CERT_ENCODING encoding
) :
50 ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE
, reinterpret_cast<SecKeychainAttributeList
*>(NULL
), UInt32(data
.Length
), reinterpret_cast<const void *>(data
.Data
)),
51 mHaveTypeAndEncoding(true),
57 mV1SubjectPublicKeyCStructValue(NULL
),
58 mV1SubjectNameCStructValue(NULL
),
59 mV1IssuerNameCStructValue(NULL
)
61 if (data
.Length
== 0 || data
.Data
== NULL
)
62 MacOSError::throwMe(paramErr
);
65 // db item constructor
66 Certificate::Certificate(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
) :
67 ItemImpl(keychain
, primaryKey
, uniqueId
),
68 mHaveTypeAndEncoding(false),
72 mV1SubjectPublicKeyCStructValue(NULL
),
73 mV1SubjectNameCStructValue(NULL
),
74 mV1IssuerNameCStructValue(NULL
)
80 Certificate
* Certificate::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
82 Certificate
* c
= new Certificate(keychain
, primaryKey
, uniqueId
);
83 keychain
->addItem(primaryKey
, c
);
89 Certificate
* Certificate::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
91 Certificate
* c
= new Certificate(keychain
, primaryKey
);
92 keychain
->addItem(primaryKey
, c
);
99 // PrimaryKey item constructor
100 Certificate::Certificate(const Keychain
&keychain
, const PrimaryKey
&primaryKey
) :
101 ItemImpl(keychain
, primaryKey
),
102 mHaveTypeAndEncoding(false),
106 mV1SubjectPublicKeyCStructValue(NULL
),
107 mV1SubjectNameCStructValue(NULL
),
108 mV1IssuerNameCStructValue(NULL
)
110 // @@@ In this case we don't know the type...
113 Certificate::Certificate(Certificate
&certificate
) :
114 ItemImpl(certificate
),
115 mHaveTypeAndEncoding(certificate
.mHaveTypeAndEncoding
),
116 mPopulated(false /* certificate.mPopulated */),
117 mType(certificate
.mType
),
118 mEncoding(certificate
.mEncoding
),
119 mCL(certificate
.mCL
),
121 mV1SubjectPublicKeyCStructValue(NULL
),
122 mV1SubjectNameCStructValue(NULL
),
123 mV1IssuerNameCStructValue(NULL
)
127 Certificate::~Certificate() throw()
129 if (mV1SubjectPublicKeyCStructValue
)
130 releaseFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct
, mV1SubjectPublicKeyCStructValue
);
132 if (mCertHandle
&& mCL
)
133 CSSM_CL_CertAbortCache(mCL
->handle(), mCertHandle
);
135 if (mV1SubjectNameCStructValue
)
136 releaseFieldValue(CSSMOID_X509V1SubjectNameCStruct
, mV1SubjectNameCStructValue
);
138 if (mV1IssuerNameCStructValue
)
139 releaseFieldValue(CSSMOID_X509V1IssuerNameCStruct
, mV1IssuerNameCStructValue
);
143 Certificate::certHandle()
145 StLock
<Mutex
>_(mMutex
);
146 const CSSM_DATA
*cert
= &data();
149 if (CSSM_RETURN retval
= CSSM_CL_CertCache(clHandle(), cert
, &mCertHandle
))
150 CssmError::throwMe(retval
);
156 /* Return a zero terminated list of CSSM_DATA_PTR's with the values of the field specified by field. Caller must call releaseFieldValues to free the storage allocated by this call. */
158 Certificate::copyFieldValues(const CSSM_OID
&field
)
160 StLock
<Mutex
>_(mMutex
);
161 CSSM_CL_HANDLE clh
= clHandle();
162 CSSM_DATA_PTR fieldValue
, *fieldValues
;
163 CSSM_HANDLE resultsHandle
= 0;
164 uint32 numberOfFields
= 0;
167 result
= CSSM_CL_CertGetFirstCachedFieldValue(clh
, certHandle(), &field
, &resultsHandle
, &numberOfFields
, &fieldValue
);
170 if (result
== CSSMERR_CL_NO_FIELD_VALUES
)
173 CssmError::throwMe(result
);
176 fieldValues
= new CSSM_DATA_PTR
[numberOfFields
+ 1];
177 fieldValues
[0] = fieldValue
;
178 fieldValues
[numberOfFields
] = NULL
;
180 for (uint32 value
= 1; value
< numberOfFields
; ++value
)
182 CSSM_RETURN cresult
= CSSM_CL_CertGetNextCachedFieldValue(clh
, resultsHandle
, &fieldValues
[value
]);
185 fieldValues
[value
] = NULL
;
187 break; // No point in continuing really.
191 CSSM_CL_CertAbortQuery(clh
, resultsHandle
);
195 releaseFieldValues(field
, fieldValues
);
196 CssmError::throwMe(result
);
203 Certificate::releaseFieldValues(const CSSM_OID
&field
, CSSM_DATA_PTR
*fieldValues
)
205 StLock
<Mutex
>_(mMutex
);
208 CSSM_CL_HANDLE clh
= clHandle();
210 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
211 CSSM_CL_FreeFieldValue(clh
, &field
, fieldValues
[ix
]);
213 delete[] fieldValues
;
218 Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO
&info
, const CSSM_OID
&field
)
220 StLock
<Mutex
>_(mMutex
);
221 CSSM_DATA_PTR
*fieldValues
= copyFieldValues(field
);
224 CssmDbAttributeData
&anAttr
= mDbAttributes
->add(info
);
225 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
226 anAttr
.add(*fieldValues
[ix
], *mDbAttributes
);
228 releaseFieldValues(field
, fieldValues
);
233 Certificate::addSubjectKeyIdentifier()
235 StLock
<Mutex
>_(mMutex
);
236 const CSSM_DB_ATTRIBUTE_INFO
&info
= Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr
);
237 const CSSM_OID
&field
= CSSMOID_SubjectKeyIdentifier
;
239 CSSM_DATA_PTR
*fieldValues
= copyFieldValues(field
);
242 CssmDbAttributeData
&anAttr
= mDbAttributes
->add(info
);
243 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
245 const CSSM_X509_EXTENSION
*extension
= reinterpret_cast<const CSSM_X509_EXTENSION
*>(fieldValues
[ix
]->Data
);
246 if (extension
== NULL
|| fieldValues
[ix
]->Length
!= sizeof(CSSM_X509_EXTENSION
))
248 assert(extension
!= NULL
&& fieldValues
[ix
]->Length
== sizeof(CSSM_X509_EXTENSION
));
251 const CE_SubjectKeyID
*skid
= reinterpret_cast<CE_SubjectKeyID
*>(extension
->value
.parsedValue
);
254 assert(skid
!= NULL
);
257 anAttr
.add(*skid
, *mDbAttributes
);
260 releaseFieldValues(field
, fieldValues
);
264 /* Return a CSSM_DATA_PTR with the value of the first field specified by field. Caller must call releaseFieldValue to free the storage allocated by this call. */
266 Certificate::copyFirstFieldValue(const CSSM_OID
&field
)
268 StLock
<Mutex
>_(mMutex
);
269 CSSM_CL_HANDLE clh
= clHandle();
270 CSSM_DATA_PTR fieldValue
;
271 CSSM_HANDLE resultsHandle
= 0;
272 uint32 numberOfFields
= 0;
275 result
= CSSM_CL_CertGetFirstCachedFieldValue(clh
, certHandle(), &field
, &resultsHandle
, &numberOfFields
, &fieldValue
);
278 if (result
== CSSMERR_CL_NO_FIELD_VALUES
)
281 CssmError::throwMe(result
);
284 result
= CSSM_CL_CertAbortQuery(clh
, resultsHandle
);
288 releaseFieldValue(field
, fieldValue
);
289 CssmError::throwMe(result
);
296 Certificate::releaseFieldValue(const CSSM_OID
&field
, CSSM_DATA_PTR fieldValue
)
298 StLock
<Mutex
>_(mMutex
);
301 CSSM_CL_HANDLE clh
= clHandle();
302 CSSM_CL_FreeFieldValue(clh
, &field
, fieldValue
);
309 This method computes the keyIdentifier for the public key in the cert as
312 The keyIdentifier is composed of the 160-bit SHA-1 hash of the
313 value of the BIT STRING subjectPublicKey (excluding the tag,
314 length, and number of unused bits).
317 Certificate::publicKeyHash()
319 StLock
<Mutex
>_(mMutex
);
320 if (mPublicKeyHash
.Length
)
321 return mPublicKeyHash
;
323 CSSM_DATA_PTR keyPtr
= copyFirstFieldValue(CSSMOID_CSSMKeyStruct
);
324 if (keyPtr
&& keyPtr
->Data
)
326 CssmClient::CSP
csp(gGuidAppleCSP
);
327 CssmClient::PassThrough
passThrough(csp
);
328 CSSM_KEY
*key
= reinterpret_cast<CSSM_KEY
*>(keyPtr
->Data
);
332 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
333 * associated key blob.
334 * Key is specified in CSSM_CSP_CreatePassThroughContext.
335 * Hash is allocated by the CSP, in the App's memory, and returned
337 passThrough
.key(key
);
338 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
339 cssmData
= reinterpret_cast<CssmData
*>(outData
);
341 assert(cssmData
->Length
<= sizeof(mPublicKeyHashBytes
));
342 mPublicKeyHash
.Data
= mPublicKeyHashBytes
;
343 mPublicKeyHash
.Length
= cssmData
->Length
;
344 memcpy(mPublicKeyHash
.Data
, cssmData
->Data
, cssmData
->Length
);
345 csp
.allocator().free(cssmData
->Data
);
346 csp
.allocator().free(cssmData
);
349 releaseFieldValue(CSSMOID_CSSMKeyStruct
, keyPtr
);
351 return mPublicKeyHash
;
355 Certificate::subjectKeyIdentifier()
357 StLock
<Mutex
>_(mMutex
);
358 if (mSubjectKeyID
.Length
)
359 return mSubjectKeyID
;
361 CSSM_DATA_PTR fieldValue
= copyFirstFieldValue(CSSMOID_SubjectKeyIdentifier
);
362 if (fieldValue
&& fieldValue
->Data
&& fieldValue
->Length
== sizeof(CSSM_X509_EXTENSION
))
364 const CSSM_X509_EXTENSION
*extension
= reinterpret_cast<const CSSM_X509_EXTENSION
*>(fieldValue
->Data
);
365 const CE_SubjectKeyID
*skid
= reinterpret_cast<CE_SubjectKeyID
*>(extension
->value
.parsedValue
); // CSSM_DATA
367 if (skid
->Length
<= sizeof(mSubjectKeyIDBytes
))
369 mSubjectKeyID
.Data
= mSubjectKeyIDBytes
;
370 mSubjectKeyID
.Length
= skid
->Length
;
371 memcpy(mSubjectKeyID
.Data
, skid
->Data
, skid
->Length
);
374 mSubjectKeyID
.Length
= 0;
377 releaseFieldValue(CSSMOID_SubjectKeyIdentifier
, fieldValue
);
379 return mSubjectKeyID
;
384 * Given an CSSM_X509_NAME, Find the first (or last) name/value pair with
385 * a printable value which matches the specified OID (e.g., CSSMOID_CommonName).
386 * Returns the CFString-style encoding associated with name component's BER tag.
387 * Returns NULL if none found.
389 static const CSSM_DATA
*
391 const CSSM_X509_NAME
&x509Name
,
392 const CSSM_OID
*tvpType
, // NULL means "any printable field"
393 bool lastInstance
, // false means return first instance
394 CFStringBuiltInEncodings
*encoding
) // RETURNED
396 const CSSM_DATA
*result
= NULL
;
397 for(uint32 rdnDex
=0; rdnDex
<x509Name
.numberOfRDNs
; rdnDex
++) {
398 const CSSM_X509_RDN
*rdnPtr
=
399 &x509Name
.RelativeDistinguishedName
[rdnDex
];
400 for(uint32 tvpDex
=0; tvpDex
<rdnPtr
->numberOfPairs
; tvpDex
++) {
401 const CSSM_X509_TYPE_VALUE_PAIR
*tvpPtr
=
402 &rdnPtr
->AttributeTypeAndValue
[tvpDex
];
404 /* type/value pair: match caller's specified type? */
405 if(tvpType
!= NULL
&& tvpType
->Data
!= NULL
) {
406 if(tvpPtr
->type
.Length
!= tvpType
->Length
) {
409 if(memcmp(tvpPtr
->type
.Data
, tvpType
->Data
, tvpType
->Length
)) {
410 /* If we don't have a match but the requested OID is CSSMOID_UserID,
411 * look for a matching X.500 UserID OID: (0.9.2342.19200300.100.1.1) */
412 const char cssm_userid_oid
[] = { 0x09,0x49,0x86,0x49,0x1f,0x12,0x8c,0xe4,0x81,0x81 };
413 const char x500_userid_oid
[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
414 if(!(tvpType
->Length
== sizeof(cssm_userid_oid
) &&
415 !memcmp(tvpPtr
->type
.Data
, x500_userid_oid
, sizeof(x500_userid_oid
)) &&
416 !memcmp(tvpType
->Data
, cssm_userid_oid
, sizeof(cssm_userid_oid
)))) {
423 switch(tvpPtr
->valueType
) {
424 case BER_TAG_PRINTABLE_STRING
:
425 case BER_TAG_IA5_STRING
:
426 *encoding
= kCFStringEncodingASCII
;
427 result
= &tvpPtr
->value
;
429 case BER_TAG_PKIX_UTF8_STRING
:
430 case BER_TAG_GENERAL_STRING
:
431 case BER_TAG_PKIX_UNIVERSAL_STRING
:
432 *encoding
= kCFStringEncodingUTF8
;
433 result
= &tvpPtr
->value
;
435 case BER_TAG_T61_STRING
:
436 case BER_TAG_VIDEOTEX_STRING
:
437 case BER_TAG_ISO646_STRING
:
438 *encoding
= kCFStringEncodingISOLatin1
;
439 result
= &tvpPtr
->value
;
441 case BER_TAG_PKIX_BMP_STRING
:
442 *encoding
= kCFStringEncodingUnicode
;
443 result
= &tvpPtr
->value
;
449 /* if we found a result and we want the first instance, return it now. */
450 if(result
&& !lastInstance
) {
454 } /* for each pair */
457 /* result is NULL if no printable component was found */
462 * Infer printable label for a given CSSM_X509_NAME. Returns NULL
463 * if no appropriate printable name found. Returns the CFString-style
464 * encoding associated with name component's BER tag. Also optionally
465 * returns Description component and its encoding if present and the
466 * returned name component was one we explicitly requested.
468 static const CSSM_DATA
*inferLabelFromX509Name(
469 const CSSM_X509_NAME
*x509Name
,
470 CFStringBuiltInEncodings
*encoding
, // RETURNED
471 const CSSM_DATA
**description
, // optionally RETURNED
472 CFStringBuiltInEncodings
*descrEncoding
) // RETURNED if description != NULL
474 const CSSM_DATA
*printValue
;
475 if(description
!= NULL
) {
476 *description
= findPrintableField(*x509Name
, &CSSMOID_Description
, false, descrEncoding
);
479 * Search order (take the first one found with a printable
482 * -- Organizational Unit
485 * -- field of any kind
487 printValue
= findPrintableField(*x509Name
, &CSSMOID_CommonName
, true, encoding
);
488 if(printValue
!= NULL
) {
491 printValue
= findPrintableField(*x509Name
, &CSSMOID_OrganizationalUnitName
, false, encoding
);
492 if(printValue
!= NULL
) {
495 printValue
= findPrintableField(*x509Name
, &CSSMOID_OrganizationName
, false, encoding
);
496 if(printValue
!= NULL
) {
499 printValue
= findPrintableField(*x509Name
, &CSSMOID_EmailAddress
, false, encoding
);
500 if(printValue
!= NULL
) {
503 /* if we didn't get one of the above names, don't append description */
504 if(description
!= NULL
) {
508 return findPrintableField(*x509Name
, NULL
, false, encoding
);
512 * Infer printable label for a given an CSSM_X509_NAME. Returns NULL
513 * if no appropriate printable name found.
515 const CSSM_DATA
*SecInferLabelFromX509Name(
516 const CSSM_X509_NAME
*x509Name
)
518 /* callees of this routine don't care about the encoding */
519 CFStringBuiltInEncodings encoding
= kCFStringEncodingASCII
;
520 return inferLabelFromX509Name(x509Name
, &encoding
, NULL
, &encoding
);
525 Certificate::inferLabel(bool addLabel
, CFStringRef
*rtnString
)
527 StLock
<Mutex
>_(mMutex
);
528 // Set PrintName and optionally the Alias attribute for this certificate, based on the
529 // X509 SubjectAltName and SubjectName.
530 const CSSM_DATA
*printName
= NULL
;
531 const CSSM_DATA
*description
= NULL
;
532 std::vector
<CssmData
> emailAddresses
;
534 CssmAutoData
printPlusDescr(Allocator::standard());
535 CssmData printPlusDescData
;
536 CFStringBuiltInEncodings printEncoding
= kCFStringEncodingUTF8
;
537 CFStringBuiltInEncodings descrEncoding
= kCFStringEncodingUTF8
;
539 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
540 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
541 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
542 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
543 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
545 getEmailAddresses(sanValues
, snValue
, emailAddresses
);
547 if (snValue
&& snValue
->Data
)
549 const CSSM_X509_NAME
&x509Name
= *(const CSSM_X509_NAME
*)snValue
->Data
;
550 printName
= inferLabelFromX509Name(&x509Name
, &printEncoding
,
551 &description
, &descrEncoding
);
554 /* Don't ever use "Thawte Freemail Member" as the label for a cert. Instead force
555 a fall back on the email address. */
556 const char tfm
[] = "Thawte Freemail Member";
557 if ( (printName
->Length
== sizeof(tfm
) - 1) &&
558 !memcmp(printName
->Data
, tfm
, sizeof(tfm
) - 1)) {
564 /* Do a check to see if a '\0' was at the end of printName and strip it. */
565 CssmData cleanedUpPrintName
;
566 if((printName
!= NULL
) &&
567 (printName
->Length
!= 0) &&
568 (printEncoding
!= kCFStringEncodingISOLatin1
) &&
569 (printEncoding
!= kCFStringEncodingUnicode
) &&
570 (printName
->Data
[printName
->Length
- 1] == '\0')) {
571 cleanedUpPrintName
.Data
= printName
->Data
;
572 cleanedUpPrintName
.Length
= printName
->Length
- 1;
573 printName
= &cleanedUpPrintName
;
576 if((printName
!= NULL
) && (description
!= NULL
) && (description
->Length
!= 0))
579 * Munge Print Name (which in this case is the CommonName) and Description
580 * together with the Description in parentheses. We convert from whatever
581 * format Print Name and Description are in to UTF8 here.
583 CFRef
<CFMutableStringRef
> combo(CFStringCreateMutable(NULL
, 0));
584 CFRef
<CFStringRef
> cfPrint(CFStringCreateWithBytes(NULL
, printName
->Data
,
585 (CFIndex
)printName
->Length
, printEncoding
, true));
586 CssmData
cleanedUpDescr(description
->Data
, description
->Length
);
587 if ((cleanedUpDescr
.Data
[cleanedUpDescr
.Length
- 1] == '\0') &&
588 (descrEncoding
!= kCFStringEncodingISOLatin1
) &&
589 (descrEncoding
!= kCFStringEncodingUnicode
)) {
590 cleanedUpDescr
.Length
--;
592 CFRef
<CFStringRef
> cfDesc(CFStringCreateWithBytes(NULL
, cleanedUpDescr
.Data
,
593 (CFIndex
)cleanedUpDescr
.Length
, descrEncoding
, true));
594 CFStringAppend(combo
, cfPrint
);
595 CFStringAppendCString(combo
, " (", kCFStringEncodingASCII
);
596 CFStringAppend(combo
, cfDesc
);
597 CFStringAppendCString(combo
, ")", kCFStringEncodingASCII
);
598 CFRef
<CFDataRef
> comboData(CFStringCreateExternalRepresentation(NULL
, combo
,
599 kCFStringEncodingUTF8
, 0));
600 printPlusDescr
.copy(CFDataGetBytePtr(comboData
), CFDataGetLength(comboData
));
601 printPlusDescData
= printPlusDescr
;
602 printName
= &printPlusDescData
;
603 printEncoding
= kCFStringEncodingUTF8
;
606 if (printName
== NULL
)
608 /* If the we couldn't find a label use the emailAddress instead. */
609 if (!emailAddresses
.empty())
610 printName
= &emailAddresses
[0];
614 puntData
.Data
= (uint8
*)"X509 Certificate";
615 puntData
.Length
= 16;
616 printName
= &puntData
;
618 printEncoding
= kCFStringEncodingUTF8
;
621 /* If we couldn't find an email address just use the printName which might be the url or something else useful. */
622 if (emailAddresses
.empty())
623 emailAddresses
.push_back(CssmData::overlay(*printName
));
625 /* What do we do with the inferred label - return it or add it mDbAttributes? */
628 mDbAttributes
->add(Schema::kX509CertificatePrintName
, *printName
);
629 CssmDbAttributeData
&attrData
= mDbAttributes
->add(Schema::kX509CertificateAlias
);
631 /* Add the email addresses to attrData and normalize them. */
633 for (std::vector
<CssmData
>::const_iterator it
= emailAddresses
.begin(); it
!= emailAddresses
.end(); ++it
, ++ix
)
635 /* Add the email address using the allocator from mDbAttributes. */
636 attrData
.add(*it
, *mDbAttributes
);
637 /* Normalize the emailAddresses in place since attrData already copied it. */
638 normalizeEmailAddress(attrData
.Value
[ix
]);
644 CFStringBuiltInEncodings testEncoding
= printEncoding
;
645 if(testEncoding
== kCFStringEncodingISOLatin1
) {
647 testEncoding
= kCFStringEncodingUTF8
;
649 *rtnString
= CFStringCreateWithBytes(NULL
, printName
->Data
,
650 (CFIndex
)printName
->Length
, testEncoding
, true);
651 if(*rtnString
== NULL
&& printEncoding
== kCFStringEncodingISOLatin1
) {
652 // string cannot be represented in UTF-8, fall back to ISO Latin 1
653 *rtnString
= CFStringCreateWithBytes(NULL
, printName
->Data
,
654 (CFIndex
)printName
->Length
, printEncoding
, true);
660 releaseFieldValue(snOid
, snValue
);
662 releaseFieldValues(sanOid
, sanValues
);
666 Certificate::populateAttributes()
668 StLock
<Mutex
>_(mMutex
);
672 addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr
), CSSMOID_X509V1SubjectName
);
673 addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr
), CSSMOID_X509V1IssuerName
);
674 addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr
), CSSMOID_X509V1SerialNumber
);
676 addSubjectKeyIdentifier();
678 if(!mHaveTypeAndEncoding
)
679 MacOSError::throwMe(errSecDataNotAvailable
); // @@@ Or some other error.
681 // Adjust mType based on the actual version of the cert.
682 CSSM_DATA_PTR versionPtr
= copyFirstFieldValue(CSSMOID_X509V1Version
);
683 if (versionPtr
&& versionPtr
->Data
&& versionPtr
->Length
== sizeof(uint32
))
685 mType
= CSSM_CERT_X_509v1
+ (*reinterpret_cast<uint32
*>(versionPtr
->Data
));
688 mType
= CSSM_CERT_X_509v1
;
690 releaseFieldValue(CSSMOID_X509V1Version
, versionPtr
);
692 mDbAttributes
->add(Schema::attributeInfo(kSecCertTypeItemAttr
), mType
);
693 mDbAttributes
->add(Schema::attributeInfo(kSecCertEncodingItemAttr
), mEncoding
);
694 mDbAttributes
->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr
), publicKeyHash());
703 StLock
<Mutex
>_(mMutex
);
704 CssmDataContainer
*data
= mData
.get();
705 if (!data
&& mKeychain
)
707 // Make sure mUniqueId is set.
709 CssmDataContainer _data
;
711 /* new data allocated by CSPDL, implicitly freed by CssmDataContainer */
712 mUniqueId
->get(NULL
, &_data
);
713 /* this saves a copy to be freed at destruction and to be passed to caller */
714 setData(_data
.length(), _data
.data());
718 // If the data hasn't been set we can't return it.
720 MacOSError::throwMe(errSecDataNotAvailable
);
728 StLock
<Mutex
>_(mMutex
);
729 if (!mHaveTypeAndEncoding
)
731 SecKeychainAttribute attr
;
732 attr
.tag
= kSecCertTypeItemAttr
;
734 attr
.length
= sizeof(mType
);
735 getAttribute(attr
, NULL
);
742 Certificate::encoding()
744 StLock
<Mutex
>_(mMutex
);
745 if (!mHaveTypeAndEncoding
)
747 SecKeychainAttribute attr
;
748 attr
.tag
= kSecCertEncodingItemAttr
;
749 attr
.data
= &mEncoding
;
750 attr
.length
= sizeof(mEncoding
);
751 getAttribute(attr
, NULL
);
757 const CSSM_X509_ALGORITHM_IDENTIFIER_PTR
758 Certificate::algorithmID()
760 StLock
<Mutex
>_(mMutex
);
761 if (!mV1SubjectPublicKeyCStructValue
)
762 mV1SubjectPublicKeyCStructValue
= copyFirstFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct
);
764 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*info
= (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*)mV1SubjectPublicKeyCStructValue
->Data
;
765 CSSM_X509_ALGORITHM_IDENTIFIER
*algid
= &info
->algorithm
;
770 Certificate::commonName()
772 StLock
<Mutex
>_(mMutex
);
773 return distinguishedName(&CSSMOID_X509V1SubjectNameCStruct
, &CSSMOID_CommonName
);
777 Certificate::distinguishedName(const CSSM_OID
*sourceOid
, const CSSM_OID
*componentOid
)
779 StLock
<Mutex
>_(mMutex
);
780 CFStringRef rtnString
= NULL
;
781 CSSM_DATA_PTR fieldValue
= copyFirstFieldValue(*sourceOid
);
782 CSSM_X509_NAME_PTR x509Name
= (CSSM_X509_NAME_PTR
)fieldValue
->Data
;
783 const CSSM_DATA
*printValue
= NULL
;
784 CFStringBuiltInEncodings encoding
;
786 if (fieldValue
&& fieldValue
->Data
)
787 printValue
= findPrintableField(*x509Name
, componentOid
, true, &encoding
);
790 rtnString
= CFStringCreateWithBytes(NULL
, printValue
->Data
,
791 CFIndex(printValue
->Length
), encoding
, true);
793 releaseFieldValue(*sourceOid
, fieldValue
);
800 * Return a CFString containing the first email addresses for this certificate, based on the
801 * X509 SubjectAltName and SubjectName.
804 Certificate::copyFirstEmailAddress()
806 StLock
<Mutex
>_(mMutex
);
807 CFStringRef rtnString
;
809 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
810 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
811 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
812 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
813 std::vector
<CssmData
> emailAddresses
;
815 getEmailAddresses(sanValues
, snValue
, emailAddresses
);
816 if (emailAddresses
.empty())
820 /* Encoding is kCFStringEncodingUTF8 since the string is either
821 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
822 rtnString
= CFStringCreateWithBytes(NULL
, emailAddresses
[0].Data
,
823 (CFIndex
)emailAddresses
[0].Length
, kCFStringEncodingUTF8
, true);
828 releaseFieldValue(snOid
, snValue
);
830 releaseFieldValues(sanOid
, sanValues
);
836 * Return a CFArray containing the email addresses for this certificate, based on the
837 * X509 SubjectAltName and SubjectName.
840 Certificate::copyEmailAddresses()
842 StLock
<Mutex
>_(mMutex
);
843 CFMutableArrayRef array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
844 std::vector
<CssmData
> emailAddresses
;
846 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
847 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
848 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
850 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
851 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
853 getEmailAddresses(sanValues
, snValue
, emailAddresses
);
855 for (std::vector
<CssmData
>::const_iterator it
= emailAddresses
.begin(); it
!= emailAddresses
.end(); ++it
)
857 /* Encoding is kCFStringEncodingUTF8 since the string is either
858 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
859 CFStringRef string
= CFStringCreateWithBytes(NULL
, it
->Data
, static_cast<CFIndex
>(it
->Length
), kCFStringEncodingUTF8
, true);
860 CFArrayAppendValue(array
, string
);
866 releaseFieldValue(snOid
, snValue
);
868 releaseFieldValues(sanOid
, sanValues
);
873 const CSSM_X509_NAME_PTR
874 Certificate::subjectName()
876 StLock
<Mutex
>_(mMutex
);
877 if (!mV1SubjectNameCStructValue
)
878 if ((mV1SubjectNameCStructValue
= copyFirstFieldValue(CSSMOID_X509V1SubjectNameCStruct
)) == NULL
)
881 return (const CSSM_X509_NAME_PTR
)mV1SubjectNameCStructValue
->Data
;
884 const CSSM_X509_NAME_PTR
885 Certificate::issuerName()
887 StLock
<Mutex
>_(mMutex
);
888 if (!mV1IssuerNameCStructValue
)
889 if ((mV1IssuerNameCStructValue
= copyFirstFieldValue(CSSMOID_X509V1IssuerNameCStruct
)) == NULL
)
892 return (const CSSM_X509_NAME_PTR
)mV1IssuerNameCStructValue
->Data
;
896 Certificate::clHandle()
898 StLock
<Mutex
>_(mMutex
);
900 mCL
= clForType(type());
902 return mCL
->handle();
906 Certificate::operator < (Certificate
&other
)
908 // Certificates in different keychains are considered equal if data is equal
909 // Note that the Identity '<' operator relies on this assumption.
910 return data() < other
.data();
914 Certificate::operator == (Certificate
&other
)
916 // Certificates in different keychains are considered equal if data is equal
917 // Note that the Identity '==' operator relies on this assumption.
918 return data() == other
.data();
922 Certificate::equal(SecCFObject
&other
)
924 return (*this) == (Certificate
&)other
;
928 Certificate::update()
934 Certificate::copyTo(const Keychain
&keychain
, Access
*newAccess
)
936 StLock
<Mutex
>_(mMutex
);
937 /* Certs can't have access controls. */
939 MacOSError::throwMe(errSecNoAccessForItem
);
941 Item
item(new Certificate(data(), type(), encoding()));
947 Certificate::didModify()
952 Certificate::add(Keychain
&keychain
)
954 StLock
<Mutex
>_(mMutex
);
955 // If we already have a Keychain we can't be added.
957 MacOSError::throwMe(errSecDuplicateItem
);
959 populateAttributes();
961 CSSM_DB_RECORDTYPE recordType
= mDbAttributes
->recordType();
963 Db
db(keychain
->database());
964 // add the item to the (regular) db
967 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
969 catch (const CssmError
&e
)
971 if (e
.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE
)
974 // Create the cert relation and try again.
975 db
->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
976 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
977 Schema::X509CertificateSchemaAttributeCount
,
978 Schema::X509CertificateSchemaAttributeList
,
979 Schema::X509CertificateSchemaIndexCount
,
980 Schema::X509CertificateSchemaIndexList
);
981 keychain
->keychainSchema()->didCreateRelation(
982 CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
983 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
984 Schema::X509CertificateSchemaAttributeCount
,
985 Schema::X509CertificateSchemaAttributeList
,
986 Schema::X509CertificateSchemaIndexCount
,
987 Schema::X509CertificateSchemaIndexList
);
989 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
992 mPrimaryKey
= keychain
->makePrimaryKey(recordType
, mUniqueId
);
993 mKeychain
= keychain
;
999 Certificate::publicKey()
1001 StLock
<Mutex
>_(mMutex
);
1002 SecPointer
<KeyItem
> keyItem
;
1003 // Return a CSSM_DATA_PTR with the value of the first field specified by field.
1004 // Caller must call releaseFieldValue to free the storage allocated by this call.
1005 // call OSStatus SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey); to retrieve
1007 CSSM_DATA_PTR keyPtr
= copyFirstFieldValue(CSSMOID_CSSMKeyStruct
);
1008 if (keyPtr
&& keyPtr
->Data
)
1010 CssmClient::CSP
csp(gGuidAppleCSP
);
1011 CssmKey
*cssmKey
= reinterpret_cast<CssmKey
*>(keyPtr
->Data
);
1012 CssmClient::Key
key(csp
, *cssmKey
);
1013 keyItem
= new KeyItem(key
);
1014 // Clear out KeyData since KeyItem() takes over ownership of the key, and we don't want it getting released.
1015 cssmKey
->KeyData
.Data
= NULL
;
1016 cssmKey
->KeyData
.Length
= 0;
1019 releaseFieldValue(CSSMOID_CSSMKeyStruct
, keyPtr
);
1024 // This function "borrowed" from the X509 CL, which is (currently) linked into
1025 // the Security.framework as a built-in plugin.
1026 extern "C" bool getField_normRDN_NSS (
1027 const CSSM_DATA
&derName
,
1028 uint32
&numFields
, // RETURNED (if successful, 0 or 1)
1029 CssmOwnedData
&fieldValue
); // RETURNED
1032 Certificate::cursorForIssuerAndSN(const StorageManager::KeychainList
&keychains
, const CssmData
&issuer
, const CssmData
&serialNumber
)
1034 CssmAutoData
fieldValue(Allocator::standard(Allocator::normal
));
1037 // We need to decode issuer, normalize it, then re-encode it
1038 if (!getField_normRDN_NSS(issuer
, numFields
, fieldValue
))
1039 MacOSError::throwMe(errSecDataNotAvailable
);
1041 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
1042 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
1043 cursor
->conjunctive(CSSM_DB_AND
);
1044 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateIssuer
, fieldValue
.get());
1045 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateSerialNumber
, serialNumber
);
1051 Certificate::cursorForIssuerAndSN_CF(const StorageManager::KeychainList
&keychains
, CFDataRef issuer
, CFDataRef serialNumber
)
1053 // This assumes a normalized issuer
1054 CSSM_DATA issuerCSSM
, serialNumberCSSM
;
1056 issuerCSSM
.Length
= CFDataGetLength(issuer
);
1057 issuerCSSM
.Data
= const_cast<uint8
*>(CFDataGetBytePtr(issuer
));
1059 serialNumberCSSM
.Length
= CFDataGetLength(serialNumber
);
1060 serialNumberCSSM
.Data
= const_cast<uint8
*>(CFDataGetBytePtr(serialNumber
));
1062 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
1063 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
1064 cursor
->conjunctive(CSSM_DB_AND
);
1065 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateIssuer
, issuerCSSM
);
1066 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateSerialNumber
, serialNumberCSSM
);
1072 Certificate::cursorForSubjectKeyID(const StorageManager::KeychainList
&keychains
, const CssmData
&subjectKeyID
)
1074 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
1075 cursor
->conjunctive(CSSM_DB_AND
);
1076 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateSubjectKeyIdentifier
, subjectKeyID
);
1082 Certificate::cursorForEmail(const StorageManager::KeychainList
&keychains
, const char *emailAddress
)
1084 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
1087 cursor
->conjunctive(CSSM_DB_AND
);
1088 CssmSelectionPredicate
&pred
= cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateAlias
, emailAddress
);
1089 /* Normalize the emailAddresses in place since cursor already copied it. */
1090 normalizeEmailAddress(pred
.Attribute
.Value
[0]);
1096 SecPointer
<Certificate
>
1097 Certificate::findInKeychain(const StorageManager::KeychainList
&keychains
)
1099 StLock
<Mutex
>_(mMutex
);
1100 const CSSM_OID
&issuerOid
= CSSMOID_X509V1IssuerName
;
1101 CSSM_DATA_PTR issuerPtr
= copyFirstFieldValue(issuerOid
);
1102 CssmData
issuer(issuerPtr
->Data
, issuerPtr
->Length
);
1104 const CSSM_OID
&serialOid
= CSSMOID_X509V1SerialNumber
;
1105 CSSM_DATA_PTR serialPtr
= copyFirstFieldValue(serialOid
);
1106 CssmData
serial(serialPtr
->Data
, serialPtr
->Length
);
1108 SecPointer
<Certificate
> foundCert
= NULL
;
1110 foundCert
= findByIssuerAndSN(keychains
, issuer
, serial
);
1115 releaseFieldValue(issuerOid
, issuerPtr
);
1116 releaseFieldValue(serialOid
, serialPtr
);
1121 SecPointer
<Certificate
>
1122 Certificate::findByIssuerAndSN(const StorageManager::KeychainList
&keychains
, const CssmData
&issuer
, const CssmData
&serialNumber
)
1125 if (!cursorForIssuerAndSN(keychains
, issuer
, serialNumber
)->next(item
))
1126 CssmError::throwMe(errSecItemNotFound
);
1128 return static_cast<Certificate
*>(&*item
);
1131 SecPointer
<Certificate
>
1132 Certificate::findBySubjectKeyID(const StorageManager::KeychainList
&keychains
, const CssmData
&subjectKeyID
)
1135 if (!cursorForSubjectKeyID(keychains
, subjectKeyID
)->next(item
))
1136 CssmError::throwMe(errSecItemNotFound
);
1138 return static_cast<Certificate
*>(&*item
);
1141 SecPointer
<Certificate
>
1142 Certificate::findByEmail(const StorageManager::KeychainList
&keychains
, const char *emailAddress
)
1145 if (!cursorForEmail(keychains
, emailAddress
)->next(item
))
1146 CssmError::throwMe(errSecItemNotFound
);
1148 return static_cast<Certificate
*>(&*item
);
1151 /* Normalize emailAddresses in place. */
1153 Certificate::normalizeEmailAddress(CSSM_DATA
&emailAddress
)
1155 /* Do a check to see if a '\0' was at the end of emailAddress and strip it. */
1156 if (emailAddress
.Length
&& emailAddress
.Data
[emailAddress
.Length
- 1] == '\0')
1157 emailAddress
.Length
--;
1158 bool foundAt
= false;
1159 for (uint32 ix
= 0; ix
< emailAddress
.Length
; ++ix
)
1161 uint8 ch
= emailAddress
.Data
[ix
];
1164 if ('A' <= ch
&& ch
<= 'Z')
1165 emailAddress
.Data
[ix
] = ch
+ 'a' - 'A';
1173 Certificate::getEmailAddresses(CSSM_DATA_PTR
*sanValues
, CSSM_DATA_PTR snValue
, std::vector
<CssmData
> &emailAddresses
)
1175 // Get the email addresses for this certificate, based on the
1176 // X509 SubjectAltName and SubjectName.
1178 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
1181 for (CSSM_DATA_PTR
*sanIx
= sanValues
; *sanIx
; ++sanIx
)
1183 CSSM_DATA_PTR sanValue
= *sanIx
;
1184 if (sanValue
&& sanValue
->Data
)
1186 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)sanValue
->Data
;
1187 CE_GeneralNames
*parsedValue
= (CE_GeneralNames
*)cssmExt
->value
.parsedValue
;
1189 /* Grab all the values that are of type GNT_RFC822Name. */
1190 for (uint32 i
= 0; i
< parsedValue
->numNames
; ++i
)
1192 if (parsedValue
->generalName
[i
].nameType
== GNT_RFC822Name
)
1194 if (parsedValue
->generalName
[i
].berEncoded
) // can't handle this
1197 emailAddresses
.push_back(CssmData::overlay(parsedValue
->generalName
[i
].name
));
1204 if (emailAddresses
.empty() && snValue
&& snValue
->Data
)
1206 const CSSM_X509_NAME
&x509Name
= *(const CSSM_X509_NAME
*)snValue
->Data
;
1207 for (uint32 rdnDex
= 0; rdnDex
< x509Name
.numberOfRDNs
; rdnDex
++)
1209 const CSSM_X509_RDN
*rdnPtr
=
1210 &x509Name
.RelativeDistinguishedName
[rdnDex
];
1211 for (uint32 tvpDex
= 0; tvpDex
< rdnPtr
->numberOfPairs
; tvpDex
++)
1213 const CSSM_X509_TYPE_VALUE_PAIR
*tvpPtr
=
1214 &rdnPtr
->AttributeTypeAndValue
[tvpDex
];
1216 /* type/value pair: match caller's specified type? */
1217 if (((tvpPtr
->type
.Length
!= CSSMOID_EmailAddress
.Length
) ||
1218 memcmp(tvpPtr
->type
.Data
, CSSMOID_EmailAddress
.Data
, CSSMOID_EmailAddress
.Length
))) {
1223 switch (tvpPtr
->valueType
)
1225 case BER_TAG_PRINTABLE_STRING
:
1226 case BER_TAG_IA5_STRING
:
1227 case BER_TAG_T61_STRING
:
1228 case BER_TAG_PKIX_UTF8_STRING
:
1230 emailAddresses
.push_back(CssmData::overlay(tvpPtr
->value
));
1235 } /* for each pair */
1236 } /* for each RDN */
1240 void Certificate::willRead()
1242 populateAttributes();
1245 Boolean
Certificate::isSelfSigned()
1247 StLock
<Mutex
>_(mMutex
);
1248 CSSM_DATA_PTR issuer
= NULL
;
1249 CSSM_DATA_PTR subject
= NULL
;
1250 OSStatus ortn
= noErr
;
1251 Boolean brtn
= false;
1253 issuer
= copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd
);
1254 subject
= copyFirstFieldValue(CSSMOID_X509V1SubjectNameStd
);
1255 if((issuer
== NULL
) || (subject
== NULL
)) {
1258 else if((issuer
->Length
== subject
->Length
) &&
1259 !memcmp(issuer
->Data
, subject
->Data
, issuer
->Length
)) {
1263 /* names match: verify signature */
1265 CSSM_DATA certData
= data();
1266 crtn
= CSSM_CL_CertVerify(clHandle(), 0,
1267 &certData
, &certData
, NULL
, 0);
1273 releaseFieldValue(CSSMOID_X509V1IssuerNameStd
, issuer
);
1276 releaseFieldValue(CSSMOID_X509V1SubjectNameStd
, subject
);
1279 MacOSError::throwMe(ortn
);