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