2 * Copyright (c) 2003-2005,2008 Apple 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
7 * obtain a copy of the License at http://www.apple.com/publicsource and
8 * read it before using this file.
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
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights and
16 * limitations under the License.
20 * dbCert.cpp - import a possibly bad cert along with its private key
24 #include <Security/Security.h>
25 #include <security_cdsa_utils/cuFileIo.h>
26 #include <security_cdsa_utils/cuDbUtils.h>
27 #include <security_cdsa_utils/cuCdsaUtils.h>
28 #include <security_cdsa_utils/cuPem.h>
33 /* Copied from clxutils/clAppUtils/tpUtils */
34 /* defined in SecKeychainAPIPriv.h */
35 static const int kSecAliasItemAttr
= 'alis';
37 /* Macro to declare a CSSM_DB_SCHEMA_ATTRIBUTE_INFO */
38 #define SCHEMA_ATTR_INFO(id, name, type) \
39 { id, (char *)name, {0, NULL}, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
41 /* Too bad we can't get this from inside of the Security framework. */
42 static CSSM_DB_SCHEMA_ATTRIBUTE_INFO certSchemaAttrInfo
[] =
44 SCHEMA_ATTR_INFO(kSecCertTypeItemAttr
, "CertType", UINT32
),
45 SCHEMA_ATTR_INFO(kSecCertEncodingItemAttr
, "CertEncoding", UINT32
),
46 SCHEMA_ATTR_INFO(kSecLabelItemAttr
, "PrintName", BLOB
),
47 SCHEMA_ATTR_INFO(kSecAliasItemAttr
, "Alias", BLOB
),
48 SCHEMA_ATTR_INFO(kSecSubjectItemAttr
, "Subject", BLOB
),
49 SCHEMA_ATTR_INFO(kSecIssuerItemAttr
, "Issuer", BLOB
),
50 SCHEMA_ATTR_INFO(kSecSerialNumberItemAttr
, "SerialNumber", BLOB
),
51 SCHEMA_ATTR_INFO(kSecSubjectKeyIdentifierItemAttr
, "SubjectKeyIdentifier", BLOB
),
52 SCHEMA_ATTR_INFO(kSecPublicKeyHashItemAttr
, "PublicKeyHash", BLOB
)
54 #define NUM_CERT_SCHEMA_ATTRS \
55 (sizeof(certSchemaAttrInfo) / sizeof(CSSM_DB_SCHEMA_ATTRIBUTE_INFO))
57 /* Macro to declare a CSSM_DB_SCHEMA_INDEX_INFO */
58 #define SCHEMA_INDEX_INFO(id, indexNum, indexType) \
59 { id, CSSM_DB_INDEX_ ## indexType, CSSM_DB_INDEX_ON_ATTRIBUTE }
62 static CSSM_DB_SCHEMA_INDEX_INFO certSchemaIndices
[] =
64 SCHEMA_INDEX_INFO(kSecCertTypeItemAttr
, 0, UNIQUE
),
65 SCHEMA_INDEX_INFO(kSecIssuerItemAttr
, 0, UNIQUE
),
66 SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr
, 0, UNIQUE
),
67 SCHEMA_INDEX_INFO(kSecCertTypeItemAttr
, 1, NONUNIQUE
),
68 SCHEMA_INDEX_INFO(kSecSubjectItemAttr
, 2, NONUNIQUE
),
69 SCHEMA_INDEX_INFO(kSecIssuerItemAttr
, 3, NONUNIQUE
),
70 SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr
, 4, NONUNIQUE
),
71 SCHEMA_INDEX_INFO(kSecSubjectKeyIdentifierItemAttr
, 5, NONUNIQUE
),
72 SCHEMA_INDEX_INFO(kSecPublicKeyHashItemAttr
, 6, NONUNIQUE
)
74 #define NUM_CERT_INDICES \
75 (sizeof(certSchemaIndices) / sizeof(CSSM_DB_SCHEMA_INDEX_INFO))
78 CSSM_RETURN
tpAddCertSchema(
79 CSSM_DL_DB_HANDLE dlDbHand
)
81 return CSSM_DL_CreateRelation(dlDbHand
,
82 CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
83 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
84 NUM_CERT_SCHEMA_ATTRS
,
91 /* copied verbatim from certTool */
94 * Find private key by label, modify its Label attr to be the
95 * hash of the associated public key.
97 static CSSM_RETURN
setPubKeyHash(
98 CSSM_CSP_HANDLE cspHand
,
99 CSSM_DL_DB_HANDLE dlDbHand
,
100 const char *keyLabel
, // look up by this
101 CSSM_DATA
*rtnKeyDigest
) // optionally RETURNED, if so,
102 // caller owns and must cuAppFree
105 CSSM_SELECTION_PREDICATE predicate
;
106 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
109 CSSM_HANDLE resultHand
;
111 labelData
.Data
= (uint8
*)keyLabel
;
112 labelData
.Length
= strlen(keyLabel
) + 1; // incl. NULL
113 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
114 query
.Conjunctive
= CSSM_DB_NONE
;
115 query
.NumSelectionPredicates
= 1;
116 predicate
.DbOperator
= CSSM_DB_EQUAL
;
118 predicate
.Attribute
.Info
.AttributeNameFormat
=
119 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
120 predicate
.Attribute
.Info
.Label
.AttributeName
= (char *)"Label";
121 predicate
.Attribute
.Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
122 /* hope this cast is OK */
123 predicate
.Attribute
.Value
= &labelData
;
124 query
.SelectionPredicate
= &predicate
;
126 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
127 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
128 query
.QueryFlags
= 0; // CSSM_QUERY_RETURN_DATA; // FIXME - used?
130 /* build Record attribute with one attr */
131 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
132 CSSM_DB_ATTRIBUTE_DATA attr
;
133 attr
.Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
134 attr
.Info
.Label
.AttributeName
= (char *)"Label";
135 attr
.Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
137 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
138 recordAttrs
.NumberOfAttributes
= 1;
139 recordAttrs
.AttributeData
= &attr
;
141 CSSM_DATA recordData
= {0, NULL
};
142 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
148 /* abort only on success */
149 if(crtn
!= CSSM_OK
) {
150 cuPrintError("CSSM_DL_DataGetFirst", crtn
);
154 CSSM_KEY_PTR keyToDigest
= (CSSM_KEY_PTR
)recordData
.Data
;
155 CSSM_DATA_PTR keyDigest
= NULL
;
156 CSSM_CC_HANDLE ccHand
;
157 crtn
= CSSM_CSP_CreatePassThroughContext(cspHand
,
161 cuPrintError("CSSM_CSP_CreatePassThroughContext", crtn
);
164 crtn
= CSSM_CSP_PassThrough(ccHand
,
165 CSSM_APPLECSP_KEYDIGEST
,
167 (void **)&keyDigest
);
169 cuPrintError("CSSM_CSP_PassThrough(PUBKEYHASH)", crtn
);
172 CSSM_FreeKey(cspHand
, NULL
, keyToDigest
, CSSM_FALSE
);
173 CSSM_DeleteContext(ccHand
);
176 * Replace Label attr data with hash.
177 * NOTE: the module which allocated this attribute data - a DL -
178 * was loaded and attached by the Sec layer, not by us. Thus
179 * we can't use the memory allocator functions *we* used when
180 * attaching to the CSPDL - we have to use the ones
181 * which the Sec layer registered with the DL.
183 CSSM_API_MEMORY_FUNCS memFuncs
;
184 crtn
= CSSM_GetAPIMemoryFunctions(dlDbHand
.DLHandle
, &memFuncs
);
186 cuPrintError("CSSM_GetAPIMemoryFunctions(DLHandle)", crtn
);
187 /* oh well, leak and continue */
190 memFuncs
.free_func(attr
.Value
->Data
, memFuncs
.AllocRef
);
191 memFuncs
.free_func(attr
.Value
, memFuncs
.AllocRef
);
193 attr
.Value
= keyDigest
;
195 /* modify key attributes */
196 crtn
= CSSM_DL_DataModify(dlDbHand
,
197 CSSM_DL_DB_RECORD_PRIVATE_KEY
,
200 NULL
, // DataToBeModified
201 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
203 cuPrintError("CSSM_DL_DataModify(PUBKEYHASH)", crtn
);
206 crtn
= CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
208 cuPrintError("CSSM_DL_DataAbortQuery", crtn
);
209 /* let's keep going in this case */
211 crtn
= CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
213 cuPrintError("CSSM_DL_FreeUniqueRecord", crtn
);
214 /* let's keep going in this case */
220 *rtnKeyDigest
= *keyDigest
;
223 cuAppFree(keyDigest
->Data
, NULL
);
224 /* FIXME - don't we have to free keyDigest itself? */
229 static CSSM_RETURN
importPrivateKey(
230 CSSM_DL_DB_HANDLE dlDbHand
,
231 CSSM_CSP_HANDLE cspHand
,
232 const char *privKeyFileName
,
233 CSSM_ALGORITHMS keyAlg
,
234 CSSM_BOOL pemFormat
, // of the file
235 CSSM_KEYBLOB_FORMAT keyFormat
, // of the key blob itself, NONE means
237 CSSM_DATA
*keyHash
) // OPTIONALLY RETURNED - if so, caller
238 // owns and must cuAppFree()
240 unsigned char *derKey
= NULL
;
242 unsigned char *pemKey
= NULL
;
245 CSSM_KEY unwrappedKey
;
246 CSSM_ACCESS_CREDENTIALS creds
;
247 CSSM_CC_HANDLE ccHand
= 0;
250 CSSM_KEYHEADER_PTR hdr
= &wrappedKey
.KeyHeader
;
251 CSSM_DATA descData
= {0, NULL
};
252 CSSM_CSP_HANDLE rawCspHand
= 0;
253 const char *privKeyLabel
= NULL
;
256 * Validate specified format for clarity
261 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
262 keyFormat
= CSSM_KEYBLOB_RAW_FORMAT_PKCS1
; // default
264 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1
:
265 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
268 printf("***RSA Private key must be in PKCS1 or PKCS8 "
270 return CSSMERR_CSSM_INTERNAL_ERROR
;
272 privKeyLabel
= "Imported RSA key";
276 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
277 keyFormat
= CSSM_KEYBLOB_RAW_FORMAT_OPENSSL
;
280 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186
:
281 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL
:
282 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
285 printf("***DSA Private key must be in openssl, FIPS186, "
286 "or PKCS8 format\n");
287 return CSSMERR_CSSM_INTERNAL_ERROR
;
289 privKeyLabel
= "Imported DSA key";
293 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
294 keyFormat
= CSSM_KEYBLOB_RAW_FORMAT_PKCS8
; // default
296 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
299 printf("***Diffie-Hellman Private key must be in"
301 return CSSMERR_CSSM_INTERNAL_ERROR
;
303 privKeyLabel
= "Imported Diffie-Hellman key";
306 if(readFile(privKeyFileName
, &pemKey
, &pemKeyLen
)) {
307 printf("***Error reading private key from file %s. Aborting.\n",
309 return CSSMERR_CSSM_INTERNAL_ERROR
;
311 /* subsequent errors to done: */
313 int rtn
= pemDecode(pemKey
, pemKeyLen
, &derKey
, &derKeyLen
);
315 printf("***%s: Bad PEM formatting. Aborting.\n",
317 crtn
= CSSMERR_CSP_INVALID_KEY
;
323 derKeyLen
= pemKeyLen
;
326 /* importing a raw key into the CSPDL involves a NULL unwrap */
327 memset(&unwrappedKey
, 0, sizeof(CSSM_KEY
));
328 memset(&wrappedKey
, 0, sizeof(CSSM_KEY
));
330 /* set up the imported key to look like a CSSM_KEY */
331 hdr
->HeaderVersion
= CSSM_KEYHEADER_VERSION
;
332 hdr
->BlobType
= CSSM_KEYBLOB_RAW
;
333 hdr
->AlgorithmId
= keyAlg
;
334 hdr
->KeyClass
= CSSM_KEYCLASS_PRIVATE_KEY
;
335 hdr
->KeyAttr
= CSSM_KEYATTR_EXTRACTABLE
;
336 hdr
->KeyUsage
= CSSM_KEYUSE_ANY
;
337 hdr
->Format
= keyFormat
;
338 wrappedKey
.KeyData
.Data
= derKey
;
339 wrappedKey
.KeyData
.Length
= derKeyLen
;
341 /* get key size in bits from raw CSP */
342 rawCspHand
= cuCspStartup(CSSM_TRUE
);
343 if(rawCspHand
== 0) {
344 printf("***Error attaching to CSP. Aborting.\n");
345 crtn
= CSSMERR_CSSM_INTERNAL_ERROR
;
348 CSSM_KEY_SIZE keySize
;
349 crtn
= CSSM_QueryKeySizeInBits(rawCspHand
, CSSM_INVALID_HANDLE
, &wrappedKey
, &keySize
);
351 cuPrintError("CSSM_QueryKeySizeInBits",crtn
);
354 hdr
->LogicalKeySizeInBits
= keySize
.LogicalKeySizeInBits
;
356 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
357 crtn
= CSSM_CSP_CreateSymmetricContext(cspHand
,
358 CSSM_ALGID_NONE
, // unwrapAlg
359 CSSM_ALGMODE_NONE
, // unwrapMode
361 NULL
, // unwrappingKey
363 CSSM_PADDING_NONE
, // unwrapPad
367 cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn
);
371 /* add DL/DB to context */
372 CSSM_CONTEXT_ATTRIBUTE newAttr
;
373 newAttr
.AttributeType
= CSSM_ATTRIBUTE_DL_DB_HANDLE
;
374 newAttr
.AttributeLength
= sizeof(CSSM_DL_DB_HANDLE
);
375 newAttr
.Attribute
.Data
= (CSSM_DATA_PTR
)&dlDbHand
;
376 crtn
= CSSM_UpdateContextAttributes(ccHand
, 1, &newAttr
);
378 cuPrintError("CSSM_UpdateContextAttributes", crtn
);
382 /* do the NULL unwrap */
383 labelData
.Data
= (uint8
*)privKeyLabel
;
384 labelData
.Length
= strlen(privKeyLabel
) + 1;
385 crtn
= CSSM_UnwrapKey(ccHand
,
389 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_PERMANENT
|
390 CSSM_KEYATTR_SENSITIVE
|CSSM_KEYATTR_EXTRACTABLE
,
392 NULL
, // CredAndAclEntry
394 &descData
); // required
395 if(crtn
!= CSSM_OK
) {
396 cuPrintError("CSSM_UnwrapKey", crtn
);
400 /* one more thing: bind this private key to its public key */
401 crtn
= setPubKeyHash(cspHand
, dlDbHand
, privKeyLabel
, keyHash
);
403 /* We don't need the unwrapped key any more */
404 CSSM_FreeKey(cspHand
,
407 CSSM_FALSE
); // delete
411 CSSM_DeleteContext(ccHand
);
414 free(derKey
); // mallocd by readFile() */
416 if(pemFormat
&& pemKey
) {
420 CSSM_ModuleDetach(rawCspHand
);
426 CSSM_RETURN
importBadCert(
427 CSSM_DL_HANDLE dlHand
,
428 const char *dbFileName
,
429 const char *certFile
,
431 CSSM_ALGORITHMS keyAlg
,
432 CSSM_BOOL pemFormat
, // of the file
433 CSSM_KEYBLOB_FORMAT keyFormat
, // of the key blob itself, NONE means
437 CSSM_DL_DB_HANDLE dlDbHand
= {dlHand
, 0};
439 CSSM_DATA keyDigest
= {0, NULL
};
440 CSSM_DATA certData
= {0, NULL
};
443 CSSM_CSP_HANDLE cspHand
= cuCspStartup(CSSM_FALSE
);
445 printf("***Error attaching to CSPDL. Aborting.\n");
446 return CSSMERR_CSSM_ADDIN_LOAD_FAILED
;
450 * 1. Open the (already existing) DB.
452 dlDbHand
.DBHandle
= cuDbStartupByName(dlHand
,
453 (char *)dbFileName
, // bogus non-const prototype
454 CSSM_FALSE
, // do NOT create it
455 CSSM_FALSE
); // quiet
456 if(dlDbHand
.DBHandle
== 0) {
457 printf("Error opening %s. Aborting.\n", dbFileName
);
458 return CSSMERR_DL_DATASTORE_DOESNOT_EXIST
;
462 * Import key to DB, snagging its key digest along the way.
464 crtn
= importPrivateKey(dlDbHand
, cspHand
,
465 keyFile
, keyAlg
, pemFormat
, keyFormat
,
468 printf("***Error importing key %s. Aborting.\n", keyFile
);
475 if(readFile(certFile
, &certData
.Data
, &len
)) {
476 printf("***Error reading cert from %s. Aborting.\n", certFile
);
479 certData
.Length
= len
;
480 crtn
= cuAddCertToDb(dlDbHand
, &certData
,
481 CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
,
482 certFile
, // printName
484 if(crtn
== CSSMERR_DL_INVALID_RECORDTYPE
) {
485 /* virgin DB, no cert schema: add schema and retry */
486 crtn
= tpAddCertSchema(dlDbHand
);
487 if(crtn
== CSSM_OK
) {
488 crtn
= cuAddCertToDb(dlDbHand
, &certData
,
489 CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
,
490 certFile
, // printName
495 printf("***Error importing cert %s. Aborting.\n", certFile
);
499 cuAppFree(keyDigest
.Data
, NULL
);
501 if(dlDbHand
.DBHandle
) {
502 CSSM_DL_DbClose(dlDbHand
);