X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTool/key_create.c?ds=inline diff --git a/SecurityTool/key_create.c b/SecurityTool/key_create.c new file mode 100644 index 00000000..db76d601 --- /dev/null +++ b/SecurityTool/key_create.c @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2003-2004,2006,2008,2012,2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * key_create.c + */ + +#include "key_create.h" + +#include "keychain_utilities.h" +#include "security.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static int +do_key_create_pair(const char *keychainName, SecAccessRef access, CSSM_ALGORITHMS algorithm, uint32 keySizeInBits, CFAbsoluteTime from_time, CFAbsoluteTime to_time, Boolean print_hash) +{ + SecKeychainRef keychain = NULL; + OSStatus status; + int result = 0; + CSSM_CC_HANDLE contextHandle = 0; + CSSM_KEYUSE publicKeyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_DERIVE; + uint32 publicKeyAttr = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE; + CSSM_KEYUSE privateKeyUsage = CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_DERIVE; + uint32 privateKeyAttr = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE; + SecKeyRef publicKey = NULL; + SecKeyRef privateKey = NULL; + SecKeychainAttributeList *attrList = NULL; + + if (keychainName) + { + keychain = keychain_open(keychainName); + if (!keychain) + { + result = 1; + goto loser; + } + } + + status = SecKeyCreatePair(keychain, algorithm, keySizeInBits, contextHandle, + publicKeyUsage, + publicKeyAttr, + privateKeyUsage, + privateKeyAttr, + access, + &publicKey, + &privateKey); + if (status) + { + sec_error("SecKeyCreatePair %s: %s", keychainName ? keychainName : "", sec_errstr(status)); + result = 1; + goto loser; + } + + if (print_hash) + { + SecItemClass itemClass = 0; + UInt32 tag = 0x00000006; + UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; + SecKeychainAttributeInfo info = { 1, &tag, &format }; + + status = SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)privateKey, &info, &itemClass, &attrList, NULL, NULL); + if (status) + { + sec_perror("SecKeychainItemCopyAttributesAndData", status); + result = 1; + goto loser; + } + + if (info.count != attrList->count) + { + sec_error("info count: %ld != attribute count: %ld", info.count, attrList->count); + result = 1; + goto loser; + } + + if (tag != attrList->attr[0].tag) + { + sec_error("attribute info tag: %ld != attribute tag: %ld", tag, attrList->attr[0].tag); + result = 1; + goto loser; + } + + print_buffer_pem(stdout, "PUBLIC KEY HASH", attrList->attr[0].length, attrList->attr[0].data); + } + +loser: + if (attrList) + { + status = SecKeychainItemFreeAttributesAndData(attrList, NULL); + if (status) + sec_perror("SecKeychainItemFreeAttributesAndData", status); + } + + if (keychain) + CFRelease(keychain); + if (publicKey) + CFRelease(publicKey); + if (privateKey) + CFRelease(privateKey); + + return result; +} + +static int +parse_algorithm(const char *name, CSSM_ALGORITHMS *algorithm) +{ + size_t len = strlen(name); + + if (!strncmp("rsa", name, len)) + *algorithm = CSSM_ALGID_RSA; + else if (!strncmp("dsa", name, len)) + *algorithm = CSSM_ALGID_DSA; + else if (!strncmp("dh", name, len)) + *algorithm = CSSM_ALGID_DH; + else if (!strncmp("fee", name, len)) + *algorithm = CSSM_ALGID_FEE; + else + { + sec_error("Invalid algorithm: %s", name); + return 2; + } + + return 0; +} + +static int +parse_time(const char *time, CFAbsoluteTime *ptime) +{ + CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, NULL, kCFDateFormatterShortStyle, kCFDateFormatterShortStyle); + CFStringRef time_string = CFStringCreateWithCString(NULL, time, kCFStringEncodingUTF8); + int result = 0; + if (!CFDateFormatterGetAbsoluteTimeFromString(formatter, time_string, NULL, ptime)) + { + sec_error("%s is not a valid date", time); + result = 1; + } + if (formatter) + CFRelease(formatter); + if (time_string) + CFRelease(time_string); + return result; +} + +int +key_create_pair(int argc, char * const *argv) +{ + const char *keychainName = NULL; + CSSM_ALGORITHMS algorithm = CSSM_ALGID_RSA; + uint32 keySizeInBits = 512; + int ch, result = 0; + OSStatus status; + Boolean always_allow = FALSE; + Boolean print_hash = FALSE; + CFAbsoluteTime from_time = 0.0, to_time = 0.0; + double days = 0.0; + SecAccessRef access = NULL; + CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFStringRef description = NULL; + +/* + { "create-keypair", key_create_pair, + "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-A|-T appPath] description\n" + " -a Use alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n" + " -s Specify the keysize in bits (default 512)\n" + " -f Make a key valid from the specified date\n" + " -t Make a key valid to the specified date\n" + " -d Make a key valid for the number of days specified from now\n" + " -k Use the specified keychain rather than the default\n" + " -H Print the public key hash attribute\n" + " -A Allow any application to access without warning\n" + " -T Allow the application specified to access without warning (multiple -T options are allowed)\n" + "If no options are provided, ask the user interactively.", +*/ + + while ((ch = getopt(argc, argv, "a:s:f:t:d:k:AHT:h")) != -1) + { + switch (ch) + { + case 'a': + result = parse_algorithm(optarg, &algorithm); + if (result) + goto loser; + break; + case 's': + keySizeInBits = atoi(optarg); + break; + case 'k': + keychainName = optarg; + break; + case 'A': + always_allow = TRUE; + break; + case 'H': + print_hash = TRUE; + break; + case 'T': + { + if (optarg[0]) + { + SecTrustedApplicationRef app = NULL; + status = SecTrustedApplicationCreateFromPath(optarg, &app); + if (status) + { + sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status)); + result = 1; + goto loser; + } + + CFArrayAppendValue(trusted_list, app); + CFRelease(app); + } + break; + } + case 'f': + result = parse_time(optarg, &from_time); + if (result) + goto loser; + break; + case 't': + result = parse_time(optarg, &to_time); + if (result) + goto loser; + break; + case 'd': + days = atof(optarg); + if (days < 1) + { + result = 2; + goto loser; + } + from_time = CFAbsoluteTimeGetCurrent(); + to_time = from_time + days * 86400.0; + break; + case '?': + default: + return 2; /* @@@ Return 2 triggers usage message. */ + } + } + + argc -= optind; + argv += optind; + + if (argc == 1) + { + if (*argv[0] == '\0') + { + result = 2; + goto loser; + } + description = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); + } + else if (argc != 0) + return 2; + else + description = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8); + + if (always_allow) + { + status = SecAccessCreate(description, NULL, &access); + if (status) + { + sec_perror("SecAccessCreate", status); + result = 1; + } + } + else + { + status = SecAccessCreate(description, trusted_list, &access); + if (status) + { + sec_perror("SecAccessCreate", status); + result = 1; + } + } + + if (result) + goto loser; + + result = do_key_create_pair(keychainName, access, algorithm, keySizeInBits, from_time, to_time, print_hash); + +loser: + if (description) + CFRelease(description); + if (trusted_list) + CFRelease(trusted_list); + if (access) + CFRelease(access); + + return result; +} + +#if 0 +static OSStatus +createCertCsr( + CSSM_TP_HANDLE tpHand, // eventually, a SecKeychainRef + CSSM_CL_HANDLE clHand, + CSSM_CSP_HANDLE cspHand, + SecKeyRef subjPubKey, + SecKeyRef signerPrivKey, + CSSM_ALGORITHMS sigAlg, + const CSSM_OID *sigOid, + /* + * Issuer's RDN is obtained from the issuer cert, if present, or is + * assumed to be the same as the subject name (i.e., we're creating + * a self-signed root cert). + */ + CSSM_BOOL useAllDefaults, + CSSM_DATA_PTR csrData) // CSR: mallocd and RETURNED +{ + CE_DataAndType exts[2]; + CE_DataAndType *extp = exts; + unsigned numExts; + + CSSM_DATA refId; // mallocd by CSSM_TP_SubmitCredRequest + CSSM_APPLE_TP_CERT_REQUEST certReq; + CSSM_TP_REQUEST_SET reqSet; + sint32 estTime; + CSSM_BOOL confirmRequired; + CSSM_TP_RESULT_SET_PTR resultSet; + CSSM_ENCODED_CERT *encCert; + CSSM_APPLE_TP_NAME_OID subjectNames[MAX_NAMES]; + uint32 numNames; + CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext; + CSSM_FIELD policyId; + + /* Note a lot of the CSSM_APPLE_TP_CERT_REQUEST fields are not + * used for the createCsr option, but we'll fill in as much as is practical + * for either case. + */ + numExts = 0; + + char challengeBuf[400]; + if(useAllDefaults) { + strcpy(challengeBuf, ZDEF_CHALLENGE); + } + else { + while(1) { + getStringWithPrompt("Enter challenge string: ", + challengeBuf, sizeof(challengeBuf)); + if(challengeBuf[0] != '\0') { + break; + } + } + } + certReq.challengeString = challengeBuf; + + /* name array, get from user. */ + if(useAllDefaults) { + subjectNames[0].string = ZDEF_COMMON_NAME; + subjectNames[0].oid = &CSSMOID_CommonName; + subjectNames[1].string = ZDEF_ORG_NAME; + subjectNames[1].oid = &CSSMOID_OrganizationName; + subjectNames[2].string = ZDEF_COUNTRY; + subjectNames[2].oid = &CSSMOID_CountryName; + subjectNames[3].string = ZDEF_STATE; + subjectNames[3].oid = &CSSMOID_StateProvinceName; + numNames = 4; + } + else { + getNameOids(subjectNames, &numNames); + } + + /* certReq */ + certReq.cspHand = cspHand; + certReq.clHand = clHand; + certReq.serialNumber = 0x12345678; // TBD - random? From user? + certReq.numSubjectNames = numNames; + certReq.subjectNames = subjectNames; + + /* TBD - if we're passed in a signing cert, certReq.issuerNameX509 will + * be obtained from that cert. For now we specify "self-signed" cert + * by not providing an issuer name at all. */ + certReq.numIssuerNames = 0; // root for now + certReq.issuerNames = NULL; + certReq.issuerNameX509 = NULL; + certReq.certPublicKey = subjPubKey; + certReq.issuerPrivateKey = signerPrivKey; + certReq.signatureAlg = sigAlg; + certReq.signatureOid = *sigOid; + certReq.notBefore = 0; // TBD - from user + certReq.notAfter = 60 * 60 * 24 * 30; // seconds from now + certReq.numExtensions = numExts; + certReq.extensions = exts; + + reqSet.NumberOfRequests = 1; + reqSet.Requests = &certReq; + + /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */ + memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT)); + memset(&policyId, 0, sizeof(CSSM_FIELD)); + policyId.FieldOid = CSSMOID_APPLE_TP_CSR_GEN; + CallerAuthContext.Policy.NumberOfPolicyIds = 1; + CallerAuthContext.Policy.PolicyIds = &policyId; + + CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tpHand, + NULL, // PreferredAuthority + CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, + &reqSet, + &CallerAuthContext, + &estTime, + &refId); + + /* before proceeding, free resources allocated thus far */ + if(!useAllDefaults) { + freeNameOids(subjectNames, numNames); + } + + if(crtn) { + printError("***Error submitting credential request","CSSM_TP_SubmitCredRequest",crtn); + return crtn; + } + crtn = CSSM_TP_RetrieveCredResult(tpHand, + &refId, + NULL, // CallerAuthCredentials + &estTime, + &confirmRequired, + &resultSet); + if(crtn) { + printError("***Error retreiving credential request","CSSM_TP_RetrieveCredResult",crtn); + return crtn; + } + if(resultSet == NULL) { + printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n"); + return ioErr; + } + encCert = (CSSM_ENCODED_CERT *)resultSet->Results; + *csrData = encCert->CertBlob; + + /* free resources allocated by TP */ + APP_FREE(refId.Data); + APP_FREE(encCert); + APP_FREE(resultSet); + return noErr; +} +#endif + +#if 0 +/* this was added in Michael's integration of PR-3420772, but this is an unimplemented command */ + +int +csr_create(int argc, char * const *argv) +{ + int result = 0; + int ch; + const char *keychainName = NULL; + CSSM_ALGORITHMS algorithm = CSSM_ALGID_RSA; + uint32 keySizeInBits = 512; + OSStatus status; + Boolean always_allow = FALSE; + CFAbsoluteTime from_time = 0.0, to_time = 0.0; + double days = 0.0; + SecAccessRef access = NULL; + CFMutableArrayRef trusted_list = NULL; + CFStringRef description = NULL; + +/* + { "create-keypair", key_create_pair, + "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-u sdux] [-c challenge] description\n" + " [-k keychain] [-u sewx] description\n" + " -a Look for key with alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n" + " -s Look for key with keysize in bits\n" + " -c Add challenge to the key as a challange string\n" + " -f Look for a key at least valid from the specified date\n" + " -t Look for a key at least valid to the specified date\n" + " -d Look for a key at least valid for the number of days specified from now\n" + " -u Look for a key with the specified usage flags (s)igning (d)ecryption (u)nwrapping e(x)change\n" + " -k Look in specified keychain rather than the default search list\n" + "If no options are provided ask the user interactively", +*/ + + while ((ch = getopt(argc, argv, "a:s:f:t:d:k:AT:h")) != -1) + { + switch (ch) + { + case 'a': + result = parse_algorithm(optarg, &algorithm); + if (result) + goto loser; + break; + case 's': + keySizeInBits = atoi(optarg); + break; + case 'k': + keychainName = optarg; + break; + case 'A': + always_allow = TRUE; + break; + case 'T': + { + if (!trusted_list) + { + trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + } + + if (optarg[0]) + { + SecTrustedApplicationRef app = NULL; + status = SecTrustedApplicationCreateFromPath(optarg, &app); + if (status) + { + sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status)); + result = 1; + goto loser; + } + + CFArrayAppendValue(trusted_list, app); + CFRelease(app); + break; + } + } + case 'f': + result = parse_time(optarg, &from_time); + if (result) + goto loser; + break; + case 't': + result = parse_time(optarg, &to_time); + if (result) + goto loser; + break; + case 'd': + days = atof(optarg); + if (days < 1) + { + result = 2; + goto loser; + } + from_time = CFAbsoluteTimeGetCurrent(); + to_time = from_time + days * 86400.0; + break; + case '?': + default: + return 2; /* @@@ Return 2 triggers usage message. */ + } + } + + argc -= optind; + argv += optind; + + if (argc == 1) + { + if (*argv[0] == '\0') + { + result = 2; + goto loser; + } + description = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); + } + else if (argc != 0) + return 2; + else + description = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8); + + if (always_allow) + { + status = SecAccessCreate(description, NULL, &access); + if (status) + { + sec_perror("SecAccessCreate", status); + result = 1; + } + // @@@ Make the acl always allow now. + } + else + { + status = SecAccessCreate(description, trusted_list, &access); + if (status) + { + sec_perror("SecAccessCreate", status); + result = 1; + } + } + + if (result) + goto loser; + + result = do_csr_create(keychainName, access, algorithm, keySizeInBits, from_time, to_time); + +loser: + if (description) + CFRelease(description); + if (trusted_list) + CFRelease(trusted_list); + if (access) + CFRelease(access); + + return result; +} +#endif