]> git.saurik.com Git - apple/security.git/blob - CertTool/CertTool/CertTool.cpp
a1bda8f76c23307cf9eb543964b1c103f67e108c
[apple/security.git] / CertTool / CertTool / CertTool.cpp
1 /*
2 File: CertTool.cpp
3
4 Description: certificate manipulation tool
5
6 Author: dmitch
7
8 Copyright: © Copyright 2002 Apple Computer, Inc. All rights reserved.
9
10 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
11 Computer, Inc. ("Apple") in consideration of your agreement to
12 the following terms, and your use, installation, modification
13 or redistribution of this Apple software constitutes acceptance
14 of these terms. If you do not agree with these terms, please
15 do not use, install, modify or redistribute this Apple software.
16
17 In consideration of your agreement to abide by the following
18 terms, and subject to these terms, Apple grants you a personal,
19 non-exclusive license, under Apple's copyrights in this
20 original Apple software (the "Apple Software"), to use,
21 reproduce, modify and redistribute the Apple Software, with
22 or without modifications, in source and/or binary forms;
23 provided that if you redistribute the Apple Software in
24 its entirety and without modifications, you must retain
25 this notice and the following text and disclaimers in all
26 such redistributions of the Apple Software. Neither the
27 name, trademarks, service marks or logos of Apple Computer,
28 Inc. may be used to endorse or promote products derived from the
29 Apple Software without specific prior written permission from
30 Apple. Except as expressly stated in this notice, no other
31 rights or licenses, express or implied, are granted by Apple
32 herein, including but not limited to any patent rights that
33 may be infringed by your derivative works or by other works
34 in which the Apple Software may be incorporated.
35
36 The Apple Software is provided by Apple on an "AS IS" basis.
37 APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
38 WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
39 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
40 REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE
41 OR IN COMBINATION WITH YOUR PRODUCTS.
42
43 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
44 INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
48 AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
49 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
50 NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE
51 HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 */
53
54 #include <Security/SecKeychainItem.h>
55 #include <Security/SecKeychain.h>
56 #include <Security/SecKey.h>
57 #include <Security/SecAccess.h>
58 #include <Security/SecACL.h>
59 #include <Security/certextensions.h>
60 #include <Security/cssmapple.h>
61 #include <Security/oidsattr.h>
62 #include <Security/oidscert.h>
63 #include <Security/oidsalg.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <strings.h>
67 #include <ctype.h>
68 #include <sys/param.h>
69 #include <cdsaUtils/cdsaUtils.h>
70 #include <cdsaUtils/printCert.h>
71 #include <cdsaUtils/fileIo.h>
72 #include <cdsaUtils/pem.h>
73 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
74 #include "CertUI.h"
75 #include <CoreFoundation/CoreFoundation.h>
76 #include <Security/utilities.h>
77 #include <Security/aclclient.h>
78
79 /* will change soon */
80 #include <Security/SecCertificate.h>
81
82 /*
83 * Workaround flags.
84 */
85
86 /* SecKeychainGetCSPHandle implemented? */
87 #define SEC_KEYCHAIN_GET_CSP 0
88
89 /* SecCertificateAddToKeychain fully functional? */
90 #define SEC_CERT_ADD_TO_KC 1
91
92 /* SecKeyCreatePair() implemented */
93 #define SEC_KEY_CREATE_PAIR 0
94
95 #if !SEC_KEY_CREATE_PAIR
96 /* munge Label attr if manually generating keys */
97 #define MUNGE_LABEL_ATTR 1
98 #endif
99
100 #define KC_DB_PATH "Library/Keychains" /* relative to home */
101
102 /*
103 * defaults for undocumented 'Z' option
104 */
105 #define ZDEF_KEY_LABEL "testCert"
106 #define ZDEF_KEY_ALG CSSM_ALGID_RSA
107 #define ZDEF_KEY_SIZE 512
108 #define ZDEF_KEY_USAGE (kKeyUseSigning | kKeyUseEncrypting)
109 #define ZDEF_SIG_ALG CSSM_ALGID_SHA1WithRSA
110 #define ZDEF_SIG_OID CSSMOID_SHA1WithRSA
111 #define ZDEF_COMMON_NAME "10.0.61.5"
112 #define ZDEF_ORG_NAME "Apple Computer - DEBUG ONLY"
113 #define ZDEF_COUNTRY "US"
114 #define ZDEF_STATE "Washington"
115 #define ZDEF_CHALLENGE "someChallenge"
116
117 static void usage(char **argv)
118 {
119 printf("usage:\n");
120 printf(" Create a keypair and cert: %s c [options]\n", argv[0]);
121 printf(" Create a CSR: %s r outFileName [options]\n",
122 argv[0]);
123 printf(" Verify a CSR: %s v infileName [options]\n", argv[0]);
124 #if SEC_CERT_ADD_TO_KC
125 printf(" Import a certificate: %s i inFileName [options]\n", argv[0]);
126 #else
127 /* this one needs the printName */
128 printf(" Import a certificate: %s i inFileName printName [options]\n",
129 argv[0]);
130 #endif
131 printf(" Display a certificate: %s d inFileName [options]\n", argv[0]);
132 printf("Options:\n");
133 printf(" k=keychainName\n");
134 printf(" c(reate the keychain)\n");
135 printf(" v(erbose)\n");
136 printf(" d (CSR in DER format; default is PEM)\n");
137 printf(" h(elp)\n");
138 exit(1);
139 }
140
141 #if SEC_KEY_CREATE_PAIR
142 /* #error Work needed to generate key pair using Keychain. */
143 #else
144
145 /*
146 * Workaround to manually generate a key pair and munge its DB attributes
147 * to include the hash of the public key in the private key's Label attr.
148 */
149 #if MUNGE_LABEL_ATTR
150
151 /* Convert a reference key to a raw key. */
152 static CSSM_RETURN refKeyToRaw(
153 CSSM_CSP_HANDLE cspHand,
154 const CSSM_KEY *refKey,
155 CSSM_KEY_PTR rawKey) // RETURNED
156 {
157 CSSM_CC_HANDLE ccHand;
158 CSSM_RETURN crtn;
159 CSSM_ACCESS_CREDENTIALS creds;
160
161 memset(rawKey, 0, sizeof(CSSM_KEY));
162 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
163 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
164 CSSM_ALGID_NONE,
165 CSSM_ALGMODE_NONE,
166 &creds, // passPhrase
167 NULL, // wrapping key
168 NULL, // init vector
169 CSSM_PADDING_NONE, // Padding
170 0, // Params
171 &ccHand);
172 if(crtn) {
173 showError(crtn, "refKeyToRaw: context err");
174 return crtn;
175 }
176 crtn = CSSM_WrapKey(ccHand,
177 &creds,
178 refKey,
179 NULL, // DescriptiveData
180 rawKey);
181 if(crtn != CSSM_OK) {
182 showError(crtn, "refKeyToRaw: CSSM_WrapKey");
183 return crtn;
184 }
185 CSSM_DeleteContext(ccHand);
186 return CSSM_OK;
187 }
188
189 /*
190 * Find private key by label, modify its Label attr to be the
191 * hash of the associated public key.
192 */
193 static CSSM_RETURN setPubKeyHash(
194 CSSM_CSP_HANDLE cspHand,
195 CSSM_DL_DB_HANDLE dlDbHand,
196 const CSSM_KEY *pubKey, // to get hash
197 CSSM_KEY_PTR privKey, // its record gets updated
198 const char *keyLabel) // look up by this
199 {
200 CSSM_QUERY query;
201 CSSM_SELECTION_PREDICATE predicate;
202 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
203 CSSM_RETURN crtn;
204 CSSM_DATA labelData;
205 CSSM_HANDLE resultHand;
206
207 labelData.Data = (uint8 *)keyLabel;
208 labelData.Length = strlen(keyLabel) + 1; // incl. NULL
209 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
210 query.Conjunctive = CSSM_DB_NONE;
211 query.NumSelectionPredicates = 1;
212 predicate.DbOperator = CSSM_DB_EQUAL;
213
214 predicate.Attribute.Info.AttributeNameFormat =
215 CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
216 predicate.Attribute.Info.Label.AttributeName = "Label";
217 predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
218 /* hope this cast is OK */
219 predicate.Attribute.Value = &labelData;
220 query.SelectionPredicate = &predicate;
221
222 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
223 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
224 query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA; // FIXME - used?
225
226 /* build Record attribute with one attr */
227 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
228 CSSM_DB_ATTRIBUTE_DATA attr;
229 attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
230 attr.Info.Label.AttributeName = "Label";
231 attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
232
233 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
234 recordAttrs.NumberOfAttributes = 1;
235 recordAttrs.AttributeData = &attr;
236
237 crtn = CSSM_DL_DataGetFirst(dlDbHand,
238 &query,
239 &resultHand,
240 &recordAttrs,
241 NULL, // hopefully optional ...theData,
242 &record);
243 /* abort only on success */
244 if(crtn != CSSM_OK) {
245 showError(crtn, "CSSM_DL_DataGetFirst");
246 printf("***setPubKeyHash: can't find private key\n");
247 return crtn;
248 }
249
250 /* do NULL unwrap of public key for use with raw CSP */
251 CSSM_KEY rawPubKey;
252 crtn = refKeyToRaw(cspHand, pubKey, &rawPubKey);
253 if(crtn) {
254 printf("***Error converting public key to raw format\n");
255 return crtn;
256 }
257
258 /* connect to raw CSP */
259 CSSM_CSP_HANDLE rawCspHand = cuCspStartup(CSSM_TRUE);
260 if(rawCspHand == 0) {
261 printf("***Error connecting to raw CSP; aborting.\n");
262 return -1;
263 }
264
265 /* calculate hash of pub key */
266 CSSM_DATA_PTR keyDigest = NULL;
267 CSSM_CC_HANDLE ccHand;
268 crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand,
269 &rawPubKey,
270 &ccHand);
271 if(ccHand == 0) {
272 showError(crtn, "CSSM_CSP_CreatePassThroughContext");
273 printf("***Error calculating public key hash. Aborting.\n");
274 return -1;
275 }
276 crtn = CSSM_CSP_PassThrough(ccHand,
277 CSSM_APPLECSP_KEYDIGEST,
278 NULL,
279 (void **)&keyDigest);
280 if(crtn) {
281 showError(crtn, "CSSM_CSP_PassThrough(PUBKEYHASH)");
282 printf("***Error calculating public key hash. Aborting.\n");
283 return -1;
284 }
285 CSSM_FreeKey(cspHand, NULL, &rawPubKey, CSSM_FALSE);
286 CSSM_DeleteContext(ccHand);
287 CSSM_ModuleDetach(rawCspHand);
288
289 /*
290 * Replace Label attr data with hash.
291 * NOTE: the module which allocated this attribute data - a DL -
292 * was loaded and attached by the Sec layer, not by us. Thus
293 * we can't use the memory allocator functions *we* used when
294 * attaching to the CSPDL - we have to use the ones
295 * which the Sec layer registered with the DL.
296 */
297 CSSM_API_MEMORY_FUNCS memFuncs;
298 crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs);
299 if(crtn) {
300 showError(crtn, "CSSM_GetAPIMemoryFunctions(DLHandle)");
301 /* oh well, leak and continue */
302 }
303 else {
304 memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef);
305 memFuncs.free_func(attr.Value, memFuncs.AllocRef);
306 }
307 attr.Value = keyDigest;
308
309 /* modify key attributes */
310 crtn = CSSM_DL_DataModify(dlDbHand,
311 CSSM_DL_DB_RECORD_PRIVATE_KEY,
312 record,
313 &recordAttrs,
314 NULL, // DataToBeModified
315 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
316 if(crtn) {
317 showError(crtn, "CSSM_DL_DataModify(PUBKEYHASH)");
318 printf("***Error setting public key hash. Aborting.\n");
319 return crtn;
320 }
321 crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
322 if(crtn) {
323 showError(crtn, "CSSM_DL_DataAbortQuery");
324 /* let's keep going in this case */
325 }
326 crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record);
327 if(crtn) {
328 showError(crtn, "CSSM_DL_FreeUniqueRecord");
329 /* let's keep going in this case */
330 crtn = CSSM_OK;
331 }
332
333 /* free resources */
334 cuAppFree(keyDigest->Data, NULL);
335 return CSSM_OK;
336 }
337 #endif /* MUNGE_LABEL_ATTR */
338
339 /* Still on the !SEC_KEY_CREATE_PAIR workaround */
340
341 /*
342 * Generate a key pair using the CSPDL.
343 */
344 static OSStatus generateKeyPair(
345 CSSM_CSP_HANDLE cspHand,
346 CSSM_DL_DB_HANDLE dlDbHand,
347 CSSM_ALGORITHMS keyAlg, // e.g., CSSM_ALGID_RSA
348 uint32 keySizeInBits,
349 const char *keyLabel, // C string
350 CU_KeyUsage keyUsage, // CUK_Signing, etc.
351 CSSM_BOOL verbose,
352 const CSSM_KEY **pubKeyPtr, // mallocd, created, RETURNED
353 const CSSM_KEY **privKeyPtr) // mallocd, created, RETURNED
354 {
355 CSSM_KEY_PTR pubKey = reinterpret_cast<CSSM_KEY_PTR>(
356 APP_MALLOC(sizeof(CSSM_KEY)));
357 CSSM_KEY_PTR privKey = reinterpret_cast<CSSM_KEY_PTR>(
358 APP_MALLOC(sizeof(CSSM_KEY)));
359 if((pubKey == NULL) || (privKey == NULL)) {
360 return memFullErr;
361 }
362
363 CSSM_RETURN crtn;
364 CSSM_KEYUSE pubKeyUse = 0;
365 CSSM_KEYUSE privKeyUse = 0;
366
367 if(keyUsage & kKeyUseSigning) {
368 pubKeyUse |= CSSM_KEYUSE_VERIFY;
369 privKeyUse |= CSSM_KEYUSE_SIGN;
370 }
371 if(keyUsage & kKeyUseEncrypting) {
372 pubKeyUse |= (CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_WRAP);
373 privKeyUse |= (CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_UNWRAP);
374 }
375
376 crtn = cuCspGenKeyPair(cspHand,
377 &dlDbHand,
378 keyAlg,
379 keyLabel,
380 strlen(keyLabel) + 1,
381 keySizeInBits,
382 pubKey,
383 pubKeyUse,
384 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
385 privKey,
386 privKeyUse,
387 CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT);
388 if(crtn) {
389 APP_FREE(pubKey);
390 APP_FREE(privKey);
391 return paramErr;
392 }
393 if(verbose) {
394 printf("...%u bit key pair generated.\n",
395 (unsigned)keySizeInBits);
396 }
397
398 #if MUNGE_LABEL_ATTR
399 /* bind private key to cert by public key hash */
400 crtn = setPubKeyHash(cspHand,
401 dlDbHand,
402 pubKey,
403 privKey,
404 keyLabel);
405 if(crtn) {
406 printf("***Error setting public key hash. Continuing at peril.\n");
407 }
408 #endif /* MUNGE_LABEL_ATTR */
409
410 *pubKeyPtr = pubKey;
411 *privKeyPtr = privKey;
412 return noErr;
413 }
414 #endif /* SEC_KEY_CREATE_PAIR */
415
416 static void verifyCsr(
417 CSSM_CL_HANDLE clHand,
418 const char *fileName,
419 CSSM_BOOL pemFormat)
420 {
421 unsigned char *csr = NULL;
422 unsigned csrLen;
423 CSSM_DATA csrData;
424 unsigned char *der = NULL;
425 unsigned derLen = 0;
426
427 if(readFile(fileName, &csr, &csrLen)) {
428 printf("***Error reading CSR from file %s. Aborting.\n",
429 fileName);
430 return;
431 }
432 if(pemFormat) {
433 int rtn = pemDecode(csr, csrLen, &der, &derLen);
434 if(rtn) {
435 printf("***%s: Bad PEM formatting. Aborting.\n", fileName);
436 return;
437 }
438 csrData.Data = der;
439 csrData.Length = derLen;
440 }
441 else {
442 csrData.Data = csr;
443 csrData.Length = csrLen;
444 }
445
446 CSSM_RETURN crtn = CSSM_CL_PassThrough(clHand,
447 0, // CCHandle
448 CSSM_APPLEX509CL_VERIFY_CSR,
449 &csrData,
450 NULL);
451 if(crtn) {
452 cuPrintError("Verify CSR", crtn);
453 }
454 else {
455 printf("...CSR verified successfully.\n");
456 }
457 if(der) {
458 free(der);
459 }
460 if(csr) {
461 free(csr);
462 }
463 }
464
465 static void displayCert(
466 const char *fileName,
467 CSSM_BOOL pemFormat)
468 {
469 unsigned char *rawCert = NULL;
470 unsigned rawCertSize;
471 unsigned char *derCert = NULL;
472 unsigned derCertSize;
473 int rtn;
474
475 rtn = readFile(fileName, &rawCert, &rawCertSize);
476 if(rtn) {
477 printf("Error reading %s; aborting.\n", fileName);
478 return;
479 }
480 if(pemFormat) {
481 rtn = pemDecode(rawCert, rawCertSize, &derCert, &derCertSize);
482 if(rtn) {
483 printf("***%s: Bad PEM formatting. Aborting.\n", fileName);
484 return;
485 }
486 printCert(derCert, derCertSize, CSSM_TRUE);
487 free(derCert);
488 }
489 else {
490 printCert(rawCert, rawCertSize, CSSM_TRUE);
491 }
492 }
493
494 static void importCert(
495 SecKeychainRef kcRef, // if SEC_CERT_ADD_TO_KC
496 CSSM_DL_DB_HANDLE dlDbHand, // otherwise
497 const char *fileName,
498 CSSM_BOOL pemFormat,
499 /* cruft needed by cuAddCertToDb */
500 const char *printName) // C string
501 {
502 unsigned char *cert = NULL;
503 unsigned certLen;
504 CSSM_DATA certData;
505 unsigned char *der = NULL;
506 unsigned derLen = 0;
507 #if !SEC_CERT_ADD_TO_KC
508 CSSM_DATA pubKeyHash = {3, (uint8 *)"foo"};
509 #endif
510
511 if(readFile(fileName, &cert, &certLen)) {
512 printf("***Error reading certificate from file %s. Aborting.\n",
513 fileName);
514 return;
515 }
516 if(pemFormat) {
517 int rtn = pemDecode(cert, certLen, &der, &derLen);
518 if(rtn) {
519 printf("***%s: Bad PEM formatting. Aborting.\n", fileName);
520 return;
521 }
522 certData.Data = der;
523 certData.Length = derLen;
524 }
525 else {
526 certData.Data = cert;
527 certData.Length = certLen;
528 }
529
530 #if SEC_CERT_ADD_TO_KC
531 SecCertificateRef certRef;
532 OSStatus ortn = SecCertificateCreateFromData(
533 &certData,
534 CSSM_CERT_X_509v3,
535 CSSM_CERT_ENCODING_DER,
536 &certRef);
537 if(ortn) {
538 printf("***SecCertificateCreateFromData returned %d; aborting.\n",
539 (int)ortn);
540 return;
541 }
542 ortn = SecCertificateAddToKeychain(certRef, kcRef);
543 if(ortn) {
544 printf("***SecCertificateAddToKeychain returned %d; aborting.\n",
545 (int)ortn);
546 return;
547 }
548 #else
549 CSSM_RETURN crtn = cuAddCertToDb(dlDbHand,
550 &certData,
551 CSSM_CERT_X_509v3,
552 CSSM_CERT_ENCODING_DER,
553 printName, // printName
554 &pubKeyHash);
555 if(crtn) {
556 printf("***Error adding cert to keychain. Aborting.\n");
557 return;
558 }
559 #endif /* SEC_CERT_ADD_TO_KC */
560
561 printf("...certificate successfully imported.\n");
562 if(der) {
563 free(der);
564 }
565 if(cert) {
566 free(cert);
567 }
568 }
569
570
571 static OSStatus createCertCsr(
572 CSSM_BOOL createCsr, // true: CSR, false: Cert
573 CSSM_TP_HANDLE tpHand, // eventually, a SecKeychainRef
574 CSSM_CL_HANDLE clHand,
575 CSSM_CSP_HANDLE cspHand,
576 const CSSM_KEY *subjPubKey,
577 const CSSM_KEY *signerPrivKey,
578 CSSM_ALGORITHMS sigAlg,
579 const CSSM_OID *sigOid,
580 CU_KeyUsage keyUsage, // kKeyUseSigning, etc.
581 /*
582 * Issuer's RDN is obtained from the issuer cert, if present, or is
583 * assumed to be the same as the subject name (i.e., we're creating
584 * a self-signed root cert).
585 */
586 const CSSM_DATA *issuerCert,
587 CSSM_BOOL useAllDefaults,
588 CSSM_DATA_PTR certData) // mallocd and RETURNED
589 {
590 CE_DataAndType exts[2];
591 CE_DataAndType *extp = exts;
592 unsigned numExts;
593
594 CSSM_DATA refId; // mallocd by CSSM_TP_SubmitCredRequest
595 CSSM_APPLE_TP_CERT_REQUEST certReq;
596 CSSM_TP_REQUEST_SET reqSet;
597 sint32 estTime;
598 CSSM_BOOL confirmRequired;
599 CSSM_TP_RESULT_SET_PTR resultSet;
600 CSSM_ENCODED_CERT *encCert;
601 CSSM_APPLE_TP_NAME_OID subjectNames[MAX_NAMES];
602 uint32 numNames;
603 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext;
604 CSSM_FIELD policyId;
605
606 /* Note a lot of the CSSM_APPLE_TP_CERT_REQUEST fields are not
607 * used for the createCsr option, but we'll fill in as much as is practical
608 * for either case.
609 */
610 if(issuerCert != NULL) {
611 printf("createCertCsr: issuerCert not implemented\n");
612 return unimpErr;
613 }
614
615 numExts = 0;
616
617 char challengeBuf[400];
618 if(createCsr) {
619 if(useAllDefaults) {
620 strcpy(challengeBuf, ZDEF_CHALLENGE);
621 }
622 else {
623 while(1) {
624 getStringWithPrompt("Enter challenge string: ",
625 challengeBuf, sizeof(challengeBuf));
626 if(challengeBuf[0] != '\0') {
627 break;
628 }
629 }
630 }
631 certReq.challengeString = challengeBuf;
632 }
633 else {
634 /* creating cert */
635 certReq.challengeString = NULL;
636
637 /* KeyUsage extension */
638 extp->type = DT_KeyUsage;
639 extp->critical = CSSM_FALSE;
640 extp->extension.keyUsage = 0;
641 if(keyUsage & kKeyUseSigning) {
642 extp->extension.keyUsage |=
643 (CE_KU_DigitalSignature | CE_KU_KeyCertSign);
644 }
645 if(keyUsage & kKeyUseEncrypting) {
646 extp->extension.keyUsage |=
647 (CE_KU_KeyEncipherment | CE_KU_DataEncipherment);
648 }
649 extp++;
650 numExts++;
651
652 /* BasicConstraints */
653 extp->type = DT_BasicConstraints;
654 extp->critical = CSSM_TRUE;
655 extp->extension.basicConstraints.cA = CSSM_TRUE;
656 extp->extension.basicConstraints.pathLenConstraintPresent = CSSM_FALSE;
657 extp++;
658 numExts++;
659 }
660
661 /* name array, get from user. */
662 if(useAllDefaults) {
663 subjectNames[0].string = ZDEF_COMMON_NAME;
664 subjectNames[0].oid = &CSSMOID_CommonName;
665 subjectNames[1].string = ZDEF_ORG_NAME;
666 subjectNames[1].oid = &CSSMOID_OrganizationName;
667 subjectNames[2].string = ZDEF_COUNTRY;
668 subjectNames[2].oid = &CSSMOID_CountryName;
669 subjectNames[3].string = ZDEF_STATE;
670 subjectNames[3].oid = &CSSMOID_StateProvinceName;
671 numNames = 4;
672 }
673 else {
674 getNameOids(subjectNames, &numNames);
675 }
676
677 /* certReq */
678 certReq.cspHand = cspHand;
679 certReq.clHand = clHand;
680 certReq.serialNumber = 0x12345678; // TBD - random? From user?
681 certReq.numSubjectNames = numNames;
682 certReq.subjectNames = subjectNames;
683
684 /* TBD - if we're passed in a signing cert, certReq.issuerNameX509 will
685 * be obtained from that cert. For now we specify "self-signed" cert
686 * by not providing an issuer name at all. */
687 certReq.numIssuerNames = 0; // root for now
688 certReq.issuerNames = NULL;
689 certReq.issuerNameX509 = NULL;
690 certReq.certPublicKey = subjPubKey;
691 certReq.issuerPrivateKey = signerPrivKey;
692 certReq.signatureAlg = sigAlg;
693 certReq.signatureOid = *sigOid;
694 certReq.notBefore = 0; // TBD - from user
695 certReq.notAfter = 60 * 60 * 24 * 30; // seconds from now
696 certReq.numExtensions = numExts;
697 certReq.extensions = exts;
698
699 reqSet.NumberOfRequests = 1;
700 reqSet.Requests = &certReq;
701
702 /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */
703 memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
704 memset(&policyId, 0, sizeof(CSSM_FIELD));
705 if(createCsr) {
706 policyId.FieldOid = CSSMOID_APPLE_TP_CSR_GEN;
707 }
708 else {
709 policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;
710 }
711 CallerAuthContext.Policy.NumberOfPolicyIds = 1;
712 CallerAuthContext.Policy.PolicyIds = &policyId;
713 CssmClient::AclFactory factory;
714 CallerAuthContext.CallerCredentials = const_cast<Security::AccessCredentials *>(factory.promptCred());
715
716 CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tpHand,
717 NULL, // PreferredAuthority
718 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
719 &reqSet,
720 &CallerAuthContext,
721 &estTime,
722 &refId);
723
724 /* before proceeding, free resources allocated thus far */
725 if(!useAllDefaults) {
726 freeNameOids(subjectNames, numNames);
727 }
728
729 if(crtn) {
730 cuPrintError("CSSM_TP_SubmitCredRequest", crtn);
731 return crtn;
732 }
733 crtn = CSSM_TP_RetrieveCredResult(tpHand,
734 &refId,
735 NULL, // CallerAuthCredentials
736 &estTime,
737 &confirmRequired,
738 &resultSet);
739 if(crtn) {
740 cuPrintError("CSSM_TP_RetrieveCredResult", crtn);
741 return crtn;
742 }
743 if(resultSet == NULL) {
744 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
745 return ioErr;
746 }
747 encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
748 *certData = encCert->CertBlob;
749
750 /* free resources allocated by TP */
751 APP_FREE(refId.Data);
752 APP_FREE(encCert);
753 APP_FREE(resultSet);
754 return noErr;
755 }
756
757 typedef enum {
758 CO_Nop,
759 CO_CreateCert,
760 CO_CreateCSR,
761 CO_VerifyCSR,
762 CO_ImportCert,
763 CO_DisplayCert
764 } CertOp;
765
766 int main(int argc, char **argv)
767 {
768 SecKeychainRef kcRef = nil;
769 char kcPath[MAXPATHLEN + 1];
770 UInt32 kcPathLen = MAXPATHLEN + 1;
771 CSSM_BOOL createKc = CSSM_FALSE;
772 OSStatus ortn;
773 CSSM_DL_DB_HANDLE dlDbHand = {0, 0};
774 CSSM_CSP_HANDLE cspHand = 0;
775 CSSM_TP_HANDLE tpHand = 0;
776 CSSM_CL_HANDLE clHand = 0;
777 const CSSM_KEY *pubKey;
778 const CSSM_KEY *privKey;
779 int arg;
780 char *argp;
781 CSSM_BOOL verbose = CSSM_FALSE;
782 CSSM_ALGORITHMS keyAlg;
783 CSSM_ALGORITHMS sigAlg;
784 const CSSM_OID *sigOid;
785 CSSM_DATA certData = {0, NULL};
786 CSSM_RETURN crtn;
787 CU_KeyUsage keyUsage = 0;
788 bool isRoot;
789 CSSM_DATA keyLabel;
790 #if SEC_KEY_CREATE_PAIR
791 CSSM_KEYUSE pubKeyUse = 0;
792 CSSM_KEYUSE privKeyUse = 0;
793 CSSM_KEYATTR_FLAGS pubKeyAttrs;
794 CSSM_KEYATTR_FLAGS privKeyAttrs;
795 CFStringRef description = NULL;
796 SecAccessRef access = NULL;
797 CFArrayRef acls = NULL;
798 SecACLRef acl = NULL;
799 bool aclFound = false;
800 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector =
801 {
802 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION, 0
803 };
804 SecKeyRef pubKeyRef = 0, privKeyRef = 0;
805 #elif !MUNGE_LABEL_ATTR
806 CSSM_DATA pubKeyHash = {3, (uint8 *)"foo"};
807 #endif
808 CSSM_BOOL createCsr = CSSM_FALSE; // else create cert
809 int optArgs = 0;
810
811 /* command line arguments */
812 char *fileName = NULL;
813 CSSM_BOOL pemFormat = CSSM_TRUE;
814 char *certPrintName = NULL;
815 CertOp op = CO_Nop;
816 uint32 keySizeInBits;
817 char *kcName = NULL;
818 CSSM_BOOL useAllDefaults = CSSM_FALSE; // undoc'd cmd option
819
820 if(argc < 2) {
821 usage(argv);
822 }
823 switch(argv[1][0]) {
824 case 'c':
825 op = CO_CreateCert;
826 optArgs = 2;
827 break;
828 case 'r':
829 if(argc < 3) {
830 usage(argv);
831 }
832 op = CO_CreateCSR;
833 createCsr = CSSM_TRUE;
834 fileName = argv[2];
835 optArgs = 3;
836 break;
837 case 'v':
838 if(argc < 3) {
839 usage(argv);
840 }
841 op = CO_VerifyCSR;
842 fileName = argv[2];
843 optArgs = 3;
844 break;
845 case 'i':
846 #if SEC_CERT_ADD_TO_KC
847 if(argc < 3) {
848 usage(argv);
849 }
850 optArgs = 3;
851 #else
852 if(argc < 4) {
853 usage(argv);
854 }
855 certPrintName = argv[3];
856 optArgs = 4;
857 #endif /* SEC_CERT_ADD_TO_KC */
858 op = CO_ImportCert;
859 fileName = argv[2];
860 break;
861 case 'd':
862 if(argc < 3) {
863 usage(argv);
864 }
865 op = CO_DisplayCert;
866 fileName = argv[2];
867 optArgs = 3;
868 break;
869 default:
870 usage(argv);
871 }
872 for(arg=optArgs; arg<argc; arg++) {
873 argp = argv[arg];
874 switch(argp[0]) {
875 case 'k':
876 kcName = &argp[2];
877 break;
878 case 'v':
879 verbose = CSSM_TRUE;
880 break;
881 case 'd':
882 pemFormat = CSSM_FALSE;
883 break;
884 case 'c':
885 createKc = CSSM_TRUE;
886 break;
887 case 'Z':
888 /* undocumented "use all defaults quickly" option */
889 useAllDefaults = CSSM_TRUE;
890 break;
891 default:
892 usage(argv);
893 }
894 }
895 if(op == CO_DisplayCert) {
896 /* ready to roll */
897 displayCert(fileName, pemFormat);
898 return 0;
899 }
900
901 clHand = cuClStartup();
902 if(clHand == 0) {
903 printf("Error connecting to CL. Aborting.\n");
904 exit(1);
905 }
906
907 /* that's all we need for verifying a CSR */
908 if(op == CO_VerifyCSR) {
909 verifyCsr(clHand, fileName, pemFormat);
910 goto abort;
911 }
912
913 /* remaining ops need TP and CSP as well */
914 #if !SEC_KEYCHAIN_GET_CSP
915 /* get it from keychain */
916 cspHand = cuCspStartup(CSSM_FALSE);
917 if(cspHand == 0) {
918 printf("Error connecting to CSP/DL. Aborting.\n");
919 exit(1);
920 }
921 #endif
922 tpHand = cuTpStartup();
923 if(tpHand == 0) {
924 printf("Error connecting to TP. Aborting.\n");
925 exit(1);
926 }
927
928 if(kcName) {
929 char *userHome = getenv("HOME");
930
931 if(userHome == NULL) {
932 /* well, this is probably not going to work */
933 userHome = "";
934 }
935 sprintf(kcPath, "%s/%s/%s", userHome, KC_DB_PATH, kcName);
936
937 }
938 else {
939 /* use default keychain */
940 ortn = SecKeychainCopyDefault(&kcRef);
941 if(ortn) {
942 showError(ortn, "SecKeychainCopyDefault");
943 exit(1);
944 }
945 ortn = SecKeychainGetPath(kcRef, &kcPathLen, kcPath);
946 if(ortn) {
947 showError(ortn, "SecKeychainGetPath");
948 exit(1);
949 }
950
951 /*
952 * OK, we have a path, we have to release the first KC ref,
953 * then get another one by opening it
954 */
955 CFRelease(kcRef);
956 }
957 if(createKc) {
958 ortn = SecKeychainCreate(kcPath,
959 0, // no password
960 NULL, // ditto
961 true, // promptUser
962 nil, // initialAccess
963 &kcRef);
964 /* fixme - do we have to open it? */
965 if(ortn) {
966 showError(ortn, "SecKeychainCreateNew");
967 printf("***Error creating keychain at %s; aborting.\n", kcPath);
968 exit(1);
969 }
970 }
971 else {
972 ortn = SecKeychainOpen(kcPath, &kcRef);
973 if(ortn) {
974 showError(ortn, "SecKeychainOpen");
975 printf("Cannot open keychain at %s. Aborting.\n", kcPath);
976 exit(1);
977 }
978 }
979
980 /* get associated DL/DB handle */
981 ortn = SecKeychainGetDLDBHandle(kcRef, &dlDbHand);
982 if(ortn) {
983 showError(ortn, "SecKeychainGetDLDBHandle");
984 exit(1);
985 }
986
987 if(op == CO_ImportCert) {
988 importCert(kcRef, dlDbHand, fileName, pemFormat, certPrintName);
989 goto abort;
990 }
991
992 #if SEC_KEYCHAIN_GET_CSP
993 /* create cert, CSR need CSP handle */
994 ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
995 if(ortn) {
996 showError(ortn, "SecKeychainGetCSPHandle");
997 exit(1);
998 }
999 #endif
1000
1001 /*
1002 * TBD: eventually we want to present the option of using an existing
1003 * SecIdentityRef from the keychain as the signing cert/key. If none
1004 * found or the user says they want a root, we generate the signing key
1005 * pair as follows....
1006 */
1007 isRoot = true;
1008
1009 /*
1010 * Generate a key pair. For now we do this via CDSA.
1011 */
1012 char labelBuf[200];
1013 if(useAllDefaults) {
1014 strcpy(labelBuf, ZDEF_KEY_LABEL);
1015 }
1016 else {
1017 while(1) {
1018 getStringWithPrompt("Enter key and certificate label: ", labelBuf,
1019 sizeof(labelBuf));
1020 if(labelBuf[0] != '\0') {
1021 break;
1022 }
1023 }
1024 }
1025 keyLabel.Data = (uint8 *)labelBuf;
1026 keyLabel.Length = strlen(labelBuf);
1027
1028 /* get key algorithm and size */
1029 if(useAllDefaults) {
1030 keyAlg = ZDEF_KEY_ALG;
1031 keySizeInBits = ZDEF_KEY_SIZE;
1032 }
1033 else {
1034 getKeyParams(keyAlg, keySizeInBits);
1035 }
1036
1037 /* get usage for keys and certs */
1038 if(useAllDefaults) {
1039 keyUsage = ZDEF_KEY_USAGE;
1040 }
1041 else {
1042 keyUsage = getKeyUsage(isRoot);
1043 }
1044
1045 printf("...Generating key pair...\n");
1046 #if SEC_KEY_CREATE_PAIR
1047
1048 description = CFStringCreateWithCString(NULL, labelBuf, kCFStringEncodingMacRoman);
1049 ortn = SecAccessCreate(description, NULL, &access);
1050 if(ortn) {
1051 printf("Error creating SecAccessRef; aborting.\n");
1052 goto abort;
1053 }
1054 ortn = SecAccessCopyACLList(access, &acls);
1055 if (ortn) {
1056 printf("Error calling SecAccessCopyACLList; aborting.\n");
1057 goto abort;
1058 }
1059 for (CFIndex ix = 0; ix < CFArrayGetCount(acls); ++ix)
1060 {
1061 CSSM_ACL_AUTHORIZATION_TAG tags[20];
1062 uint32 tagCount = 20;
1063
1064 acl = (SecACLRef)CFArrayGetValueAtIndex(acls, ix);
1065 ortn = SecACLGetAuthorizations(acl, tags, &tagCount);
1066 if(ortn) {
1067 printf("Error calling SecACLGetAuthorizations; aborting.\n");
1068 goto abort;
1069 }
1070
1071 for (uint32 tix = 0; tix < tagCount; ++tix)
1072 {
1073 if (tags[tix] == CSSM_ACL_AUTHORIZATION_DECRYPT
1074 || tags[tix] == CSSM_ACL_AUTHORIZATION_ANY)
1075 {
1076 aclFound = true;
1077 break;
1078 }
1079 }
1080
1081 if (aclFound)
1082 break;
1083 }
1084
1085 if (!aclFound) {
1086 printf("Could not find ACL for decrypt right; aborting.\n");
1087 goto abort;
1088 }
1089
1090 // Make the ACL an any allowed acl by setting the trusted application list to NULL
1091 ortn = SecACLSetSimpleContents(acl, NULL, description, &promptSelector);
1092 if(ortn) {
1093 printf("Error calling SecACLSetSimpleContents; aborting.\n");
1094 goto abort;
1095 }
1096
1097 pubKeyAttrs = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT;
1098 privKeyAttrs = CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT;
1099
1100 if(keyUsage & kKeyUseSigning) {
1101 pubKeyUse |= CSSM_KEYUSE_VERIFY;
1102 privKeyUse |= CSSM_KEYUSE_SIGN;
1103 }
1104 if(keyUsage & kKeyUseEncrypting) {
1105 pubKeyUse |= (CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_WRAP);
1106 privKeyUse |= (CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_UNWRAP);
1107 }
1108
1109 ortn = SecKeyCreatePair(kcRef, keyAlg, keySizeInBits, 0, pubKeyUse, pubKeyAttrs, privKeyUse, privKeyAttrs, access, &pubKeyRef, &privKeyRef);
1110
1111 if (!ortn)
1112 ortn = SecKeyGetCSSMKey(pubKeyRef, &pubKey);
1113 if (!ortn)
1114 ortn = SecKeyGetCSSMKey(privKeyRef, &privKey);
1115 #else
1116 ortn = generateKeyPair(cspHand,
1117 dlDbHand,
1118 keyAlg,
1119 keySizeInBits,
1120 labelBuf,
1121 keyUsage,
1122 verbose,
1123 &pubKey,
1124 &privKey);
1125 #endif /* SEC_KEY_CREATE_PAIR */
1126 if(ortn) {
1127 printf("Error generating keys; aborting.\n");
1128 goto abort;
1129 }
1130
1131 /* get signing algorithm per the signing key */
1132 if(useAllDefaults) {
1133 sigAlg = ZDEF_SIG_ALG;
1134 sigOid = &ZDEF_SIG_OID;
1135 }
1136 else {
1137 ortn = getSigAlg(privKey, sigAlg, sigOid);
1138 if(ortn) {
1139 printf("Can not sign with this private key. Aborting.\n");
1140 goto abort;
1141 }
1142 }
1143
1144 if(createCsr) {
1145 printf("...creating CSR...\n");
1146 }
1147 else {
1148 printf("...creating certificate...\n");
1149 }
1150 /* generate the cert */
1151 ortn = createCertCsr(createCsr,
1152 tpHand,
1153 clHand,
1154 cspHand,
1155 pubKey,
1156 privKey,
1157 sigAlg,
1158 sigOid,
1159 keyUsage,
1160 NULL, // issuer cert
1161 useAllDefaults,
1162 &certData);
1163 if(ortn) {
1164 goto abort;
1165 }
1166 if(verbose) {
1167 printCert(certData.Data, certData.Length, CSSM_FALSE);
1168 printCertShutdown();
1169 }
1170
1171 if(createCsr) {
1172 /* just write this to a file */
1173 unsigned char *pem = NULL;
1174 unsigned pemLen;
1175 int rtn;
1176
1177 if(pemFormat) {
1178 rtn = pemEncode(certData.Data, certData.Length, &pem, &pemLen,
1179 "CERTIFICATE REQUEST");
1180 if(rtn) {
1181 /* very unlikely, I think malloc is the only failure */
1182 printf("***Error PEM-encoding CSR. Aborting.\n");
1183 goto abort;
1184 }
1185 rtn = writeFile(fileName, pem, pemLen);
1186 }
1187 else {
1188 rtn = writeFile(fileName, certData.Data, certData.Length);
1189 }
1190 if(rtn) {
1191 printf("***Error writing CSR to %s\n", fileName);
1192 }
1193 else {
1194 printf("Wrote %u bytes of CSR to %s\n", (unsigned)certData.Length,
1195 fileName);
1196 }
1197 if(pem) {
1198 free(pem);
1199 }
1200 }
1201 else {
1202 /* store the cert in the same DL/DB as the key pair */
1203 #if SEC_CERT_ADD_TO_KC
1204 crtn = cuAddCertToKC(kcRef,
1205 &certData,
1206 CSSM_CERT_X_509v3,
1207 CSSM_CERT_ENCODING_DER,
1208 labelBuf, // printName
1209 &keyLabel);
1210 #else
1211 crtn = cuAddCertToDb(dlDbHand,
1212 &certData,
1213 CSSM_CERT_X_509v3,
1214 CSSM_CERT_ENCODING_DER,
1215 labelBuf, // printName
1216 &pubKeyHash);
1217 #endif /* SEC_CERT_ADD_TO_KC */
1218 if(crtn == CSSM_OK) {
1219 printf("..cert stored in Keychain.\n");
1220 }
1221 }
1222 abort:
1223 /* CLEANUP */
1224 #if SEC_KEY_CREATE_PAIR
1225 if (description) CFRelease(description);
1226 if (access) CFRelease(access);
1227 if (acls) CFRelease(acls);
1228 if (pubKeyRef) CFRelease(pubKeyRef);
1229 if (privKeyRef) CFRelease(privKeyRef);
1230 #endif /* SEC_KEY_CREATE_PAIR */
1231 return 0;
1232 }
1233