2 * pubKeyTool.cpp - calculate public key hash of arbitrary keys and certs; derive
3 * public key from a private key or a cert.
10 #include <Security/Security.h>
11 #include <security_cdsa_utils/cuFileIo.h>
12 #include <security_cdsa_utils/cuCdsaUtils.h>
16 static void usage(char **argv
)
18 printf("usage: %s [options]\n", argv
[0]);
20 printf(" -k priv_key_file -- private key file to read\n");
21 printf(" -b pub_key_file -- public key file to read\n");
22 printf(" -c cert_file -- cert file to read\n");
23 printf(" -d -- print public key digest\n");
24 printf(" -o out_file -- write public key to out_file\n");
25 printf(" -f pkcs1|pkcs8|x509 -- input key format\n");
26 printf(" -- default is PKCS8 for private key, PKCS1 for"
28 printf(" -K keychain -- import pub key to this keychain; workaround "
29 "for Radar 4191851)\n");
33 /* Convert raw key blob into a respectable CSSM_KEY. */
34 static CSSM_RETURN
inferCssmKey(
35 const CSSM_DATA
&keyBlob
,
37 CSSM_KEYBLOB_FORMAT keyForm
,
38 CSSM_CSP_HANDLE cspHand
,
41 memset(&outKey
, 0, sizeof(CSSM_KEY
));
42 outKey
.KeyData
= keyBlob
;
43 CSSM_KEYHEADER
&hdr
= outKey
.KeyHeader
;
44 hdr
.HeaderVersion
= CSSM_KEYHEADER_VERSION
;
46 hdr
.BlobType
= CSSM_KEYBLOB_RAW
;
47 hdr
.AlgorithmId
= CSSM_ALGID_RSA
;
48 hdr
.KeyAttr
= CSSM_KEYATTR_EXTRACTABLE
;
50 hdr
.KeyClass
= isPrivKey
? CSSM_KEYCLASS_PRIVATE_KEY
: CSSM_KEYCLASS_PUBLIC_KEY
;
51 hdr
.KeyUsage
= CSSM_KEYUSE_ANY
;
52 hdr
.WrapAlgorithmId
= CSSM_ALGID_NONE
;
53 hdr
.WrapMode
= CSSM_ALGMODE_NONE
;
55 * LogicalKeySizeInBits - ask the CSP
57 CSSM_KEY_SIZE keySize
;
59 crtn
= CSSM_QueryKeySizeInBits(cspHand
, CSSM_INVALID_HANDLE
, &outKey
,
62 cssmPerror("CSSM_QueryKeySizeInBits", crtn
);
65 hdr
.LogicalKeySizeInBits
= keySize
.LogicalKeySizeInBits
;
70 * Given any key in either blob or reference format,
71 * obtain the associated public key's SHA-1 hash.
73 static CSSM_RETURN
keyDigest(
74 CSSM_CSP_HANDLE cspHand
,
76 CSSM_DATA_PTR
*hashData
) /* struct and contents cuAppMalloc'd and RETURNED */
78 CSSM_CC_HANDLE ccHand
;
84 /* validate input params */
87 printf("keyHash: bogus args\n");
88 return CSSMERR_CSSM_INTERNAL_ERROR
;
91 /* cook up a context for a passthrough op */
92 crtn
= CSSM_CSP_CreatePassThroughContext(cspHand
,
96 cssmPerror("CSSM_CSP_CreatePassThroughContext", crtn
);
100 /* now it's up to the CSP */
101 crtn
= CSSM_CSP_PassThrough(ccHand
,
102 CSSM_APPLECSP_KEYDIGEST
,
106 cssmPerror("CSSM_CSP_PassThrough(KEYDIGEST)", crtn
);
112 CSSM_DeleteContext(ccHand
);
117 * Here's a tricky one. Given a private key, obtain the correspoding public key.
118 * This uses a private key blob format that's used internally in the CSP
119 * to generate key digests.
123 * this magic const copied from BinaryKey.h
125 #define CSSM_KEYBLOB_RAW_FORMAT_DIGEST \
126 (CSSM_KEYBLOB_RAW_FORMAT_VENDOR_DEFINED + 0x12345)
128 static CSSM_RETURN
pubKeyFromPrivKey(
129 CSSM_CSP_HANDLE cspHand
,
130 const CSSM_KEY
*privKey
, // assumed to be raw format
133 /* first convert to reference key */
136 crtn
= cspRawKeyToRef(cspHand
, privKey
, &refKey
);
141 /* now a NULL wrap with the magic format attribute */
142 CSSM_CC_HANDLE ccHand
;
143 CSSM_ACCESS_CREDENTIALS creds
;
144 CSSM_DATA descData
= {0, 0};
146 crtn
= CSSM_CSP_CreateSymmetricContext(cspHand
,
156 cssmPerror("CSSM_CSP_CreateSymmetricContext", crtn
);
159 crtn
= AddContextAttribute(ccHand
,
161 * The output of the WrapKey is a private key as far as the CSP is
162 * concerned, at the level that this attribute is used anyway....
164 CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT
,
168 CSSM_KEYBLOB_RAW_FORMAT_DIGEST
);
170 cssmPerror("CSSM_CSP_CreateSymmetricContext", crtn
);
173 memset(pubKey
, 0, sizeof(CSSM_KEY
));
174 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
175 crtn
= CSSM_WrapKey(ccHand
,
181 cssmPerror("CSSM_WrapKey", crtn
);
185 /* now: presto chango - don't do this at home! */
186 pubKey
->KeyHeader
.KeyClass
= CSSM_KEYCLASS_PUBLIC_KEY
;
188 CSSM_FreeKey(cspHand
, NULL
, &refKey
, CSSM_FALSE
);
189 CSSM_DeleteContext(ccHand
);
194 * Import a key into a DLDB.
196 static CSSM_RETURN
importToDlDb(
197 CSSM_CSP_HANDLE cspHand
,
198 CSSM_DL_DB_HANDLE_PTR dlDbHand
,
199 const CSSM_KEY
*rawPubKey
,
200 CSSM_DATA_PTR labelData
,
201 CSSM_KEY_PTR importedKey
)
203 CSSM_CC_HANDLE ccHand
= 0;
206 CSSM_ACCESS_CREDENTIALS creds
;
207 CSSM_CONTEXT_ATTRIBUTE newAttr
;
208 CSSM_DATA descData
= {0, 0};
210 memset(importedKey
, 0, sizeof(CSSM_KEY
));
211 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
212 crtn
= CSSM_CSP_CreateSymmetricContext(cspHand
,
216 NULL
, // unwrappingKey
222 cssmPerror("CSSM_CSP_CreateSymmetricContext", crtn
);
225 keyAttr
= CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_EXTRACTABLE
| CSSM_KEYATTR_PERMANENT
;
227 /* Add DLDB to context */
228 newAttr
.AttributeType
= CSSM_ATTRIBUTE_DL_DB_HANDLE
;
229 newAttr
.AttributeLength
= sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE
);
230 newAttr
.Attribute
.Data
= (CSSM_DATA_PTR
)dlDbHand
;
231 crtn
= CSSM_UpdateContextAttributes(ccHand
, 1, &newAttr
);
233 cssmPerror("CSSM_UpdateContextAttributes", crtn
);
238 crtn
= CSSM_UnwrapKey(ccHand
,
244 NULL
, // CredAndAclEntry
246 &descData
); // required
248 cssmPerror("CSSM_UnwrapKey", crtn
);
252 CSSM_DeleteContext(ccHand
);
258 * Free memory via specified plugin's app-level allocator
260 void impExpFreeCssmMemory(
264 CSSM_API_MEMORY_FUNCS memFuncs
;
265 CSSM_RETURN crtn
= CSSM_GetAPIMemoryFunctions(hand
, &memFuncs
);
269 memFuncs
.free_func(p
, memFuncs
.AllocRef
);
273 * Key attrribute names and values.
275 * This is where the public key hash goes.
277 #define SEC_KEY_HASH_ATTR_NAME "Label"
280 * This is where the publicly visible name goes.
282 #define SEC_KEY_PRINT_NAME_ATTR_NAME "PrintName"
285 * Look up public key by label
286 * Set label to new specified label (SHA1 digest)
287 * Set print name to new specified user-visible name
289 static CSSM_RETURN
setPubKeyLabel(
290 CSSM_CSP_HANDLE cspHand
, // where the key lives
291 CSSM_DL_DB_HANDLE
*dlDbHand
, // ditto
292 const CSSM_DATA
*existKeyLabel
, // existing label, a random string, for lookup
293 const CSSM_DATA
*keyDigest
, // SHA1 digest, the new label
294 const CSSM_DATA
*newPrintName
) // new user-visible name
297 CSSM_SELECTION_PREDICATE predicate
;
298 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
300 CSSM_HANDLE resultHand
= 0;
303 * Look up the key in the DL.
305 query
.RecordType
= CSSM_DL_DB_RECORD_PUBLIC_KEY
;
306 query
.Conjunctive
= CSSM_DB_NONE
;
307 query
.NumSelectionPredicates
= 1;
308 predicate
.DbOperator
= CSSM_DB_EQUAL
;
310 predicate
.Attribute
.Info
.AttributeNameFormat
=
311 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
312 predicate
.Attribute
.Info
.Label
.AttributeName
= (char *)"Label";
313 predicate
.Attribute
.Info
.AttributeFormat
=
314 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
315 /* hope this cast is OK */
316 predicate
.Attribute
.Value
= (CSSM_DATA_PTR
)existKeyLabel
;
317 query
.SelectionPredicate
= &predicate
;
319 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
320 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
321 query
.QueryFlags
= 0; // CSSM_QUERY_RETURN_DATA; // FIXME - used?
323 /* build Record attribute with two attrs */
324 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
325 CSSM_DB_ATTRIBUTE_DATA attr
[2];
327 attr
[0].Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
328 attr
[0].Info
.Label
.AttributeName
= (char *)SEC_KEY_HASH_ATTR_NAME
;
329 attr
[0].Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
330 attr
[1].Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
331 attr
[1].Info
.Label
.AttributeName
= (char *)SEC_KEY_PRINT_NAME_ATTR_NAME
;
332 attr
[1].Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
334 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_PUBLIC_KEY
;
335 recordAttrs
.NumberOfAttributes
= 2;
336 recordAttrs
.AttributeData
= attr
;
338 crtn
= CSSM_DL_DataGetFirst(*dlDbHand
,
344 /* abort only on success */
345 if(crtn
!= CSSM_OK
) {
346 cssmPerror("CSSM_DL_DataGetFirst", crtn
);
351 * Update existing attr data.
352 * NOTE: the module which allocated this attribute data - a DL -
353 * was loaded and attached by the keychain layer, not by us. Thus
354 * we can't use the memory allocator functions *we* used when
355 * attaching to the CSP - we have to use the ones
356 * which the client registered with the DL.
358 impExpFreeCssmMemory(dlDbHand
->DLHandle
, attr
[0].Value
->Data
);
359 impExpFreeCssmMemory(dlDbHand
->DLHandle
, attr
[0].Value
);
360 impExpFreeCssmMemory(dlDbHand
->DLHandle
, attr
[1].Value
->Data
);
361 impExpFreeCssmMemory(dlDbHand
->DLHandle
, attr
[1].Value
);
362 attr
[0].Value
= const_cast<CSSM_DATA
*>(keyDigest
);
363 attr
[1].Value
= const_cast<CSSM_DATA
*>(newPrintName
);
365 crtn
= CSSM_DL_DataModify(*dlDbHand
,
366 CSSM_DL_DB_RECORD_PUBLIC_KEY
,
369 NULL
, // DataToBeModified
370 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
372 cssmPerror("CSSM_DL_DataModify", crtn
);
377 CSSM_DL_DataAbortQuery(*dlDbHand
, resultHand
);
380 CSSM_DL_FreeUniqueRecord(*dlDbHand
, record
);
385 #define SHA1_LABEL_LEN 20
386 #define IMPORTED_KEY_NAME "Imported Public Key"
389 * Import a public key into a keychain, with proper Label attribute setting.
390 * A workaround for Radar 4191851.
392 static int pubKeyImport(
394 const CSSM_KEY
*pubKey
,
395 CSSM_CSP_HANDLE rawCspHand
) /* raw CSP handle for calculating digest */
397 CSSM_CSP_HANDLE cspHand
;
398 CSSM_DL_DB_HANDLE dlDbHand
;
401 SecKeychainRef kcRef
= NULL
;
403 CSSM_DATA_PTR digest
= NULL
;
404 CSSM_KEY importedKey
;
405 CSSM_DATA newPrintName
=
406 { (uint32
)strlen(IMPORTED_KEY_NAME
), (uint8
*)IMPORTED_KEY_NAME
};
408 /* NULL unwrap stuff */
409 uint8 tempLabel
[SHA1_LABEL_LEN
];
410 CSSM_DATA labelData
= {SHA1_LABEL_LEN
, tempLabel
};
412 ortn
= SecKeychainOpen(kcName
, &kcRef
);
414 cssmPerror("SecKeychainOpen", ortn
);
417 /* subsequent errors to errOut: */
419 /* Get CSSM handles */
420 ortn
= SecKeychainGetCSPHandle(kcRef
, &cspHand
);
422 cssmPerror("SecKeychainGetCSPHandle", ortn
);
426 ortn
= SecKeychainGetDLDBHandle(kcRef
, &dlDbHand
);
428 cssmPerror("SecKeychainGetCSPHandle", ortn
);
433 /* public key hash from raw CSP */
434 crtn
= keyDigest(rawCspHand
, pubKey
, &digest
);
440 /* random label for initial storage and later retrieval */
441 appGetRandomBytes(tempLabel
, SHA1_LABEL_LEN
);
443 /* import the key into the keychain's DLDB */
444 memset(&importedKey
, 0, sizeof(CSSM_KEY
));
445 crtn
= importToDlDb(cspHand
, &dlDbHand
, pubKey
, &labelData
, &importedKey
);
451 /* don't need this */
452 CSSM_FreeKey(cspHand
, NULL
, &importedKey
, CSSM_FALSE
);
454 /* update the label and printName attributes */
455 crtn
= setPubKeyLabel(cspHand
, &dlDbHand
, &labelData
, digest
, &newPrintName
);
462 APP_FREE(digest
->Data
);
468 int main(int argc
, char **argv
)
470 char *privKeyFile
= NULL
;
471 char *pubKeyFile
= NULL
;
472 char *certFile
= NULL
;
473 char *outFile
= NULL
;
474 bool printDigest
= false;
475 CSSM_KEYBLOB_FORMAT keyForm
= CSSM_KEYBLOB_RAW_FORMAT_NONE
;
483 while ((arg
= getopt(argc
, argv
, "k:b:c:do:f:K:h")) != -1) {
486 privKeyFile
= optarg
;
501 if(!strcmp("pkcs1", optarg
)) {
502 keyForm
= CSSM_KEYBLOB_RAW_FORMAT_PKCS1
;
504 else if(!strcmp("pkcs8", optarg
)) {
505 keyForm
= CSSM_KEYBLOB_RAW_FORMAT_PKCS8
;
507 else if(!strcmp("x509", optarg
)) {
508 keyForm
= CSSM_KEYBLOB_RAW_FORMAT_X509
;
522 CSSM_DATA privKeyBlob
= {0, NULL
};
523 CSSM_DATA pubKeyBlob
= {0, NULL
};
524 CSSM_KEY thePrivKey
; // constructed
525 CSSM_KEY thePubKey
; // null-wrapped
526 CSSM_KEY_PTR pubKey
= NULL
;
527 CSSM_KEY_PTR privKey
= NULL
;
529 CSSM_CL_HANDLE clHand
= 0;
530 CSSM_CSP_HANDLE cspHand
= cuCspStartup(CSSM_TRUE
);
534 /* key blob from a file ==> a private CSSM_KEY */
536 if(pubKeyFile
|| certFile
) {
537 printf("****Specify exactly one of {cert_file, priv_key_file, "
542 if(readFile(privKeyFile
, &privKeyBlob
.Data
, &len
)) {
543 printf("***Error reading private key from %s. Aborting.\n", privKeyFile
);
546 privKeyBlob
.Length
= len
;
547 if(keyForm
== CSSM_KEYBLOB_RAW_FORMAT_NONE
) {
548 /* default for private keys */
549 keyForm
= CSSM_KEYBLOB_RAW_FORMAT_PKCS8
;
551 crtn
= inferCssmKey(privKeyBlob
, true, keyForm
, cspHand
, thePrivKey
);
555 privKey
= &thePrivKey
;
558 /* key blob from a file ==> a public CSSM_KEY */
560 if(privKeyFile
|| certFile
) {
561 printf("****Specify exactly one of {cert_file, priv_key_file, "
567 if(readFile(pubKeyFile
, &pubKeyBlob
.Data
, &len
)) {
568 printf("***Error reading public key from %s. Aborting.\n", pubKeyFile
);
571 pubKeyBlob
.Length
= len
;
572 if(keyForm
== CSSM_KEYBLOB_RAW_FORMAT_NONE
) {
573 /* default for public keys */
574 keyForm
= CSSM_KEYBLOB_RAW_FORMAT_PKCS1
;
576 crtn
= inferCssmKey(pubKeyBlob
, false, keyForm
, cspHand
, thePubKey
);
583 /* cert from a file ==> a public CSSM_KEY */
585 if(privKeyFile
|| pubKeyFile
) {
586 printf("****Specify exactly one of {cert_file, priv_key_file, "
591 CSSM_DATA certData
= {0, NULL
};
593 if(readFile(certFile
, &certData
.Data
, &len
)) {
594 printf("***Error reading cert from %s. Aborting.\n", certFile
);
597 certData
.Length
= len
;
599 /* Extract public key - that's what we will be using later */
600 clHand
= cuClStartup();
601 crtn
= CSSM_CL_CertGetKeyInfo(clHand
, &certData
, &pubKey
);
603 cssmPerror("CSSM_CL_CertGetKeyInfo", crtn
);
608 /* now do something useful */
610 CSSM_KEY_PTR theKey
= privKey
;
612 /* maybe we got public key from a cert */
616 printf("***Can't calculate digest because I don't have a key or a clue.\n");
619 CSSM_DATA_PTR dig
= NULL
;
620 crtn
= keyDigest(cspHand
, theKey
, &dig
);
622 printf("Sorry, can't get the digest for this key.\n");
625 if((dig
== NULL
) || (dig
->Length
== 0)) {
626 printf("Screwup calculating digest.\n");
629 printf("Key Digest:\n");
630 for(unsigned dex
=0; dex
<dig
->Length
; dex
++) {
631 printf("%02X ", dig
->Data
[dex
]);
638 if(outFile
|| kcName
) {
639 /* get a public key if we don't already have one */
641 if(privKey
== NULL
) {
642 printf("***PubKey file name specified but no privKey or cert. "
646 crtn
= pubKeyFromPrivKey(cspHand
, privKey
, &thePubKey
);
654 if(writeFile(outFile
, pubKey
->KeyData
.Data
, pubKey
->KeyData
.Length
)) {
655 printf("***Error writing to %s.\n", outFile
);
658 printf("...%lu bytes written to %s.\n", pubKey
->KeyData
.Length
, outFile
);
662 if(pubKeyImport(kcName
, pubKey
, cspHand
) == 0) {
663 printf("....public key %s imported to %s\n", pubKeyFile
, kcName
);
666 printf("***Error importing public key %s to %s\n", pubKeyFile
, kcName
);
670 /* clean up here if you must */