+++ /dev/null
-/*
- * pubKeyTool.cpp - calculate public key hash of arbitrary keys and certs; derive
- * public key from a private key or a cert.
- */
-
-#include <stdlib.h>
-#include <strings.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <Security/Security.h>
-#include <security_cdsa_utils/cuFileIo.h>
-#include <security_cdsa_utils/cuCdsaUtils.h>
-#include "cspwrap.h"
-#include "common.h"
-
-static void usage(char **argv)
-{
- printf("usage: %s [options]\n", argv[0]);
- printf("Options:\n");
- printf(" -k priv_key_file -- private key file to read\n");
- printf(" -b pub_key_file -- public key file to read\n");
- printf(" -c cert_file -- cert file to read\n");
- printf(" -d -- print public key digest\n");
- printf(" -o out_file -- write public key to out_file\n");
- printf(" -f pkcs1|pkcs8|x509 -- input key format\n");
- printf(" -- default is PKCS8 for private key, PKCS1 for"
- " public\n");
- printf(" -K keychain -- import pub key to this keychain; workaround "
- "for Radar 4191851)\n");
- exit(1);
-}
-
-/* Convert raw key blob into a respectable CSSM_KEY. */
-static CSSM_RETURN inferCssmKey(
- const CSSM_DATA &keyBlob,
- bool isPrivKey,
- CSSM_KEYBLOB_FORMAT keyForm,
- CSSM_CSP_HANDLE cspHand,
- CSSM_KEY &outKey)
-{
- memset(&outKey, 0, sizeof(CSSM_KEY));
- outKey.KeyData = keyBlob;
- CSSM_KEYHEADER &hdr = outKey.KeyHeader;
- hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
- /* CspId blank */
- hdr.BlobType = CSSM_KEYBLOB_RAW;
- hdr.AlgorithmId = CSSM_ALGID_RSA;
- hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
- hdr.Format = keyForm;
- hdr.KeyClass = isPrivKey ? CSSM_KEYCLASS_PRIVATE_KEY : CSSM_KEYCLASS_PUBLIC_KEY;
- hdr.KeyUsage = CSSM_KEYUSE_ANY;
- hdr.WrapAlgorithmId = CSSM_ALGID_NONE;
- hdr.WrapMode = CSSM_ALGMODE_NONE;
- /*
- * LogicalKeySizeInBits - ask the CSP
- */
- CSSM_KEY_SIZE keySize;
- CSSM_RETURN crtn;
- crtn = CSSM_QueryKeySizeInBits(cspHand, CSSM_INVALID_HANDLE, &outKey,
- &keySize);
- if(crtn) {
- cssmPerror("CSSM_QueryKeySizeInBits", crtn);
- return crtn;
- }
- hdr.LogicalKeySizeInBits = keySize.LogicalKeySizeInBits;
- return CSSM_OK;
-}
-
-/*
- * Given any key in either blob or reference format,
- * obtain the associated public key's SHA-1 hash.
- */
-static CSSM_RETURN keyDigest(
- CSSM_CSP_HANDLE cspHand,
- const CSSM_KEY *key,
- CSSM_DATA_PTR *hashData) /* struct and contents cuAppMalloc'd and RETURNED */
-{
- CSSM_CC_HANDLE ccHand;
- CSSM_RETURN crtn;
- CSSM_DATA_PTR dp;
-
- *hashData = NULL;
-
- /* validate input params */
- if((key == NULL) ||
- (hashData == NULL)) {
- printf("keyHash: bogus args\n");
- return CSSMERR_CSSM_INTERNAL_ERROR;
- }
-
- /* cook up a context for a passthrough op */
- crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
- key,
- &ccHand);
- if(ccHand == 0) {
- cssmPerror("CSSM_CSP_CreatePassThroughContext", crtn);
- return crtn;
- }
-
- /* now it's up to the CSP */
- crtn = CSSM_CSP_PassThrough(ccHand,
- CSSM_APPLECSP_KEYDIGEST,
- NULL,
- (void **)&dp);
- if(crtn) {
- cssmPerror("CSSM_CSP_PassThrough(KEYDIGEST)", crtn);
- }
- else {
- *hashData = dp;
- crtn = CSSM_OK;
- }
- CSSM_DeleteContext(ccHand);
- return crtn;
-}
-
-/*
- * Here's a tricky one. Given a private key, obtain the correspoding public key.
- * This uses a private key blob format that's used internally in the CSP
- * to generate key digests.
- */
-
-/*
- * this magic const copied from BinaryKey.h
- */
-#define CSSM_KEYBLOB_RAW_FORMAT_DIGEST \
- (CSSM_KEYBLOB_RAW_FORMAT_VENDOR_DEFINED + 0x12345)
-
-static CSSM_RETURN pubKeyFromPrivKey(
- CSSM_CSP_HANDLE cspHand,
- const CSSM_KEY *privKey, // assumed to be raw format
- CSSM_KEY *pubKey)
-{
- /* first convert to reference key */
- CSSM_KEY refKey;
- CSSM_RETURN crtn;
- crtn = cspRawKeyToRef(cspHand, privKey, &refKey);
- if(crtn) {
- return crtn;
- }
-
- /* now a NULL wrap with the magic format attribute */
- CSSM_CC_HANDLE ccHand;
- CSSM_ACCESS_CREDENTIALS creds;
- CSSM_DATA descData = {0, 0};
-
- crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
- CSSM_ALGID_NONE,
- CSSM_ALGMODE_NONE,
- NULL, // passPhrase,
- NULL, // key
- NULL, // initVector,
- CSSM_PADDING_NONE,
- NULL, // Reserved
- &ccHand);
- if(crtn) {
- cssmPerror("CSSM_CSP_CreateSymmetricContext", crtn);
- return crtn;
- }
- crtn = AddContextAttribute(ccHand,
- /*
- * The output of the WrapKey is a private key as far as the CSP is
- * concerned, at the level that this attribute is used anyway....
- */
- CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT,
- sizeof(uint32),
- CAT_Uint32,
- NULL,
- CSSM_KEYBLOB_RAW_FORMAT_DIGEST);
- if(crtn) {
- cssmPerror("CSSM_CSP_CreateSymmetricContext", crtn);
- goto errOut;
- }
- memset(pubKey, 0, sizeof(CSSM_KEY));
- memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
- crtn = CSSM_WrapKey(ccHand,
- &creds,
- &refKey,
- &descData,
- pubKey);
- if(crtn) {
- cssmPerror("CSSM_WrapKey", crtn);
- goto errOut;
- }
-
- /* now: presto chango - don't do this at home! */
- pubKey->KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
-errOut:
- CSSM_FreeKey(cspHand, NULL, &refKey, CSSM_FALSE);
- CSSM_DeleteContext(ccHand);
- return crtn;
-}
-
-/*
- * Import a key into a DLDB.
- */
-static CSSM_RETURN importToDlDb(
- CSSM_CSP_HANDLE cspHand,
- CSSM_DL_DB_HANDLE_PTR dlDbHand,
- const CSSM_KEY *rawPubKey,
- CSSM_DATA_PTR labelData,
- CSSM_KEY_PTR importedKey)
-{
- CSSM_CC_HANDLE ccHand = 0;
- CSSM_RETURN crtn;
- uint32 keyAttr;
- CSSM_ACCESS_CREDENTIALS creds;
- CSSM_CONTEXT_ATTRIBUTE newAttr;
- CSSM_DATA descData = {0, 0};
-
- memset(importedKey, 0, sizeof(CSSM_KEY));
- memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
- crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
- CSSM_ALGID_NONE,
- CSSM_ALGMODE_NONE,
- &creds,
- NULL, // unwrappingKey
- NULL, // initVector
- CSSM_PADDING_NONE,
- 0, // Params
- &ccHand);
- if(crtn) {
- cssmPerror("CSSM_CSP_CreateSymmetricContext", crtn);
- return crtn;
- }
- keyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
-
- /* Add DLDB to context */
- newAttr.AttributeType = CSSM_ATTRIBUTE_DL_DB_HANDLE;
- newAttr.AttributeLength = sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE);
- newAttr.Attribute.Data = (CSSM_DATA_PTR)dlDbHand;
- crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr);
- if(crtn) {
- cssmPerror("CSSM_UpdateContextAttributes", crtn);
- goto errOut;
- }
-
- /* import */
- crtn = CSSM_UnwrapKey(ccHand,
- NULL, // PublicKey
- rawPubKey,
- CSSM_KEYUSE_ANY,
- keyAttr,
- labelData,
- NULL, // CredAndAclEntry
- importedKey,
- &descData); // required
- if(crtn) {
- cssmPerror("CSSM_UnwrapKey", crtn);
- }
-errOut:
- if(ccHand) {
- CSSM_DeleteContext(ccHand);
- }
- return crtn;
-}
-
-/*
- * Free memory via specified plugin's app-level allocator
- */
-void impExpFreeCssmMemory(
- CSSM_HANDLE hand,
- void *p)
-{
- CSSM_API_MEMORY_FUNCS memFuncs;
- CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
- if(crtn) {
- return;
- }
- memFuncs.free_func(p, memFuncs.AllocRef);
-}
-
-/*
- * Key attrribute names and values.
- *
- * This is where the public key hash goes.
- */
-#define SEC_KEY_HASH_ATTR_NAME "Label"
-
-/*
- * This is where the publicly visible name goes.
- */
-#define SEC_KEY_PRINT_NAME_ATTR_NAME "PrintName"
-
-/*
- * Look up public key by label
- * Set label to new specified label (SHA1 digest)
- * Set print name to new specified user-visible name
- */
-static CSSM_RETURN setPubKeyLabel(
- CSSM_CSP_HANDLE cspHand, // where the key lives
- CSSM_DL_DB_HANDLE *dlDbHand, // ditto
- const CSSM_DATA *existKeyLabel, // existing label, a random string, for lookup
- const CSSM_DATA *keyDigest, // SHA1 digest, the new label
- const CSSM_DATA *newPrintName) // new user-visible name
-{
- CSSM_QUERY query;
- CSSM_SELECTION_PREDICATE predicate;
- CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
- CSSM_RETURN crtn;
- CSSM_HANDLE resultHand = 0;
-
- /*
- * Look up the key in the DL.
- */
- query.RecordType = CSSM_DL_DB_RECORD_PUBLIC_KEY;
- query.Conjunctive = CSSM_DB_NONE;
- query.NumSelectionPredicates = 1;
- predicate.DbOperator = CSSM_DB_EQUAL;
-
- predicate.Attribute.Info.AttributeNameFormat =
- CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
- predicate.Attribute.Info.Label.AttributeName = (char *)"Label";
- predicate.Attribute.Info.AttributeFormat =
- CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
- /* hope this cast is OK */
- predicate.Attribute.Value = (CSSM_DATA_PTR)existKeyLabel;
- query.SelectionPredicate = &predicate;
-
- query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
- query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
- query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA; // FIXME - used?
-
- /* build Record attribute with two attrs */
- CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
- CSSM_DB_ATTRIBUTE_DATA attr[2];
-
- attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
- attr[0].Info.Label.AttributeName = (char *)SEC_KEY_HASH_ATTR_NAME;
- attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
- attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
- attr[1].Info.Label.AttributeName = (char *)SEC_KEY_PRINT_NAME_ATTR_NAME;
- attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
-
- recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PUBLIC_KEY;
- recordAttrs.NumberOfAttributes = 2;
- recordAttrs.AttributeData = attr;
-
- crtn = CSSM_DL_DataGetFirst(*dlDbHand,
- &query,
- &resultHand,
- &recordAttrs,
- NULL, // theData
- &record);
- /* abort only on success */
- if(crtn != CSSM_OK) {
- cssmPerror("CSSM_DL_DataGetFirst", crtn);
- goto errOut;
- }
-
- /*
- * Update existing attr data.
- * NOTE: the module which allocated this attribute data - a DL -
- * was loaded and attached by the keychain layer, not by us. Thus
- * we can't use the memory allocator functions *we* used when
- * attaching to the CSP - we have to use the ones
- * which the client registered with the DL.
- */
- impExpFreeCssmMemory(dlDbHand->DLHandle, attr[0].Value->Data);
- impExpFreeCssmMemory(dlDbHand->DLHandle, attr[0].Value);
- impExpFreeCssmMemory(dlDbHand->DLHandle, attr[1].Value->Data);
- impExpFreeCssmMemory(dlDbHand->DLHandle, attr[1].Value);
- attr[0].Value = const_cast<CSSM_DATA *>(keyDigest);
- attr[1].Value = const_cast<CSSM_DATA *>(newPrintName);
-
- crtn = CSSM_DL_DataModify(*dlDbHand,
- CSSM_DL_DB_RECORD_PUBLIC_KEY,
- record,
- &recordAttrs,
- NULL, // DataToBeModified
- CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
- if(crtn) {
- cssmPerror("CSSM_DL_DataModify", crtn);
- }
-errOut:
- /* free resources */
- if(resultHand) {
- CSSM_DL_DataAbortQuery(*dlDbHand, resultHand);
- }
- if(record) {
- CSSM_DL_FreeUniqueRecord(*dlDbHand, record);
- }
- return crtn;
-}
-
-#define SHA1_LABEL_LEN 20
-#define IMPORTED_KEY_NAME "Imported Public Key"
-
-/*
- * Import a public key into a keychain, with proper Label attribute setting.
- * A workaround for Radar 4191851.
- */
-static int pubKeyImport(
- const char *kcName,
- const CSSM_KEY *pubKey,
- CSSM_CSP_HANDLE rawCspHand) /* raw CSP handle for calculating digest */
-{
- CSSM_CSP_HANDLE cspHand;
- CSSM_DL_DB_HANDLE dlDbHand;
- OSStatus ortn;
- CSSM_RETURN crtn;
- SecKeychainRef kcRef = NULL;
- int ourRtn = 0;
- CSSM_DATA_PTR digest = NULL;
- CSSM_KEY importedKey;
- CSSM_DATA newPrintName =
- { (uint32)strlen(IMPORTED_KEY_NAME), (uint8 *)IMPORTED_KEY_NAME};
-
- /* NULL unwrap stuff */
- uint8 tempLabel[SHA1_LABEL_LEN];
- CSSM_DATA labelData = {SHA1_LABEL_LEN, tempLabel};
-
- ortn = SecKeychainOpen(kcName, &kcRef);
- if(ortn) {
- cssmPerror("SecKeychainOpen", ortn);
- return -1;
- }
- /* subsequent errors to errOut: */
-
- /* Get CSSM handles */
- ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
- if(ortn) {
- cssmPerror("SecKeychainGetCSPHandle", ortn);
- ourRtn = -1;
- goto errOut;
- }
- ortn = SecKeychainGetDLDBHandle(kcRef, &dlDbHand);
- if(ortn) {
- cssmPerror("SecKeychainGetCSPHandle", ortn);
- ourRtn = -1;
- goto errOut;
- }
-
- /* public key hash from raw CSP */
- crtn = keyDigest(rawCspHand, pubKey, &digest);
- if(crtn) {
- ourRtn = -1;
- goto errOut;
- }
-
- /* random label for initial storage and later retrieval */
- appGetRandomBytes(tempLabel, SHA1_LABEL_LEN);
-
- /* import the key into the keychain's DLDB */
- memset(&importedKey, 0, sizeof(CSSM_KEY));
- crtn = importToDlDb(cspHand, &dlDbHand, pubKey, &labelData, &importedKey);
- if(crtn) {
- ourRtn = -1;
- goto errOut;
- }
-
- /* don't need this */
- CSSM_FreeKey(cspHand, NULL, &importedKey, CSSM_FALSE);
-
- /* update the label and printName attributes */
- crtn = setPubKeyLabel(cspHand, &dlDbHand, &labelData, digest, &newPrintName);
- if(crtn) {
- ourRtn = -1;
- }
-errOut:
- CFRelease(kcRef);
- if(digest) {
- APP_FREE(digest->Data);
- APP_FREE(digest);
- }
- return ourRtn;
-}
-
-int main(int argc, char **argv)
-{
- char *privKeyFile = NULL;
- char *pubKeyFile = NULL;
- char *certFile = NULL;
- char *outFile = NULL;
- bool printDigest = false;
- CSSM_KEYBLOB_FORMAT keyForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
- char *kcName = NULL;
-
- if(argc < 3) {
- usage(argv);
- }
- extern char *optarg;
- int arg;
- while ((arg = getopt(argc, argv, "k:b:c:do:f:K:h")) != -1) {
- switch (arg) {
- case 'k':
- privKeyFile = optarg;
- break;
- case 'b':
- pubKeyFile = optarg;
- break;
- case 'c':
- certFile = optarg;
- break;
- case 'd':
- printDigest = true;
- break;
- case 'o':
- outFile = optarg;
- break;
- case 'f':
- if(!strcmp("pkcs1", optarg)) {
- keyForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
- }
- else if(!strcmp("pkcs8", optarg)) {
- keyForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
- }
- else if(!strcmp("x509", optarg)) {
- keyForm = CSSM_KEYBLOB_RAW_FORMAT_X509;
- }
- break;
- case 'K':
- kcName = optarg;
- break;
- case 'h':
- usage(argv);
- }
- }
- if(optind != argc) {
- usage(argv);
- }
-
- CSSM_DATA privKeyBlob = {0, NULL};
- CSSM_DATA pubKeyBlob = {0, NULL};
- CSSM_KEY thePrivKey; // constructed
- CSSM_KEY thePubKey; // null-wrapped
- CSSM_KEY_PTR pubKey = NULL;
- CSSM_KEY_PTR privKey = NULL;
- CSSM_RETURN crtn;
- CSSM_CL_HANDLE clHand = 0;
- CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE);
-
- /* gather input */
- if(privKeyFile) {
- /* key blob from a file ==> a private CSSM_KEY */
-
- if(pubKeyFile || certFile) {
- printf("****Specify exactly one of {cert_file, priv_key_file, "
- "pub_key_file}.\n");
- exit(1);
- }
- unsigned len;
- if(readFile(privKeyFile, &privKeyBlob.Data, &len)) {
- printf("***Error reading private key from %s. Aborting.\n", privKeyFile);
- exit(1);
- }
- privKeyBlob.Length = len;
- if(keyForm == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
- /* default for private keys */
- keyForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
- }
- crtn = inferCssmKey(privKeyBlob, true, keyForm, cspHand, thePrivKey);
- if(crtn) {
- goto errOut;
- }
- privKey = &thePrivKey;
- }
- if(pubKeyFile) {
- /* key blob from a file ==> a public CSSM_KEY */
-
- if(privKeyFile || certFile) {
- printf("****Specify exactly one of {cert_file, priv_key_file, "
- "pub_key_file}.\n");
- exit(1);
- }
-
- unsigned len;
- if(readFile(pubKeyFile, &pubKeyBlob.Data, &len)) {
- printf("***Error reading public key from %s. Aborting.\n", pubKeyFile);
- exit(1);
- }
- pubKeyBlob.Length = len;
- if(keyForm == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
- /* default for public keys */
- keyForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
- }
- crtn = inferCssmKey(pubKeyBlob, false, keyForm, cspHand, thePubKey);
- if(crtn) {
- goto errOut;
- }
- pubKey = &thePubKey;
- }
- if(certFile) {
- /* cert from a file ==> a public CSSM_KEY */
-
- if(privKeyFile || pubKeyFile) {
- printf("****Specify exactly one of {cert_file, priv_key_file, "
- "pub_key_file}.\n");
- exit(1);
- }
-
- CSSM_DATA certData = {0, NULL};
- unsigned len;
- if(readFile(certFile, &certData.Data, &len)) {
- printf("***Error reading cert from %s. Aborting.\n", certFile);
- exit(1);
- }
- certData.Length = len;
-
- /* Extract public key - that's what we will be using later */
- clHand = cuClStartup();
- crtn = CSSM_CL_CertGetKeyInfo(clHand, &certData, &pubKey);
- if(crtn) {
- cssmPerror("CSSM_CL_CertGetKeyInfo", crtn);
- goto errOut;
- }
- }
-
- /* now do something useful */
- if(printDigest) {
- CSSM_KEY_PTR theKey = privKey;
- if(theKey == NULL) {
- /* maybe we got public key from a cert */
- theKey = pubKey;
- }
- if(theKey == NULL) {
- printf("***Can't calculate digest because I don't have a key or a clue.\n");
- goto errOut;
- }
- CSSM_DATA_PTR dig = NULL;
- crtn = keyDigest(cspHand, theKey, &dig);
- if(crtn) {
- printf("Sorry, can't get the digest for this key.\n");
- goto errOut;
- }
- if((dig == NULL) || (dig->Length == 0)) {
- printf("Screwup calculating digest.\n");
- goto errOut;
- }
- printf("Key Digest:\n");
- for(unsigned dex=0; dex<dig->Length; dex++) {
- printf("%02X ", dig->Data[dex]);
- }
- printf("\n");
- APP_FREE(dig->Data);
- APP_FREE(dig);
- }
-
- if(outFile || kcName) {
- /* get a public key if we don't already have one */
- if(pubKey == NULL) {
- if(privKey == NULL) {
- printf("***PubKey file name specified but no privKey or cert. "
- "Aborting.\n");
- goto errOut;
- }
- crtn = pubKeyFromPrivKey(cspHand, privKey, &thePubKey);
- if(crtn) {
- goto errOut;
- }
- pubKey = &thePubKey;
- }
- }
- if(outFile) {
- if(writeFile(outFile, pubKey->KeyData.Data, pubKey->KeyData.Length)) {
- printf("***Error writing to %s.\n", outFile);
- }
- else {
- printf("...%lu bytes written to %s.\n", pubKey->KeyData.Length, outFile);
- }
- }
- if(kcName) {
- if(pubKeyImport(kcName, pubKey, cspHand) == 0) {
- printf("....public key %s imported to %s\n", pubKeyFile, kcName);
- }
- else {
- printf("***Error importing public key %s to %s\n", pubKeyFile, kcName);
- }
- }
-errOut:
- /* clean up here if you must */
- return 0;
-}