]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_keychain/lib/Certificate.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / Certificate.cpp
diff --git a/Security/libsecurity_keychain/lib/Certificate.cpp b/Security/libsecurity_keychain/lib/Certificate.cpp
new file mode 100644 (file)
index 0000000..76d6c1c
--- /dev/null
@@ -0,0 +1,1363 @@
+/*
+ * Copyright (c) 2002-2007,2011-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// Certificate.cpp
+//
+#include <security_keychain/Certificate.h>
+#include <security_cdsa_utilities/Schema.h>
+#include <Security/oidscert.h>
+#include <Security/oidsattr.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecCertificatePriv.h>
+#include <security_cdsa_client/cspclient.h>
+#include <security_keychain/KeyItem.h>
+#include <security_keychain/KCCursor.h>
+#include <vector>
+#include <CommonCrypto/CommonDigestSPI.h>
+#include <SecBase.h>
+
+using namespace KeychainCore;
+
+CL
+Certificate::clForType(CSSM_CERT_TYPE type)
+{
+       return CL(gGuidAppleX509CL);
+}
+
+Certificate::Certificate(const CSSM_DATA &data, CSSM_CERT_TYPE type, CSSM_CERT_ENCODING encoding) :
+       ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE, reinterpret_cast<SecKeychainAttributeList *>(NULL), UInt32(data.Length), reinterpret_cast<const void *>(data.Data)),
+       mHaveTypeAndEncoding(true),
+       mPopulated(false),
+       mType(type),
+       mEncoding(encoding),
+       mCL(clForType(type)),
+       mCertHandle(0),
+       mV1SubjectPublicKeyCStructValue(NULL),
+       mV1SubjectNameCStructValue(NULL),
+       mV1IssuerNameCStructValue(NULL),
+       mSha1Hash(NULL)
+{
+       if (data.Length == 0 || data.Data == NULL)
+               MacOSError::throwMe(errSecParam);
+}
+
+// db item constructor
+Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
+       ItemImpl(keychain, primaryKey, uniqueId),
+       mHaveTypeAndEncoding(false),
+       mPopulated(false),
+       mCL(NULL),
+       mCertHandle(0),
+       mV1SubjectPublicKeyCStructValue(NULL),
+       mV1SubjectNameCStructValue(NULL),
+       mV1IssuerNameCStructValue(NULL),
+       mSha1Hash(NULL)
+{
+}
+
+
+
+Certificate* Certificate::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
+{
+       Certificate* c = new Certificate(keychain, primaryKey, uniqueId);
+       keychain->addItem(primaryKey, c);
+       return c;
+}
+
+
+
+Certificate* Certificate::make(const Keychain &keychain, const PrimaryKey &primaryKey)
+{
+       Certificate* c = new Certificate(keychain, primaryKey);
+       keychain->addItem(primaryKey, c);
+       return c;
+}
+
+
+
+
+// PrimaryKey item constructor
+Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey) :
+       ItemImpl(keychain, primaryKey),
+       mHaveTypeAndEncoding(false),
+       mPopulated(false),
+       mCL(NULL),
+       mCertHandle(0),
+       mV1SubjectPublicKeyCStructValue(NULL),
+       mV1SubjectNameCStructValue(NULL),
+       mV1IssuerNameCStructValue(NULL),
+       mSha1Hash(NULL)
+{
+       // @@@ In this case we don't know the type...
+}
+
+Certificate::Certificate(Certificate &certificate) :
+       ItemImpl(certificate),
+       mHaveTypeAndEncoding(certificate.mHaveTypeAndEncoding),
+       mPopulated(false /* certificate.mPopulated */),
+       mType(certificate.mType),
+       mEncoding(certificate.mEncoding),
+       mCL(certificate.mCL),
+       mCertHandle(0),
+       mV1SubjectPublicKeyCStructValue(NULL),
+       mV1SubjectNameCStructValue(NULL),
+       mV1IssuerNameCStructValue(NULL),
+       mSha1Hash(NULL)
+{
+}
+
+Certificate::~Certificate()
+try
+{
+       if (mV1SubjectPublicKeyCStructValue)
+               releaseFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct, mV1SubjectPublicKeyCStructValue);
+
+       if (mCertHandle && mCL)
+               CSSM_CL_CertAbortCache(mCL->handle(), mCertHandle);
+
+       if (mV1SubjectNameCStructValue)
+               releaseFieldValue(CSSMOID_X509V1SubjectNameCStruct, mV1SubjectNameCStructValue);
+
+       if (mV1IssuerNameCStructValue)
+               releaseFieldValue(CSSMOID_X509V1IssuerNameCStruct, mV1IssuerNameCStructValue);
+
+       if (mSha1Hash)
+               CFRelease(mSha1Hash);
+}
+catch (...)
+{
+}
+
+CSSM_HANDLE
+Certificate::certHandle()
+{
+       StLock<Mutex>_(mMutex);
+       const CSSM_DATA *cert = &data();
+       if (!mCertHandle)
+       {
+               if (CSSM_RETURN retval = CSSM_CL_CertCache(clHandle(), cert, &mCertHandle))
+                       CssmError::throwMe(retval);
+       }
+
+       return mCertHandle;
+}
+
+/* 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.  */
+CSSM_DATA_PTR *
+Certificate::copyFieldValues(const CSSM_OID &field)
+{
+       StLock<Mutex>_(mMutex);
+       CSSM_CL_HANDLE clh = clHandle();
+       CSSM_DATA_PTR fieldValue, *fieldValues;
+       CSSM_HANDLE resultsHandle = 0;
+       uint32 numberOfFields = 0;
+       CSSM_RETURN result;
+
+       result = CSSM_CL_CertGetFirstCachedFieldValue(clh, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
+       if (result)
+       {
+               if (result == CSSMERR_CL_NO_FIELD_VALUES)
+                       return NULL;
+
+               CssmError::throwMe(result);
+       }
+
+       fieldValues = new CSSM_DATA_PTR[numberOfFields + 1];
+       fieldValues[0] = fieldValue;
+       fieldValues[numberOfFields] = NULL;
+
+       for (uint32 value = 1; value < numberOfFields; ++value)
+       {
+               CSSM_RETURN cresult = CSSM_CL_CertGetNextCachedFieldValue(clh, resultsHandle, &fieldValues[value]);
+               if (cresult)
+               {
+                       fieldValues[value] = NULL;
+                       result = cresult;
+                       break; // No point in continuing really.
+               }
+       }
+
+       CSSM_CL_CertAbortQuery(clh, resultsHandle);
+
+       if (result)
+       {
+               releaseFieldValues(field, fieldValues);
+               CssmError::throwMe(result);
+       }
+
+       return fieldValues;
+}
+
+void
+Certificate::releaseFieldValues(const CSSM_OID &field, CSSM_DATA_PTR *fieldValues)
+{
+       StLock<Mutex>_(mMutex);
+       if (fieldValues)
+       {
+               CSSM_CL_HANDLE clh = clHandle();
+
+               for (int ix = 0; fieldValues[ix]; ++ix)
+                       CSSM_CL_FreeFieldValue(clh, &field, fieldValues[ix]);
+
+               delete[] fieldValues;
+       }
+}
+
+void
+Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO &info, const CSSM_OID &field)
+{
+       StLock<Mutex>_(mMutex);
+       CSSM_DATA_PTR *fieldValues = copyFieldValues(field);
+       if (fieldValues)
+       {
+               CssmDbAttributeData &anAttr = mDbAttributes->add(info);
+               for (int ix = 0; fieldValues[ix]; ++ix)
+                       anAttr.add(*fieldValues[ix], *mDbAttributes);
+
+               releaseFieldValues(field, fieldValues);
+       }
+}
+
+void
+Certificate::addSubjectKeyIdentifier()
+{
+       StLock<Mutex>_(mMutex);
+       const CSSM_DB_ATTRIBUTE_INFO &info = Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr);
+       const CSSM_OID &field = CSSMOID_SubjectKeyIdentifier;
+
+       CSSM_DATA_PTR *fieldValues = copyFieldValues(field);
+       if (fieldValues)
+       {
+               CssmDbAttributeData &anAttr = mDbAttributes->add(info);
+               for (int ix = 0; fieldValues[ix]; ++ix)
+               {
+                       const CSSM_X509_EXTENSION *extension = reinterpret_cast<const CSSM_X509_EXTENSION *>(fieldValues[ix]->Data);
+                       if (extension == NULL || fieldValues[ix]->Length != sizeof(CSSM_X509_EXTENSION))
+                       {
+                               assert(extension != NULL && fieldValues[ix]->Length == sizeof(CSSM_X509_EXTENSION));
+                               continue;
+                       }
+                       const CE_SubjectKeyID *skid = reinterpret_cast<CE_SubjectKeyID *>(extension->value.parsedValue);
+                       if (skid == NULL)
+                       {
+                               assert(skid != NULL);
+                               continue;
+                       }
+                       anAttr.add(*skid, *mDbAttributes);
+               }
+
+               releaseFieldValues(field, fieldValues);
+       }
+}
+
+/* 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.  */
+CSSM_DATA_PTR
+Certificate::copyFirstFieldValue(const CSSM_OID &field)
+{
+       StLock<Mutex>_(mMutex);
+       CSSM_CL_HANDLE clh = clHandle();
+       CSSM_DATA_PTR fieldValue;
+       CSSM_HANDLE resultsHandle = 0;
+       uint32 numberOfFields = 0;
+       CSSM_RETURN result;
+
+       result = CSSM_CL_CertGetFirstCachedFieldValue(clh, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
+       if (result)
+       {
+               if (result == CSSMERR_CL_NO_FIELD_VALUES)
+                       return NULL;
+
+               CssmError::throwMe(result);
+       }
+
+       result = CSSM_CL_CertAbortQuery(clh, resultsHandle);
+
+       if (result)
+       {
+               releaseFieldValue(field, fieldValue);
+               CssmError::throwMe(result);
+       }
+
+       return fieldValue;
+}
+
+void
+Certificate::releaseFieldValue(const CSSM_OID &field, CSSM_DATA_PTR fieldValue)
+{
+       StLock<Mutex>_(mMutex);
+       if (fieldValue)
+       {
+               CSSM_CL_HANDLE clh = clHandle();
+               CSSM_CL_FreeFieldValue(clh, &field, fieldValue);
+       }
+}
+
+
+
+/*
+       This method computes the keyIdentifier for the public key in the cert as
+       described below:
+
+      The keyIdentifier is composed of the 160-bit SHA-1 hash of the
+      value of the BIT STRING subjectPublicKey (excluding the tag,
+      length, and number of unused bits).
+*/
+const CssmData &
+Certificate::publicKeyHash()
+{
+       StLock<Mutex>_(mMutex);
+       if (mPublicKeyHash.Length)
+               return mPublicKeyHash;
+
+       CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
+       if (keyPtr && keyPtr->Data)
+       {
+               CssmClient::CSP csp(gGuidAppleCSP);
+               CssmClient::PassThrough passThrough(csp);
+               CSSM_KEY *key = reinterpret_cast<CSSM_KEY *>(keyPtr->Data);
+               void *outData;
+               CssmData *cssmData;
+
+               /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
+                * associated key blob.
+                * Key is specified in CSSM_CSP_CreatePassThroughContext.
+                * Hash is allocated by the CSP, in the App's memory, and returned
+                * in *outData. */
+               passThrough.key(key);
+               passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
+               cssmData = reinterpret_cast<CssmData *>(outData);
+
+               assert(cssmData->Length <= sizeof(mPublicKeyHashBytes));
+               mPublicKeyHash.Data = mPublicKeyHashBytes;
+               mPublicKeyHash.Length = cssmData->Length;
+               memcpy(mPublicKeyHash.Data, cssmData->Data, cssmData->Length);
+               csp.allocator().free(cssmData->Data);
+               csp.allocator().free(cssmData);
+       }
+
+       releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
+
+       return mPublicKeyHash;
+}
+
+const CssmData &
+Certificate::subjectKeyIdentifier()
+{
+       StLock<Mutex>_(mMutex);
+       if (mSubjectKeyID.Length)
+               return mSubjectKeyID;
+
+       CSSM_DATA_PTR fieldValue = copyFirstFieldValue(CSSMOID_SubjectKeyIdentifier);
+       if (fieldValue && fieldValue->Data && fieldValue->Length == sizeof(CSSM_X509_EXTENSION))
+       {
+               const CSSM_X509_EXTENSION *extension = reinterpret_cast<const CSSM_X509_EXTENSION *>(fieldValue->Data);
+               const CE_SubjectKeyID *skid = reinterpret_cast<CE_SubjectKeyID *>(extension->value.parsedValue);        // CSSM_DATA
+
+               if (skid->Length <= sizeof(mSubjectKeyIDBytes))
+               {
+                       mSubjectKeyID.Data = mSubjectKeyIDBytes;
+                       mSubjectKeyID.Length = skid->Length;
+                       memcpy(mSubjectKeyID.Data, skid->Data, skid->Length);
+               }
+               else
+                       mSubjectKeyID.Length = 0;
+       }
+
+       releaseFieldValue(CSSMOID_SubjectKeyIdentifier, fieldValue);
+
+       return mSubjectKeyID;
+}
+
+
+/*
+ * Given an CSSM_X509_NAME, Find the first (or last) name/value pair with
+ * a printable value which matches the specified OID (e.g., CSSMOID_CommonName).
+ * Returns the CFString-style encoding associated with name component's BER tag.
+ * Returns NULL if none found.
+ */
+static const CSSM_DATA *
+findPrintableField(
+       const CSSM_X509_NAME &x509Name,
+       const CSSM_OID *tvpType,                                // NULL means "any printable field"
+       bool lastInstance,                                              // false means return first instance
+       CFStringBuiltInEncodings *encoding)             // RETURNED
+{
+       const CSSM_DATA *result = NULL;
+       for(uint32 rdnDex=0; rdnDex<x509Name.numberOfRDNs; rdnDex++) {
+               const CSSM_X509_RDN *rdnPtr =
+                       &x509Name.RelativeDistinguishedName[rdnDex];
+               for(uint32 tvpDex=0; tvpDex<rdnPtr->numberOfPairs; tvpDex++) {
+                       const CSSM_X509_TYPE_VALUE_PAIR *tvpPtr =
+                               &rdnPtr->AttributeTypeAndValue[tvpDex];
+
+                       /* type/value pair: match caller's specified type? */
+                       if(tvpType != NULL && tvpType->Data != NULL) {
+                               if(tvpPtr->type.Length != tvpType->Length) {
+                                       continue;
+                               }
+                               if(memcmp(tvpPtr->type.Data, tvpType->Data, tvpType->Length)) {
+                                       /* If we don't have a match but the requested OID is CSSMOID_UserID,
+                                        * look for a matching X.500 UserID OID: (0.9.2342.19200300.100.1.1)  */
+                                       const char cssm_userid_oid[] = { 0x09,0x49,0x86,0x49,0x1f,0x12,0x8c,0xe4,0x81,0x81 };
+                                       const char x500_userid_oid[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
+                                       if(!(tvpType->Length == sizeof(cssm_userid_oid) &&
+                                               !memcmp(tvpPtr->type.Data, x500_userid_oid, sizeof(x500_userid_oid)) &&
+                                               !memcmp(tvpType->Data, cssm_userid_oid, sizeof(cssm_userid_oid)))) {
+                                               continue;
+                                       }
+                               }
+                       }
+
+                       /* printable? */
+                       switch(tvpPtr->valueType) {
+                               case BER_TAG_PRINTABLE_STRING:
+                               case BER_TAG_IA5_STRING:
+                                       *encoding = kCFStringEncodingASCII;
+                                       result = &tvpPtr->value;
+                                       break;
+                               case BER_TAG_PKIX_UTF8_STRING:
+                               case BER_TAG_GENERAL_STRING:
+                               case BER_TAG_PKIX_UNIVERSAL_STRING:
+                                       *encoding = kCFStringEncodingUTF8;
+                                       result = &tvpPtr->value;
+                                       break;
+                               case BER_TAG_T61_STRING:
+                               case BER_TAG_VIDEOTEX_STRING:
+                               case BER_TAG_ISO646_STRING:
+                                       *encoding = kCFStringEncodingISOLatin1;
+                                       result = &tvpPtr->value;
+                                       break;
+                               case BER_TAG_PKIX_BMP_STRING:
+                                       *encoding = kCFStringEncodingUnicode;
+                                       result = &tvpPtr->value;
+                                       break;
+                               default:
+                                       /* not printable */
+                                       break;
+                       }
+                       /* if we found a result and we want the first instance, return it now. */
+                       if(result && !lastInstance) {
+                               return result;
+                       }
+
+               }       /* for each pair */
+       }               /* for each RDN */
+
+       /* result is NULL if no printable component was found */
+       return result;
+}
+
+/*
+ * Infer printable label for a given CSSM_X509_NAME. Returns NULL
+ * if no appropriate printable name found. Returns the CFString-style
+ * encoding associated with name component's BER tag. Also optionally
+ * returns Description component and its encoding if present and the
+ * returned name component was one we explicitly requested.
+ */
+static const CSSM_DATA *inferLabelFromX509Name(
+       const CSSM_X509_NAME *x509Name,
+       CFStringBuiltInEncodings *encoding,                     // RETURNED
+       const CSSM_DATA **description,                          // optionally RETURNED
+       CFStringBuiltInEncodings *descrEncoding)        // RETURNED if description != NULL
+{
+       const CSSM_DATA *printValue;
+       if(description != NULL) {
+               *description = findPrintableField(*x509Name, &CSSMOID_Description, false, descrEncoding);
+       }
+       /*
+        * Search order (take the first one found with a printable
+        * value):
+        *  -- common name
+        *  -- Organizational Unit
+        *  -- Organization
+        *  -- email address
+        *  -- field of any kind
+        */
+       printValue = findPrintableField(*x509Name, &CSSMOID_CommonName, true, encoding);
+       if(printValue != NULL) {
+               return printValue;
+       }
+       printValue = findPrintableField(*x509Name, &CSSMOID_OrganizationalUnitName, false, encoding);
+       if(printValue != NULL) {
+               return printValue;
+       }
+       printValue = findPrintableField(*x509Name, &CSSMOID_OrganizationName, false, encoding);
+       if(printValue != NULL) {
+               return printValue;
+       }
+       printValue = findPrintableField(*x509Name, &CSSMOID_EmailAddress, false, encoding);
+       if(printValue != NULL) {
+               return printValue;
+       }
+       /* if we didn't get one of the above names, don't append description */
+       if(description != NULL) {
+               *description = NULL;
+       }
+       /* take anything */
+       return findPrintableField(*x509Name, NULL, false, encoding);
+}
+
+/*
+ * Infer printable label for a given an CSSM_X509_NAME. Returns NULL
+ * if no appropriate printable name found.
+ */
+const CSSM_DATA *SecInferLabelFromX509Name(
+       const CSSM_X509_NAME *x509Name)
+{
+       /* callees of this routine don't care about the encoding */
+       CFStringBuiltInEncodings encoding = kCFStringEncodingASCII;
+       return inferLabelFromX509Name(x509Name, &encoding, NULL, &encoding);
+}
+
+
+void
+Certificate::inferLabel(bool addLabel, CFStringRef *rtnString)
+{
+       StLock<Mutex>_(mMutex);
+       // Set PrintName and optionally the Alias attribute for this certificate, based on the
+       // X509 SubjectAltName and SubjectName.
+       const CSSM_DATA *printName = NULL;
+       const CSSM_DATA *description = NULL;
+       std::vector<CssmData> emailAddresses;
+       CSSM_DATA puntData;
+       CssmAutoData printPlusDescr(Allocator::standard());
+       CssmData printPlusDescData;
+       CFStringBuiltInEncodings printEncoding = kCFStringEncodingUTF8;
+       CFStringBuiltInEncodings descrEncoding = kCFStringEncodingUTF8;
+
+       // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
+       const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
+       CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
+       const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
+       CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
+
+       getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
+
+       if (snValue && snValue->Data)
+       {
+               const CSSM_X509_NAME &x509Name = *(const CSSM_X509_NAME *)snValue->Data;
+               printName = inferLabelFromX509Name(&x509Name, &printEncoding,
+                       &description, &descrEncoding);
+        if (printName)
+        {
+            /* Don't ever use "Thawte Freemail Member" as the label for a cert.  Instead force
+               a fall back on the email address. */
+            const char tfm[] = "Thawte Freemail Member";
+            if ( (printName->Length == sizeof(tfm) - 1) &&
+                             !memcmp(printName->Data, tfm, sizeof(tfm) - 1)) {
+                printName = NULL;
+                       }
+        }
+       }
+
+       /* Do a check to see if a '\0' was at the end of printName and strip it. */
+       CssmData cleanedUpPrintName;
+       if((printName != NULL) &&
+          (printName->Length != 0) &&
+          (printEncoding != kCFStringEncodingISOLatin1) &&
+          (printEncoding != kCFStringEncodingUnicode) &&
+          (printName->Data[printName->Length - 1] == '\0')) {
+               cleanedUpPrintName.Data = printName->Data;
+               cleanedUpPrintName.Length = printName->Length - 1;
+               printName = &cleanedUpPrintName;
+       }
+
+       if((printName != NULL) && (description != NULL) && (description->Length != 0))
+       {
+               /*
+                * Munge Print Name (which in this case is the CommonName) and Description
+                * together with the Description in parentheses. We convert from whatever
+                * format Print Name and Description are in to UTF8 here.
+                */
+               CFRef<CFMutableStringRef> combo(CFStringCreateMutable(NULL, 0));
+               CFRef<CFStringRef> cfPrint(CFStringCreateWithBytes(NULL, printName->Data,
+                       (CFIndex)printName->Length, printEncoding, true));
+               CssmData cleanedUpDescr(description->Data, description->Length);
+               if ((cleanedUpDescr.Data[cleanedUpDescr.Length - 1] == '\0') &&
+                       (descrEncoding != kCFStringEncodingISOLatin1) &&
+                       (descrEncoding != kCFStringEncodingUnicode)) {
+                       cleanedUpDescr.Length--;
+               }
+               CFRef<CFStringRef> cfDesc(CFStringCreateWithBytes(NULL, cleanedUpDescr.Data,
+                       (CFIndex)cleanedUpDescr.Length, descrEncoding, true));
+               CFStringAppend(combo, cfPrint);
+               CFStringAppendCString(combo, " (", kCFStringEncodingASCII);
+               CFStringAppend(combo, cfDesc);
+               CFStringAppendCString(combo, ")", kCFStringEncodingASCII);
+               CFRef<CFDataRef> comboData(CFStringCreateExternalRepresentation(NULL, combo,
+                       kCFStringEncodingUTF8, 0));
+               printPlusDescr.copy(CFDataGetBytePtr(comboData), CFDataGetLength(comboData));
+               printPlusDescData = printPlusDescr;
+               printName = &printPlusDescData;
+               printEncoding = kCFStringEncodingUTF8;
+       }
+
+       if (printName == NULL)
+       {
+               /* If the we couldn't find a label use the emailAddress instead. */
+               if (!emailAddresses.empty())
+                       printName = &emailAddresses[0];
+               else
+               {
+                       /* punt! */
+                       puntData.Data = (uint8 *)"X509 Certificate";
+                       puntData.Length = 16;
+                       printName = &puntData;
+               }
+               printEncoding = kCFStringEncodingUTF8;
+       }
+
+       /* If we couldn't find an email address just use the printName which might be the url or something else useful. */
+       if (emailAddresses.empty())
+               emailAddresses.push_back(CssmData::overlay(*printName));
+
+       /* What do we do with the inferred label - return it or add it mDbAttributes? */
+       if (addLabel)
+       {
+               mDbAttributes->add(Schema::kX509CertificatePrintName, *printName);
+               CssmDbAttributeData &attrData = mDbAttributes->add(Schema::kX509CertificateAlias);
+
+               /* Add the email addresses to attrData and normalize them. */
+               uint32 ix = 0;
+               for (std::vector<CssmData>::const_iterator it = emailAddresses.begin(); it != emailAddresses.end(); ++it, ++ix)
+               {
+                       /* Add the email address using the allocator from mDbAttributes. */
+                       attrData.add(*it, *mDbAttributes);
+                       /* Normalize the emailAddresses in place since attrData already copied it. */
+                       normalizeEmailAddress(attrData.Value[ix]);
+               }
+       }
+
+       if (rtnString)
+       {
+               CFStringBuiltInEncodings testEncoding = printEncoding;
+               if(testEncoding == kCFStringEncodingISOLatin1) {
+                       // try UTF-8 first
+                       testEncoding = kCFStringEncodingUTF8;
+               }
+               *rtnString = CFStringCreateWithBytes(NULL, printName->Data,
+                       (CFIndex)printName->Length, testEncoding, true);
+               if(*rtnString == NULL && printEncoding == kCFStringEncodingISOLatin1) {
+                       // string cannot be represented in UTF-8, fall back to ISO Latin 1
+                       *rtnString = CFStringCreateWithBytes(NULL, printName->Data,
+                               (CFIndex)printName->Length, printEncoding, true);
+               }
+       }
+
+       // Clean up
+       if (snValue)
+               releaseFieldValue(snOid, snValue);
+       if (sanValues)
+               releaseFieldValues(sanOid, sanValues);
+}
+
+void
+Certificate::populateAttributes()
+{
+       StLock<Mutex>_(mMutex);
+       if (mPopulated)
+               return;
+
+       addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr), CSSMOID_X509V1SubjectName);
+       addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr), CSSMOID_X509V1IssuerName);
+       addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr), CSSMOID_X509V1SerialNumber);
+
+       addSubjectKeyIdentifier();
+
+       if(!mHaveTypeAndEncoding)
+               MacOSError::throwMe(errSecDataNotAvailable); // @@@ Or some other error.
+
+       // Adjust mType based on the actual version of the cert.
+       CSSM_DATA_PTR versionPtr = copyFirstFieldValue(CSSMOID_X509V1Version);
+       if (versionPtr && versionPtr->Data && versionPtr->Length == sizeof(uint32))
+       {
+               mType = CSSM_CERT_X_509v1 + (*reinterpret_cast<uint32 *>(versionPtr->Data));
+       }
+       else
+               mType = CSSM_CERT_X_509v1;
+
+       releaseFieldValue(CSSMOID_X509V1Version, versionPtr);
+
+       mDbAttributes->add(Schema::attributeInfo(kSecCertTypeItemAttr), mType);
+       mDbAttributes->add(Schema::attributeInfo(kSecCertEncodingItemAttr), mEncoding);
+       mDbAttributes->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr), publicKeyHash());
+       inferLabel(true);
+
+       mPopulated = true;
+}
+
+const CssmData &
+Certificate::data()
+{
+       StLock<Mutex>_(mMutex);
+       CssmDataContainer *data = mData.get();
+       if (!data && mKeychain)
+       {
+           // Make sure mUniqueId is set.
+               dbUniqueRecord();
+               CssmDataContainer _data;
+               mData = NULL;
+               /* new data allocated by CSPDL, implicitly freed by CssmDataContainer */
+               mUniqueId->get(NULL, &_data);
+               /* this saves a copy to be freed at destruction and to be passed to caller */
+               setData((UInt32)_data.length(), _data.data());
+               return *mData.get();
+       }
+
+       // If the data hasn't been set we can't return it.
+       if (!data)
+               MacOSError::throwMe(errSecDataNotAvailable);
+
+       return *data;
+}
+
+CFHashCode Certificate::hash()
+{
+       (void)data();  // ensure that mData is set up
+       return ItemImpl::hash();
+}
+
+CSSM_CERT_TYPE
+Certificate::type()
+{
+       StLock<Mutex>_(mMutex);
+       if (!mHaveTypeAndEncoding)
+       {
+               SecKeychainAttribute attr;
+               attr.tag = kSecCertTypeItemAttr;
+               attr.data = &mType;
+               attr.length = sizeof(mType);
+               getAttribute(attr, NULL);
+       }
+
+       return mType;
+}
+
+CSSM_CERT_ENCODING
+Certificate::encoding()
+{
+       StLock<Mutex>_(mMutex);
+       if (!mHaveTypeAndEncoding)
+       {
+               SecKeychainAttribute attr;
+               attr.tag = kSecCertEncodingItemAttr;
+               attr.data = &mEncoding;
+               attr.length = sizeof(mEncoding);
+               getAttribute(attr, NULL);
+       }
+
+       return mEncoding;
+}
+
+const CSSM_X509_ALGORITHM_IDENTIFIER_PTR
+Certificate::algorithmID()
+{
+       StLock<Mutex>_(mMutex);
+       if (!mV1SubjectPublicKeyCStructValue)
+               mV1SubjectPublicKeyCStructValue = copyFirstFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct);
+
+       CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *info = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)mV1SubjectPublicKeyCStructValue->Data;
+       CSSM_X509_ALGORITHM_IDENTIFIER *algid = &info->algorithm;
+       return algid;
+}
+
+CFDataRef
+Certificate::sha1Hash()
+{
+       StLock<Mutex>_(mMutex);
+       if (!mSha1Hash) {
+               SecCertificateRef certRef = handle(false);
+               CFAllocatorRef allocRef = (certRef) ? CFGetAllocator(certRef) : NULL;
+               CSSM_DATA certData = data();
+               if (certData.Length == 0 || !certData.Data) {
+                       MacOSError::throwMe(errSecDataNotAvailable);
+               }
+               const UInt8 *dataPtr = (const UInt8 *)certData.Data;
+               CFIndex dataLen = (CFIndex)certData.Length;
+               CFMutableDataRef digest = CFDataCreateMutable(allocRef, CC_SHA1_DIGEST_LENGTH);
+               CFDataSetLength(digest, CC_SHA1_DIGEST_LENGTH);
+               CCDigest(kCCDigestSHA1, dataPtr, dataLen, CFDataGetMutableBytePtr(digest));
+               mSha1Hash = digest;
+       }
+       return mSha1Hash; /* object is owned by our instance; caller should NOT release it */
+}
+
+CFStringRef
+Certificate::commonName()
+{
+       StLock<Mutex>_(mMutex);
+       return distinguishedName(&CSSMOID_X509V1SubjectNameCStruct, &CSSMOID_CommonName);
+}
+
+CFStringRef
+Certificate::distinguishedName(const CSSM_OID *sourceOid, const CSSM_OID *componentOid)
+{
+       StLock<Mutex>_(mMutex);
+       CFStringRef rtnString = NULL;
+       CSSM_DATA_PTR fieldValue = copyFirstFieldValue(*sourceOid);
+       CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue->Data;
+       const CSSM_DATA *printValue = NULL;
+       CFStringBuiltInEncodings encoding;
+
+       if (fieldValue && fieldValue->Data)
+               printValue = findPrintableField(*x509Name, componentOid, true, &encoding);
+
+       if (printValue)
+               rtnString = CFStringCreateWithBytes(NULL, printValue->Data,
+                       CFIndex(printValue->Length), encoding, true);
+
+       releaseFieldValue(*sourceOid, fieldValue);
+
+       return rtnString;
+}
+
+
+/*
+ * Return a CFString containing the first email addresses for this certificate, based on the
+ * X509 SubjectAltName and SubjectName.
+ */
+CFStringRef
+Certificate::copyFirstEmailAddress()
+{
+       StLock<Mutex>_(mMutex);
+       CFStringRef rtnString;
+
+       const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
+       CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
+       const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
+       CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
+       std::vector<CssmData> emailAddresses;
+
+       getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
+       if (emailAddresses.empty())
+               rtnString = NULL;
+       else
+       {
+               /* Encoding is kCFStringEncodingUTF8 since the string is either
+                  PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
+               rtnString = CFStringCreateWithBytes(NULL, emailAddresses[0].Data,
+                       (CFIndex)emailAddresses[0].Length, kCFStringEncodingUTF8, true);
+       }
+
+       // Clean up
+       if (snValue)
+               releaseFieldValue(snOid, snValue);
+       if (sanValues)
+               releaseFieldValues(sanOid, sanValues);
+
+       return rtnString;
+}
+
+/*
+ * Return a CFArray containing the DNS hostnames for this certificate, based on the
+ * X509 SubjectAltName and SubjectName.
+ */
+CFArrayRef
+Certificate::copyDNSNames()
+{
+       StLock<Mutex>_(mMutex);
+       CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       std::vector<CssmData> dnsNames;
+
+       // Find the SubjectAltName fields, if any, and extract the GNT_DNSName entries from all of them
+       const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
+       CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
+
+       const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
+       CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
+
+       getNames(sanValues, snValue, GNT_DNSName, dnsNames);
+
+       for (std::vector<CssmData>::const_iterator it = dnsNames.begin(); it != dnsNames.end(); ++it)
+       {
+               /* Encoding is kCFStringEncodingUTF8 since the string is either
+                  PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
+               CFStringRef string = CFStringCreateWithBytes(NULL, it->Data, static_cast<CFIndex>(it->Length), kCFStringEncodingUTF8, true);
+               /* Be prepared for improperly formatted (non-UTF8) strings! */
+               if (!string) continue;
+               CFArrayAppendValue(array, string);
+               CFRelease(string);
+       }
+
+       // Clean up
+       if (snValue)
+               releaseFieldValue(snOid, snValue);
+       if (sanValues)
+               releaseFieldValues(sanOid, sanValues);
+
+       return array;
+}
+
+/*
+ * Return a CFArray containing the email addresses for this certificate, based on the
+ * X509 SubjectAltName and SubjectName.
+ */
+CFArrayRef
+Certificate::copyEmailAddresses()
+{
+       StLock<Mutex>_(mMutex);
+       CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       std::vector<CssmData> emailAddresses;
+
+       // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
+       const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
+       CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
+
+       const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
+       CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
+
+       getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
+
+       for (std::vector<CssmData>::const_iterator it = emailAddresses.begin(); it != emailAddresses.end(); ++it)
+       {
+               /* Encoding is kCFStringEncodingUTF8 since the string is either
+                  PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
+               CFStringRef string = CFStringCreateWithBytes(NULL, it->Data, static_cast<CFIndex>(it->Length), kCFStringEncodingUTF8, true);
+               /* Be prepared for improperly formatted (non-UTF8) strings! */
+               if (!string) continue;
+               CFArrayAppendValue(array, string);
+               CFRelease(string);
+       }
+
+       // Clean up
+       if (snValue)
+               releaseFieldValue(snOid, snValue);
+       if (sanValues)
+               releaseFieldValues(sanOid, sanValues);
+
+       return array;
+}
+
+const CSSM_X509_NAME_PTR
+Certificate::subjectName()
+{
+       StLock<Mutex>_(mMutex);
+       if (!mV1SubjectNameCStructValue)
+               if ((mV1SubjectNameCStructValue = copyFirstFieldValue(CSSMOID_X509V1SubjectNameCStruct)) == NULL)
+            return NULL;
+
+    return (const CSSM_X509_NAME_PTR)mV1SubjectNameCStructValue->Data;
+}
+
+const CSSM_X509_NAME_PTR
+Certificate::issuerName()
+{
+       StLock<Mutex>_(mMutex);
+       if (!mV1IssuerNameCStructValue)
+               if ((mV1IssuerNameCStructValue = copyFirstFieldValue(CSSMOID_X509V1IssuerNameCStruct)) == NULL)
+            return NULL;
+
+    return (const CSSM_X509_NAME_PTR)mV1IssuerNameCStructValue->Data;
+}
+
+CSSM_CL_HANDLE
+Certificate::clHandle()
+{
+       StLock<Mutex>_(mMutex);
+       if (!mCL)
+               mCL = clForType(type());
+
+       return mCL->handle();
+}
+
+bool
+Certificate::operator < (Certificate &other)
+{
+       // Certificates in different keychains are considered equal if data is equal
+       // Note that the Identity '<' operator relies on this assumption.
+       return data() < other.data();
+}
+
+bool
+Certificate::operator == (Certificate &other)
+{
+       // Certificates in different keychains are considered equal if data is equal
+       // Note that the Identity '==' operator relies on this assumption.
+       return data() == other.data();
+}
+
+void
+Certificate::update()
+{
+       ItemImpl::update();
+}
+
+Item
+Certificate::copyTo(const Keychain &keychain, Access *newAccess)
+{
+       StLock<Mutex>_(mMutex);
+       /* Certs can't have access controls. */
+       if (newAccess)
+               MacOSError::throwMe(errSecNoAccessForItem);
+
+       Item item(new Certificate(data(), type(), encoding()));
+       keychain->add(item);
+       return item;
+}
+
+void
+Certificate::didModify()
+{
+}
+
+PrimaryKey
+Certificate::add(Keychain &keychain)
+{
+       StLock<Mutex>_(mMutex);
+       // If we already have a Keychain we can't be added.
+       if (mKeychain)
+               MacOSError::throwMe(errSecDuplicateItem);
+
+       populateAttributes();
+
+       CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
+
+       Db db(keychain->database());
+       // add the item to the (regular) db
+       try
+       {
+               mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
+       }
+       catch (const CssmError &e)
+       {
+               if (e.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE)
+                       throw;
+
+               // Create the cert relation and try again.
+               db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE,
+                       "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
+                       Schema::X509CertificateSchemaAttributeCount,
+                       Schema::X509CertificateSchemaAttributeList,
+                       Schema::X509CertificateSchemaIndexCount,
+                       Schema::X509CertificateSchemaIndexList);
+               keychain->keychainSchema()->didCreateRelation(
+                       CSSM_DL_DB_RECORD_X509_CERTIFICATE,
+                       "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
+                       Schema::X509CertificateSchemaAttributeCount,
+                       Schema::X509CertificateSchemaAttributeList,
+                       Schema::X509CertificateSchemaIndexCount,
+                       Schema::X509CertificateSchemaIndexList);
+
+               mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
+       }
+
+       mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
+    mKeychain = keychain;
+
+       return mPrimaryKey;
+}
+
+SecPointer<KeyItem>
+Certificate::publicKey()
+{
+       StLock<Mutex>_(mMutex);
+       SecPointer<KeyItem> keyItem;
+       // 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.
+       // call OSStatus SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey); to retrieve
+
+       CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
+       if (keyPtr && keyPtr->Data)
+       {
+               CssmClient::CSP csp(gGuidAppleCSP);
+               CssmKey *cssmKey = reinterpret_cast<CssmKey *>(keyPtr->Data);
+               CssmClient::Key key(csp, *cssmKey);
+               keyItem = new KeyItem(key);
+               // Clear out KeyData since KeyItem() takes over ownership of the key, and we don't want it getting released.
+               cssmKey->KeyData.Data = NULL;
+               cssmKey->KeyData.Length = 0;
+       }
+
+       releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
+
+       return keyItem;
+}
+
+// This function "borrowed" from the X509 CL, which is (currently) linked into
+// the Security.framework as a built-in plugin.
+extern "C" bool getField_normRDN_NSS (
+       const CSSM_DATA         &derName,
+       uint32                          &numFields,             // RETURNED (if successful, 0 or 1)
+       CssmOwnedData           &fieldValue);   // RETURNED
+
+KCCursor
+Certificate::cursorForIssuerAndSN(const StorageManager::KeychainList &keychains, const CssmData &issuer, const CssmData &serialNumber)
+{
+       CssmAutoData fieldValue(Allocator::standard(Allocator::normal));
+       uint32 numFields;
+
+       // We need to decode issuer, normalize it, then re-encode it
+       if (!getField_normRDN_NSS(issuer, numFields, fieldValue))
+               MacOSError::throwMe(errSecDataNotAvailable);
+
+       // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
+       KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
+       cursor->conjunctive(CSSM_DB_AND);
+       cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateIssuer, fieldValue.get());
+       cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSerialNumber, serialNumber);
+
+       return cursor;
+}
+
+KCCursor
+Certificate::cursorForIssuerAndSN_CF(const StorageManager::KeychainList &keychains, CFDataRef issuer, CFDataRef serialNumber)
+{
+       // This assumes a normalized issuer
+       CSSM_DATA issuerCSSM, serialNumberCSSM;
+
+       issuerCSSM.Length = CFDataGetLength(issuer);
+       issuerCSSM.Data = const_cast<uint8 *>(CFDataGetBytePtr(issuer));
+
+       serialNumberCSSM.Length = CFDataGetLength(serialNumber);
+       serialNumberCSSM.Data = const_cast<uint8 *>(CFDataGetBytePtr(serialNumber));
+
+       // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
+       KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
+       cursor->conjunctive(CSSM_DB_AND);
+       cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateIssuer, issuerCSSM);
+       cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSerialNumber, serialNumberCSSM);
+
+       return cursor;
+}
+
+KCCursor
+Certificate::cursorForSubjectKeyID(const StorageManager::KeychainList &keychains, const CssmData &subjectKeyID)
+{
+       KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
+       cursor->conjunctive(CSSM_DB_AND);
+       cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSubjectKeyIdentifier, subjectKeyID);
+
+       return cursor;
+}
+
+KCCursor
+Certificate::cursorForEmail(const StorageManager::KeychainList &keychains, const char *emailAddress)
+{
+       KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
+       if (emailAddress)
+       {
+               cursor->conjunctive(CSSM_DB_AND);
+               CssmSelectionPredicate &pred = cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateAlias, emailAddress);
+               /* Normalize the emailAddresses in place since cursor already copied it. */
+               normalizeEmailAddress(pred.Attribute.Value[0]);
+       }
+
+       return cursor;
+}
+
+SecPointer<Certificate>
+Certificate::findInKeychain(const StorageManager::KeychainList &keychains)
+{
+       StLock<Mutex>_(mMutex);
+       const CSSM_OID &issuerOid = CSSMOID_X509V1IssuerName;
+       CSSM_DATA_PTR issuerPtr = copyFirstFieldValue(issuerOid);
+       CssmData issuer(issuerPtr->Data, issuerPtr->Length);
+
+       const CSSM_OID &serialOid = CSSMOID_X509V1SerialNumber;
+       CSSM_DATA_PTR serialPtr = copyFirstFieldValue(serialOid);
+       CssmData serial(serialPtr->Data, serialPtr->Length);
+
+       SecPointer<Certificate> foundCert = NULL;
+       try {
+               foundCert = findByIssuerAndSN(keychains, issuer, serial);
+       } catch (...) {
+               foundCert = NULL;
+       }
+
+       releaseFieldValue(issuerOid, issuerPtr);
+       releaseFieldValue(serialOid, serialPtr);
+
+       return foundCert;
+}
+
+SecPointer<Certificate>
+Certificate::findByIssuerAndSN(const StorageManager::KeychainList &keychains, const CssmData &issuer, const CssmData &serialNumber)
+{
+       Item item;
+       if (!cursorForIssuerAndSN(keychains, issuer, serialNumber)->next(item))
+               CssmError::throwMe(errSecItemNotFound);
+
+       return static_cast<Certificate *>(&*item);
+}
+
+SecPointer<Certificate>
+Certificate::findBySubjectKeyID(const StorageManager::KeychainList &keychains, const CssmData &subjectKeyID)
+{
+       Item item;
+       if (!cursorForSubjectKeyID(keychains, subjectKeyID)->next(item))
+               CssmError::throwMe(errSecItemNotFound);
+
+       return static_cast<Certificate *>(&*item);
+}
+
+SecPointer<Certificate>
+Certificate::findByEmail(const StorageManager::KeychainList &keychains, const char *emailAddress)
+{
+       Item item;
+       if (!cursorForEmail(keychains, emailAddress)->next(item))
+               CssmError::throwMe(errSecItemNotFound);
+
+       return static_cast<Certificate *>(&*item);
+}
+
+/* Normalize emailAddresses in place. */
+void
+Certificate::normalizeEmailAddress(CSSM_DATA &emailAddress)
+{
+       /* Do a check to see if a '\0' was at the end of emailAddress and strip it. */
+       if (emailAddress.Length && emailAddress.Data[emailAddress.Length - 1] == '\0')
+               emailAddress.Length--;
+       bool foundAt = false;
+       for (uint32 ix = 0; ix < emailAddress.Length; ++ix)
+       {
+               uint8 ch = emailAddress.Data[ix];
+               if (foundAt)
+               {
+                       if ('A' <= ch && ch <= 'Z')
+                               emailAddress.Data[ix] = ch + 'a' - 'A';
+               }
+               else if (ch == '@')
+                       foundAt = true;
+       }
+}
+
+void
+Certificate::getNames(CSSM_DATA_PTR *sanValues, CSSM_DATA_PTR snValue, CE_GeneralNameType generalNameType, std::vector<CssmData> &names)
+{
+       // Get the DNS host names or RFC822 email addresses for this certificate (depending on generalNameType),
+       // within the X509 SubjectAltName and SubjectName.
+
+       // Find the SubjectAltName fields, if any, and extract the nameType entries from all of them
+       if (sanValues)
+       {
+               for (CSSM_DATA_PTR *sanIx = sanValues; *sanIx; ++sanIx)
+               {
+                       CSSM_DATA_PTR sanValue = *sanIx;
+                       if (sanValue && sanValue->Data)
+                       {
+                               CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)sanValue->Data;
+                               CE_GeneralNames *parsedValue = (CE_GeneralNames *)cssmExt->value.parsedValue;
+
+                               /* Grab all the values that are of the specified name type. */
+                               for (uint32 i = 0; i < parsedValue->numNames; ++i)
+                               {
+                                       if (parsedValue->generalName[i].nameType == generalNameType)
+                                       {
+                                               if (parsedValue->generalName[i].berEncoded) // can't handle this
+                                                       continue;
+
+                                               names.push_back(CssmData::overlay(parsedValue->generalName[i].name));
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (names.empty() && snValue && snValue->Data)
+       {
+               const CSSM_X509_NAME &x509Name = *(const CSSM_X509_NAME *)snValue->Data;
+               for (uint32 rdnDex = 0; rdnDex < x509Name.numberOfRDNs; rdnDex++)
+               {
+                       const CSSM_X509_RDN *rdnPtr =
+                               &x509Name.RelativeDistinguishedName[rdnDex];
+                       for (uint32 tvpDex = 0; tvpDex < rdnPtr->numberOfPairs; tvpDex++)
+                       {
+                               const CSSM_X509_TYPE_VALUE_PAIR *tvpPtr =
+                                       &rdnPtr->AttributeTypeAndValue[tvpDex];
+
+                               /* type/value pair: match caller's specified type */
+                               if (GNT_RFC822Name == generalNameType) {
+                                       if (((tvpPtr->type.Length != CSSMOID_EmailAddress.Length) ||
+                                               memcmp(tvpPtr->type.Data, CSSMOID_EmailAddress.Data, CSSMOID_EmailAddress.Length))) {
+                                                       continue;
+                                       }
+                               }
+                               if (GNT_DNSName == generalNameType) {
+                                       if (((tvpPtr->type.Length != CSSMOID_CommonName.Length) ||
+                                               memcmp(tvpPtr->type.Data, CSSMOID_CommonName.Data, CSSMOID_CommonName.Length))) {
+                                                       continue;
+                                       }
+                               }
+
+                               /* printable? */
+                               switch (tvpPtr->valueType)
+                               {
+                                       case BER_TAG_PRINTABLE_STRING:
+                                       case BER_TAG_IA5_STRING:
+                                       case BER_TAG_T61_STRING:
+                                       case BER_TAG_PKIX_UTF8_STRING:
+                                               /* success */
+                                               names.push_back(CssmData::overlay(tvpPtr->value));
+                                               break;
+                                       default:
+                                               break;
+                               }
+                       }       /* for each pair */
+               }               /* for each RDN */
+       }
+}
+
+void Certificate::willRead()
+{
+       populateAttributes();
+}
+
+Boolean Certificate::isSelfSigned()
+{
+       StLock<Mutex>_(mMutex);
+       CSSM_DATA_PTR issuer = NULL;
+       CSSM_DATA_PTR subject = NULL;
+       OSStatus ortn = errSecSuccess;
+       Boolean brtn = false;
+
+       issuer  = copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd);
+       subject = copyFirstFieldValue(CSSMOID_X509V1SubjectNameStd);
+       if((issuer == NULL) || (subject == NULL)) {
+               ortn = errSecParam;
+       }
+       else if((issuer->Length == subject->Length) &&
+               !memcmp(issuer->Data, subject->Data, issuer->Length)) {
+               brtn = true;
+       }
+       if(brtn) {
+               /* names match: verify signature */
+               CSSM_RETURN crtn;
+               CSSM_DATA certData = data();
+               crtn = CSSM_CL_CertVerify(clHandle(), 0,
+                       &certData, &certData, NULL, 0);
+               if(crtn) {
+                       brtn = false;
+               }
+       }
+       if(issuer) {
+               releaseFieldValue(CSSMOID_X509V1IssuerNameStd,  issuer);
+       }
+       if(subject) {
+               releaseFieldValue(CSSMOID_X509V1SubjectNameStd, subject);
+       }
+       if(ortn) {
+               MacOSError::throwMe(ortn);
+       }
+       return brtn;
+}