2 * Copyright (c) 2002-2007,2011-2014 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 <CommonCrypto/CommonDigestSPI.h>
39 #include <libDER/libDER.h>
40 #include <libDER/DER_Decode.h>
42 using namespace KeychainCore
;
45 Certificate::clForType(CSSM_CERT_TYPE type
)
47 return CL(gGuidAppleX509CL
);
50 Certificate::Certificate(const CSSM_DATA
&data
, CSSM_CERT_TYPE type
, CSSM_CERT_ENCODING encoding
) :
51 ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE
, reinterpret_cast<SecKeychainAttributeList
*>(NULL
), UInt32(data
.Length
), reinterpret_cast<const void *>(data
.Data
)),
52 mHaveTypeAndEncoding(true),
58 mV1SubjectPublicKeyCStructValue(NULL
),
59 mV1SubjectNameCStructValue(NULL
),
60 mV1IssuerNameCStructValue(NULL
),
63 mEncodingVerified(false)
65 if (data
.Length
== 0 || data
.Data
== NULL
)
66 MacOSError::throwMe(errSecParam
);
69 // db item constructor
70 Certificate::Certificate(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
) :
71 ItemImpl(keychain
, primaryKey
, uniqueId
),
72 mHaveTypeAndEncoding(false),
76 mV1SubjectPublicKeyCStructValue(NULL
),
77 mV1SubjectNameCStructValue(NULL
),
78 mV1IssuerNameCStructValue(NULL
),
81 mEncodingVerified(false)
87 Certificate
* Certificate::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
89 Certificate
* c
= new Certificate(keychain
, primaryKey
, uniqueId
);
90 keychain
->addItem(primaryKey
, c
);
96 Certificate
* Certificate::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
98 Certificate
* c
= new Certificate(keychain
, primaryKey
);
99 keychain
->addItem(primaryKey
, c
);
106 // PrimaryKey item constructor
107 Certificate::Certificate(const Keychain
&keychain
, const PrimaryKey
&primaryKey
) :
108 ItemImpl(keychain
, primaryKey
),
109 mHaveTypeAndEncoding(false),
113 mV1SubjectPublicKeyCStructValue(NULL
),
114 mV1SubjectNameCStructValue(NULL
),
115 mV1IssuerNameCStructValue(NULL
),
118 mEncodingVerified(false)
120 // @@@ In this case we don't know the type...
123 Certificate::Certificate(Certificate
&certificate
) :
124 ItemImpl(certificate
),
125 mHaveTypeAndEncoding(certificate
.mHaveTypeAndEncoding
),
126 mPopulated(false /* certificate.mPopulated */),
127 mType(certificate
.mType
),
128 mEncoding(certificate
.mEncoding
),
129 mCL(certificate
.mCL
),
131 mV1SubjectPublicKeyCStructValue(NULL
),
132 mV1SubjectNameCStructValue(NULL
),
133 mV1IssuerNameCStructValue(NULL
),
136 mEncodingVerified(false)
140 Certificate::~Certificate()
143 if (mV1SubjectPublicKeyCStructValue
)
144 releaseFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct
, mV1SubjectPublicKeyCStructValue
);
146 if (mCertHandle
&& mCL
)
147 CSSM_CL_CertAbortCache(mCL
->handle(), mCertHandle
);
149 if (mV1SubjectNameCStructValue
)
150 releaseFieldValue(CSSMOID_X509V1SubjectNameCStruct
, mV1SubjectNameCStructValue
);
152 if (mV1IssuerNameCStructValue
)
153 releaseFieldValue(CSSMOID_X509V1IssuerNameCStruct
, mV1IssuerNameCStructValue
);
156 CFRelease(mSha1Hash
);
158 CFRelease(mSha256Hash
);
165 Certificate::certHandle()
167 StLock
<Mutex
>_(mMutex
);
168 const CSSM_DATA
*cert
= &data();
171 if (CSSM_RETURN retval
= CSSM_CL_CertCache(clHandle(), cert
, &mCertHandle
))
172 CssmError::throwMe(retval
);
178 /* 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. */
180 Certificate::copyFieldValues(const CSSM_OID
&field
)
182 StLock
<Mutex
>_(mMutex
);
183 CSSM_CL_HANDLE clh
= clHandle();
184 CSSM_DATA_PTR fieldValue
, *fieldValues
;
185 CSSM_HANDLE resultsHandle
= 0;
186 uint32 numberOfFields
= 0;
189 result
= CSSM_CL_CertGetFirstCachedFieldValue(clh
, certHandle(), &field
, &resultsHandle
, &numberOfFields
, &fieldValue
);
192 if (result
== CSSMERR_CL_NO_FIELD_VALUES
)
195 CssmError::throwMe(result
);
198 fieldValues
= new CSSM_DATA_PTR
[numberOfFields
+ 1];
199 fieldValues
[0] = fieldValue
;
200 fieldValues
[numberOfFields
] = NULL
;
202 for (uint32 value
= 1; value
< numberOfFields
; ++value
)
204 CSSM_RETURN cresult
= CSSM_CL_CertGetNextCachedFieldValue(clh
, resultsHandle
, &fieldValues
[value
]);
207 fieldValues
[value
] = NULL
;
209 break; // No point in continuing really.
213 CSSM_CL_CertAbortQuery(clh
, resultsHandle
);
217 releaseFieldValues(field
, fieldValues
);
218 CssmError::throwMe(result
);
225 Certificate::releaseFieldValues(const CSSM_OID
&field
, CSSM_DATA_PTR
*fieldValues
)
227 StLock
<Mutex
>_(mMutex
);
230 CSSM_CL_HANDLE clh
= clHandle();
232 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
233 CSSM_CL_FreeFieldValue(clh
, &field
, fieldValues
[ix
]);
235 delete[] fieldValues
;
240 Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO
&info
, const CSSM_OID
&field
)
242 StLock
<Mutex
>_(mMutex
);
243 CSSM_DATA_PTR
*fieldValues
= copyFieldValues(field
);
246 CssmDbAttributeData
&anAttr
= mDbAttributes
->add(info
);
247 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
248 anAttr
.add(*fieldValues
[ix
], *mDbAttributes
);
250 releaseFieldValues(field
, fieldValues
);
255 Certificate::addSubjectKeyIdentifier()
257 StLock
<Mutex
>_(mMutex
);
258 const CSSM_DB_ATTRIBUTE_INFO
&info
= Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr
);
259 const CSSM_OID
&field
= CSSMOID_SubjectKeyIdentifier
;
261 CSSM_DATA_PTR
*fieldValues
= copyFieldValues(field
);
264 CssmDbAttributeData
&anAttr
= mDbAttributes
->add(info
);
265 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
267 const CSSM_X509_EXTENSION
*extension
= reinterpret_cast<const CSSM_X509_EXTENSION
*>(fieldValues
[ix
]->Data
);
268 if (extension
== NULL
|| fieldValues
[ix
]->Length
!= sizeof(CSSM_X509_EXTENSION
))
270 assert(extension
!= NULL
&& fieldValues
[ix
]->Length
== sizeof(CSSM_X509_EXTENSION
));
273 const CE_SubjectKeyID
*skid
= reinterpret_cast<CE_SubjectKeyID
*>(extension
->value
.parsedValue
);
276 assert(skid
!= NULL
);
279 anAttr
.add(*skid
, *mDbAttributes
);
282 releaseFieldValues(field
, fieldValues
);
286 /* 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. */
288 Certificate::copyFirstFieldValue(const CSSM_OID
&field
)
290 StLock
<Mutex
>_(mMutex
);
291 CSSM_CL_HANDLE clh
= clHandle();
292 CSSM_DATA_PTR fieldValue
;
293 CSSM_HANDLE resultsHandle
= 0;
294 uint32 numberOfFields
= 0;
297 result
= CSSM_CL_CertGetFirstCachedFieldValue(clh
, certHandle(), &field
, &resultsHandle
, &numberOfFields
, &fieldValue
);
300 if (result
== CSSMERR_CL_NO_FIELD_VALUES
)
303 CssmError::throwMe(result
);
306 result
= CSSM_CL_CertAbortQuery(clh
, resultsHandle
);
310 releaseFieldValue(field
, fieldValue
);
311 CssmError::throwMe(result
);
318 Certificate::releaseFieldValue(const CSSM_OID
&field
, CSSM_DATA_PTR fieldValue
)
320 StLock
<Mutex
>_(mMutex
);
323 CSSM_CL_HANDLE clh
= clHandle();
324 CSSM_CL_FreeFieldValue(clh
, &field
, fieldValue
);
331 This method computes the keyIdentifier for the public key in the cert as
334 The keyIdentifier is composed of the 160-bit SHA-1 hash of the
335 value of the BIT STRING subjectPublicKey (excluding the tag,
336 length, and number of unused bits).
339 Certificate::publicKeyHash()
341 StLock
<Mutex
>_(mMutex
);
342 if (mPublicKeyHash
.Length
)
343 return mPublicKeyHash
;
345 CSSM_DATA_PTR keyPtr
= copyFirstFieldValue(CSSMOID_CSSMKeyStruct
);
346 if (keyPtr
&& keyPtr
->Data
)
348 CssmClient::CSP
csp(gGuidAppleCSP
);
349 CssmClient::PassThrough
passThrough(csp
);
350 CSSM_KEY
*key
= reinterpret_cast<CSSM_KEY
*>(keyPtr
->Data
);
354 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
355 * associated key blob.
356 * Key is specified in CSSM_CSP_CreatePassThroughContext.
357 * Hash is allocated by the CSP, in the App's memory, and returned
359 passThrough
.key(key
);
360 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
361 cssmData
= reinterpret_cast<CssmData
*>(outData
);
363 assert(cssmData
->Length
<= sizeof(mPublicKeyHashBytes
));
364 mPublicKeyHash
.Data
= mPublicKeyHashBytes
;
365 mPublicKeyHash
.Length
= cssmData
->Length
;
366 memcpy(mPublicKeyHash
.Data
, cssmData
->Data
, cssmData
->Length
);
367 csp
.allocator().free(cssmData
->Data
);
368 csp
.allocator().free(cssmData
);
371 releaseFieldValue(CSSMOID_CSSMKeyStruct
, keyPtr
);
373 return mPublicKeyHash
;
377 Certificate::subjectKeyIdentifier()
379 StLock
<Mutex
>_(mMutex
);
380 if (mSubjectKeyID
.Length
)
381 return mSubjectKeyID
;
383 CSSM_DATA_PTR fieldValue
= copyFirstFieldValue(CSSMOID_SubjectKeyIdentifier
);
384 if (fieldValue
&& fieldValue
->Data
&& fieldValue
->Length
== sizeof(CSSM_X509_EXTENSION
))
386 const CSSM_X509_EXTENSION
*extension
= reinterpret_cast<const CSSM_X509_EXTENSION
*>(fieldValue
->Data
);
387 const CE_SubjectKeyID
*skid
= reinterpret_cast<CE_SubjectKeyID
*>(extension
->value
.parsedValue
); // CSSM_DATA
389 if (skid
->Length
<= sizeof(mSubjectKeyIDBytes
))
391 mSubjectKeyID
.Data
= mSubjectKeyIDBytes
;
392 mSubjectKeyID
.Length
= skid
->Length
;
393 memcpy(mSubjectKeyID
.Data
, skid
->Data
, skid
->Length
);
396 mSubjectKeyID
.Length
= 0;
399 releaseFieldValue(CSSMOID_SubjectKeyIdentifier
, fieldValue
);
401 return mSubjectKeyID
;
406 * Given an CSSM_X509_NAME, Find the first (or last) name/value pair with
407 * a printable value which matches the specified OID (e.g., CSSMOID_CommonName).
408 * Returns the CFString-style encoding associated with name component's BER tag.
409 * Returns NULL if none found.
411 static const CSSM_DATA
*
413 const CSSM_X509_NAME
&x509Name
,
414 const CSSM_OID
*tvpType
, // NULL means "any printable field"
415 bool lastInstance
, // false means return first instance
416 CFStringBuiltInEncodings
*encoding
) // RETURNED
418 const CSSM_DATA
*result
= NULL
;
419 for(uint32 rdnDex
=0; rdnDex
<x509Name
.numberOfRDNs
; rdnDex
++) {
420 const CSSM_X509_RDN
*rdnPtr
=
421 &x509Name
.RelativeDistinguishedName
[rdnDex
];
422 for(uint32 tvpDex
=0; tvpDex
<rdnPtr
->numberOfPairs
; tvpDex
++) {
423 const CSSM_X509_TYPE_VALUE_PAIR
*tvpPtr
=
424 &rdnPtr
->AttributeTypeAndValue
[tvpDex
];
426 /* type/value pair: match caller's specified type? */
427 if(tvpType
!= NULL
&& tvpType
->Data
!= NULL
) {
428 if(tvpPtr
->type
.Length
!= tvpType
->Length
) {
431 if(memcmp(tvpPtr
->type
.Data
, tvpType
->Data
, tvpType
->Length
)) {
432 /* If we don't have a match but the requested OID is CSSMOID_UserID,
433 * look for a matching X.500 UserID OID: (0.9.2342.19200300.100.1.1) */
434 const char cssm_userid_oid
[] = { 0x09,0x49,0x86,0x49,0x1f,0x12,0x8c,0xe4,0x81,0x81 };
435 const char x500_userid_oid
[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
436 if(!(tvpType
->Length
== sizeof(cssm_userid_oid
) &&
437 !memcmp(tvpPtr
->type
.Data
, x500_userid_oid
, sizeof(x500_userid_oid
)) &&
438 !memcmp(tvpType
->Data
, cssm_userid_oid
, sizeof(cssm_userid_oid
)))) {
445 switch(tvpPtr
->valueType
) {
446 case BER_TAG_PRINTABLE_STRING
:
447 case BER_TAG_IA5_STRING
:
448 *encoding
= kCFStringEncodingASCII
;
449 result
= &tvpPtr
->value
;
451 case BER_TAG_PKIX_UTF8_STRING
:
452 case BER_TAG_GENERAL_STRING
:
453 case BER_TAG_PKIX_UNIVERSAL_STRING
:
454 *encoding
= kCFStringEncodingUTF8
;
455 result
= &tvpPtr
->value
;
457 case BER_TAG_T61_STRING
:
458 case BER_TAG_VIDEOTEX_STRING
:
459 case BER_TAG_ISO646_STRING
:
460 *encoding
= kCFStringEncodingISOLatin1
;
461 result
= &tvpPtr
->value
;
463 case BER_TAG_PKIX_BMP_STRING
:
464 *encoding
= kCFStringEncodingUnicode
;
465 result
= &tvpPtr
->value
;
471 /* if we found a result and we want the first instance, return it now. */
472 if(result
&& !lastInstance
) {
476 } /* for each pair */
479 /* result is NULL if no printable component was found */
484 * Infer printable label for a given CSSM_X509_NAME. Returns NULL
485 * if no appropriate printable name found. Returns the CFString-style
486 * encoding associated with name component's BER tag. Also optionally
487 * returns Description component and its encoding if present and the
488 * returned name component was one we explicitly requested.
490 static const CSSM_DATA
*inferLabelFromX509Name(
491 const CSSM_X509_NAME
*x509Name
,
492 CFStringBuiltInEncodings
*encoding
, // RETURNED
493 const CSSM_DATA
**description
, // optionally RETURNED
494 CFStringBuiltInEncodings
*descrEncoding
) // RETURNED if description != NULL
496 const CSSM_DATA
*printValue
;
497 if(description
!= NULL
) {
498 *description
= findPrintableField(*x509Name
, &CSSMOID_Description
, false, descrEncoding
);
501 * Search order (take the first one found with a printable
504 * -- Organizational Unit
507 * -- field of any kind
509 printValue
= findPrintableField(*x509Name
, &CSSMOID_CommonName
, true, encoding
);
510 if(printValue
!= NULL
) {
513 printValue
= findPrintableField(*x509Name
, &CSSMOID_OrganizationalUnitName
, false, encoding
);
514 if(printValue
!= NULL
) {
517 printValue
= findPrintableField(*x509Name
, &CSSMOID_OrganizationName
, false, encoding
);
518 if(printValue
!= NULL
) {
521 printValue
= findPrintableField(*x509Name
, &CSSMOID_EmailAddress
, false, encoding
);
522 if(printValue
!= NULL
) {
525 /* if we didn't get one of the above names, don't append description */
526 if(description
!= NULL
) {
530 return findPrintableField(*x509Name
, NULL
, false, encoding
);
534 * Infer printable label for a given an CSSM_X509_NAME. Returns NULL
535 * if no appropriate printable name found.
537 const CSSM_DATA
*SecInferLabelFromX509Name(
538 const CSSM_X509_NAME
*x509Name
)
540 /* callees of this routine don't care about the encoding */
541 CFStringBuiltInEncodings encoding
= kCFStringEncodingASCII
;
542 return inferLabelFromX509Name(x509Name
, &encoding
, NULL
, &encoding
);
547 Certificate::inferLabel(bool addLabel
, CFStringRef
*rtnString
)
549 StLock
<Mutex
>_(mMutex
);
550 // Set PrintName and optionally the Alias attribute for this certificate, based on the
551 // X509 SubjectAltName and SubjectName.
552 const CSSM_DATA
*printName
= NULL
;
553 const CSSM_DATA
*description
= NULL
;
554 std::vector
<CssmData
> emailAddresses
;
556 CssmAutoData
printPlusDescr(Allocator::standard());
557 CssmData printPlusDescData
;
558 CFStringBuiltInEncodings printEncoding
= kCFStringEncodingUTF8
;
559 CFStringBuiltInEncodings descrEncoding
= kCFStringEncodingUTF8
;
561 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
562 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
563 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
564 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
565 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
567 getNames(sanValues
, snValue
, GNT_RFC822Name
, emailAddresses
);
569 if (snValue
&& snValue
->Data
)
571 const CSSM_X509_NAME
&x509Name
= *(const CSSM_X509_NAME
*)snValue
->Data
;
572 printName
= inferLabelFromX509Name(&x509Name
, &printEncoding
,
573 &description
, &descrEncoding
);
576 /* Don't ever use "Thawte Freemail Member" as the label for a cert. Instead force
577 a fall back on the email address. */
578 const char tfm
[] = "Thawte Freemail Member";
579 if ( (printName
->Length
== sizeof(tfm
) - 1) &&
580 !memcmp(printName
->Data
, tfm
, sizeof(tfm
) - 1)) {
586 /* Do a check to see if a '\0' was at the end of printName and strip it. */
587 CssmData cleanedUpPrintName
;
588 if((printName
!= NULL
) &&
589 (printName
->Length
!= 0) &&
590 (printEncoding
!= kCFStringEncodingISOLatin1
) &&
591 (printEncoding
!= kCFStringEncodingUnicode
) &&
592 (printName
->Data
[printName
->Length
- 1] == '\0')) {
593 cleanedUpPrintName
.Data
= printName
->Data
;
594 cleanedUpPrintName
.Length
= printName
->Length
- 1;
595 printName
= &cleanedUpPrintName
;
598 if((printName
!= NULL
) && (description
!= NULL
) && (description
->Length
!= 0))
601 * Munge Print Name (which in this case is the CommonName) and Description
602 * together with the Description in parentheses. We convert from whatever
603 * format Print Name and Description are in to UTF8 here.
605 CFRef
<CFMutableStringRef
> combo(CFStringCreateMutable(NULL
, 0));
606 CFRef
<CFStringRef
> cfPrint(CFStringCreateWithBytes(NULL
, printName
->Data
,
607 (CFIndex
)printName
->Length
, printEncoding
, true));
608 CssmData
cleanedUpDescr(description
->Data
, description
->Length
);
609 if ((cleanedUpDescr
.Data
[cleanedUpDescr
.Length
- 1] == '\0') &&
610 (descrEncoding
!= kCFStringEncodingISOLatin1
) &&
611 (descrEncoding
!= kCFStringEncodingUnicode
)) {
612 cleanedUpDescr
.Length
--;
614 CFRef
<CFStringRef
> cfDesc(CFStringCreateWithBytes(NULL
, cleanedUpDescr
.Data
,
615 (CFIndex
)cleanedUpDescr
.Length
, descrEncoding
, true));
616 CFStringAppend(combo
, cfPrint
);
617 CFStringAppendCString(combo
, " (", kCFStringEncodingASCII
);
618 CFStringAppend(combo
, cfDesc
);
619 CFStringAppendCString(combo
, ")", kCFStringEncodingASCII
);
620 CFRef
<CFDataRef
> comboData(CFStringCreateExternalRepresentation(NULL
, combo
,
621 kCFStringEncodingUTF8
, 0));
622 printPlusDescr
.copy(CFDataGetBytePtr(comboData
), CFDataGetLength(comboData
));
623 printPlusDescData
= printPlusDescr
;
624 printName
= &printPlusDescData
;
625 printEncoding
= kCFStringEncodingUTF8
;
628 if (printName
== NULL
)
630 /* If the we couldn't find a label use the emailAddress instead. */
631 if (!emailAddresses
.empty())
632 printName
= &emailAddresses
[0];
636 puntData
.Data
= (uint8
*)"X509 Certificate";
637 puntData
.Length
= 16;
638 printName
= &puntData
;
640 printEncoding
= kCFStringEncodingUTF8
;
643 /* If we couldn't find an email address just use the printName which might be the url or something else useful. */
644 if (emailAddresses
.empty())
645 emailAddresses
.push_back(CssmData::overlay(*printName
));
647 /* What do we do with the inferred label - return it or add it mDbAttributes? */
650 mDbAttributes
->add(Schema::kX509CertificatePrintName
, *printName
);
651 CssmDbAttributeData
&attrData
= mDbAttributes
->add(Schema::kX509CertificateAlias
);
653 /* Add the email addresses to attrData and normalize them. */
655 for (std::vector
<CssmData
>::const_iterator it
= emailAddresses
.begin(); it
!= emailAddresses
.end(); ++it
, ++ix
)
657 /* Add the email address using the allocator from mDbAttributes. */
658 attrData
.add(*it
, *mDbAttributes
);
659 /* Normalize the emailAddresses in place since attrData already copied it. */
660 normalizeEmailAddress(attrData
.Value
[ix
]);
666 CFStringBuiltInEncodings testEncoding
= printEncoding
;
667 if(testEncoding
== kCFStringEncodingISOLatin1
) {
669 testEncoding
= kCFStringEncodingUTF8
;
671 *rtnString
= CFStringCreateWithBytes(NULL
, printName
->Data
,
672 (CFIndex
)printName
->Length
, testEncoding
, true);
673 if(*rtnString
== NULL
&& printEncoding
== kCFStringEncodingISOLatin1
) {
674 // string cannot be represented in UTF-8, fall back to ISO Latin 1
675 *rtnString
= CFStringCreateWithBytes(NULL
, printName
->Data
,
676 (CFIndex
)printName
->Length
, printEncoding
, true);
682 releaseFieldValue(snOid
, snValue
);
684 releaseFieldValues(sanOid
, sanValues
);
688 Certificate::populateAttributes()
690 StLock
<Mutex
>_(mMutex
);
694 addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr
), CSSMOID_X509V1SubjectName
);
695 addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr
), CSSMOID_X509V1IssuerName
);
696 addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr
), CSSMOID_X509V1SerialNumber
);
698 addSubjectKeyIdentifier();
700 if(!mHaveTypeAndEncoding
)
701 MacOSError::throwMe(errSecDataNotAvailable
); // @@@ Or some other error.
703 // Adjust mType based on the actual version of the cert.
704 CSSM_DATA_PTR versionPtr
= copyFirstFieldValue(CSSMOID_X509V1Version
);
705 if (versionPtr
&& versionPtr
->Data
&& versionPtr
->Length
== sizeof(uint32
))
707 mType
= CSSM_CERT_X_509v1
+ (*reinterpret_cast<uint32
*>(versionPtr
->Data
));
710 mType
= CSSM_CERT_X_509v1
;
712 releaseFieldValue(CSSMOID_X509V1Version
, versionPtr
);
714 mDbAttributes
->add(Schema::attributeInfo(kSecCertTypeItemAttr
), mType
);
715 mDbAttributes
->add(Schema::attributeInfo(kSecCertEncodingItemAttr
), mEncoding
);
716 mDbAttributes
->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr
), publicKeyHash());
723 Certificate::verifyEncoding(CSSM_DATA_PTR data
)
725 bool verified
= false;
726 CSSM_SIZE verifiedLength
= 0;
728 StLock
<Mutex
>_(mMutex
);
729 if (!data
|| !data
->Data
|| !data
->Length
) {
730 mEncodingVerified
= false;
733 verified
= mEncodingVerified
;
738 // Note: the Certificate class supports X509v1 through X509v3 certs,
739 // with CSSM_CERT_ENCODING_BER or CSSM_CERT_ENCODING_DER encoding.
740 // Any other types/encodings would need additional verification code here.
742 if (mHaveTypeAndEncoding
) {
743 if (mType
< CSSM_CERT_X_509v1
|| mType
> CSSM_CERT_X_509v3
) {
744 secdebug("Certificate", "verifyEncoding: certificate has custom type (%d)", (int)mType
);
746 if (mEncoding
< CSSM_CERT_ENCODING_BER
|| mEncoding
> CSSM_CERT_ENCODING_DER
) {
747 secdebug("Certificate", "verifyEncoding: certificate has custom encoding (%d)", (int)mEncoding
);
751 // attempt to decode the top-level ASN.1 sequence
752 const DERItem der
= { (DERByte
*)data
->Data
, (DERSize
)data
->Length
};
753 DERDecodedInfo derInfo
;
754 // sanity check the first byte to avoid decoding a non-DER blob
755 if ((DERByte
)0x30 != *(der
.data
)) {
758 DERReturn drtn
= DERDecodeItem(&der
, &derInfo
);
759 if (drtn
== DR_Success
) {
760 CSSM_SIZE tagLength
= (CSSM_SIZE
)((uintptr_t)derInfo
.content
.data
- (uintptr_t)der
.data
);
761 CSSM_SIZE derLength
= (CSSM_SIZE
)derInfo
.content
.length
+ tagLength
;
762 if (derLength
!= data
->Length
) {
763 secdebug("Certificate", "Certificate DER length is %d, but data length is %d",
764 (int)derLength
, (int)data
->Length
);
765 // will adjust data size if DER length is positive, but smaller than actual length
766 if ((derLength
> 0) && (derLength
< data
->Length
)) {
767 verifiedLength
= derLength
;
768 secdebug("Certificate", "Will adjust certificate data length to %d",
772 secdebug("Certificate", "Certificate encoding invalid (DER length is %d)",
777 verified
= mEncodingVerified
= true;
780 // failure to decode provided data as DER sequence
781 secdebug("Certificate", "Certificate not in DER encoding (error %d)",
787 if (verifiedLength
> 0) {
788 // setData acquires the mMutex lock, so we call it while not holding the lock
789 setData((UInt32
)verifiedLength
, data
->Data
);
790 secdebug("Certificate", "Adjusted certificate data length to %d",
791 (int)verifiedLength
);
800 CssmDataContainer
*data
= NULL
;
801 bool hasKeychain
= false;
802 bool verified
= false;
804 StLock
<Mutex
>_(mMutex
);
806 hasKeychain
= (mKeychain
!= NULL
);
807 verified
= mEncodingVerified
;
810 // If data has been set but not yet verified, verify it now.
811 if (!verified
&& data
) {
812 // verifyEncoding might modify mData, so refresh the data container
813 verified
= verifyEncoding(data
);
815 StLock
<Mutex
>_(mMutex
);
820 // If data isn't set at this point, try to read it from the db record
821 if (!data
&& hasKeychain
)
823 // Make sure mUniqueId is set.
825 CssmDataContainer _data
;
827 StLock
<Mutex
>_(mMutex
);
829 /* new data allocated by CSPDL, implicitly freed by CssmDataContainer */
830 mUniqueId
->get(NULL
, &_data
);
832 /* this saves a copy to be freed at destruction and to be passed to caller */
833 setData((UInt32
)_data
.length(), _data
.data());
834 // verifyEncoding might modify mData, so refresh the data container
835 verified
= verifyEncoding(&_data
);
837 StLock
<Mutex
>_(mMutex
);
842 // If the data hasn't been set we can't return it.
844 MacOSError::throwMe(errSecDataNotAvailable
);
849 CFHashCode
Certificate::hash()
851 (void)data(); // ensure that mData is set up
852 return ItemImpl::hash();
858 StLock
<Mutex
>_(mMutex
);
859 if (!mHaveTypeAndEncoding
)
861 SecKeychainAttribute attr
;
862 attr
.tag
= kSecCertTypeItemAttr
;
864 attr
.length
= sizeof(mType
);
865 getAttribute(attr
, NULL
);
872 Certificate::encoding()
874 StLock
<Mutex
>_(mMutex
);
875 if (!mHaveTypeAndEncoding
)
877 SecKeychainAttribute attr
;
878 attr
.tag
= kSecCertEncodingItemAttr
;
879 attr
.data
= &mEncoding
;
880 attr
.length
= sizeof(mEncoding
);
881 getAttribute(attr
, NULL
);
887 const CSSM_X509_ALGORITHM_IDENTIFIER_PTR
888 Certificate::algorithmID()
890 StLock
<Mutex
>_(mMutex
);
891 if (!mV1SubjectPublicKeyCStructValue
)
892 mV1SubjectPublicKeyCStructValue
= copyFirstFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct
);
894 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*info
= (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*)mV1SubjectPublicKeyCStructValue
->Data
;
895 CSSM_X509_ALGORITHM_IDENTIFIER
*algid
= &info
->algorithm
;
900 Certificate::sha1Hash()
902 StLock
<Mutex
>_(mMutex
);
904 SecCertificateRef certRef
= handle(false);
905 CFAllocatorRef allocRef
= (certRef
) ? CFGetAllocator(certRef
) : NULL
;
906 CSSM_DATA certData
= data();
907 if (certData
.Length
== 0 || !certData
.Data
) {
908 MacOSError::throwMe(errSecDataNotAvailable
);
910 const UInt8
*dataPtr
= (const UInt8
*)certData
.Data
;
911 CFIndex dataLen
= (CFIndex
)certData
.Length
;
912 CFMutableDataRef digest
= CFDataCreateMutable(allocRef
, CC_SHA1_DIGEST_LENGTH
);
913 CFDataSetLength(digest
, CC_SHA1_DIGEST_LENGTH
);
914 CCDigest(kCCDigestSHA1
, dataPtr
, dataLen
, CFDataGetMutableBytePtr(digest
));
917 return mSha1Hash
; /* object is owned by our instance; caller should NOT release it */
921 Certificate::sha256Hash()
923 StLock
<Mutex
>_(mMutex
);
925 SecCertificateRef certRef
= handle(false);
926 CFAllocatorRef allocRef
= (certRef
) ? CFGetAllocator(certRef
) : NULL
;
927 CSSM_DATA certData
= data();
928 if (certData
.Length
== 0 || !certData
.Data
) {
929 MacOSError::throwMe(errSecDataNotAvailable
);
931 const UInt8
*dataPtr
= (const UInt8
*)certData
.Data
;
932 CFIndex dataLen
= (CFIndex
)certData
.Length
;
933 CFMutableDataRef digest
= CFDataCreateMutable(allocRef
, CC_SHA256_DIGEST_LENGTH
);
934 CFDataSetLength(digest
, CC_SHA256_DIGEST_LENGTH
);
935 CCDigest(kCCDigestSHA256
, dataPtr
, dataLen
, CFDataGetMutableBytePtr(digest
));
936 mSha256Hash
= digest
;
938 return mSha256Hash
; /* object is owned by our instance; caller should NOT release it */
943 Certificate::commonName()
945 StLock
<Mutex
>_(mMutex
);
946 return distinguishedName(&CSSMOID_X509V1SubjectNameCStruct
, &CSSMOID_CommonName
);
950 Certificate::distinguishedName(const CSSM_OID
*sourceOid
, const CSSM_OID
*componentOid
)
952 StLock
<Mutex
>_(mMutex
);
953 CFStringRef rtnString
= NULL
;
954 CSSM_DATA_PTR fieldValue
= copyFirstFieldValue(*sourceOid
);
955 CSSM_X509_NAME_PTR x509Name
= (CSSM_X509_NAME_PTR
)fieldValue
->Data
;
956 const CSSM_DATA
*printValue
= NULL
;
957 CFStringBuiltInEncodings encoding
;
959 if (fieldValue
&& fieldValue
->Data
)
960 printValue
= findPrintableField(*x509Name
, componentOid
, true, &encoding
);
963 rtnString
= CFStringCreateWithBytes(NULL
, printValue
->Data
,
964 CFIndex(printValue
->Length
), encoding
, true);
966 releaseFieldValue(*sourceOid
, fieldValue
);
973 * Return a CFString containing the first email addresses for this certificate, based on the
974 * X509 SubjectAltName and SubjectName.
977 Certificate::copyFirstEmailAddress()
979 StLock
<Mutex
>_(mMutex
);
980 CFStringRef rtnString
;
982 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
983 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
984 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
985 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
986 std::vector
<CssmData
> emailAddresses
;
988 getNames(sanValues
, snValue
, GNT_RFC822Name
, emailAddresses
);
989 if (emailAddresses
.empty())
993 /* Encoding is kCFStringEncodingUTF8 since the string is either
994 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
995 rtnString
= CFStringCreateWithBytes(NULL
, emailAddresses
[0].Data
,
996 (CFIndex
)emailAddresses
[0].Length
, kCFStringEncodingUTF8
, true);
1001 releaseFieldValue(snOid
, snValue
);
1003 releaseFieldValues(sanOid
, sanValues
);
1009 * Return a CFArray containing the DNS hostnames for this certificate, based on the
1010 * X509 SubjectAltName and SubjectName.
1013 Certificate::copyDNSNames()
1015 StLock
<Mutex
>_(mMutex
);
1016 CFMutableArrayRef array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1017 std::vector
<CssmData
> dnsNames
;
1019 // Find the SubjectAltName fields, if any, and extract the GNT_DNSName entries from all of them
1020 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
1021 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
1023 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
1024 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
1026 getNames(sanValues
, snValue
, GNT_DNSName
, dnsNames
);
1028 for (std::vector
<CssmData
>::const_iterator it
= dnsNames
.begin(); it
!= dnsNames
.end(); ++it
)
1030 /* Encoding is kCFStringEncodingUTF8 since the string is either
1031 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
1032 CFStringRef string
= CFStringCreateWithBytes(NULL
, it
->Data
, static_cast<CFIndex
>(it
->Length
), kCFStringEncodingUTF8
, true);
1033 /* Be prepared for improperly formatted (non-UTF8) strings! */
1034 if (!string
) continue;
1035 CFArrayAppendValue(array
, string
);
1041 releaseFieldValue(snOid
, snValue
);
1043 releaseFieldValues(sanOid
, sanValues
);
1049 * Return a CFArray containing the email addresses for this certificate, based on the
1050 * X509 SubjectAltName and SubjectName.
1053 Certificate::copyEmailAddresses()
1055 StLock
<Mutex
>_(mMutex
);
1056 CFMutableArrayRef array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1057 std::vector
<CssmData
> emailAddresses
;
1059 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
1060 const CSSM_OID
&sanOid
= CSSMOID_SubjectAltName
;
1061 CSSM_DATA_PTR
*sanValues
= copyFieldValues(sanOid
);
1063 const CSSM_OID
&snOid
= CSSMOID_X509V1SubjectNameCStruct
;
1064 CSSM_DATA_PTR snValue
= copyFirstFieldValue(snOid
);
1066 getNames(sanValues
, snValue
, GNT_RFC822Name
, emailAddresses
);
1068 for (std::vector
<CssmData
>::const_iterator it
= emailAddresses
.begin(); it
!= emailAddresses
.end(); ++it
)
1070 /* Encoding is kCFStringEncodingUTF8 since the string is either
1071 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
1072 CFStringRef string
= CFStringCreateWithBytes(NULL
, it
->Data
, static_cast<CFIndex
>(it
->Length
), kCFStringEncodingUTF8
, true);
1073 /* Be prepared for improperly formatted (non-UTF8) strings! */
1074 if (!string
) continue;
1075 CFArrayAppendValue(array
, string
);
1081 releaseFieldValue(snOid
, snValue
);
1083 releaseFieldValues(sanOid
, sanValues
);
1088 const CSSM_X509_NAME_PTR
1089 Certificate::subjectName()
1091 StLock
<Mutex
>_(mMutex
);
1092 if (!mV1SubjectNameCStructValue
)
1093 if ((mV1SubjectNameCStructValue
= copyFirstFieldValue(CSSMOID_X509V1SubjectNameCStruct
)) == NULL
)
1096 return (const CSSM_X509_NAME_PTR
)mV1SubjectNameCStructValue
->Data
;
1099 const CSSM_X509_NAME_PTR
1100 Certificate::issuerName()
1102 StLock
<Mutex
>_(mMutex
);
1103 if (!mV1IssuerNameCStructValue
)
1104 if ((mV1IssuerNameCStructValue
= copyFirstFieldValue(CSSMOID_X509V1IssuerNameCStruct
)) == NULL
)
1107 return (const CSSM_X509_NAME_PTR
)mV1IssuerNameCStructValue
->Data
;
1111 Certificate::clHandle()
1113 StLock
<Mutex
>_(mMutex
);
1115 mCL
= clForType(type());
1117 return mCL
->handle();
1121 Certificate::operator < (Certificate
&other
)
1123 // Certificates in different keychains are considered equal if data is equal
1124 // Note that the Identity '<' operator relies on this assumption.
1125 return data() < other
.data();
1129 Certificate::operator == (Certificate
&other
)
1131 // Certificates in different keychains are considered equal if data is equal
1132 // Note that the Identity '==' operator relies on this assumption.
1133 return data() == other
.data();
1137 Certificate::update()
1143 Certificate::copyTo(const Keychain
&keychain
, Access
*newAccess
)
1145 StLock
<Mutex
>_(mMutex
);
1146 /* Certs can't have access controls. */
1148 MacOSError::throwMe(errSecNoAccessForItem
);
1150 Item
item(new Certificate(data(), type(), encoding()));
1151 keychain
->add(item
);
1156 Certificate::didModify()
1161 Certificate::add(Keychain
&keychain
)
1163 StLock
<Mutex
>_(mMutex
);
1164 // If we already have a Keychain we can't be added.
1166 MacOSError::throwMe(errSecDuplicateItem
);
1168 populateAttributes();
1170 CSSM_DB_RECORDTYPE recordType
= mDbAttributes
->recordType();
1172 Db
db(keychain
->database());
1173 // add the item to the (regular) db
1176 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
1178 catch (const CssmError
&e
)
1180 if (e
.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE
)
1183 // Create the cert relation and try again.
1184 db
->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
1185 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
1186 Schema::X509CertificateSchemaAttributeCount
,
1187 Schema::X509CertificateSchemaAttributeList
,
1188 Schema::X509CertificateSchemaIndexCount
,
1189 Schema::X509CertificateSchemaIndexList
);
1190 keychain
->keychainSchema()->didCreateRelation(
1191 CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
1192 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
1193 Schema::X509CertificateSchemaAttributeCount
,
1194 Schema::X509CertificateSchemaAttributeList
,
1195 Schema::X509CertificateSchemaIndexCount
,
1196 Schema::X509CertificateSchemaIndexList
);
1198 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
1201 mPrimaryKey
= keychain
->makePrimaryKey(recordType
, mUniqueId
);
1202 mKeychain
= keychain
;
1208 Certificate::publicKey()
1210 StLock
<Mutex
>_(mMutex
);
1211 SecPointer
<KeyItem
> keyItem
;
1212 // Return a CSSM_DATA_PTR with the value of the first field specified by field.
1213 // Caller must call releaseFieldValue to free the storage allocated by this call.
1214 // call OSStatus SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey); to retrieve
1216 CSSM_DATA_PTR keyPtr
= copyFirstFieldValue(CSSMOID_CSSMKeyStruct
);
1217 if (keyPtr
&& keyPtr
->Data
)
1219 CssmClient::CSP
csp(gGuidAppleCSP
);
1220 CssmKey
*cssmKey
= reinterpret_cast<CssmKey
*>(keyPtr
->Data
);
1221 CssmClient::Key
key(csp
, *cssmKey
);
1222 keyItem
= new KeyItem(key
);
1223 // Clear out KeyData since KeyItem() takes over ownership of the key, and we don't want it getting released.
1224 cssmKey
->KeyData
.Data
= NULL
;
1225 cssmKey
->KeyData
.Length
= 0;
1228 releaseFieldValue(CSSMOID_CSSMKeyStruct
, keyPtr
);
1233 // This function "borrowed" from the X509 CL, which is (currently) linked into
1234 // the Security.framework as a built-in plugin.
1235 extern "C" bool getField_normRDN_NSS (
1236 const CSSM_DATA
&derName
,
1237 uint32
&numFields
, // RETURNED (if successful, 0 or 1)
1238 CssmOwnedData
&fieldValue
); // RETURNED
1241 Certificate::cursorForIssuerAndSN(const StorageManager::KeychainList
&keychains
, const CssmData
&issuer
, const CssmData
&serialNumber
)
1243 CssmAutoData
fieldValue(Allocator::standard(Allocator::normal
));
1246 // We need to decode issuer, normalize it, then re-encode it
1247 if (!getField_normRDN_NSS(issuer
, numFields
, fieldValue
))
1248 MacOSError::throwMe(errSecDataNotAvailable
);
1250 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
1251 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
1252 cursor
->conjunctive(CSSM_DB_AND
);
1253 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateIssuer
, fieldValue
.get());
1254 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateSerialNumber
, serialNumber
);
1260 Certificate::cursorForIssuerAndSN_CF(const StorageManager::KeychainList
&keychains
, CFDataRef issuer
, CFDataRef serialNumber
)
1262 // This assumes a normalized issuer
1263 CSSM_DATA issuerCSSM
, serialNumberCSSM
;
1265 issuerCSSM
.Length
= CFDataGetLength(issuer
);
1266 issuerCSSM
.Data
= const_cast<uint8
*>(CFDataGetBytePtr(issuer
));
1268 serialNumberCSSM
.Length
= CFDataGetLength(serialNumber
);
1269 serialNumberCSSM
.Data
= const_cast<uint8
*>(CFDataGetBytePtr(serialNumber
));
1271 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
1272 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
1273 cursor
->conjunctive(CSSM_DB_AND
);
1274 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateIssuer
, issuerCSSM
);
1275 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateSerialNumber
, serialNumberCSSM
);
1281 Certificate::cursorForSubjectKeyID(const StorageManager::KeychainList
&keychains
, const CssmData
&subjectKeyID
)
1283 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
1284 cursor
->conjunctive(CSSM_DB_AND
);
1285 cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateSubjectKeyIdentifier
, subjectKeyID
);
1291 Certificate::cursorForEmail(const StorageManager::KeychainList
&keychains
, const char *emailAddress
)
1293 KCCursor
cursor(keychains
, kSecCertificateItemClass
, NULL
);
1296 cursor
->conjunctive(CSSM_DB_AND
);
1297 CssmSelectionPredicate
&pred
= cursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificateAlias
, emailAddress
);
1298 /* Normalize the emailAddresses in place since cursor already copied it. */
1299 normalizeEmailAddress(pred
.Attribute
.Value
[0]);
1305 SecPointer
<Certificate
>
1306 Certificate::findInKeychain(const StorageManager::KeychainList
&keychains
)
1308 StLock
<Mutex
>_(mMutex
);
1309 const CSSM_OID
&issuerOid
= CSSMOID_X509V1IssuerName
;
1310 CSSM_DATA_PTR issuerPtr
= copyFirstFieldValue(issuerOid
);
1311 CssmData
issuer(issuerPtr
->Data
, issuerPtr
->Length
);
1313 const CSSM_OID
&serialOid
= CSSMOID_X509V1SerialNumber
;
1314 CSSM_DATA_PTR serialPtr
= copyFirstFieldValue(serialOid
);
1315 CssmData
serial(serialPtr
->Data
, serialPtr
->Length
);
1317 SecPointer
<Certificate
> foundCert
= NULL
;
1319 foundCert
= findByIssuerAndSN(keychains
, issuer
, serial
);
1324 releaseFieldValue(issuerOid
, issuerPtr
);
1325 releaseFieldValue(serialOid
, serialPtr
);
1330 SecPointer
<Certificate
>
1331 Certificate::findByIssuerAndSN(const StorageManager::KeychainList
&keychains
, const CssmData
&issuer
, const CssmData
&serialNumber
)
1334 if (!cursorForIssuerAndSN(keychains
, issuer
, serialNumber
)->next(item
))
1335 CssmError::throwMe(errSecItemNotFound
);
1337 return static_cast<Certificate
*>(&*item
);
1340 SecPointer
<Certificate
>
1341 Certificate::findBySubjectKeyID(const StorageManager::KeychainList
&keychains
, const CssmData
&subjectKeyID
)
1344 if (!cursorForSubjectKeyID(keychains
, subjectKeyID
)->next(item
))
1345 CssmError::throwMe(errSecItemNotFound
);
1347 return static_cast<Certificate
*>(&*item
);
1350 SecPointer
<Certificate
>
1351 Certificate::findByEmail(const StorageManager::KeychainList
&keychains
, const char *emailAddress
)
1354 if (!cursorForEmail(keychains
, emailAddress
)->next(item
))
1355 CssmError::throwMe(errSecItemNotFound
);
1357 return static_cast<Certificate
*>(&*item
);
1360 /* Normalize emailAddresses in place. */
1362 Certificate::normalizeEmailAddress(CSSM_DATA
&emailAddress
)
1364 /* Do a check to see if a '\0' was at the end of emailAddress and strip it. */
1365 if (emailAddress
.Length
&& emailAddress
.Data
[emailAddress
.Length
- 1] == '\0')
1366 emailAddress
.Length
--;
1367 bool foundAt
= false;
1368 for (uint32 ix
= 0; ix
< emailAddress
.Length
; ++ix
)
1370 uint8 ch
= emailAddress
.Data
[ix
];
1373 if ('A' <= ch
&& ch
<= 'Z')
1374 emailAddress
.Data
[ix
] = ch
+ 'a' - 'A';
1382 Certificate::getNames(CSSM_DATA_PTR
*sanValues
, CSSM_DATA_PTR snValue
, CE_GeneralNameType generalNameType
, std::vector
<CssmData
> &names
)
1384 // Get the DNS host names or RFC822 email addresses for this certificate (depending on generalNameType),
1385 // within the X509 SubjectAltName and SubjectName.
1387 // Find the SubjectAltName fields, if any, and extract the nameType entries from all of them
1390 for (CSSM_DATA_PTR
*sanIx
= sanValues
; *sanIx
; ++sanIx
)
1392 CSSM_DATA_PTR sanValue
= *sanIx
;
1393 if (sanValue
&& sanValue
->Data
)
1395 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)sanValue
->Data
;
1396 CE_GeneralNames
*parsedValue
= (CE_GeneralNames
*)cssmExt
->value
.parsedValue
;
1398 /* Grab all the values that are of the specified name type. */
1399 for (uint32 i
= 0; i
< parsedValue
->numNames
; ++i
)
1401 if (parsedValue
->generalName
[i
].nameType
== generalNameType
)
1403 if (parsedValue
->generalName
[i
].berEncoded
) // can't handle this
1406 names
.push_back(CssmData::overlay(parsedValue
->generalName
[i
].name
));
1413 if (names
.empty() && snValue
&& snValue
->Data
)
1415 const CSSM_X509_NAME
&x509Name
= *(const CSSM_X509_NAME
*)snValue
->Data
;
1416 for (uint32 rdnDex
= 0; rdnDex
< x509Name
.numberOfRDNs
; rdnDex
++)
1418 const CSSM_X509_RDN
*rdnPtr
=
1419 &x509Name
.RelativeDistinguishedName
[rdnDex
];
1420 for (uint32 tvpDex
= 0; tvpDex
< rdnPtr
->numberOfPairs
; tvpDex
++)
1422 const CSSM_X509_TYPE_VALUE_PAIR
*tvpPtr
=
1423 &rdnPtr
->AttributeTypeAndValue
[tvpDex
];
1425 /* type/value pair: match caller's specified type */
1426 if (GNT_RFC822Name
== generalNameType
) {
1427 if (((tvpPtr
->type
.Length
!= CSSMOID_EmailAddress
.Length
) ||
1428 memcmp(tvpPtr
->type
.Data
, CSSMOID_EmailAddress
.Data
, CSSMOID_EmailAddress
.Length
))) {
1432 if (GNT_DNSName
== generalNameType
) {
1433 if (((tvpPtr
->type
.Length
!= CSSMOID_CommonName
.Length
) ||
1434 memcmp(tvpPtr
->type
.Data
, CSSMOID_CommonName
.Data
, CSSMOID_CommonName
.Length
))) {
1440 switch (tvpPtr
->valueType
)
1442 case BER_TAG_PRINTABLE_STRING
:
1443 case BER_TAG_IA5_STRING
:
1444 case BER_TAG_T61_STRING
:
1445 case BER_TAG_PKIX_UTF8_STRING
:
1447 names
.push_back(CssmData::overlay(tvpPtr
->value
));
1452 } /* for each pair */
1453 } /* for each RDN */
1457 void Certificate::willRead()
1459 populateAttributes();
1462 Boolean
Certificate::isSelfSigned()
1464 StLock
<Mutex
>_(mMutex
);
1465 CSSM_DATA_PTR issuer
= NULL
;
1466 CSSM_DATA_PTR subject
= NULL
;
1467 OSStatus ortn
= errSecSuccess
;
1468 Boolean brtn
= false;
1470 issuer
= copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd
);
1471 subject
= copyFirstFieldValue(CSSMOID_X509V1SubjectNameStd
);
1472 if((issuer
== NULL
) || (subject
== NULL
)) {
1475 else if((issuer
->Length
== subject
->Length
) &&
1476 !memcmp(issuer
->Data
, subject
->Data
, issuer
->Length
)) {
1480 /* names match: verify signature */
1482 CSSM_DATA certData
= data();
1483 crtn
= CSSM_CL_CertVerify(clHandle(), 0,
1484 &certData
, &certData
, NULL
, 0);
1490 releaseFieldValue(CSSMOID_X509V1IssuerNameStd
, issuer
);
1493 releaseFieldValue(CSSMOID_X509V1SubjectNameStd
, subject
);
1496 MacOSError::throwMe(ortn
);