2 * Copyright (c) 2002-2003 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please
7 * obtain a copy of the License at http://www.apple.com/publicsource and
8 * read it before using this file.
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights and
16 * limitations under the License.
22 Description: certificate manipulation tool
27 #include <Security/Security.h>
28 #include <Security/certextensions.h>
29 #include <Security/cssmapple.h>
30 #include <Security/oidsattr.h>
31 #include <Security/oidscert.h>
32 #include <Security/oidsalg.h>
33 #include <Security/cssmalloc.h>
38 #include <sys/param.h>
39 #include <CdsaUtils/cuCdsaUtils.h>
40 #include <CdsaUtils/cuDbUtils.h>
41 #include <CdsaUtils/cuPrintCert.h>
42 #include <CdsaUtils/cuFileIo.h>
43 #include <CdsaUtils/cuPem.h>
44 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
46 #include <CoreFoundation/CoreFoundation.h>
47 #include <Security/utilities.h>
48 #include <Security/aclclient.h>
54 /* SecKeychainGetCSPHandle implemented? */
55 #define SEC_KEYCHAIN_GET_CSP 1
57 /* SecKeyCreatePair() implemented */
58 #define SEC_KEY_CREATE_PAIR 1
60 /* munge Label attr if manually generating or importing keys */
61 #define MUNGE_LABEL_ATTR 1
63 #define KC_DB_PATH "Library/Keychains" /* relative to home */
66 * defaults for undocumented 'Z' option
68 #define ZDEF_KEY_LABEL "testCert"
69 #define ZDEF_KEY_ALG CSSM_ALGID_RSA
70 #define ZDEF_KEY_SIZE 512
71 #define ZDEF_KEY_USAGE (kKeyUseSigning | kKeyUseEncrypting)
72 #define ZDEF_SIG_ALG CSSM_ALGID_SHA1WithRSA
73 #define ZDEF_SIG_OID CSSMOID_SHA1WithRSA
74 #define ZDEF_COMMON_NAME "localhost"
75 #define ZDEF_ORG_NAME "Apple Computer - DEBUG ONLY"
76 #define ZDEF_COUNTRY "US"
77 #define ZDEF_STATE "Washington"
78 #define ZDEF_CHALLENGE "someChallenge"
80 CSSM_BOOL verbose
= CSSM_FALSE
;
82 static void usage(char **argv
)
85 printf(" Create a keypair and cert: %s c [options]\n", argv
[0]);
86 printf(" Create a CSR: %s r outFileName [options]\n",
88 printf(" Verify a CSR: %s v infileName [options]\n", argv
[0]);
89 printf(" Import a certificate: %s i inFileName [options]\n", argv
[0]);
90 printf(" Display a certificate: %s d inFileName [options]\n", argv
[0]);
91 printf(" Import a CRL: %s I inFileName [options]\n", argv
[0]);
92 printf(" Display a CRL: %s D inFileName [options]\n", argv
[0]);
93 printf(" Display certs and CRLs in keychain: %s y [options]\n", argv
[0]);
95 printf(" k=keychainName\n");
96 printf(" c (create the keychain)\n");
97 printf(" p=passphrase (specify passphrase at keychain creation)\n");
98 printf(" o=outFileName (create cert command only)\n");
99 printf(" v (verbose)\n");
100 printf(" d (infile/outfile in DER format; default is PEM)\n");
101 printf(" r=privateKeyFileName (optional; for Import Certificate only)\n");
102 printf(" f=[18f] (private key format = PKCS1/PKCS8/FIPS186; default is PKCS1\n"
103 " (openssl) for RSA, openssl for DSA, PKCS8 for Diffie-Hellman\n");
104 #if SEC_KEY_CREATE_PAIR
105 printf(" a (create key with default ACL)\n");
111 static void printError(const char *errDescription
,const char *errLocation
,OSStatus crtn
)
113 // Show error in text form. If verbose, show location and decimal and hex error values
114 int len
=64+(errLocation
?strlen(errLocation
):0);
117 char *buf
=(char *)malloc(len
);
119 fprintf(stderr
,"%s : ",errDescription
);
120 // sprintf(buf," %s : %d [0x%x] : ", errLocation,(int)crtn,(unsigned int)crtn);
121 // cuPrintError(buf, crtn);
122 cuPrintError(errLocation
, crtn
);
128 fprintf(stderr
,"%s\n",errDescription
);
131 fprintf(stderr
,"%s\n",errLocation
);
133 fprintf(stderr
,"Error: %d [0x%x]\n",(int)crtn
,(unsigned int)crtn
);
137 #if SEC_KEY_CREATE_PAIR
139 * Generate a key pair using the SecKeyCreatePair.
141 static OSStatus
generateSecKeyPair(
142 SecKeychainRef kcRef
,
143 CSSM_ALGORITHMS keyAlg
, // e.g., CSSM_ALGID_RSA
144 uint32 keySizeInBits
,
145 CU_KeyUsage keyUsage
, // CUK_Signing, etc.
147 CSSM_KEY_PTR
*pubKeyPtr
, // RETURNED, owned by Sec layer
148 CSSM_KEY_PTR
*privKeyPtr
, // RETURNED, owned by Sec layer
149 SecKeyRef
*pubSecKey
, // caller must release
150 SecKeyRef
*privSecKey
) // caller must release
153 CSSM_KEYUSE pubKeyUse
= 0;
154 CSSM_KEYUSE privKeyUse
= 0;
156 if(keyUsage
& kKeyUseSigning
) {
157 pubKeyUse
|= CSSM_KEYUSE_VERIFY
;
158 privKeyUse
|= CSSM_KEYUSE_SIGN
;
160 if(keyUsage
& kKeyUseEncrypting
) {
161 pubKeyUse
|= (CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_WRAP
);
162 privKeyUse
|= (CSSM_KEYUSE_DECRYPT
| CSSM_KEYUSE_UNWRAP
);
164 ortn
= SecKeyCreatePair(kcRef
,
165 keyAlg
, keySizeInBits
,
168 CSSM_KEYATTR_PERMANENT
| CSSM_KEYATTR_EXTRACTABLE
|
169 CSSM_KEYATTR_RETURN_REF
,
171 CSSM_KEYATTR_SENSITIVE
| CSSM_KEYATTR_RETURN_REF
|
172 CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_EXTRACTABLE
,
173 NULL
, // FIXME - initialAccess
177 printError("***Error creating key pair",
178 "SecKeyCreatePair", ortn
);
179 cuPrintError("", ortn
);
183 /* extract CSSM keys for caller */
184 ortn
= SecKeyGetCSSMKey(*pubSecKey
, const_cast<const CSSM_KEY
**>(pubKeyPtr
));
186 printError("***Error extracting public key",
187 "SecKeyGetCSSMKey", ortn
);
188 cuPrintError("", ortn
);
190 else ortn
= SecKeyGetCSSMKey(*privSecKey
, const_cast<const CSSM_KEY
**>(privKeyPtr
));
192 printError("***Error extracting private key",
193 "SecKeyGetCSSMKey", ortn
);
194 cuPrintError("", ortn
);
197 CFRelease(*pubSecKey
);
199 CFRelease(*privSecKey
);
207 * Workaround to manually generate a key pair and munge its DB attributes
208 * to include the hash of the public key in the private key's Label attr.
213 * Find private key by label, modify its Label attr to be the
214 * hash of the associated public key.
216 static CSSM_RETURN
setPubKeyHash(
217 CSSM_CSP_HANDLE cspHand
,
218 CSSM_DL_DB_HANDLE dlDbHand
,
219 const char *keyLabel
) // look up by this
222 CSSM_SELECTION_PREDICATE predicate
;
223 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
226 CSSM_HANDLE resultHand
;
228 labelData
.Data
= (uint8
*)keyLabel
;
229 labelData
.Length
= strlen(keyLabel
) + 1; // incl. NULL
230 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
231 query
.Conjunctive
= CSSM_DB_NONE
;
232 query
.NumSelectionPredicates
= 1;
233 predicate
.DbOperator
= CSSM_DB_EQUAL
;
235 predicate
.Attribute
.Info
.AttributeNameFormat
=
236 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
237 predicate
.Attribute
.Info
.Label
.AttributeName
= "Label";
238 predicate
.Attribute
.Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
239 /* hope this cast is OK */
240 predicate
.Attribute
.Value
= &labelData
;
241 query
.SelectionPredicate
= &predicate
;
243 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
244 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
245 query
.QueryFlags
= 0; // CSSM_QUERY_RETURN_DATA; // FIXME - used?
247 /* build Record attribute with one attr */
248 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
249 CSSM_DB_ATTRIBUTE_DATA attr
;
250 attr
.Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
251 attr
.Info
.Label
.AttributeName
= "Label";
252 attr
.Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
254 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
255 recordAttrs
.NumberOfAttributes
= 1;
256 recordAttrs
.AttributeData
= &attr
;
258 CSSM_DATA recordData
= {NULL
, 0};
259 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
265 /* abort only on success */
266 if(crtn
!= CSSM_OK
) {
267 printError("***setPubKeyHash: can't find private key","CSSM_DL_DataGetFirst",crtn
);
271 CSSM_KEY_PTR keyToDigest
= (CSSM_KEY_PTR
)recordData
.Data
;
272 CSSM_DATA_PTR keyDigest
= NULL
;
273 CSSM_CC_HANDLE ccHand
;
274 crtn
= CSSM_CSP_CreatePassThroughContext(cspHand
,
278 printError("***Error calculating public key hash. Aborting.",
279 "CSSM_CSP_CreatePassThroughContext", crtn
);
282 crtn
= CSSM_CSP_PassThrough(ccHand
,
283 CSSM_APPLECSP_KEYDIGEST
,
285 (void **)&keyDigest
);
287 printError("***Error calculating public key hash. Aborting.",
288 "CSSM_CSP_PassThrough(PUBKEYHASH)", crtn
);
291 CSSM_FreeKey(cspHand
, NULL
, keyToDigest
, CSSM_FALSE
);
292 CSSM_DeleteContext(ccHand
);
295 * Replace Label attr data with hash.
296 * NOTE: the module which allocated this attribute data - a DL -
297 * was loaded and attached by the Sec layer, not by us. Thus
298 * we can't use the memory allocator functions *we* used when
299 * attaching to the CSPDL - we have to use the ones
300 * which the Sec layer registered with the DL.
302 CSSM_API_MEMORY_FUNCS memFuncs
;
303 crtn
= CSSM_GetAPIMemoryFunctions(dlDbHand
.DLHandle
, &memFuncs
);
305 printError("***Error ","CSSM_GetAPIMemoryFunctions(DLHandle)",crtn
);
306 /* oh well, leak and continue */
309 memFuncs
.free_func(attr
.Value
->Data
, memFuncs
.AllocRef
);
310 memFuncs
.free_func(attr
.Value
, memFuncs
.AllocRef
);
312 attr
.Value
= keyDigest
;
314 /* modify key attributes */
315 crtn
= CSSM_DL_DataModify(dlDbHand
,
316 CSSM_DL_DB_RECORD_PRIVATE_KEY
,
319 NULL
, // DataToBeModified
320 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
322 printError("***Error setting public key hash. Aborting.",
323 "CSSM_DL_DataModify(PUBKEYHASH)", crtn
);
326 crtn
= CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
328 printError("***Error while stopping query",
329 "CSSM_DL_DataAbortQuery", crtn
);
330 /* let's keep going in this case */
332 crtn
= CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
334 printError("***Error while freeing record",
335 "CSSM_DL_FreeUniqueRecord", crtn
);
336 /* let's keep going in this case */
341 cuAppFree(keyDigest
->Data
, NULL
);
344 #endif /* MUNGE_LABEL_ATTR */
347 * Generate a key pair using the CSPDL.
349 static OSStatus
generateKeyPair(
350 CSSM_CSP_HANDLE cspHand
,
351 CSSM_DL_DB_HANDLE dlDbHand
,
352 CSSM_ALGORITHMS keyAlg
, // e.g., CSSM_ALGID_RSA
353 uint32 keySizeInBits
,
354 const char *keyLabel
, // C string
355 CU_KeyUsage keyUsage
, // CUK_Signing, etc.
357 CSSM_KEY_PTR
*pubKeyPtr
, // mallocd, created, RETURNED
358 CSSM_KEY_PTR
*privKeyPtr
) // mallocd, created, RETURNED
360 CSSM_KEY_PTR pubKey
= reinterpret_cast<CSSM_KEY_PTR
>(
361 APP_MALLOC(sizeof(CSSM_KEY
)));
362 CSSM_KEY_PTR privKey
= reinterpret_cast<CSSM_KEY_PTR
>(
363 APP_MALLOC(sizeof(CSSM_KEY
)));
364 if((pubKey
== NULL
) || (privKey
== NULL
)) {
369 CSSM_KEYUSE pubKeyUse
= 0;
370 CSSM_KEYUSE privKeyUse
= 0;
372 if(keyUsage
& kKeyUseSigning
) {
373 pubKeyUse
|= CSSM_KEYUSE_VERIFY
;
374 privKeyUse
|= CSSM_KEYUSE_SIGN
;
376 if(keyUsage
& kKeyUseEncrypting
) {
377 pubKeyUse
|= (CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_WRAP
);
378 privKeyUse
|= (CSSM_KEYUSE_DECRYPT
| CSSM_KEYUSE_UNWRAP
);
381 crtn
= cuCspGenKeyPair(cspHand
,
385 strlen(keyLabel
) + 1,
389 CSSM_KEYATTR_EXTRACTABLE
| CSSM_KEYATTR_RETURN_REF
,
392 CSSM_KEYATTR_SENSITIVE
| CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_PERMANENT
|
393 CSSM_KEYATTR_EXTRACTABLE
);
400 printf("...%u bit key pair generated.\n",
401 (unsigned)keySizeInBits
);
405 /* bind private key to cert by public key hash */
406 crtn
= setPubKeyHash(cspHand
,
410 printError("***Error setting public key hash. Continuing at peril.",
411 "setPubKeyHash", crtn
);
413 #endif /* MUNGE_LABEL_ATTR */
416 *privKeyPtr
= privKey
;
420 static OSStatus
verifyCsr(
421 CSSM_CL_HANDLE clHand
,
422 const char *fileName
,
425 unsigned char *csr
= NULL
;
428 unsigned char *der
= NULL
;
431 if(readFile(fileName
, &csr
, &csrLen
)) {
432 printf("***Error reading CSR from file %s. Aborting.\n",
437 int rtn
= pemDecode(csr
, csrLen
, &der
, &derLen
);
439 printf("***%s: Bad PEM formatting. Aborting.\n", fileName
);
443 csrData
.Length
= derLen
;
447 csrData
.Length
= csrLen
;
450 CSSM_RETURN crtn
= CSSM_CL_PassThrough(clHand
,
452 CSSM_APPLEX509CL_VERIFY_CSR
,
456 printError("***Error verifying CSR","Verify CSR",crtn
);
459 printf("...CSR verified successfully.\n");
475 static OSStatus
displayCertCRL(
476 const char *fileName
,
481 unsigned char *rawData
= NULL
;
482 unsigned rawDataSize
;
483 unsigned char *derData
= NULL
;
484 unsigned derDataSize
;
487 rtn
= readFile(fileName
, &rawData
, &rawDataSize
);
489 printf("Error reading %s; aborting.\n", fileName
);
492 if(pemFormat
&& isPem(rawData
, rawDataSize
)) {
494 * Here we cut the user some slack. See if the thing is actually
495 * PEM encoded and assume DER-encoded if it's not.
497 rtn
= pemDecode(rawData
, rawDataSize
, &derData
, &derDataSize
);
499 printf("***%s: Bad PEM formatting. Aborting.\n", fileName
);
503 rawDataSize
= derDataSize
;
505 if(certOrCrl
== CC_Cert
) {
506 printCert(rawData
, rawDataSize
, verbose
);
509 printCrl(rawData
, rawDataSize
, verbose
);
511 if(derData
!= NULL
) {
517 static CSSM_RETURN
importPrivateKey(
518 CSSM_DL_DB_HANDLE dlDbHand
,
519 CSSM_CSP_HANDLE cspHand
,
520 const char *privKeyFileName
,
521 CSSM_ALGORITHMS keyAlg
,
522 CSSM_BOOL pemFormat
, // of the file
523 CSSM_KEYBLOB_FORMAT keyFormat
) // of the key blob itself, NONE means use
526 unsigned char *derKey
= NULL
;
528 unsigned char *pemKey
= NULL
;
531 CSSM_KEY unwrappedKey
;
532 CSSM_ACCESS_CREDENTIALS creds
;
533 CSSM_CC_HANDLE ccHand
= 0;
536 CSSM_KEYHEADER_PTR hdr
= &wrappedKey
.KeyHeader
;
537 CSSM_DATA descData
= {0, NULL
};
538 CSSM_CSP_HANDLE rawCspHand
= 0;
539 const char *privKeyLabel
= NULL
;
542 * Validate specified format for clarity
547 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
548 keyFormat
= CSSM_KEYBLOB_RAW_FORMAT_PKCS1
; // default
550 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1
:
551 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
554 printf("***RSA Private key must be in PKCS1 or PKCS8 format\n");
555 return CSSMERR_CSSM_INTERNAL_ERROR
;
557 privKeyLabel
= "Imported RSA key";
561 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
562 keyFormat
= CSSM_KEYBLOB_RAW_FORMAT_OPENSSL
; // default
564 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186
:
565 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL
:
566 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
569 printf("***DSA Private key must be in openssl, FIPS186, "
570 "or PKCS8 format\n");
571 return CSSMERR_CSSM_INTERNAL_ERROR
;
573 privKeyLabel
= "Imported DSA key";
577 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
578 keyFormat
= CSSM_KEYBLOB_RAW_FORMAT_PKCS8
; // default
580 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
583 printf("***Diffie-Hellman Private key must be in PKCS8 format.\n");
584 return CSSMERR_CSSM_INTERNAL_ERROR
;
586 privKeyLabel
= "Imported Diffie-Hellman key";
589 if(readFile(privKeyFileName
, &pemKey
, &pemKeyLen
)) {
590 printf("***Error reading private key from file %s. Aborting.\n",
592 return CSSMERR_CSSM_INTERNAL_ERROR
;
594 /* subsequent errors to done: */
596 int rtn
= pemDecode(pemKey
, pemKeyLen
, &derKey
, &derKeyLen
);
598 printf("***%s: Bad PEM formatting. Aborting.\n", privKeyFileName
);
599 crtn
= CSSMERR_CSP_INVALID_KEY
;
605 derKeyLen
= pemKeyLen
;
608 /* importing a raw key into the CSPDL involves a NULL unwrap */
609 memset(&unwrappedKey
, 0, sizeof(CSSM_KEY
));
610 memset(&wrappedKey
, 0, sizeof(CSSM_KEY
));
612 /* set up the imported key to look like a CSSM_KEY */
613 hdr
->HeaderVersion
= CSSM_KEYHEADER_VERSION
;
614 hdr
->BlobType
= CSSM_KEYBLOB_RAW
;
615 hdr
->AlgorithmId
= keyAlg
;
616 hdr
->KeyClass
= CSSM_KEYCLASS_PRIVATE_KEY
;
617 hdr
->KeyAttr
= CSSM_KEYATTR_EXTRACTABLE
;
618 hdr
->KeyUsage
= CSSM_KEYUSE_ANY
;
619 hdr
->Format
= keyFormat
;
620 wrappedKey
.KeyData
.Data
= derKey
;
621 wrappedKey
.KeyData
.Length
= derKeyLen
;
623 /* get key size in bits from raw CSP */
624 rawCspHand
= cuCspStartup(CSSM_TRUE
);
625 if(rawCspHand
== 0) {
626 printf("***Error attaching to CSP. Aborting.\n");
627 crtn
= CSSMERR_CSSM_INTERNAL_ERROR
;
630 CSSM_KEY_SIZE keySize
;
631 crtn
= CSSM_QueryKeySizeInBits(rawCspHand
, NULL
, &wrappedKey
, &keySize
);
633 printError("***Error finding size of key","CSSM_QueryKeySizeInBits",crtn
);
636 hdr
->LogicalKeySizeInBits
= keySize
.LogicalKeySizeInBits
;
638 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
639 crtn
= CSSM_CSP_CreateSymmetricContext(cspHand
,
640 CSSM_ALGID_NONE
, // unwrapAlg
641 CSSM_ALGMODE_NONE
, // unwrapMode
643 NULL
, // unwrappingKey
645 CSSM_PADDING_NONE
, // unwrapPad
649 printError("***Error creating context","CSSM_CSP_CreateSymmetricContext",crtn
);
653 /* add DL/DB to context */
654 CSSM_CONTEXT_ATTRIBUTE newAttr
;
655 newAttr
.AttributeType
= CSSM_ATTRIBUTE_DL_DB_HANDLE
;
656 newAttr
.AttributeLength
= sizeof(CSSM_DL_DB_HANDLE
);
657 newAttr
.Attribute
.Data
= (CSSM_DATA_PTR
)&dlDbHand
;
658 crtn
= CSSM_UpdateContextAttributes(ccHand
, 1, &newAttr
);
660 printError("***Error updating context attributes","CSSM_UpdateContextAttributes",crtn
);
664 /* do the NULL unwrap */
665 labelData
.Data
= (uint8
*)privKeyLabel
;
666 labelData
.Length
= strlen(privKeyLabel
) + 1;
667 crtn
= CSSM_UnwrapKey(ccHand
,
671 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_PERMANENT
| CSSM_KEYATTR_SENSITIVE
|
672 CSSM_KEYATTR_EXTRACTABLE
,
674 NULL
, // CredAndAclEntry
676 &descData
); // required
677 if(crtn
!= CSSM_OK
) {
678 cuPrintError("CSSM_UnwrapKey", crtn
);
682 /* one more thing: bind this private key to its public key */
683 crtn
= setPubKeyHash(cspHand
, dlDbHand
, privKeyLabel
);
685 /* We don't need the unwrapped key any more */
686 CSSM_FreeKey(cspHand
,
689 CSSM_FALSE
); // delete
693 CSSM_DeleteContext(ccHand
);
698 if(pemFormat
&& pemKey
) {
702 CSSM_ModuleDetach(rawCspHand
);
707 static OSStatus
importCert(
708 SecKeychainRef kcRef
,
709 CSSM_DL_DB_HANDLE dlDbHand
,
710 CSSM_CSP_HANDLE cspHand
,
711 CSSM_CL_HANDLE clHand
,
712 const char *fileName
,
713 const char *privKeyFileName
, // optional for importing priv key
714 CSSM_BOOL pemFormat
, // format of files
715 CSSM_KEYBLOB_FORMAT privKeyFormat
) // optional format of priv key
717 unsigned char *cert
= NULL
;
720 unsigned char *der
= NULL
;
723 if(readFile(fileName
, &cert
, &certLen
)) {
724 printf("***Error reading certificate from file %s. Aborting.\n",
729 int rtn
= pemDecode(cert
, certLen
, &der
, &derLen
);
731 printf("***%s: Bad PEM formatting. Aborting.\n", fileName
);
735 certData
.Length
= derLen
;
738 certData
.Data
= cert
;
739 certData
.Length
= certLen
;
742 SecCertificateRef certRef
;
743 OSStatus ortn
= SecCertificateCreateFromData(
746 CSSM_CERT_ENCODING_DER
,
749 printError("***Error creating certificate","SecCertificateCreateFromData",ortn
);
750 cuPrintError("", ortn
);
753 ortn
= SecCertificateAddToKeychain(certRef
, kcRef
);
755 printError("***Error adding certificate to keychain","SecCertificateAddToKeychain",ortn
);
759 if(privKeyFileName
) {
760 /* Importing private key requires algorithm, from cert */
763 crtn
= CSSM_CL_CertGetKeyInfo(clHand
, &certData
, &pubKey
);
765 printError("***Error obtaining public key from cert. Aborting","CSSM_CL_CertGetKeyInfo",crtn
);
768 crtn
= importPrivateKey(dlDbHand
, cspHand
, privKeyFileName
,
769 pubKey
->KeyHeader
.AlgorithmId
, pemFormat
, privKeyFormat
);
771 printError("***Error importing private key. Aborting","importPrivateKey",crtn
);
774 /* this was mallocd by the CL */
775 cuAppFree(pubKey
->KeyData
.Data
, NULL
);
776 cuAppFree(pubKey
, NULL
);
778 printf("...certificate successfully imported.\n");
788 static OSStatus
importCRL(
789 CSSM_DL_DB_HANDLE dlDbHand
,
790 CSSM_CL_HANDLE clHand
,
791 const char *fileName
,
794 unsigned char *crl
= NULL
;
797 unsigned char *der
= NULL
;
800 if(readFile(fileName
, &crl
, &crlLen
)) {
801 printf("***Error reading CRL from file %s. Aborting.\n",
806 int rtn
= pemDecode(crl
, crlLen
, &der
, &derLen
);
808 printf("***%s: Bad PEM formatting. Aborting.\n", fileName
);
812 crlData
.Length
= derLen
;
816 crlData
.Length
= crlLen
;
818 CSSM_RETURN crtn
= cuAddCrlToDb(dlDbHand
, clHand
, &crlData
, NULL
);
820 printError("***Error adding CRL to keychain. Aborting","cuAddCrlToDb",crtn
);
823 printf("...CRL successfully imported.\n");
834 static OSStatus
createCertCsr(
835 CSSM_BOOL createCsr
, // true: CSR, false: Cert
836 CSSM_TP_HANDLE tpHand
, // eventually, a SecKeychainRef
837 CSSM_CL_HANDLE clHand
,
838 CSSM_CSP_HANDLE cspHand
,
839 CSSM_KEY_PTR subjPubKey
,
840 CSSM_KEY_PTR signerPrivKey
,
841 CSSM_ALGORITHMS sigAlg
,
842 const CSSM_OID
*sigOid
,
843 CU_KeyUsage keyUsage
, // kKeyUseSigning, etc.
845 * Issuer's RDN is obtained from the issuer cert, if present, or is
846 * assumed to be the same as the subject name (i.e., we're creating
847 * a self-signed root cert).
849 const CSSM_DATA
*issuerCert
,
850 CSSM_BOOL useAllDefaults
,
851 CSSM_DATA_PTR certData
) // cert or CSR: mallocd and RETURNED
853 CE_DataAndType exts
[2];
854 CE_DataAndType
*extp
= exts
;
857 CSSM_DATA refId
; // mallocd by CSSM_TP_SubmitCredRequest
858 CSSM_APPLE_TP_CERT_REQUEST certReq
;
859 CSSM_TP_REQUEST_SET reqSet
;
861 CSSM_BOOL confirmRequired
;
862 CSSM_TP_RESULT_SET_PTR resultSet
;
863 CSSM_ENCODED_CERT
*encCert
;
864 CSSM_APPLE_TP_NAME_OID subjectNames
[MAX_NAMES
];
866 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext
;
869 /* Note a lot of the CSSM_APPLE_TP_CERT_REQUEST fields are not
870 * used for the createCsr option, but we'll fill in as much as is practical
873 if(issuerCert
!= NULL
) {
874 printf("createCertCsr: issuerCert not implemented\n");
880 char challengeBuf
[400];
883 strcpy(challengeBuf
, ZDEF_CHALLENGE
);
887 getStringWithPrompt("Enter challenge string: ",
888 challengeBuf
, sizeof(challengeBuf
));
889 if(challengeBuf
[0] != '\0') {
894 certReq
.challengeString
= challengeBuf
;
898 certReq
.challengeString
= NULL
;
900 /* KeyUsage extension */
901 extp
->type
= DT_KeyUsage
;
902 extp
->critical
= CSSM_FALSE
;
903 extp
->extension
.keyUsage
= 0;
904 if(keyUsage
& kKeyUseSigning
) {
905 extp
->extension
.keyUsage
|=
906 (CE_KU_DigitalSignature
| CE_KU_KeyCertSign
);
908 if(keyUsage
& kKeyUseEncrypting
) {
909 extp
->extension
.keyUsage
|=
910 (CE_KU_KeyEncipherment
| CE_KU_DataEncipherment
);
915 /* BasicConstraints */
916 extp
->type
= DT_BasicConstraints
;
917 extp
->critical
= CSSM_TRUE
;
918 extp
->extension
.basicConstraints
.cA
= CSSM_TRUE
;
919 extp
->extension
.basicConstraints
.pathLenConstraintPresent
= CSSM_FALSE
;
924 /* name array, get from user. */
926 subjectNames
[0].string
= ZDEF_COMMON_NAME
;
927 subjectNames
[0].oid
= &CSSMOID_CommonName
;
928 subjectNames
[1].string
= ZDEF_ORG_NAME
;
929 subjectNames
[1].oid
= &CSSMOID_OrganizationName
;
930 subjectNames
[2].string
= ZDEF_COUNTRY
;
931 subjectNames
[2].oid
= &CSSMOID_CountryName
;
932 subjectNames
[3].string
= ZDEF_STATE
;
933 subjectNames
[3].oid
= &CSSMOID_StateProvinceName
;
937 getNameOids(subjectNames
, &numNames
);
941 certReq
.cspHand
= cspHand
;
942 certReq
.clHand
= clHand
;
943 certReq
.serialNumber
= 0x12345678; // TBD - random? From user?
944 certReq
.numSubjectNames
= numNames
;
945 certReq
.subjectNames
= subjectNames
;
947 /* TBD - if we're passed in a signing cert, certReq.issuerNameX509 will
948 * be obtained from that cert. For now we specify "self-signed" cert
949 * by not providing an issuer name at all. */
950 certReq
.numIssuerNames
= 0; // root for now
951 certReq
.issuerNames
= NULL
;
952 certReq
.issuerNameX509
= NULL
;
953 certReq
.certPublicKey
= subjPubKey
;
954 certReq
.issuerPrivateKey
= signerPrivKey
;
955 certReq
.signatureAlg
= sigAlg
;
956 certReq
.signatureOid
= *sigOid
;
957 certReq
.notBefore
= 0; // TBD - from user
958 certReq
.notAfter
= 60 * 60 * 24 * 30; // seconds from now
959 certReq
.numExtensions
= numExts
;
960 certReq
.extensions
= exts
;
962 reqSet
.NumberOfRequests
= 1;
963 reqSet
.Requests
= &certReq
;
965 /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */
966 memset(&CallerAuthContext
, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT
));
967 memset(&policyId
, 0, sizeof(CSSM_FIELD
));
969 policyId
.FieldOid
= CSSMOID_APPLE_TP_CSR_GEN
;
972 policyId
.FieldOid
= CSSMOID_APPLE_TP_LOCAL_CERT_GEN
;
974 CallerAuthContext
.Policy
.NumberOfPolicyIds
= 1;
975 CallerAuthContext
.Policy
.PolicyIds
= &policyId
;
977 #if SEC_KEY_CREATE_PAIR
979 CssmClient::AclFactory factory
;
980 CallerAuthContext
.CallerCredentials
=
981 const_cast<Security::AccessCredentials
*>(factory
.promptCred());
982 #endif /* SEC_KEY_CREATE_PAIR */
984 CSSM_RETURN crtn
= CSSM_TP_SubmitCredRequest(tpHand
,
985 NULL
, // PreferredAuthority
986 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE
,
992 /* before proceeding, free resources allocated thus far */
993 if(!useAllDefaults
) {
994 freeNameOids(subjectNames
, numNames
);
998 printError("***Error submitting credential request","CSSM_TP_SubmitCredRequest",crtn
);
1001 crtn
= CSSM_TP_RetrieveCredResult(tpHand
,
1003 NULL
, // CallerAuthCredentials
1008 printError("***Error retreiving credential request","CSSM_TP_RetrieveCredResult",crtn
);
1011 if(resultSet
== NULL
) {
1012 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
1015 encCert
= (CSSM_ENCODED_CERT
*)resultSet
->Results
;
1016 *certData
= encCert
->CertBlob
;
1018 /* free resources allocated by TP */
1019 APP_FREE(refId
.Data
);
1021 APP_FREE(resultSet
);
1025 /* dump all certs & CRLs in a DL/DB */
1026 static OSStatus
dumpCrlsCerts(
1027 CSSM_DL_DB_HANDLE dlDbHand
,
1028 CSSM_CL_HANDLE clHand
,
1034 crtn
= cuDumpCrlsCerts(dlDbHand
, clHand
, CSSM_TRUE
, numItems
, verbose
);
1035 if(crtn
&& (crtn
!= CSSMERR_DL_INVALID_RECORDTYPE
)) {
1036 /* invalid record type just means "this hasn't been set up
1037 * for certs yet". */
1040 printf("...%u certificates found\n", numItems
);
1041 crtn
= cuDumpCrlsCerts(dlDbHand
, clHand
, CSSM_FALSE
, numItems
, verbose
);
1042 if(crtn
&& (crtn
!= CSSMERR_DL_INVALID_RECORDTYPE
)) {
1043 /* invalid record type just means "this hasn't been set up
1047 printf("...%u CRLs found\n", numItems
);
1061 CO_DumpDb
// display certs & CRLs from a DB
1064 int realmain (int argc
, char **argv
)
1066 SecKeychainRef kcRef
= nil
;
1067 char kcPath
[MAXPATHLEN
+ 1];
1068 UInt32 kcPathLen
= MAXPATHLEN
+ 1;
1069 CSSM_BOOL createKc
= CSSM_FALSE
;
1071 CSSM_DL_DB_HANDLE dlDbHand
= {0, 0};
1072 CSSM_CSP_HANDLE cspHand
= 0;
1073 CSSM_TP_HANDLE tpHand
= 0;
1074 CSSM_CL_HANDLE clHand
= 0;
1075 CSSM_KEY_PTR pubKey
;
1076 CSSM_KEY_PTR privKey
;
1079 CSSM_ALGORITHMS keyAlg
;
1080 CSSM_ALGORITHMS sigAlg
;
1081 const CSSM_OID
*sigOid
;
1082 CSSM_DATA certData
= {0, NULL
};
1084 CU_KeyUsage keyUsage
= 0;
1087 CSSM_BOOL createCsr
= CSSM_FALSE
; // else create cert
1090 Boolean promptUser
= true;
1091 char *allocdPassPhrase
= NULL
;
1092 OSStatus ourRtn
= noErr
;
1094 /* command line arguments */
1095 char *fileName
= NULL
;
1096 CSSM_BOOL pemFormat
= CSSM_TRUE
;
1098 uint32 keySizeInBits
;
1099 char *kcName
= NULL
;
1100 CSSM_BOOL useAllDefaults
= CSSM_FALSE
; // undoc'd cmd option
1101 char *passPhrase
= NULL
;
1102 const char *privKeyFileName
= NULL
; // optional openssl-style private key
1103 CSSM_KEYBLOB_FORMAT privKeyFormat
= CSSM_KEYBLOB_RAW_FORMAT_NONE
;
1104 #if SEC_KEY_CREATE_PAIR
1105 SecKeyRef pubSecKey
= NULL
;
1106 SecKeyRef privSecKey
= NULL
;
1108 CSSM_BOOL useSecKey
= CSSM_FALSE
; // w/default ACL
1113 switch(argv
[1][0]) {
1123 createCsr
= CSSM_TRUE
;
1147 op
= CO_DisplayCert
;
1174 for(arg
=optArgs
; arg
<argc
; arg
++) {
1178 if(argp
[1] != '=') {
1184 verbose
= CSSM_TRUE
;
1187 pemFormat
= CSSM_FALSE
;
1190 createKc
= CSSM_TRUE
;
1193 if(argp
[1] != '=') {
1196 passPhrase
= &argp
[2];
1199 if((op
!= CO_CreateCert
) || (argp
[1] != '=')){
1202 fileName
= &argp
[2];
1205 privKeyFileName
= &argp
[2];
1208 if(argp
[1] != '=') {
1213 privKeyFormat
= CSSM_KEYBLOB_RAW_FORMAT_PKCS1
;
1216 privKeyFormat
= CSSM_KEYBLOB_RAW_FORMAT_PKCS8
;
1219 privKeyFormat
= CSSM_KEYBLOB_RAW_FORMAT_FIPS186
;
1221 default:usage(argv
);
1225 /* undocumented "use all defaults quickly" option */
1226 useAllDefaults
= CSSM_TRUE
;
1228 #if SEC_KEY_CREATE_PAIR
1230 useSecKey
= CSSM_TRUE
;
1237 if((passPhrase
!= NULL
) && !createKc
) {
1238 printf("***passphrase specification only allowed on keychain create. Aborting.\n");
1242 case CO_DisplayCert
:
1244 displayCertCRL(fileName
, pemFormat
, CC_Cert
, verbose
);
1247 displayCertCRL(fileName
, pemFormat
, CC_CRL
, verbose
);
1254 clHand
= cuClStartup();
1256 printf("Error connecting to CL. Aborting.\n");
1260 /* that's all we need for verifying a CSR */
1261 if(op
== CO_VerifyCSR
) {
1262 ourRtn
= verifyCsr(clHand
, fileName
, pemFormat
);
1267 if(kcName
[0] == '/') {
1268 /* specific keychain not in Library/Keychains */
1269 strcpy(kcPath
, kcName
);
1272 char *userHome
= getenv("HOME");
1274 if(userHome
== NULL
) {
1275 /* well, this is probably not going to work */
1278 sprintf(kcPath
, "%s/%s/%s", userHome
, KC_DB_PATH
, kcName
);
1282 /* use default keychain */
1283 ortn
= SecKeychainCopyDefault(&kcRef
);
1285 printError("***Error retreiving default keychain","SecKeychainCopyDefault",ortn
);
1288 ortn
= SecKeychainGetPath(kcRef
, &kcPathLen
, kcPath
);
1290 printError("***Error retreiving default keychain path","SecKeychainGetPath",ortn
);
1295 * OK, we have a path, we have to release the first KC ref,
1296 * then get another one by opening it
1301 if(passPhrase
!= NULL
) {
1302 pwdLen
= strlen(passPhrase
);
1303 /* work around bug - incoming passphrase gets freed */
1304 Security::CssmAllocator
&alloc
= Security::CssmAllocator::standard();
1305 allocdPassPhrase
= (char *)alloc
.malloc(pwdLen
);
1306 memmove(allocdPassPhrase
, passPhrase
, pwdLen
);
1310 ortn
= SecKeychainCreate(kcPath
,
1314 nil
, // initialAccess
1316 /* fixme - do we have to open it? */
1318 printError("***Error creating keychain","SecKeychainCreate",ortn
);
1319 printf("***Path: %s\n", kcPath
);
1324 ortn
= SecKeychainOpen(kcPath
, &kcRef
);
1326 printError("***Error opening keychain. Aborting","SecKeychainOpen",ortn
);
1327 printf("***Path: %s\n", kcPath
);
1332 /* get associated DL/DB handle */
1333 ortn
= SecKeychainGetDLDBHandle(kcRef
, &dlDbHand
);
1335 printError("***Error getting keychain handle","SecKeychainGetDLDBHandle",ortn
);
1339 #if SEC_KEYCHAIN_GET_CSP
1340 ortn
= SecKeychainGetCSPHandle(kcRef
, &cspHand
);
1342 printError("***Error getting keychain CSP handle","SecKeychainGetCSPHandle",ortn
);
1346 cspHand
= cuCspStartup(CSSM_FALSE
);
1348 printf("Error connecting to CSP/DL. Aborting.\n");
1351 #endif /* SEC_KEYCHAIN_GET_CSP */
1355 ourRtn
= importCert(kcRef
, dlDbHand
, cspHand
, clHand
, fileName
, privKeyFileName
,
1356 pemFormat
, privKeyFormat
);
1359 ourRtn
= importCRL(dlDbHand
, clHand
, fileName
, pemFormat
);
1362 ourRtn
= dumpCrlsCerts(dlDbHand
, clHand
, verbose
);
1368 /* remaining ops need TP as well */
1369 tpHand
= cuTpStartup();
1371 printf("Error connecting to TP. Aborting.\n");
1375 /*** op = CO_CreateCert or CO_CreateCSR ***/
1378 * TBD: eventually we want to present the option of using an existing
1379 * SecIdentityRef from the keychain as the signing cert/key. If none
1380 * found or the user says they want a root, we generate the signing key
1381 * pair as follows....
1386 * Generate a key pair. For now we do this via CDSA.
1389 if(useAllDefaults
) {
1390 strcpy(labelBuf
, ZDEF_KEY_LABEL
);
1394 getStringWithPrompt("Enter key and certificate label: ", labelBuf
,
1396 if(labelBuf
[0] != '\0') {
1401 keyLabel
.Data
= (uint8
*)labelBuf
;
1402 keyLabel
.Length
= strlen(labelBuf
);
1404 /* get key algorithm and size */
1405 if(useAllDefaults
) {
1406 keyAlg
= ZDEF_KEY_ALG
;
1407 keySizeInBits
= ZDEF_KEY_SIZE
;
1410 getKeyParams(keyAlg
, keySizeInBits
);
1413 /* get usage for keys and certs */
1414 if(useAllDefaults
) {
1415 keyUsage
= ZDEF_KEY_USAGE
;
1418 keyUsage
= getKeyUsage(isRoot
);
1421 printf("...Generating key pair...\n");
1424 /* generate keys using SecKeyCreatePair */
1425 #if SEC_KEY_CREATE_PAIR
1426 ourRtn
= generateSecKeyPair(kcRef
,
1436 /* can't happen, useSecKey must be false */
1440 /* generate keys using CSPDL */
1441 ourRtn
= generateKeyPair(cspHand
,
1452 printError("Error generating keys; aborting","generateKeyPair",ourRtn
);
1456 /* get signing algorithm per the signing key */
1457 if(useAllDefaults
) {
1458 sigAlg
= ZDEF_SIG_ALG
;
1459 sigOid
= &ZDEF_SIG_OID
;
1462 ourRtn
= getSigAlg(privKey
, sigAlg
, sigOid
);
1464 printError("Cannot sign with this private key. Aborting","getSigAlg",ourRtn
);
1470 printf("...creating CSR...\n");
1473 printf("...creating certificate...\n");
1475 /* generate the cert */
1476 ourRtn
= createCertCsr(createCsr
,
1485 NULL
, // issuer cert
1492 printCert(certData
.Data
, certData
.Length
, CSSM_FALSE
);
1493 printCertShutdown();
1498 * Create CSR, or create cert with outFileName option.
1499 * Write results to a file
1501 unsigned char *pem
= NULL
;
1509 headerStr
= "CERTIFICATE REQUEST";
1512 headerStr
= "CERTIFICATE";
1515 printf("***INTERNAL ERROR; aborting.\n");
1518 rtn
= pemEncode(certData
.Data
, certData
.Length
, &pem
, &pemLen
, headerStr
);
1520 /* very unlikely, I think malloc is the only failure */
1521 printf("***Error PEM-encoding output. Aborting.\n");
1524 rtn
= writeFile(fileName
, pem
, pemLen
);
1527 rtn
= writeFile(fileName
, certData
.Data
, certData
.Length
);
1530 printf("***Error writing CSR to %s\n", fileName
);
1534 printf("Wrote %u bytes of CSR to %s\n", (unsigned)certData
.Length
,
1541 if(op
== CO_CreateCert
) {
1542 /* store the cert in the same DL/DB as the key pair */
1543 crtn
= cuAddCertToKC(kcRef
,
1546 CSSM_CERT_ENCODING_DER
,
1547 labelBuf
, // printName
1549 if(crtn
== CSSM_OK
) {
1550 printf("..cert stored in Keychain.\n");
1553 printError("Cannot store certificate","cuAddCertToKC",crtn
);
1559 #if SEC_KEY_CREATE_PAIR
1560 if(pubSecKey
!= NULL
) {
1561 CFRelease(pubSecKey
);
1563 if(privSecKey
!= NULL
) {
1564 CFRelease(privSecKey
);
1570 int main (int argc
, char **argv
)
1573 return realmain (argc
, argv
);
1575 catch (AbortException e
)
1577 putchar ('\n'); // prompt on the next line.