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
);
169 CFRelease(time_string
);
174 key_create_pair(int argc
, char * const *argv
)
176 const char *keychainName
= NULL
;
177 CSSM_ALGORITHMS algorithm
= CSSM_ALGID_RSA
;
178 uint32 keySizeInBits
= 512;
181 Boolean always_allow
= FALSE
;
182 Boolean print_hash
= FALSE
;
183 CFAbsoluteTime from_time
= 0.0, to_time
= 0.0;
185 SecAccessRef access
= NULL
;
186 CFMutableArrayRef trusted_list
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
187 CFStringRef description
= NULL
;
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.",
204 while ((ch
= getopt(argc
, argv
, "a:s:f:t:d:k:AHT:h")) != -1)
209 result
= parse_algorithm(optarg
, &algorithm
);
214 keySizeInBits
= atoi(optarg
);
217 keychainName
= optarg
;
229 SecTrustedApplicationRef app
= NULL
;
230 status
= SecTrustedApplicationCreateFromPath(optarg
, &app
);
233 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg
, sec_errstr(status
));
238 CFArrayAppendValue(trusted_list
, app
);
244 result
= parse_time(optarg
, &from_time
);
249 result
= parse_time(optarg
, &to_time
);
260 from_time
= CFAbsoluteTimeGetCurrent();
261 to_time
= from_time
+ days
* 86400.0;
265 return SHOW_USAGE_MESSAGE
;
274 if (*argv
[0] == '\0')
279 description
= CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingUTF8
);
282 return SHOW_USAGE_MESSAGE
;
284 description
= CFStringCreateWithCString(NULL
, "<key>", kCFStringEncodingUTF8
);
288 status
= SecAccessCreate(description
, NULL
, &access
);
291 sec_perror("SecAccessCreate", status
);
297 status
= SecAccessCreate(description
, trusted_list
, &access
);
300 sec_perror("SecAccessCreate", status
);
308 result
= do_key_create_pair(keychainName
, access
, algorithm
, keySizeInBits
, from_time
, to_time
, print_hash
);
312 CFRelease(description
);
314 CFRelease(trusted_list
);
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
,
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).
336 CSSM_BOOL useAllDefaults
,
337 CSSM_DATA_PTR csrData
) // CSR: mallocd and RETURNED
339 CE_DataAndType exts
[2];
340 CE_DataAndType
*extp
= exts
;
343 CSSM_DATA refId
; // mallocd by CSSM_TP_SubmitCredRequest
344 CSSM_APPLE_TP_CERT_REQUEST certReq
;
345 CSSM_TP_REQUEST_SET reqSet
;
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
];
352 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext
;
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
361 char challengeBuf
[400];
363 strcpy(challengeBuf
, ZDEF_CHALLENGE
);
367 getStringWithPrompt("Enter challenge string: ",
368 challengeBuf
, sizeof(challengeBuf
));
369 if(challengeBuf
[0] != '\0') {
374 certReq
.challengeString
= challengeBuf
;
376 /* name array, get from user. */
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
;
389 getNameOids(subjectNames
, &numNames
);
393 certReq
.cspHand
= cspHand
;
394 certReq
.clHand
= clHand
;
395 certReq
.serialNumber
= 0x12345678; // TBD - random? From user?
396 certReq
.numSubjectNames
= numNames
;
397 certReq
.subjectNames
= subjectNames
;
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
;
414 reqSet
.NumberOfRequests
= 1;
415 reqSet
.Requests
= &certReq
;
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
;
424 CSSM_RETURN crtn
= CSSM_TP_SubmitCredRequest(tpHand
,
425 NULL
, // PreferredAuthority
426 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE
,
432 /* before proceeding, free resources allocated thus far */
433 if(!useAllDefaults
) {
434 freeNameOids(subjectNames
, numNames
);
438 printError("***Error submitting credential request","CSSM_TP_SubmitCredRequest",crtn
);
441 crtn
= CSSM_TP_RetrieveCredResult(tpHand
,
443 NULL
, // CallerAuthCredentials
448 printError("***Error retreiving credential request","CSSM_TP_RetrieveCredResult",crtn
);
451 if(resultSet
== NULL
) {
452 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
455 encCert
= (CSSM_ENCODED_CERT
*)resultSet
->Results
;
456 *csrData
= encCert
->CertBlob
;
458 /* free resources allocated by TP */
459 APP_FREE(refId
.Data
);
467 /* this was added in Michael's integration of PR-3420772, but this is an unimplemented command */
470 csr_create(int argc
, char * const *argv
)
474 const char *keychainName
= NULL
;
475 CSSM_ALGORITHMS algorithm
= CSSM_ALGID_RSA
;
476 uint32 keySizeInBits
= 512;
478 Boolean always_allow
= FALSE
;
479 CFAbsoluteTime from_time
= 0.0, to_time
= 0.0;
481 SecAccessRef access
= NULL
;
482 CFMutableArrayRef trusted_list
= NULL
;
483 CFStringRef description
= NULL
;
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",
500 while ((ch
= getopt(argc
, argv
, "a:s:f:t:d:k:AT:h")) != -1)
505 result
= parse_algorithm(optarg
, &algorithm
);
510 keySizeInBits
= atoi(optarg
);
513 keychainName
= optarg
;
522 trusted_list
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
527 SecTrustedApplicationRef app
= NULL
;
528 status
= SecTrustedApplicationCreateFromPath(optarg
, &app
);
531 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg
, sec_errstr(status
));
536 CFArrayAppendValue(trusted_list
, app
);
542 result
= parse_time(optarg
, &from_time
);
547 result
= parse_time(optarg
, &to_time
);
558 from_time
= CFAbsoluteTimeGetCurrent();
559 to_time
= from_time
+ days
* 86400.0;
563 return SHOW_USAGE_MESSAGE
;
572 if (*argv
[0] == '\0')
577 description
= CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingUTF8
);
580 return SHOW_USAGE_MESSAGE
;
582 description
= CFStringCreateWithCString(NULL
, "<key>", kCFStringEncodingUTF8
);
586 status
= SecAccessCreate(description
, NULL
, &access
);
589 sec_perror("SecAccessCreate", status
);
592 // @@@ Make the acl always allow now.
596 status
= SecAccessCreate(description
, trusted_list
, &access
);
599 sec_perror("SecAccessCreate", status
);
607 result
= do_csr_create(keychainName
, access
, algorithm
, keySizeInBits
, from_time
, to_time
);
611 CFRelease(description
);
613 CFRelease(trusted_list
);