#include "dbCert.h"
#include <Security/Security.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <security_cdsa_utils/cuDbUtils.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <security_cdsa_utils/cuPem.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static const int kSecAliasItemAttr = 'alis';
#define SCHEMA_ATTR_INFO(id, name, type) \
{ id, (char *)name, {0, NULL}, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
static CSSM_DB_SCHEMA_ATTRIBUTE_INFO certSchemaAttrInfo[] =
{
SCHEMA_ATTR_INFO(kSecCertTypeItemAttr, "CertType", UINT32),
SCHEMA_ATTR_INFO(kSecCertEncodingItemAttr, "CertEncoding", UINT32),
SCHEMA_ATTR_INFO(kSecLabelItemAttr, "PrintName", BLOB),
SCHEMA_ATTR_INFO(kSecAliasItemAttr, "Alias", BLOB),
SCHEMA_ATTR_INFO(kSecSubjectItemAttr, "Subject", BLOB),
SCHEMA_ATTR_INFO(kSecIssuerItemAttr, "Issuer", BLOB),
SCHEMA_ATTR_INFO(kSecSerialNumberItemAttr, "SerialNumber", BLOB),
SCHEMA_ATTR_INFO(kSecSubjectKeyIdentifierItemAttr, "SubjectKeyIdentifier", BLOB),
SCHEMA_ATTR_INFO(kSecPublicKeyHashItemAttr, "PublicKeyHash", BLOB)
};
#define NUM_CERT_SCHEMA_ATTRS \
(sizeof(certSchemaAttrInfo) / sizeof(CSSM_DB_SCHEMA_ATTRIBUTE_INFO))
#define SCHEMA_INDEX_INFO(id, indexNum, indexType) \
{ id, CSSM_DB_INDEX_ ## indexType, CSSM_DB_INDEX_ON_ATTRIBUTE }
static CSSM_DB_SCHEMA_INDEX_INFO certSchemaIndices[] =
{
SCHEMA_INDEX_INFO(kSecCertTypeItemAttr, 0, UNIQUE),
SCHEMA_INDEX_INFO(kSecIssuerItemAttr, 0, UNIQUE),
SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr, 0, UNIQUE),
SCHEMA_INDEX_INFO(kSecCertTypeItemAttr, 1, NONUNIQUE),
SCHEMA_INDEX_INFO(kSecSubjectItemAttr, 2, NONUNIQUE),
SCHEMA_INDEX_INFO(kSecIssuerItemAttr, 3, NONUNIQUE),
SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr, 4, NONUNIQUE),
SCHEMA_INDEX_INFO(kSecSubjectKeyIdentifierItemAttr, 5, NONUNIQUE),
SCHEMA_INDEX_INFO(kSecPublicKeyHashItemAttr, 6, NONUNIQUE)
};
#define NUM_CERT_INDICES \
(sizeof(certSchemaIndices) / sizeof(CSSM_DB_SCHEMA_INDEX_INFO))
CSSM_RETURN tpAddCertSchema(
CSSM_DL_DB_HANDLE dlDbHand)
{
return CSSM_DL_CreateRelation(dlDbHand,
CSSM_DL_DB_RECORD_X509_CERTIFICATE,
"CSSM_DL_DB_RECORD_X509_CERTIFICATE",
NUM_CERT_SCHEMA_ATTRS,
certSchemaAttrInfo,
NUM_CERT_INDICES,
certSchemaIndices);
}
static CSSM_RETURN setPubKeyHash(
CSSM_CSP_HANDLE cspHand,
CSSM_DL_DB_HANDLE dlDbHand,
const char *keyLabel, CSSM_DATA *rtnKeyDigest) {
CSSM_QUERY query;
CSSM_SELECTION_PREDICATE predicate;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_RETURN crtn;
CSSM_DATA labelData;
CSSM_HANDLE resultHand;
labelData.Data = (uint8 *)keyLabel;
labelData.Length = strlen(keyLabel) + 1; query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_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;
predicate.Attribute.Value = &labelData;
query.SelectionPredicate = &predicate;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DB_ATTRIBUTE_DATA attr;
attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr.Info.Label.AttributeName = (char *)"Label";
attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
recordAttrs.NumberOfAttributes = 1;
recordAttrs.AttributeData = &attr;
CSSM_DATA recordData = {0, NULL};
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
&recordAttrs,
&recordData,
&record);
if(crtn != CSSM_OK) {
cuPrintError("CSSM_DL_DataGetFirst", crtn);
return crtn;
}
CSSM_KEY_PTR keyToDigest = (CSSM_KEY_PTR)recordData.Data;
CSSM_DATA_PTR keyDigest = NULL;
CSSM_CC_HANDLE ccHand;
crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
keyToDigest,
&ccHand);
if(crtn) {
cuPrintError("CSSM_CSP_CreatePassThroughContext", crtn);
return crtn;
}
crtn = CSSM_CSP_PassThrough(ccHand,
CSSM_APPLECSP_KEYDIGEST,
NULL,
(void **)&keyDigest);
if(crtn) {
cuPrintError("CSSM_CSP_PassThrough(PUBKEYHASH)", crtn);
return -1;
}
CSSM_FreeKey(cspHand, NULL, keyToDigest, CSSM_FALSE);
CSSM_DeleteContext(ccHand);
CSSM_API_MEMORY_FUNCS memFuncs;
crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs);
if(crtn) {
cuPrintError("CSSM_GetAPIMemoryFunctions(DLHandle)", crtn);
}
else {
memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef);
memFuncs.free_func(attr.Value, memFuncs.AllocRef);
}
attr.Value = keyDigest;
crtn = CSSM_DL_DataModify(dlDbHand,
CSSM_DL_DB_RECORD_PRIVATE_KEY,
record,
&recordAttrs,
NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
if(crtn) {
cuPrintError("CSSM_DL_DataModify(PUBKEYHASH)", crtn);
return crtn;
}
crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
if(crtn) {
cuPrintError("CSSM_DL_DataAbortQuery", crtn);
}
crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record);
if(crtn) {
cuPrintError("CSSM_DL_FreeUniqueRecord", crtn);
crtn = CSSM_OK;
}
if(rtnKeyDigest) {
*rtnKeyDigest = *keyDigest;
}
else {
cuAppFree(keyDigest->Data, NULL);
}
return CSSM_OK;
}
static CSSM_RETURN importPrivateKey(
CSSM_DL_DB_HANDLE dlDbHand,
CSSM_CSP_HANDLE cspHand,
const char *privKeyFileName,
CSSM_ALGORITHMS keyAlg,
CSSM_BOOL pemFormat, CSSM_KEYBLOB_FORMAT keyFormat, CSSM_DATA *keyHash) {
unsigned char *derKey = NULL;
unsigned derKeyLen;
unsigned char *pemKey = NULL;
unsigned pemKeyLen;
CSSM_KEY wrappedKey;
CSSM_KEY unwrappedKey;
CSSM_ACCESS_CREDENTIALS creds;
CSSM_CC_HANDLE ccHand = 0;
CSSM_RETURN crtn;
CSSM_DATA labelData;
CSSM_KEYHEADER_PTR hdr = &wrappedKey.KeyHeader;
CSSM_DATA descData = {0, NULL};
CSSM_CSP_HANDLE rawCspHand = 0;
const char *privKeyLabel = NULL;
switch(keyAlg) {
case CSSM_ALGID_RSA:
switch(keyFormat) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE:
keyFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; break;
case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
break;
default:
printf("***RSA Private key must be in PKCS1 or PKCS8 "
"format\n");
return CSSMERR_CSSM_INTERNAL_ERROR;
}
privKeyLabel = "Imported RSA key";
break;
case CSSM_ALGID_DSA:
switch(keyFormat) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE:
keyFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSL;
break;
case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
break;
default:
printf("***DSA Private key must be in openssl, FIPS186, "
"or PKCS8 format\n");
return CSSMERR_CSSM_INTERNAL_ERROR;
}
privKeyLabel = "Imported DSA key";
break;
case CSSM_ALGID_DH:
switch(keyFormat) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE:
keyFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS8; break;
case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
break;
default:
printf("***Diffie-Hellman Private key must be in"
"PKCS8 format.\n");
return CSSMERR_CSSM_INTERNAL_ERROR;
}
privKeyLabel = "Imported Diffie-Hellman key";
break;
}
if(readFile(privKeyFileName, &pemKey, &pemKeyLen)) {
printf("***Error reading private key from file %s. Aborting.\n",
privKeyFileName);
return CSSMERR_CSSM_INTERNAL_ERROR;
}
if(pemFormat) {
int rtn = pemDecode(pemKey, pemKeyLen, &derKey, &derKeyLen);
if(rtn) {
printf("***%s: Bad PEM formatting. Aborting.\n",
privKeyFileName);
crtn = CSSMERR_CSP_INVALID_KEY;
goto done;
}
}
else {
derKey = pemKey;
derKeyLen = pemKeyLen;
}
memset(&unwrappedKey, 0, sizeof(CSSM_KEY));
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
hdr->HeaderVersion = CSSM_KEYHEADER_VERSION;
hdr->BlobType = CSSM_KEYBLOB_RAW;
hdr->AlgorithmId = keyAlg;
hdr->KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;
hdr->KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
hdr->KeyUsage = CSSM_KEYUSE_ANY;
hdr->Format = keyFormat;
wrappedKey.KeyData.Data = derKey;
wrappedKey.KeyData.Length = derKeyLen;
rawCspHand = cuCspStartup(CSSM_TRUE);
if(rawCspHand == 0) {
printf("***Error attaching to CSP. Aborting.\n");
crtn = CSSMERR_CSSM_INTERNAL_ERROR;
goto done;
}
CSSM_KEY_SIZE keySize;
crtn = CSSM_QueryKeySizeInBits(rawCspHand, CSSM_INVALID_HANDLE, &wrappedKey, &keySize);
if(crtn) {
cuPrintError("CSSM_QueryKeySizeInBits",crtn);
goto done;
}
hdr->LogicalKeySizeInBits = keySize.LogicalKeySizeInBits;
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
CSSM_ALGID_NONE, CSSM_ALGMODE_NONE, &creds,
NULL, NULL, CSSM_PADDING_NONE, 0, &ccHand);
if(crtn) {
cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn);
goto done;
}
CSSM_CONTEXT_ATTRIBUTE newAttr;
newAttr.AttributeType = CSSM_ATTRIBUTE_DL_DB_HANDLE;
newAttr.AttributeLength = sizeof(CSSM_DL_DB_HANDLE);
newAttr.Attribute.Data = (CSSM_DATA_PTR)&dlDbHand;
crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr);
if(crtn) {
cuPrintError("CSSM_UpdateContextAttributes", crtn);
goto done;
}
labelData.Data = (uint8 *)privKeyLabel;
labelData.Length = strlen(privKeyLabel) + 1;
crtn = CSSM_UnwrapKey(ccHand,
NULL, &wrappedKey,
CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT |
CSSM_KEYATTR_SENSITIVE |CSSM_KEYATTR_EXTRACTABLE,
&labelData,
NULL, &unwrappedKey,
&descData); if(crtn != CSSM_OK) {
cuPrintError("CSSM_UnwrapKey", crtn);
goto done;
}
crtn = setPubKeyHash(cspHand, dlDbHand, privKeyLabel, keyHash);
CSSM_FreeKey(cspHand,
NULL, &unwrappedKey,
CSSM_FALSE);
done:
if(ccHand) {
CSSM_DeleteContext(ccHand);
}
if(derKey) {
free(derKey); }
if(pemFormat && pemKey) {
free(pemKey);
}
if(rawCspHand) {
CSSM_ModuleDetach(rawCspHand);
}
return crtn;
}
CSSM_RETURN importBadCert(
CSSM_DL_HANDLE dlHand,
const char *dbFileName,
const char *certFile,
const char *keyFile,
CSSM_ALGORITHMS keyAlg,
CSSM_BOOL pemFormat, CSSM_KEYBLOB_FORMAT keyFormat, CSSM_BOOL verbose)
{
CSSM_DL_DB_HANDLE dlDbHand = {dlHand, 0};
CSSM_RETURN crtn;
CSSM_DATA keyDigest = {0, NULL};
CSSM_DATA certData = {0, NULL};
unsigned len;
CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_FALSE);
if(cspHand == 0) {
printf("***Error attaching to CSPDL. Aborting.\n");
return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
}
dlDbHand.DBHandle = cuDbStartupByName(dlHand,
(char *)dbFileName, CSSM_FALSE, CSSM_FALSE); if(dlDbHand.DBHandle == 0) {
printf("Error opening %s. Aborting.\n", dbFileName);
return CSSMERR_DL_DATASTORE_DOESNOT_EXIST;
}
crtn = importPrivateKey(dlDbHand, cspHand,
keyFile, keyAlg, pemFormat, keyFormat,
&keyDigest);
if(crtn) {
printf("***Error importing key %s. Aborting.\n", keyFile);
goto errOut;
}
if(readFile(certFile, &certData.Data, &len)) {
printf("***Error reading cert from %s. Aborting.\n", certFile);
goto errOut;
}
certData.Length = len;
crtn = cuAddCertToDb(dlDbHand, &certData,
CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
certFile, &keyDigest);
if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) {
crtn = tpAddCertSchema(dlDbHand);
if(crtn == CSSM_OK) {
crtn = cuAddCertToDb(dlDbHand, &certData,
CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
certFile, &keyDigest);
}
}
if(crtn) {
printf("***Error importing cert %s. Aborting.\n", certFile);
}
errOut:
if(keyDigest.Data) {
cuAppFree(keyDigest.Data, NULL);
}
if(dlDbHand.DBHandle) {
CSSM_DL_DbClose(dlDbHand);
}
if(certData.Data) {
free(certData.Data);
}
return crtn;
}