]> git.saurik.com Git - apple/security.git/blob - SecurityTool/macOS/key_create.c
Security-59754.41.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 }
169 if (time_string) {
170 CFRelease(time_string);
171 }
172 return result;
173 }
174
175 int
176 key_create_pair(int argc, char * const *argv)
177 {
178 const char *keychainName = NULL;
179 CSSM_ALGORITHMS algorithm = CSSM_ALGID_RSA;
180 uint32 keySizeInBits = 512;
181 int ch, result = 0;
182 OSStatus status;
183 Boolean always_allow = FALSE;
184 Boolean print_hash = FALSE;
185 CFAbsoluteTime from_time = 0.0, to_time = 0.0;
186 double days = 0.0;
187 SecAccessRef access = NULL;
188 CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
189 CFStringRef description = NULL;
190
191 /*
192 { "create-keypair", key_create_pair,
193 "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-A|-T appPath] description\n"
194 " -a Use alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n"
195 " -s Specify the keysize in bits (default 512)\n"
196 " -f Make a key valid from the specified date\n"
197 " -t Make a key valid to the specified date\n"
198 " -d Make a key valid for the number of days specified from now\n"
199 " -k Use the specified keychain rather than the default\n"
200 " -H Print the public key hash attribute\n"
201 " -A Allow any application to access without warning\n"
202 " -T Allow the application specified to access without warning (multiple -T options are allowed)\n"
203 "If no options are provided, ask the user interactively.",
204 */
205
206 while ((ch = getopt(argc, argv, "a:s:f:t:d:k:AHT:h")) != -1)
207 {
208 switch (ch)
209 {
210 case 'a':
211 result = parse_algorithm(optarg, &algorithm);
212 if (result)
213 goto loser;
214 break;
215 case 's':
216 keySizeInBits = atoi(optarg);
217 break;
218 case 'k':
219 keychainName = optarg;
220 break;
221 case 'A':
222 always_allow = TRUE;
223 break;
224 case 'H':
225 print_hash = TRUE;
226 break;
227 case 'T':
228 {
229 if (optarg[0])
230 {
231 SecTrustedApplicationRef app = NULL;
232 status = SecTrustedApplicationCreateFromPath(optarg, &app);
233 if (status)
234 {
235 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
236 result = 1;
237 goto loser;
238 }
239
240 CFArrayAppendValue(trusted_list, app);
241 CFRelease(app);
242 }
243 break;
244 }
245 case 'f':
246 result = parse_time(optarg, &from_time);
247 if (result)
248 goto loser;
249 break;
250 case 't':
251 result = parse_time(optarg, &to_time);
252 if (result)
253 goto loser;
254 break;
255 case 'd':
256 days = atof(optarg);
257 if (days < 1)
258 {
259 result = 2;
260 goto loser;
261 }
262 from_time = CFAbsoluteTimeGetCurrent();
263 to_time = from_time + days * 86400.0;
264 break;
265 case '?':
266 default:
267 return SHOW_USAGE_MESSAGE;
268 }
269 }
270
271 argc -= optind;
272 argv += optind;
273
274 if (argc == 1)
275 {
276 if (*argv[0] == '\0')
277 {
278 result = 2;
279 goto loser;
280 }
281 description = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
282 }
283 else if (argc != 0)
284 return SHOW_USAGE_MESSAGE;
285 else
286 description = CFStringCreateWithCString(NULL, "<key>", kCFStringEncodingUTF8);
287
288 if (always_allow)
289 {
290 status = SecAccessCreate(description, NULL, &access);
291 if (status)
292 {
293 sec_perror("SecAccessCreate", status);
294 result = 1;
295 }
296 }
297 else
298 {
299 status = SecAccessCreate(description, trusted_list, &access);
300 if (status)
301 {
302 sec_perror("SecAccessCreate", status);
303 result = 1;
304 }
305 }
306
307 if (result)
308 goto loser;
309
310 result = do_key_create_pair(keychainName, access, algorithm, keySizeInBits, from_time, to_time, print_hash);
311
312 loser:
313 if (description)
314 CFRelease(description);
315 if (trusted_list)
316 CFRelease(trusted_list);
317 if (access)
318 CFRelease(access);
319
320 return result;
321 }
322
323 #if 0
324 static OSStatus
325 createCertCsr(
326 CSSM_TP_HANDLE tpHand, // eventually, a SecKeychainRef
327 CSSM_CL_HANDLE clHand,
328 CSSM_CSP_HANDLE cspHand,
329 SecKeyRef subjPubKey,
330 SecKeyRef signerPrivKey,
331 CSSM_ALGORITHMS sigAlg,
332 const CSSM_OID *sigOid,
333 /*
334 * Issuer's RDN is obtained from the issuer cert, if present, or is
335 * assumed to be the same as the subject name (i.e., we're creating
336 * a self-signed root cert).
337 */
338 CSSM_BOOL useAllDefaults,
339 CSSM_DATA_PTR csrData) // CSR: mallocd and RETURNED
340 {
341 CE_DataAndType exts[2];
342 CE_DataAndType *extp = exts;
343 unsigned numExts;
344
345 CSSM_DATA refId; // mallocd by CSSM_TP_SubmitCredRequest
346 CSSM_APPLE_TP_CERT_REQUEST certReq;
347 CSSM_TP_REQUEST_SET reqSet;
348 sint32 estTime;
349 CSSM_BOOL confirmRequired;
350 CSSM_TP_RESULT_SET_PTR resultSet;
351 CSSM_ENCODED_CERT *encCert;
352 CSSM_APPLE_TP_NAME_OID subjectNames[MAX_NAMES];
353 uint32 numNames;
354 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext;
355 CSSM_FIELD policyId;
356
357 /* Note a lot of the CSSM_APPLE_TP_CERT_REQUEST fields are not
358 * used for the createCsr option, but we'll fill in as much as is practical
359 * for either case.
360 */
361 numExts = 0;
362
363 char challengeBuf[400];
364 if(useAllDefaults) {
365 strcpy(challengeBuf, ZDEF_CHALLENGE);
366 }
367 else {
368 while(1) {
369 getStringWithPrompt("Enter challenge string: ",
370 challengeBuf, sizeof(challengeBuf));
371 if(challengeBuf[0] != '\0') {
372 break;
373 }
374 }
375 }
376 certReq.challengeString = challengeBuf;
377
378 /* name array, get from user. */
379 if(useAllDefaults) {
380 subjectNames[0].string = ZDEF_COMMON_NAME;
381 subjectNames[0].oid = &CSSMOID_CommonName;
382 subjectNames[1].string = ZDEF_ORG_NAME;
383 subjectNames[1].oid = &CSSMOID_OrganizationName;
384 subjectNames[2].string = ZDEF_COUNTRY;
385 subjectNames[2].oid = &CSSMOID_CountryName;
386 subjectNames[3].string = ZDEF_STATE;
387 subjectNames[3].oid = &CSSMOID_StateProvinceName;
388 numNames = 4;
389 }
390 else {
391 getNameOids(subjectNames, &numNames);
392 }
393
394 /* certReq */
395 certReq.cspHand = cspHand;
396 certReq.clHand = clHand;
397 certReq.serialNumber = 0x12345678; // TBD - random? From user?
398 certReq.numSubjectNames = numNames;
399 certReq.subjectNames = subjectNames;
400
401 /* TBD - if we're passed in a signing cert, certReq.issuerNameX509 will
402 * be obtained from that cert. For now we specify "self-signed" cert
403 * by not providing an issuer name at all. */
404 certReq.numIssuerNames = 0; // root for now
405 certReq.issuerNames = NULL;
406 certReq.issuerNameX509 = NULL;
407 certReq.certPublicKey = subjPubKey;
408 certReq.issuerPrivateKey = signerPrivKey;
409 certReq.signatureAlg = sigAlg;
410 certReq.signatureOid = *sigOid;
411 certReq.notBefore = 0; // TBD - from user
412 certReq.notAfter = 60 * 60 * 24 * 30; // seconds from now
413 certReq.numExtensions = numExts;
414 certReq.extensions = exts;
415
416 reqSet.NumberOfRequests = 1;
417 reqSet.Requests = &certReq;
418
419 /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */
420 memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
421 memset(&policyId, 0, sizeof(CSSM_FIELD));
422 policyId.FieldOid = CSSMOID_APPLE_TP_CSR_GEN;
423 CallerAuthContext.Policy.NumberOfPolicyIds = 1;
424 CallerAuthContext.Policy.PolicyIds = &policyId;
425
426 CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tpHand,
427 NULL, // PreferredAuthority
428 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
429 &reqSet,
430 &CallerAuthContext,
431 &estTime,
432 &refId);
433
434 /* before proceeding, free resources allocated thus far */
435 if(!useAllDefaults) {
436 freeNameOids(subjectNames, numNames);
437 }
438
439 if(crtn) {
440 printError("***Error submitting credential request","CSSM_TP_SubmitCredRequest",crtn);
441 return crtn;
442 }
443 crtn = CSSM_TP_RetrieveCredResult(tpHand,
444 &refId,
445 NULL, // CallerAuthCredentials
446 &estTime,
447 &confirmRequired,
448 &resultSet);
449 if(crtn) {
450 printError("***Error retreiving credential request","CSSM_TP_RetrieveCredResult",crtn);
451 return crtn;
452 }
453 if(resultSet == NULL) {
454 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
455 return ioErr;
456 }
457 encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
458 *csrData = encCert->CertBlob;
459
460 /* free resources allocated by TP */
461 APP_FREE(refId.Data);
462 APP_FREE(encCert);
463 APP_FREE(resultSet);
464 return noErr;
465 }
466 #endif
467
468 #if 0
469 /* this was added in Michael's integration of PR-3420772, but this is an unimplemented command */
470
471 int
472 csr_create(int argc, char * const *argv)
473 {
474 int result = 0;
475 int ch;
476 const char *keychainName = NULL;
477 CSSM_ALGORITHMS algorithm = CSSM_ALGID_RSA;
478 uint32 keySizeInBits = 512;
479 OSStatus status;
480 Boolean always_allow = FALSE;
481 CFAbsoluteTime from_time = 0.0, to_time = 0.0;
482 double days = 0.0;
483 SecAccessRef access = NULL;
484 CFMutableArrayRef trusted_list = NULL;
485 CFStringRef description = NULL;
486
487 /*
488 { "create-keypair", key_create_pair,
489 "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-u sdux] [-c challenge] description\n"
490 " [-k keychain] [-u sewx] description\n"
491 " -a Look for key with alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n"
492 " -s Look for key with keysize in bits\n"
493 " -c Add challenge to the key as a challange string\n"
494 " -f Look for a key at least valid from the specified date\n"
495 " -t Look for a key at least valid to the specified date\n"
496 " -d Look for a key at least valid for the number of days specified from now\n"
497 " -u Look for a key with the specified usage flags (s)igning (d)ecryption (u)nwrapping e(x)change\n"
498 " -k Look in specified keychain rather than the default search list\n"
499 "If no options are provided ask the user interactively",
500 */
501
502 while ((ch = getopt(argc, argv, "a:s:f:t:d:k:AT:h")) != -1)
503 {
504 switch (ch)
505 {
506 case 'a':
507 result = parse_algorithm(optarg, &algorithm);
508 if (result)
509 goto loser;
510 break;
511 case 's':
512 keySizeInBits = atoi(optarg);
513 break;
514 case 'k':
515 keychainName = optarg;
516 break;
517 case 'A':
518 always_allow = TRUE;
519 break;
520 case 'T':
521 {
522 if (!trusted_list)
523 {
524 trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
525 }
526
527 if (optarg[0])
528 {
529 SecTrustedApplicationRef app = NULL;
530 status = SecTrustedApplicationCreateFromPath(optarg, &app);
531 if (status)
532 {
533 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
534 result = 1;
535 goto loser;
536 }
537
538 CFArrayAppendValue(trusted_list, app);
539 CFRelease(app);
540 break;
541 }
542 }
543 case 'f':
544 result = parse_time(optarg, &from_time);
545 if (result)
546 goto loser;
547 break;
548 case 't':
549 result = parse_time(optarg, &to_time);
550 if (result)
551 goto loser;
552 break;
553 case 'd':
554 days = atof(optarg);
555 if (days < 1)
556 {
557 result = 2;
558 goto loser;
559 }
560 from_time = CFAbsoluteTimeGetCurrent();
561 to_time = from_time + days * 86400.0;
562 break;
563 case '?':
564 default:
565 return SHOW_USAGE_MESSAGE;
566 }
567 }
568
569 argc -= optind;
570 argv += optind;
571
572 if (argc == 1)
573 {
574 if (*argv[0] == '\0')
575 {
576 result = 2;
577 goto loser;
578 }
579 description = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
580 }
581 else if (argc != 0)
582 return SHOW_USAGE_MESSAGE;
583 else
584 description = CFStringCreateWithCString(NULL, "<key>", kCFStringEncodingUTF8);
585
586 if (always_allow)
587 {
588 status = SecAccessCreate(description, NULL, &access);
589 if (status)
590 {
591 sec_perror("SecAccessCreate", status);
592 result = 1;
593 }
594 // @@@ Make the acl always allow now.
595 }
596 else
597 {
598 status = SecAccessCreate(description, trusted_list, &access);
599 if (status)
600 {
601 sec_perror("SecAccessCreate", status);
602 result = 1;
603 }
604 }
605
606 if (result)
607 goto loser;
608
609 result = do_csr_create(keychainName, access, algorithm, keySizeInBits, from_time, to_time);
610
611 loser:
612 if (description)
613 CFRelease(description);
614 if (trusted_list)
615 CFRelease(trusted_list);
616 if (access)
617 CFRelease(access);
618
619 return result;
620 }
621 #endif