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.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 43static int 44do_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 115loser: 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 133static int 134parse_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 2; 150 } 151 152 return 0; 153} 154 155static int 156parse_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 173int 174key_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 2; /* @@@ Return 2 triggers 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 2; 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 310loser: 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 322static OSStatus 323createCertCsr( 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 469int 470csr_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 2; /* @@@ Return 2 triggers 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 2; 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 609loser: 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 620