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