2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
21 #include <Security/Certificate.h>
22 #include <Security/Schema.h>
23 #include <Security/oidscert.h>
24 #include <Security/SecCertificate.h>
25 #include <Security/cspclient.h>
27 using namespace KeychainCore
;
30 Certificate::clForType(CSSM_CERT_TYPE type
)
32 return CL(gGuidAppleX509CL
);
35 Certificate::Certificate(const CSSM_DATA
&data
, CSSM_CERT_TYPE type
, CSSM_CERT_ENCODING encoding
) :
36 ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE
, reinterpret_cast<SecKeychainAttributeList
*>(NULL
), UInt32(data
.Length
), reinterpret_cast<const void *>(data
.Data
)),
37 mHaveTypeAndEncoding(true),
45 // db item contstructor
46 Certificate::Certificate(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
) :
47 ItemImpl(keychain
, primaryKey
, uniqueId
),
48 mHaveTypeAndEncoding(false),
54 // PrimaryKey item contstructor
55 Certificate::Certificate(const Keychain
&keychain
, const PrimaryKey
&primaryKey
) :
56 ItemImpl(keychain
, primaryKey
),
57 mHaveTypeAndEncoding(false),
61 // @@@ In this case we don't know the type...
64 Certificate::Certificate(Certificate
&certificate
) :
65 ItemImpl(certificate
),
66 mHaveTypeAndEncoding(certificate
.mHaveTypeAndEncoding
),
67 mType(certificate
.mType
),
68 mEncoding(certificate
.mEncoding
),
74 Certificate::~Certificate()
77 CSSM_CL_CertAbortCache(mCL
->handle(), mCertHandle
);
81 Certificate::certHandle()
83 const CSSM_DATA
*cert
= &data();
86 if (CSSM_RETURN retval
= CSSM_CL_CertCache(mCL
->handle(), cert
, &mCertHandle
))
87 CssmError::throwMe(retval
);
93 /* 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. */
95 Certificate::copyFieldValues(const CSSM_OID
&field
)
97 CSSM_CL_HANDLE clHandle
= mCL
->handle();
98 CSSM_DATA_PTR fieldValue
, *fieldValues
;
99 CSSM_HANDLE resultsHandle
= 0;
100 uint32 numberOfFields
= 0;
103 result
= CSSM_CL_CertGetFirstCachedFieldValue(clHandle
, certHandle(), &field
, &resultsHandle
, &numberOfFields
, &fieldValue
);
106 if (result
== CSSMERR_CL_NO_FIELD_VALUES
)
109 CssmError::throwMe(result
);
112 fieldValues
= new CSSM_DATA_PTR
[numberOfFields
+ 1];
113 fieldValues
[0] = fieldValue
;
114 fieldValues
[numberOfFields
] = NULL
;
116 for (uint32 value
= 1; value
< numberOfFields
; ++value
)
118 CSSM_RETURN cresult
= CSSM_CL_CertGetNextCachedFieldValue(clHandle
, resultsHandle
, &fieldValues
[value
]);
121 fieldValues
[value
] = NULL
;
123 break; // No point in continuing really.
129 releaseFieldValues(field
, fieldValues
);
130 CssmError::throwMe(result
);
137 Certificate::releaseFieldValues(const CSSM_OID
&field
, CSSM_DATA_PTR
*fieldValues
)
141 CSSM_CL_HANDLE clHandle
= mCL
->handle();
143 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
144 CSSM_CL_FreeFieldValue(clHandle
, &field
, fieldValues
[ix
]);
146 delete[] fieldValues
;
151 Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO
&info
, const CSSM_OID
&field
)
153 CSSM_DATA_PTR
*fieldValues
= copyFieldValues(field
);
156 CssmDbAttributeData
&anAttr
= mDbAttributes
->add(info
);
157 for (int ix
= 0; fieldValues
[ix
]; ++ix
)
158 anAttr
.add(*fieldValues
[ix
], *mDbAttributes
);
160 releaseFieldValues(field
, fieldValues
);
164 /* 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. */
166 Certificate::copyFirstFieldValue(const CSSM_OID
&field
)
168 CSSM_CL_HANDLE clHandle
= mCL
->handle();
169 CSSM_DATA_PTR fieldValue
;
170 CSSM_HANDLE resultsHandle
= 0;
171 uint32 numberOfFields
= 0;
174 result
= CSSM_CL_CertGetFirstCachedFieldValue(clHandle
, certHandle(), &field
, &resultsHandle
, &numberOfFields
, &fieldValue
);
177 if (result
== CSSMERR_CL_NO_FIELD_VALUES
)
180 CssmError::throwMe(result
);
183 result
= CSSM_CL_CertAbortQuery(clHandle
, resultsHandle
);
187 releaseFieldValue(field
, fieldValue
);
188 CssmError::throwMe(result
);
195 Certificate::releaseFieldValue(const CSSM_OID
&field
, CSSM_DATA_PTR fieldValue
)
199 CSSM_CL_HANDLE clHandle
= mCL
->handle();
200 CSSM_CL_FreeFieldValue(clHandle
, &field
, fieldValue
);
207 This method computes the keyIdentifier for the public key in the cert as
210 The keyIdentifier is composed of the 160-bit SHA-1 hash of the
211 value of the BIT STRING subjectPublicKey (excluding the tag,
212 length, and number of unused bits).
215 Certificate::publicKeyHash(CssmData
&digestData
)
218 CSSM_DATA_PTR
*keysPtr
= copyFieldValues(CSSMOID_X509V1SubjectPublicKey
);
220 if (keysPtr
&& keysPtr
[0])
222 CssmData
&key
= CssmData::overlay(*keysPtr
[0]);
223 CssmClient::CSP
csp(gGuidAppleCSP
);
224 CssmClient::Digest
digest(csp
, CSSM_ALGID_SHA1
);
225 digest
.digest(key
, digestData
);
228 releaseFieldValues(CSSMOID_X509V1SubjectPublicKey
, keysPtr
);
230 CSSM_DATA_PTR keyPtr
= copyFirstFieldValue(CSSMOID_CSSMKeyStruct
);
231 if (keyPtr
&& keyPtr
->Data
)
233 CssmClient::CSP
csp(gGuidAppleCSP
);
234 CssmClient::PassThrough
passThrough(csp
);
235 CSSM_KEY
*key
= reinterpret_cast<CSSM_KEY
*>(keyPtr
->Data
);
239 /* Given a CSSM_KEY_PTR in any format, obtain the SSHA-1 hash of the
240 * associated key blob.
241 * Key is specified in CSSM_CSP_CreatePassThroughContext.
242 * Hash is allocated bythe CSP, in the App's memory, and returned
244 passThrough
.key(key
);
245 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
246 cssmData
= reinterpret_cast<CssmData
*>(outData
);
247 assert(cssmData
->Length
<= digestData
.Length
);
248 digestData
.Length
= cssmData
->Length
;
249 memcpy(digestData
.Data
, cssmData
->Data
, cssmData
->Length
);
250 csp
.allocator().free(cssmData
->Data
);
251 csp
.allocator().free(cssmData
);
254 releaseFieldValue(CSSMOID_CSSMKeyStruct
, keyPtr
);
259 Certificate::addLabel()
261 // Set label attribute for this certificate, based on the X509 subject name.
262 const CSSM_OID
&fieldOid
= CSSMOID_X509V1SubjectNameCStruct
;
263 CSSM_DATA_PTR fieldValue
= copyFirstFieldValue(fieldOid
);
264 if (fieldValue
&& fieldValue
->Data
)
266 CSSM_X509_NAME_PTR x509Name
= (CSSM_X509_NAME_PTR
)fieldValue
->Data
;
267 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
=0;
268 CSSM_X509_RDN_PTR rdnp
;
269 unsigned int rdnDex
, pairDex
;
271 // iterate through all RDN pairs; ptvp points to last entry when done
272 if (x509Name
->numberOfRDNs
) {
273 rdnp
= &x509Name
->RelativeDistinguishedName
[x509Name
->numberOfRDNs
-1];
274 if (rdnp
->numberOfPairs
)
275 ptvp
= &rdnp
->AttributeTypeAndValue
[rdnp
->numberOfPairs
-1];
279 CSSM_BER_TAG btag
= ptvp
->valueType
;
280 if (btag
==BER_TAG_PRINTABLE_STRING
|| btag
==BER_TAG_IA5_STRING
||
281 btag
==BER_TAG_T61_STRING
|| btag
==BER_TAG_PKIX_UTF8_STRING
)
283 mDbAttributes
->add(Schema::attributeInfo(kSecLabelItemAttr
), ptvp
->value
);
286 releaseFieldValue(fieldOid
, fieldValue
);
291 Certificate::populateAttributes()
293 addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr
), CSSMOID_X509V1SubjectName
);
294 addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr
), CSSMOID_X509V1IssuerName
);
295 addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr
), CSSMOID_X509V1SerialNumber
);
297 addParsedAttribute(Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr
), CSSMOID_SubjectKeyIdentifier
);
299 if(!mHaveTypeAndEncoding
)
300 MacOSError::throwMe(errSecDataNotAvailable
); // @@@ Or some other error.
302 // Adjust mType based on the actual version of the cert.
303 CSSM_DATA_PTR versionPtr
= copyFirstFieldValue(CSSMOID_X509V1Version
);
304 if (versionPtr
&& versionPtr
->Data
&& versionPtr
->Length
== sizeof(uint32
))
306 mType
= CSSM_CERT_X_509v1
+ (*reinterpret_cast<uint32
*>(versionPtr
->Data
));
309 mType
= CSSM_CERT_X_509v1
;
311 releaseFieldValue(CSSMOID_X509V1Version
, versionPtr
);
313 mDbAttributes
->add(Schema::attributeInfo(kSecCertTypeItemAttr
), mType
);
314 mDbAttributes
->add(Schema::attributeInfo(kSecCertEncodingItemAttr
), mEncoding
);
316 uint8 digestBytes
[20];
317 CssmData
digestData(digestBytes
, 20);
318 publicKeyHash(digestData
);
320 mDbAttributes
->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr
), digestData
);
327 CssmDataContainer
*data
= mData
.get();
328 if (!data
&& mKeychain
)
330 // Make sure mUniqueId is set.
332 data
= new CssmDataContainer();
334 mUniqueId
->get(NULL
, data
);
337 // If the data hasn't been set we can't return it.
339 MacOSError::throwMe(errSecDataNotAvailable
);
347 if (!mHaveTypeAndEncoding
)
349 SecKeychainAttribute attr
;
350 attr
.tag
= kSecCertTypeItemAttr
;
352 attr
.length
= sizeof(mType
);
353 getAttribute(attr
, NULL
);
360 Certificate::encoding()
362 if (!mHaveTypeAndEncoding
)
364 SecKeychainAttribute attr
;
365 attr
.tag
= kSecCertEncodingItemAttr
;
366 attr
.data
= &mEncoding
;
367 attr
.length
= sizeof(mEncoding
);
368 getAttribute(attr
, NULL
);
375 Certificate::getSubject(CSSM_X509_NAME
&outSubject
)
380 Certificate::getIssuer(CSSM_X509_NAME
&outName
)
385 Certificate::clHandle()
388 mCL
= clForType(type());
390 return mCL
->handle();
394 Certificate::operator < (Certificate
&other
)
396 return data() < other
.data();
400 Certificate::operator == (Certificate
&other
)
402 return data() == other
.data();
406 Certificate::update()
412 Certificate::copyTo(const Keychain
&keychain
)
414 return ItemImpl::copyTo(keychain
);
418 Certificate::didModify()
423 Certificate::add(Keychain
&keychain
)
425 // If we already have a Keychain we can't be added.
427 MacOSError::throwMe(errSecDuplicateItem
);
429 populateAttributes();
431 CSSM_DB_RECORDTYPE recordType
= mDbAttributes
->recordType();
433 Db
db(keychain
->database());
434 // add the item to the (regular) db
437 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
439 catch (const CssmError
&e
)
441 if (e
.cssmError() != CSSMERR_DL_INVALID_RECORDTYPE
)
444 // Create the cert relation and try again.
445 db
->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE
, "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
446 Schema::X509CertificateSchemaAttributeCount
,
447 Schema::X509CertificateSchemaAttributeList
,
448 Schema::X509CertificateSchemaIndexCount
,
449 Schema::X509CertificateSchemaIndexList
);
451 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
454 mPrimaryKey
= keychain
->makePrimaryKey(recordType
, mUniqueId
);
455 mKeychain
= keychain
;