2 * Copyright (c) 2003-2004,2006,2008,2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include "key_create.h"
28 #include "keychain_utilities.h"
29 #include "security_tool.h"
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>
44 do_key_create_pair(const char *keychainName
, SecAccessRef access
, CSSM_ALGORITHMS algorithm
, uint32 keySizeInBits
, CFAbsoluteTime from_time
, CFAbsoluteTime to_time
, Boolean print_hash
)
46 SecKeychainRef keychain
= NULL
;
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
;
60 keychain
= keychain_open(keychainName
);
68 status
= SecKeyCreatePair(keychain
, algorithm
, keySizeInBits
, contextHandle
,
78 sec_error("SecKeyCreatePair %s: %s", keychainName
? keychainName
: "<NULL>", sec_errstr(status
));
85 SecItemClass itemClass
= 0;
86 UInt32 tag
= 0x00000006;
87 UInt32 format
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
88 SecKeychainAttributeInfo info
= { 1, &tag
, &format
};
90 status
= SecKeychainItemCopyAttributesAndData((SecKeychainItemRef
)privateKey
, &info
, &itemClass
, &attrList
, NULL
, NULL
);
93 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
98 if (info
.count
!= attrList
->count
)
100 sec_error("info count: %ld != attribute count: %ld", info
.count
, attrList
->count
);
105 if (tag
!= attrList
->attr
[0].tag
)
107 sec_error("attribute info tag: %ld != attribute tag: %ld", tag
, attrList
->attr
[0].tag
);
112 print_buffer_pem(stdout
, "PUBLIC KEY HASH", attrList
->attr
[0].length
, attrList
->attr
[0].data
);
118 status
= SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
120 sec_perror("SecKeychainItemFreeAttributesAndData", status
);
126 CFRelease(publicKey
);
128 CFRelease(privateKey
);
134 parse_algorithm(const char *name
, CSSM_ALGORITHMS
*algorithm
)
136 size_t len
= strlen(name
);
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
;
148 sec_error("Invalid algorithm: %s", name
);
149 return SHOW_USAGE_MESSAGE
;
156 parse_time(const char *time
, CFAbsoluteTime
*ptime
)
158 CFDateFormatterRef formatter
= CFDateFormatterCreate(NULL
, NULL
, kCFDateFormatterShortStyle
, kCFDateFormatterShortStyle
);
159 CFStringRef time_string
= CFStringCreateWithCString(NULL
, time
, kCFStringEncodingUTF8
);
161 if (!CFDateFormatterGetAbsoluteTimeFromString(formatter
, time_string
, NULL
, ptime
))
163 sec_error("%s is not a valid date", time
);
167 CFRelease(formatter
);
170 CFRelease(time_string
);
176 key_create_pair(int argc
, char * const *argv
)
178 const char *keychainName
= NULL
;
179 CSSM_ALGORITHMS algorithm
= CSSM_ALGID_RSA
;
180 uint32 keySizeInBits
= 512;
183 Boolean always_allow
= FALSE
;
184 Boolean print_hash
= FALSE
;
185 CFAbsoluteTime from_time
= 0.0, to_time
= 0.0;
187 SecAccessRef access
= NULL
;
188 CFMutableArrayRef trusted_list
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
189 CFStringRef description
= NULL
;
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.",
206 while ((ch
= getopt(argc
, argv
, "a:s:f:t:d:k:AHT:h")) != -1)
211 result
= parse_algorithm(optarg
, &algorithm
);
216 keySizeInBits
= atoi(optarg
);
219 keychainName
= optarg
;
231 SecTrustedApplicationRef app
= NULL
;
232 status
= SecTrustedApplicationCreateFromPath(optarg
, &app
);
235 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg
, sec_errstr(status
));
240 CFArrayAppendValue(trusted_list
, app
);
246 result
= parse_time(optarg
, &from_time
);
251 result
= parse_time(optarg
, &to_time
);
262 from_time
= CFAbsoluteTimeGetCurrent();
263 to_time
= from_time
+ days
* 86400.0;
267 return SHOW_USAGE_MESSAGE
;
276 if (*argv
[0] == '\0')
281 description
= CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingUTF8
);
284 return SHOW_USAGE_MESSAGE
;
286 description
= CFStringCreateWithCString(NULL
, "<key>", kCFStringEncodingUTF8
);
290 status
= SecAccessCreate(description
, NULL
, &access
);
293 sec_perror("SecAccessCreate", status
);
299 status
= SecAccessCreate(description
, trusted_list
, &access
);
302 sec_perror("SecAccessCreate", status
);
310 result
= do_key_create_pair(keychainName
, access
, algorithm
, keySizeInBits
, from_time
, to_time
, print_hash
);
314 CFRelease(description
);
316 CFRelease(trusted_list
);
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
,
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).
338 CSSM_BOOL useAllDefaults
,
339 CSSM_DATA_PTR csrData
) // CSR: mallocd and RETURNED
341 CE_DataAndType exts
[2];
342 CE_DataAndType
*extp
= exts
;
345 CSSM_DATA refId
; // mallocd by CSSM_TP_SubmitCredRequest
346 CSSM_APPLE_TP_CERT_REQUEST certReq
;
347 CSSM_TP_REQUEST_SET reqSet
;
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
];
354 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext
;
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
363 char challengeBuf
[400];
365 strcpy(challengeBuf
, ZDEF_CHALLENGE
);
369 getStringWithPrompt("Enter challenge string: ",
370 challengeBuf
, sizeof(challengeBuf
));
371 if(challengeBuf
[0] != '\0') {
376 certReq
.challengeString
= challengeBuf
;
378 /* name array, get from user. */
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
;
391 getNameOids(subjectNames
, &numNames
);
395 certReq
.cspHand
= cspHand
;
396 certReq
.clHand
= clHand
;
397 certReq
.serialNumber
= 0x12345678; // TBD - random? From user?
398 certReq
.numSubjectNames
= numNames
;
399 certReq
.subjectNames
= subjectNames
;
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
;
416 reqSet
.NumberOfRequests
= 1;
417 reqSet
.Requests
= &certReq
;
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
;
426 CSSM_RETURN crtn
= CSSM_TP_SubmitCredRequest(tpHand
,
427 NULL
, // PreferredAuthority
428 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE
,
434 /* before proceeding, free resources allocated thus far */
435 if(!useAllDefaults
) {
436 freeNameOids(subjectNames
, numNames
);
440 printError("***Error submitting credential request","CSSM_TP_SubmitCredRequest",crtn
);
443 crtn
= CSSM_TP_RetrieveCredResult(tpHand
,
445 NULL
, // CallerAuthCredentials
450 printError("***Error retreiving credential request","CSSM_TP_RetrieveCredResult",crtn
);
453 if(resultSet
== NULL
) {
454 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
457 encCert
= (CSSM_ENCODED_CERT
*)resultSet
->Results
;
458 *csrData
= encCert
->CertBlob
;
460 /* free resources allocated by TP */
461 APP_FREE(refId
.Data
);
469 /* this was added in Michael's integration of PR-3420772, but this is an unimplemented command */
472 csr_create(int argc
, char * const *argv
)
476 const char *keychainName
= NULL
;
477 CSSM_ALGORITHMS algorithm
= CSSM_ALGID_RSA
;
478 uint32 keySizeInBits
= 512;
480 Boolean always_allow
= FALSE
;
481 CFAbsoluteTime from_time
= 0.0, to_time
= 0.0;
483 SecAccessRef access
= NULL
;
484 CFMutableArrayRef trusted_list
= NULL
;
485 CFStringRef description
= NULL
;
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",
502 while ((ch
= getopt(argc
, argv
, "a:s:f:t:d:k:AT:h")) != -1)
507 result
= parse_algorithm(optarg
, &algorithm
);
512 keySizeInBits
= atoi(optarg
);
515 keychainName
= optarg
;
524 trusted_list
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
529 SecTrustedApplicationRef app
= NULL
;
530 status
= SecTrustedApplicationCreateFromPath(optarg
, &app
);
533 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg
, sec_errstr(status
));
538 CFArrayAppendValue(trusted_list
, app
);
544 result
= parse_time(optarg
, &from_time
);
549 result
= parse_time(optarg
, &to_time
);
560 from_time
= CFAbsoluteTimeGetCurrent();
561 to_time
= from_time
+ days
* 86400.0;
565 return SHOW_USAGE_MESSAGE
;
574 if (*argv
[0] == '\0')
579 description
= CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingUTF8
);
582 return SHOW_USAGE_MESSAGE
;
584 description
= CFStringCreateWithCString(NULL
, "<key>", kCFStringEncodingUTF8
);
588 status
= SecAccessCreate(description
, NULL
, &access
);
591 sec_perror("SecAccessCreate", status
);
594 // @@@ Make the acl always allow now.
598 status
= SecAccessCreate(description
, trusted_list
, &access
);
601 sec_perror("SecAccessCreate", status
);
609 result
= do_csr_create(keychainName
, access
, algorithm
, keySizeInBits
, from_time
, to_time
);
613 CFRelease(description
);
615 CFRelease(trusted_list
);