]> git.saurik.com Git - apple/security.git/blob - SecurityTool/macOS/key_create.c
Security-59306.101.1.tar.gz
[apple/security.git] / SecurityTool / macOS / key_create.c
1 /*
2 * Copyright (c) 2003-2004,2006,2008,2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * key_create.c
24 */
25
26 #include "key_create.h"
27
28 #include "keychain_utilities.h"
29 #include "security_tool.h"
30
31 #include <CoreFoundation/CFDateFormatter.h>
32 #include <CoreFoundation/CFString.h>
33 #include <Security/SecAccess.h>
34 #include <Security/SecKey.h>
35 #include <Security/SecKeychainItem.h>
36 #include <Security/SecTrustedApplication.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42
43 static int
44 do_key_create_pair(const char *keychainName, SecAccessRef access, CSSM_ALGORITHMS algorithm, uint32 keySizeInBits, CFAbsoluteTime from_time, CFAbsoluteTime to_time, Boolean print_hash)
45 {
46 SecKeychainRef keychain = NULL;
47 OSStatus status;
48 int result = 0;
49 CSSM_CC_HANDLE contextHandle = 0;
50 CSSM_KEYUSE publicKeyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_DERIVE;
51 uint32 publicKeyAttr = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE;
52 CSSM_KEYUSE privateKeyUsage = CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_DERIVE;
53 uint32 privateKeyAttr = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE;
54 SecKeyRef publicKey = NULL;
55 SecKeyRef privateKey = NULL;
56 SecKeychainAttributeList *attrList = NULL;
57
58 if (keychainName)
59 {
60 keychain = keychain_open(keychainName);
61 if (!keychain)
62 {
63 result = 1;
64 goto loser;
65 }
66 }
67
68 status = SecKeyCreatePair(keychain, algorithm, keySizeInBits, contextHandle,
69 publicKeyUsage,
70 publicKeyAttr,
71 privateKeyUsage,
72 privateKeyAttr,
73 access,
74 &publicKey,
75 &privateKey);
76 if (status)
77 {
78 sec_error("SecKeyCreatePair %s: %s", keychainName ? keychainName : "<NULL>", sec_errstr(status));
79 result = 1;
80 goto loser;
81 }
82
83 if (print_hash)
84 {
85 SecItemClass itemClass = 0;
86 UInt32 tag = 0x00000006;
87 UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
88 SecKeychainAttributeInfo info = { 1, &tag, &format };
89
90 status = SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)privateKey, &info, &itemClass, &attrList, NULL, NULL);
91 if (status)
92 {
93 sec_perror("SecKeychainItemCopyAttributesAndData", status);
94 result = 1;
95 goto loser;
96 }
97
98 if (info.count != attrList->count)
99 {
100 sec_error("info count: %ld != attribute count: %ld", info.count, attrList->count);
101 result = 1;
102 goto loser;
103 }
104
105 if (tag != attrList->attr[0].tag)
106 {
107 sec_error("attribute info tag: %ld != attribute tag: %ld", tag, attrList->attr[0].tag);
108 result = 1;
109 goto loser;
110 }
111
112 print_buffer_pem(stdout, "PUBLIC KEY HASH", attrList->attr[0].length, attrList->attr[0].data);
113 }
114
115 loser:
116 if (attrList)
117 {
118 status = SecKeychainItemFreeAttributesAndData(attrList, NULL);
119 if (status)
120 sec_perror("SecKeychainItemFreeAttributesAndData", status);
121 }
122
123 if (keychain)
124 CFRelease(keychain);
125 if (publicKey)
126 CFRelease(publicKey);
127 if (privateKey)
128 CFRelease(privateKey);
129
130 return result;
131 }
132
133 static int
134 parse_algorithm(const char *name, CSSM_ALGORITHMS *algorithm)
135 {
136 size_t len = strlen(name);
137
138 if (!strncmp("rsa", name, len))
139 *algorithm = CSSM_ALGID_RSA;
140 else if (!strncmp("dsa", name, len))
141 *algorithm = CSSM_ALGID_DSA;
142 else if (!strncmp("dh", name, len))
143 *algorithm = CSSM_ALGID_DH;
144 else if (!strncmp("fee", name, len))
145 *algorithm = CSSM_ALGID_FEE;
146 else
147 {
148 sec_error("Invalid algorithm: %s", name);
149 return SHOW_USAGE_MESSAGE;
150 }
151
152 return 0;
153 }
154
155 static int
156 parse_time(const char *time, CFAbsoluteTime *ptime)
157 {
158 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, NULL, kCFDateFormatterShortStyle, kCFDateFormatterShortStyle);
159 CFStringRef time_string = CFStringCreateWithCString(NULL, time, kCFStringEncodingUTF8);
160 int result = 0;
161 if (!CFDateFormatterGetAbsoluteTimeFromString(formatter, time_string, NULL, ptime))
162 {
163 sec_error("%s is not a valid date", time);
164 result = 1;
165 }
166 if (formatter)
167 CFRelease(formatter);
168 if (time_string)
169 CFRelease(time_string);
170 return result;
171 }
172
173 int
174 key_create_pair(int argc, char * const *argv)
175 {
176 const char *keychainName = NULL;
177 CSSM_ALGORITHMS algorithm = CSSM_ALGID_RSA;
178 uint32 keySizeInBits = 512;
179 int ch, result = 0;
180 OSStatus status;
181 Boolean always_allow = FALSE;
182 Boolean print_hash = FALSE;
183 CFAbsoluteTime from_time = 0.0, to_time = 0.0;
184 double days = 0.0;
185 SecAccessRef access = NULL;
186 CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
187 CFStringRef description = NULL;
188
189 /*
190 { "create-keypair", key_create_pair,
191 "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-A|-T appPath] description\n"
192 " -a Use alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n"
193 " -s Specify the keysize in bits (default 512)\n"
194 " -f Make a key valid from the specified date\n"
195 " -t Make a key valid to the specified date\n"
196 " -d Make a key valid for the number of days specified from now\n"
197 " -k Use the specified keychain rather than the default\n"
198 " -H Print the public key hash attribute\n"
199 " -A Allow any application to access without warning\n"
200 " -T Allow the application specified to access without warning (multiple -T options are allowed)\n"
201 "If no options are provided, ask the user interactively.",
202 */
203
204 while ((ch = getopt(argc, argv, "a:s:f:t:d:k:AHT:h")) != -1)
205 {
206 switch (ch)
207 {
208 case 'a':
209 result = parse_algorithm(optarg, &algorithm);
210 if (result)
211 goto loser;
212 break;
213 case 's':
214 keySizeInBits = atoi(optarg);
215 break;
216 case 'k':
217 keychainName = optarg;
218 break;
219 case 'A':
220 always_allow = TRUE;
221 break;
222 case 'H':
223 print_hash = TRUE;
224 break;
225 case 'T':
226 {
227 if (optarg[0])
228 {
229 SecTrustedApplicationRef app = NULL;
230 status = SecTrustedApplicationCreateFromPath(optarg, &app);
231 if (status)
232 {
233 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
234 result = 1;
235 goto loser;
236 }
237
238 CFArrayAppendValue(trusted_list, app);
239 CFRelease(app);
240 }
241 break;
242 }
243 case 'f':
244 result = parse_time(optarg, &from_time);
245 if (result)
246 goto loser;
247 break;
248 case 't':
249 result = parse_time(optarg, &to_time);
250 if (result)
251 goto loser;
252 break;
253 case 'd':
254 days = atof(optarg);
255 if (days < 1)
256 {
257 result = 2;
258 goto loser;
259 }
260 from_time = CFAbsoluteTimeGetCurrent();
261 to_time = from_time + days * 86400.0;
262 break;
263 case '?':
264 default:
265 return SHOW_USAGE_MESSAGE;
266 }
267 }
268
269 argc -= optind;
270 argv += optind;
271
272 if (argc == 1)
273 {
274 if (*argv[0] == '\0')
275 {
276 result = 2;
277 goto loser;
278 }
279 description = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
280 }
281 else if (argc != 0)
282 return SHOW_USAGE_MESSAGE;
283 else
284 description = CFStringCreateWithCString(NULL, "<key>", kCFStringEncodingUTF8);
285
286 if (always_allow)
287 {
288 status = SecAccessCreate(description, NULL, &access);
289 if (status)
290 {
291 sec_perror("SecAccessCreate", status);
292 result = 1;
293 }
294 }
295 else
296 {
297 status = SecAccessCreate(description, trusted_list, &access);
298 if (status)
299 {
300 sec_perror("SecAccessCreate", status);
301 result = 1;
302 }
303 }
304
305 if (result)
306 goto loser;
307
308 result = do_key_create_pair(keychainName, access, algorithm, keySizeInBits, from_time, to_time, print_hash);
309
310 loser:
311 if (description)
312 CFRelease(description);
313 if (trusted_list)
314 CFRelease(trusted_list);
315 if (access)
316 CFRelease(access);
317
318 return result;
319 }
320
321 #if 0
322 static OSStatus
323 createCertCsr(
324 CSSM_TP_HANDLE tpHand, // eventually, a SecKeychainRef
325 CSSM_CL_HANDLE clHand,
326 CSSM_CSP_HANDLE cspHand,
327 SecKeyRef subjPubKey,
328 SecKeyRef signerPrivKey,
329 CSSM_ALGORITHMS sigAlg,
330 const CSSM_OID *sigOid,
331 /*
332 * Issuer's RDN is obtained from the issuer cert, if present, or is
333 * assumed to be the same as the subject name (i.e., we're creating
334 * a self-signed root cert).
335 */
336 CSSM_BOOL useAllDefaults,
337 CSSM_DATA_PTR csrData) // CSR: mallocd and RETURNED
338 {
339 CE_DataAndType exts[2];
340 CE_DataAndType *extp = exts;
341 unsigned numExts;
342
343 CSSM_DATA refId; // mallocd by CSSM_TP_SubmitCredRequest
344 CSSM_APPLE_TP_CERT_REQUEST certReq;
345 CSSM_TP_REQUEST_SET reqSet;
346 sint32 estTime;
347 CSSM_BOOL confirmRequired;
348 CSSM_TP_RESULT_SET_PTR resultSet;
349 CSSM_ENCODED_CERT *encCert;
350 CSSM_APPLE_TP_NAME_OID subjectNames[MAX_NAMES];
351 uint32 numNames;
352 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext;
353 CSSM_FIELD policyId;
354
355 /* Note a lot of the CSSM_APPLE_TP_CERT_REQUEST fields are not
356 * used for the createCsr option, but we'll fill in as much as is practical
357 * for either case.
358 */
359 numExts = 0;
360
361 char challengeBuf[400];
362 if(useAllDefaults) {
363 strcpy(challengeBuf, ZDEF_CHALLENGE);
364 }
365 else {
366 while(1) {
367 getStringWithPrompt("Enter challenge string: ",
368 challengeBuf, sizeof(challengeBuf));
369 if(challengeBuf[0] != '\0') {
370 break;
371 }
372 }
373 }
374 certReq.challengeString = challengeBuf;
375
376 /* name array, get from user. */
377 if(useAllDefaults) {
378 subjectNames[0].string = ZDEF_COMMON_NAME;
379 subjectNames[0].oid = &CSSMOID_CommonName;
380 subjectNames[1].string = ZDEF_ORG_NAME;
381 subjectNames[1].oid = &CSSMOID_OrganizationName;
382 subjectNames[2].string = ZDEF_COUNTRY;
383 subjectNames[2].oid = &CSSMOID_CountryName;
384 subjectNames[3].string = ZDEF_STATE;
385 subjectNames[3].oid = &CSSMOID_StateProvinceName;
386 numNames = 4;
387 }
388 else {
389 getNameOids(subjectNames, &numNames);
390 }
391
392 /* certReq */
393 certReq.cspHand = cspHand;
394 certReq.clHand = clHand;
395 certReq.serialNumber = 0x12345678; // TBD - random? From user?
396 certReq.numSubjectNames = numNames;
397 certReq.subjectNames = subjectNames;
398
399 /* TBD - if we're passed in a signing cert, certReq.issuerNameX509 will
400 * be obtained from that cert. For now we specify "self-signed" cert
401 * by not providing an issuer name at all. */
402 certReq.numIssuerNames = 0; // root for now
403 certReq.issuerNames = NULL;
404 certReq.issuerNameX509 = NULL;
405 certReq.certPublicKey = subjPubKey;
406 certReq.issuerPrivateKey = signerPrivKey;
407 certReq.signatureAlg = sigAlg;
408 certReq.signatureOid = *sigOid;
409 certReq.notBefore = 0; // TBD - from user
410 certReq.notAfter = 60 * 60 * 24 * 30; // seconds from now
411 certReq.numExtensions = numExts;
412 certReq.extensions = exts;
413
414 reqSet.NumberOfRequests = 1;
415 reqSet.Requests = &certReq;
416
417 /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */
418 memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
419 memset(&policyId, 0, sizeof(CSSM_FIELD));
420 policyId.FieldOid = CSSMOID_APPLE_TP_CSR_GEN;
421 CallerAuthContext.Policy.NumberOfPolicyIds = 1;
422 CallerAuthContext.Policy.PolicyIds = &policyId;
423
424 CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tpHand,
425 NULL, // PreferredAuthority
426 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
427 &reqSet,
428 &CallerAuthContext,
429 &estTime,
430 &refId);
431
432 /* before proceeding, free resources allocated thus far */
433 if(!useAllDefaults) {
434 freeNameOids(subjectNames, numNames);
435 }
436
437 if(crtn) {
438 printError("***Error submitting credential request","CSSM_TP_SubmitCredRequest",crtn);
439 return crtn;
440 }
441 crtn = CSSM_TP_RetrieveCredResult(tpHand,
442 &refId,
443 NULL, // CallerAuthCredentials
444 &estTime,
445 &confirmRequired,
446 &resultSet);
447 if(crtn) {
448 printError("***Error retreiving credential request","CSSM_TP_RetrieveCredResult",crtn);
449 return crtn;
450 }
451 if(resultSet == NULL) {
452 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
453 return ioErr;
454 }
455 encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
456 *csrData = encCert->CertBlob;
457
458 /* free resources allocated by TP */
459 APP_FREE(refId.Data);
460 APP_FREE(encCert);
461 APP_FREE(resultSet);
462 return noErr;
463 }
464 #endif
465
466 #if 0
467 /* this was added in Michael's integration of PR-3420772, but this is an unimplemented command */
468
469 int
470 csr_create(int argc, char * const *argv)
471 {
472 int result = 0;
473 int ch;
474 const char *keychainName = NULL;
475 CSSM_ALGORITHMS algorithm = CSSM_ALGID_RSA;
476 uint32 keySizeInBits = 512;
477 OSStatus status;
478 Boolean always_allow = FALSE;
479 CFAbsoluteTime from_time = 0.0, to_time = 0.0;
480 double days = 0.0;
481 SecAccessRef access = NULL;
482 CFMutableArrayRef trusted_list = NULL;
483 CFStringRef description = NULL;
484
485 /*
486 { "create-keypair", key_create_pair,
487 "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-u sdux] [-c challenge] description\n"
488 " [-k keychain] [-u sewx] description\n"
489 " -a Look for key with alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n"
490 " -s Look for key with keysize in bits\n"
491 " -c Add challenge to the key as a challange string\n"
492 " -f Look for a key at least valid from the specified date\n"
493 " -t Look for a key at least valid to the specified date\n"
494 " -d Look for a key at least valid for the number of days specified from now\n"
495 " -u Look for a key with the specified usage flags (s)igning (d)ecryption (u)nwrapping e(x)change\n"
496 " -k Look in specified keychain rather than the default search list\n"
497 "If no options are provided ask the user interactively",
498 */
499
500 while ((ch = getopt(argc, argv, "a:s:f:t:d:k:AT:h")) != -1)
501 {
502 switch (ch)
503 {
504 case 'a':
505 result = parse_algorithm(optarg, &algorithm);
506 if (result)
507 goto loser;
508 break;
509 case 's':
510 keySizeInBits = atoi(optarg);
511 break;
512 case 'k':
513 keychainName = optarg;
514 break;
515 case 'A':
516 always_allow = TRUE;
517 break;
518 case 'T':
519 {
520 if (!trusted_list)
521 {
522 trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
523 }
524
525 if (optarg[0])
526 {
527 SecTrustedApplicationRef app = NULL;
528 status = SecTrustedApplicationCreateFromPath(optarg, &app);
529 if (status)
530 {
531 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
532 result = 1;
533 goto loser;
534 }
535
536 CFArrayAppendValue(trusted_list, app);
537 CFRelease(app);
538 break;
539 }
540 }
541 case 'f':
542 result = parse_time(optarg, &from_time);
543 if (result)
544 goto loser;
545 break;
546 case 't':
547 result = parse_time(optarg, &to_time);
548 if (result)
549 goto loser;
550 break;
551 case 'd':
552 days = atof(optarg);
553 if (days < 1)
554 {
555 result = 2;
556 goto loser;
557 }
558 from_time = CFAbsoluteTimeGetCurrent();
559 to_time = from_time + days * 86400.0;
560 break;
561 case '?':
562 default:
563 return SHOW_USAGE_MESSAGE;
564 }
565 }
566
567 argc -= optind;
568 argv += optind;
569
570 if (argc == 1)
571 {
572 if (*argv[0] == '\0')
573 {
574 result = 2;
575 goto loser;
576 }
577 description = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
578 }
579 else if (argc != 0)
580 return SHOW_USAGE_MESSAGE;
581 else
582 description = CFStringCreateWithCString(NULL, "<key>", kCFStringEncodingUTF8);
583
584 if (always_allow)
585 {
586 status = SecAccessCreate(description, NULL, &access);
587 if (status)
588 {
589 sec_perror("SecAccessCreate", status);
590 result = 1;
591 }
592 // @@@ Make the acl always allow now.
593 }
594 else
595 {
596 status = SecAccessCreate(description, trusted_list, &access);
597 if (status)
598 {
599 sec_perror("SecAccessCreate", status);
600 result = 1;
601 }
602 }
603
604 if (result)
605 goto loser;
606
607 result = do_csr_create(keychainName, access, algorithm, keySizeInBits, from_time, to_time);
608
609 loser:
610 if (description)
611 CFRelease(description);
612 if (trusted_list)
613 CFRelease(trusted_list);
614 if (access)
615 CFRelease(access);
616
617 return result;
618 }
619 #endif