2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
21 #include <Security/Certificate.h>
22 #include <Security/Schema.h>
23 #include <Security/oidscert.h>
24 #include <Security/oidsattr.h>
25 #include <Security/SecCertificate.h>
26 #include <Security/SecCertificatePriv.h>
27 #include <Security/cspclient.h>
28 #include <Security/KeyItem.h>
29 #include <Security/KCCursor.h>
31 #include "CLFieldsCommon.h"
34 using namespace KeychainCore
;
37 Certificate::clForType(CSSM_CERT_TYPE type
)
39 return CL(gGuidAppleX509CL
);
42 Certificate::Certificate(const CSSM_DATA
&data
, CSSM_CERT_TYPE type
, CSSM_CERT_ENCODING encoding
) :
43 ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE
, reinterpret_cast<SecKeychainAttributeList
*>(NULL
), UInt32(data
.Length
), reinterpret_cast<const void *>(data
.Data
)),
44 mHaveTypeAndEncoding(true),
49 mV1SubjectPublicKeyCStructValue(NULL
)
53 // db item contstructor
54 Certificate::Certificate(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
) :
55 ItemImpl(keychain
, primaryKey
, uniqueId
),
56 mHaveTypeAndEncoding(false),
59 mV1SubjectPublicKeyCStructValue(NULL
)
63 // PrimaryKey item contstructor
64 Certificate::Certificate(const Keychain
&keychain
, const PrimaryKey
&primaryKey
) :
65 ItemImpl(keychain
, primaryKey
),
66 mHaveTypeAndEncoding(false),
69 mV1SubjectPublicKeyCStructValue(NULL
)
71 // @@@ In this case we don't know the type...
74 Certificate::Certificate(Certificate
&certificate
) :
75 ItemImpl(certificate
),
76 mHaveTypeAndEncoding(certificate
.mHaveTypeAndEncoding
),
77 mType(certificate
.mType
),
78 mEncoding(certificate
.mEncoding
),
81 mV1SubjectPublicKeyCStructValue(NULL
)
85 Certificate::~Certificate() throw()
87 if (mV1SubjectPublicKeyCStructValue
)
88 releaseFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct
, mV1SubjectPublicKeyCStructValue
);
90 if (mCertHandle
&& mCL
)
91 CSSM_CL_CertAbortCache(mCL
->handle(), mCertHandle
);
95 Certificate::certHandle()
97 const CSSM_DATA
*cert
= &data();
100 if (CSSM_RETURN retval
= CSSM_CL_CertCache(clHandle(), cert
, &mCertHandle
))
101 CssmError::throwMe(retval
);
107 /* 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. */
109 Certificate::copyFieldValues(const CSSM_OID
&field
)
111 CSSM_CL_HANDLE clh
= clHandle();
112 CSSM_DATA_PTR fieldValue
, *fieldValues
;
113 CSSM_HANDLE resultsHandle
= 0;
114 uint32 numberOfFields
= 0;
117 result
= CSSM_CL_CertGetFirstCachedFieldValue(clh
, certHandle(), &field
, &resultsHandle
, &numberOfFields
, &fieldValue
);
120 if (result
== CSSMERR_CL_NO_FIELD_VALUES
)
123 CssmError::throwMe(result
);
126 fieldValues
= new CSSM_DATA_PTR
[numberOfFields
+ 1];
127 fieldValues
[0] = fieldValue
;
128 fieldValues
[numberOfFields
] = NULL
;
130 for (uint32 value
= 1; value
< numberOfFields
; ++value
)
132 CSSM_RETURN cresult
= CSSM_CL_CertGetNextCachedFieldValue(clh
, resultsHandle
, &fieldValues
[value
]);
135 fieldValues
[value
] = NULL
;
137 break; // No point in continuing really.
143 releaseFieldValues(field
, fieldValues
);
144 CssmError::throwMe(result
);
151 Certificate::releaseFieldValues(const CSSM_OID
&field
, CSSM_DATA_PTR
*fieldValues
)
155 CSSM_CL_HANDLE clh
= clHandle();
157 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
158 CSSM_CL_FreeFieldValue(clh
, &field
, fieldValues
[ix
]);
160 delete[] fieldValues
;
165 Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO
&info
, const CSSM_OID
&field
)
167 CSSM_DATA_PTR
*fieldValues
= copyFieldValues(field
);
170 CssmDbAttributeData
&anAttr
= mDbAttributes
->add(info
);
171 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
172 anAttr
.add(*fieldValues
[ix
], *mDbAttributes
);
174 releaseFieldValues(field
, fieldValues
);
178 /* 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. */
180 Certificate::copyFirstFieldValue(const CSSM_OID
&field
)
182 CSSM_CL_HANDLE clh
= clHandle();
183 CSSM_DATA_PTR fieldValue
;
184 CSSM_HANDLE resultsHandle
= 0;
185 uint32 numberOfFields
= 0;
188 result
= CSSM_CL_CertGetFirstCachedFieldValue(clh
, certHandle(), &field
, &resultsHandle
, &numberOfFields
, &fieldValue
);
191 if (result
== CSSMERR_CL_NO_FIELD_VALUES
)
194 CssmError::throwMe(result
);
197 result
= CSSM_CL_CertAbortQuery(clh
, resultsHandle
);
201 releaseFieldValue(field
, fieldValue
);
202 CssmError::throwMe(result
);
209 Certificate::releaseFieldValue(const CSSM_OID
&field
, CSSM_DATA_PTR fieldValue
)
213 CSSM_CL_HANDLE clh
= clHandle();
214 CSSM_CL_FreeFieldValue(clh
, &field
, fieldValue
);
221 This method computes the keyIdentifier for the public key in the cert as
224 The keyIdentifier is composed of the 160-bit SHA-1 hash of the
225 value of the BIT STRING subjectPublicKey (excluding the tag,
226 length, and number of unused bits).
229 Certificate::publicKeyHash()
231 if (mPublicKeyHash
.Length
)
232 return mPublicKeyHash
;
234 CSSM_DATA_PTR keyPtr
= copyFirstFieldValue(CSSMOID_CSSMKeyStruct
);
235 if (keyPtr
&& keyPtr
->Data
)
237 CssmClient::CSP
csp(gGuidAppleCSP
);
238 CssmClient::PassThrough
passThrough(csp
);
239 CSSM_KEY
*key
= reinterpret_cast<CSSM_KEY
*>(keyPtr
->Data
);
243 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
244 * associated key blob.
245 * Key is specified in CSSM_CSP_CreatePassThroughContext.
246 * Hash is allocated by the CSP, in the App's memory, and returned
248 passThrough
.key(key
);
249 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
250 cssmData
= reinterpret_cast<CssmData
*>(outData
);
252 assert(cssmData
->Length
<= sizeof(mPublicKeyHashBytes
));
253 mPublicKeyHash
.Data
= mPublicKeyHashBytes
;
254 mPublicKeyHash
.Length
= cssmData
->Length
;
255 memcpy(mPublicKeyHash
.Data
, cssmData
->Data
, cssmData
->Length
);
256 csp
.allocator().free(cssmData
->Data
);
257 csp
.allocator().free(cssmData
);
260 releaseFieldValue(CSSMOID_CSSMKeyStruct
, keyPtr
);
262 return mPublicKeyHash
;
266 * Given an CSSM_X509_NAME, Find the first name/value pair with
267 * a printable value which matches the specified OID (e.g., CSSMOID_CommonName).
268 * Returns NULL if none found.
270 static const CSSM_DATA
*
272 const CSSM_X509_NAME
&x509Name
,
273 const CSSM_OID
*tvpType
) // NULL means "any printable field"
275 for(uint32 rdnDex
=0; rdnDex
<x509Name
.numberOfRDNs
; rdnDex
++) {
276 const CSSM_X509_RDN
*rdnPtr
=
277 &x509Name
.RelativeDistinguishedName
[rdnDex
];
278 for(uint32 tvpDex
=0; tvpDex
<rdnPtr
->numberOfPairs
; tvpDex
++) {
279 const CSSM_X509_TYPE_VALUE_PAIR
*tvpPtr
=
280 &rdnPtr
->AttributeTypeAndValue
[tvpDex
];
282 /* type/value pair: match caller's specified type? */
283 if((tvpType
!= NULL
) &&
284 ((tvpPtr
->type
.Length
!= tvpType
->Length
) ||
285 memcmp(tvpPtr
->type
.Data
, tvpType
->Data
, tvpType
->Length
))) {
290 switch(tvpPtr
->valueType
) {
291 case BER_TAG_PRINTABLE_STRING
:
292 case BER_TAG_IA5_STRING
:
293 case BER_TAG_T61_STRING
:
294 case BER_TAG_PKIX_UTF8_STRING
:
296 return &tvpPtr
->value
;
300 } /* for each pair */
303 /* no printable component of specified type found */
308 * Infer printable label for a given an CSSM_X509_NAME. Returns NULL
309 * if no appropriate printable name found.
311 const CSSM_DATA
*SecInferLabelFromX509Name(
312 const CSSM_X509_NAME
*x509Name
)
314 const CSSM_DATA
*printValue
;
316 * Search order (take the first one found with a printable
319 * -- Orgnaizational Unit
321 * -- field of any kind
323 printValue
= findPrintableField(*x509Name
, &CSSMOID_CommonName
);
324 if(printValue
!= NULL
) {
327 printValue
= findPrintableField(*x509Name
, &CSSMOID_OrganizationalUnitName
);
328 if(printValue
!= NULL
) {
331 printValue
= findPrintableField(*x509Name
, &CSSMOID_OrganizationName
);
332 if(printValue
!= NULL
) {
336 return findPrintableField(*x509Name
, NULL
);
340 Certificate::inferLabel(bool addLabel
, CFStringRef
*rtnString
)
342 // Set PrintName and optionally the Alias attribute for this certificate, based on the
343 // X509 SubjectAltName and SubjectName.
344 const CSSM_DATA
*printName
= NULL
;
345 std::vector
<CssmData
> emailAddresses
;
348 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
349 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
350 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
351 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
352 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
354 getEmailAddresses(sanValues
, snValue
, emailAddresses
);
356 if (snValue
&& snValue
->Data
)
358 const CSSM_X509_NAME
&x509Name
= *(const CSSM_X509_NAME
*)snValue
->Data
;
359 printName
= SecInferLabelFromX509Name(&x509Name
);
362 if (printName
== NULL
)
364 /* If the we couldn't find a label use the emailAddress instead. */
365 if (!emailAddresses
.empty())
366 printName
= &emailAddresses
[0];
370 puntData
.Data
= (uint8
*)"X509 Certificate";
371 puntData
.Length
= 16;
372 printName
= &puntData
;
376 /* If we couldn't find an email address just use the printName which might be the url or something else useful. */
377 if (emailAddresses
.empty())
378 emailAddresses
.push_back(CssmData::overlay(*printName
));
380 /* Do a check to see if a '\0' was at the end of printName and strip it. */
381 CssmData
cleanedUpPrintName(printName
->Data
, printName
->Length
);
382 if (cleanedUpPrintName
.Length
&& cleanedUpPrintName
.Data
[cleanedUpPrintName
.Length
- 1] == '\0')
383 cleanedUpPrintName
.Length
--;
385 /* What do we do with the inferred label - return it or add it mDbAttributes? */
388 mDbAttributes
->add(Schema::kX509CertificatePrintName
, cleanedUpPrintName
);
389 CssmDbAttributeData
&attrData
= mDbAttributes
->add(Schema::kX509CertificateAlias
);
391 /* Add the email addresses to attrData and normalize them. */
393 for (std::vector
<CssmData
>::const_iterator it
= emailAddresses
.begin(); it
!= emailAddresses
.end(); ++it
, ++ix
)
395 /* Add the email address using the allocator from mDbAttributes. */
396 attrData
.add(*it
, *mDbAttributes
);
397 /* Normalize the emailAddresses in place since attrData already copied it. */
398 normalizeEmailAddress(attrData
.Value
[ix
]);
404 /* Encoding is kCFStringEncodingUTF8 since the string is either
405 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
406 *rtnString
= CFStringCreateWithBytes(NULL
, cleanedUpPrintName
.Data
,
407 (CFIndex
)cleanedUpPrintName
.Length
, kCFStringEncodingUTF8
, true);
412 releaseFieldValue(snOid
, snValue
);
414 releaseFieldValues(sanOid
, sanValues
);
418 Certificate::populateAttributes()
420 addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr
), CSSMOID_X509V1SubjectName
);
421 addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr
), CSSMOID_X509V1IssuerName
);
422 addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr
), CSSMOID_X509V1SerialNumber
);
424 addParsedAttribute(Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr
), CSSMOID_SubjectKeyIdentifier
);
426 if(!mHaveTypeAndEncoding
)
427 MacOSError::throwMe(errSecDataNotAvailable
); // @@@ Or some other error.
429 // Adjust mType based on the actual version of the cert.
430 CSSM_DATA_PTR versionPtr
= copyFirstFieldValue(CSSMOID_X509V1Version
);
431 if (versionPtr
&& versionPtr
->Data
&& versionPtr
->Length
== sizeof(uint32
))
433 mType
= CSSM_CERT_X_509v1
+ (*reinterpret_cast<uint32
*>(versionPtr
->Data
));
436 mType
= CSSM_CERT_X_509v1
;
438 releaseFieldValue(CSSMOID_X509V1Version
, versionPtr
);
440 mDbAttributes
->add(Schema::attributeInfo(kSecCertTypeItemAttr
), mType
);
441 mDbAttributes
->add(Schema::attributeInfo(kSecCertEncodingItemAttr
), mEncoding
);
442 mDbAttributes
->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr
), publicKeyHash());
449 CssmDataContainer
*data
= mData
.get();
450 if (!data
&& mKeychain
)
452 // Make sure mUniqueId is set.
454 data
= new CssmDataContainer();
456 mUniqueId
->get(NULL
, data
);
459 // If the data hasn't been set we can't return it.
461 MacOSError::throwMe(errSecDataNotAvailable
);
469 if (!mHaveTypeAndEncoding
)
471 SecKeychainAttribute attr
;
472 attr
.tag
= kSecCertTypeItemAttr
;
474 attr
.length
= sizeof(mType
);
475 getAttribute(attr
, NULL
);
482 Certificate::encoding()
484 if (!mHaveTypeAndEncoding
)
486 SecKeychainAttribute attr
;
487 attr
.tag
= kSecCertEncodingItemAttr
;
488 attr
.data
= &mEncoding
;
489 attr
.length
= sizeof(mEncoding
);
490 getAttribute(attr
, NULL
);
496 const CSSM_X509_ALGORITHM_IDENTIFIER
*
497 Certificate::algorithmID()
499 if (!mV1SubjectPublicKeyCStructValue
)
500 mV1SubjectPublicKeyCStructValue
= copyFirstFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct
);
502 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*info
= (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*)mV1SubjectPublicKeyCStructValue
->Data
;
503 CSSM_X509_ALGORITHM_IDENTIFIER
*algid
= &info
->algorithm
;
508 Certificate::commonName()
510 CFStringRef rtnString
;
511 const CSSM_OID
&fieldOid
= CSSMOID_X509V1SubjectNameCStruct
;
512 CSSM_DATA_PTR fieldValue
= copyFirstFieldValue(fieldOid
);
513 CSSM_X509_NAME_PTR x509Name
= (CSSM_X509_NAME_PTR
)fieldValue
->Data
;
514 const CSSM_DATA
*printValue
= NULL
;
515 if (fieldValue
&& fieldValue
->Data
)
516 printValue
= findPrintableField(*x509Name
, &CSSMOID_CommonName
);
518 if (printValue
== NULL
)
522 /* Encoding is kCFStringEncodingUTF8 since the string is either
523 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
524 rtnString
= CFStringCreateWithBytes(NULL
, printValue
->Data
,
525 (CFIndex
)printValue
->Length
, kCFStringEncodingUTF8
, true);
528 releaseFieldValue(CSSMOID_X509V1SubjectNameCStruct
, fieldValue
);
534 * Return a CFString containing the first email addresses for this certificate, based on the
535 * X509 SubjectAltName and SubjectName.
538 Certificate::copyFirstEmailAddress()
540 CFStringRef rtnString
;
542 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
543 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
544 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
545 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
546 std::vector
<CssmData
> emailAddresses
;
548 getEmailAddresses(sanValues
, snValue
, emailAddresses
);
549 if (emailAddresses
.empty())
553 /* Encoding is kCFStringEncodingUTF8 since the string is either
554 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
555 rtnString
= CFStringCreateWithBytes(NULL
, emailAddresses
[0].Data
,
556 (CFIndex
)emailAddresses
[0].Length
, kCFStringEncodingUTF8
, true);
561 releaseFieldValue(snOid
, snValue
);
563 releaseFieldValues(sanOid
, sanValues
);
569 * Return a CFArray containing the email addresses for this certificate, based on the
570 * X509 SubjectAltName and SubjectName.
573 Certificate::copyEmailAddresses()
575 CFMutableArrayRef array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
576 std::vector
<CssmData
> emailAddresses
;
578 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
579 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
580 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
582 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
583 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
585 getEmailAddresses(sanValues
, snValue
, emailAddresses
);
587 for (std::vector
<CssmData
>::const_iterator it
= emailAddresses
.begin(); it
!= emailAddresses
.end(); ++it
)
589 /* Encoding is kCFStringEncodingUTF8 since the string is either
590 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
591 CFStringRef string
= CFStringCreateWithBytes(NULL
, it
->Data
, static_cast<CFIndex
>(it
->Length
), kCFStringEncodingUTF8
, true);
592 CFArrayAppendValue(array
, string
);
598 releaseFieldValue(snOid
, snValue
);
600 releaseFieldValues(sanOid
, sanValues
);
606 Certificate::getSubject(CSSM_X509_NAME
&outSubject
)
611 Certificate::getIssuer(CSSM_X509_NAME
&outName
)
616 Certificate::clHandle()
619 mCL
= clForType(type());
621 return mCL
->handle();
625 Certificate::operator < (Certificate
&other
)
627 return data() < other
.data();
631 Certificate::operator == (Certificate
&other
)
633 return data() == other
.data();
637 Certificate::update()
643 Certificate::copyTo(const Keychain
&keychain
, Access
*newAccess
)
645 /* Certs can't have access controls. */
647 MacOSError::throwMe(errSecNoAccessForItem
);
649 Item
item(new Certificate(data(), type(), encoding()));
655 Certificate::didModify()
660 Certificate::add(Keychain
&keychain
)
662 // If we already have a Keychain we can't be added.
664 MacOSError::throwMe(errSecDuplicateItem
);
666 populateAttributes();
668 CSSM_DB_RECORDTYPE recordType
= mDbAttributes
->recordType();
670 Db
db(keychain
->database());
671 // add the item to the (regular) db
674 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
676 catch (const CssmError
&e
)
678 if (e
.cssmError() != CSSMERR_DL_INVALID_RECORDTYPE
)
681 // Create the cert relation and try again.
682 db
->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE
, "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
683 Schema::X509CertificateSchemaAttributeCount
,
684 Schema::X509CertificateSchemaAttributeList
,
685 Schema::X509CertificateSchemaIndexCount
,
686 Schema::X509CertificateSchemaIndexList
);
687 keychain
->resetSchema();
689 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
692 mPrimaryKey
= keychain
->makePrimaryKey(recordType
, mUniqueId
);
693 mKeychain
= keychain
;
699 Certificate::publicKey()
701 SecPointer
<KeyItem
> keyItem
;
702 // Return a CSSM_DATA_PTR with the value of the first field specified by field.
703 // Caller must call releaseFieldValue to free the storage allocated by this call.
704 // call OSStatus SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey); to retrieve
706 CSSM_DATA_PTR keyPtr
= copyFirstFieldValue(CSSMOID_CSSMKeyStruct
);
707 if (keyPtr
&& keyPtr
->Data
)
709 CssmClient::CSP
csp(gGuidAppleCSP
);
710 CssmKey
*cssmKey
= reinterpret_cast<CssmKey
*>(keyPtr
->Data
);
711 CssmClient::Key
key(csp
, *cssmKey
);
712 keyItem
= new KeyItem(key
);
713 // Clear out KeyData since KeyItem() takes over ownership of the key, and we don't want it getting released.
714 cssmKey
->KeyData
.Data
= NULL
;
715 cssmKey
->KeyData
.Length
= 0;
718 releaseFieldValue(CSSMOID_CSSMKeyStruct
, keyPtr
);
724 Certificate::cursorForIssuerAndSN(const StorageManager::KeychainList
&keychains
, const CssmData
&issuer
, const CssmData
&serialNumber
)
726 CssmAutoData
fieldValue(CssmAllocator::standard(CssmAllocator::normal
));
729 // We need to decode issuer, normalize it, then re-encode it
730 if (!getField_normRDN_NSS(issuer
, numFields
, fieldValue
))
731 MacOSError::throwMe(errSecDataNotAvailable
);
733 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
734 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
735 cursor
->conjunctive(CSSM_DB_AND
);
736 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateIssuer
, fieldValue
.get());
737 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateSerialNumber
, serialNumber
);
743 Certificate::cursorForSubjectKeyID(const StorageManager::KeychainList
&keychains
, const CssmData
&subjectKeyID
)
745 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
746 cursor
->conjunctive(CSSM_DB_AND
);
747 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateSubjectKeyIdentifier
, subjectKeyID
);
753 Certificate::cursorForEmail(const StorageManager::KeychainList
&keychains
, const char *emailAddress
)
755 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
758 cursor
->conjunctive(CSSM_DB_AND
);
759 CssmSelectionPredicate
&pred
= cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateAlias
, emailAddress
);
760 /* Normalize the emailAddresses in place since cursor already copied it. */
761 normalizeEmailAddress(pred
.Attribute
.Value
[0]);
767 SecPointer
<Certificate
>
768 Certificate::findByIssuerAndSN(const StorageManager::KeychainList
&keychains
, const CssmData
&issuer
, const CssmData
&serialNumber
)
771 if (!cursorForIssuerAndSN(keychains
, issuer
, serialNumber
)->next(item
))
772 CssmError::throwMe(errSecItemNotFound
);
774 return static_cast<Certificate
*>(&*item
);
777 SecPointer
<Certificate
>
778 Certificate::findBySubjectKeyID(const StorageManager::KeychainList
&keychains
, const CssmData
&subjectKeyID
)
781 if (!cursorForSubjectKeyID(keychains
, subjectKeyID
)->next(item
))
782 CssmError::throwMe(errSecItemNotFound
);
784 return static_cast<Certificate
*>(&*item
);
787 SecPointer
<Certificate
>
788 Certificate::findByEmail(const StorageManager::KeychainList
&keychains
, const char *emailAddress
)
791 if (!cursorForEmail(keychains
, emailAddress
)->next(item
))
792 CssmError::throwMe(errSecItemNotFound
);
794 return static_cast<Certificate
*>(&*item
);
797 /* Normalize emailAddresses in place. */
799 Certificate::normalizeEmailAddress(CSSM_DATA
&emailAddress
)
801 /* Do a check to see if a '\0' was at the end of emailAddress and strip it. */
802 if (emailAddress
.Length
&& emailAddress
.Data
[emailAddress
.Length
- 1] == '\0')
803 emailAddress
.Length
--;
804 bool foundAt
= false;
805 for (uint32 ix
= 0; ix
< emailAddress
.Length
; ++ix
)
807 uint8 ch
= emailAddress
.Data
[ix
];
810 if ('A' <= ch
&& ch
<= 'Z')
811 emailAddress
.Data
[ix
] = ch
+ 'a' - 'A';
819 Certificate::getEmailAddresses(CSSM_DATA_PTR
*sanValues
, CSSM_DATA_PTR snValue
, std::vector
<CssmData
> &emailAddresses
)
821 // Get the email addresses for this certificate, based on the
822 // X509 SubjectAltName and SubjectName.
824 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
827 for (CSSM_DATA_PTR
*sanIx
= sanValues
; *sanIx
; ++sanIx
)
829 CSSM_DATA_PTR sanValue
= *sanIx
;
830 if (sanValue
&& sanValue
->Data
)
832 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)sanValue
->Data
;
833 CE_GeneralNames
*parsedValue
= (CE_GeneralNames
*)cssmExt
->value
.parsedValue
;
835 /* Grab all the values that are of type GNT_RFC822Name. */
836 for (uint32 i
= 0; i
< parsedValue
->numNames
; ++i
)
838 if (parsedValue
->generalName
[i
].nameType
== GNT_RFC822Name
)
840 if (parsedValue
->generalName
[i
].berEncoded
) // can't handle this
843 emailAddresses
.push_back(CssmData::overlay(parsedValue
->generalName
[i
].name
));
850 if (emailAddresses
.empty() && snValue
&& snValue
->Data
)
852 const CSSM_X509_NAME
&x509Name
= *(const CSSM_X509_NAME
*)snValue
->Data
;
853 for (uint32 rdnDex
= 0; rdnDex
< x509Name
.numberOfRDNs
; rdnDex
++)
855 const CSSM_X509_RDN
*rdnPtr
=
856 &x509Name
.RelativeDistinguishedName
[rdnDex
];
857 for (uint32 tvpDex
= 0; tvpDex
< rdnPtr
->numberOfPairs
; tvpDex
++)
859 const CSSM_X509_TYPE_VALUE_PAIR
*tvpPtr
=
860 &rdnPtr
->AttributeTypeAndValue
[tvpDex
];
862 /* type/value pair: match caller's specified type? */
863 if (((tvpPtr
->type
.Length
!= CSSMOID_EmailAddress
.Length
) ||
864 memcmp(tvpPtr
->type
.Data
, CSSMOID_EmailAddress
.Data
, CSSMOID_EmailAddress
.Length
))) {
869 switch (tvpPtr
->valueType
)
871 case BER_TAG_PRINTABLE_STRING
:
872 case BER_TAG_IA5_STRING
:
873 case BER_TAG_T61_STRING
:
874 case BER_TAG_PKIX_UTF8_STRING
:
876 emailAddresses
.push_back(CssmData::overlay(tvpPtr
->value
));
881 } /* for each pair */