SecImportExportPkcs8.cpp [plain text]
#include <Security/SecImportExport.h>
#include "SecImportExportPkcs8.h"
#include "SecPkcs8Templates.h"
#include "SecImportExportUtils.h"
#include "SecImportExportCrypto.h"
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <security_pkcs12/pkcs12Utils.h>
#include <security_pkcs12/pkcs12Crypto.h>
#include <security_asn1/SecNssCoder.h>
#include <Security/keyTemplates.h>
#include <Security/SecAsn1Templates.h>
#include <Security/secasn1t.h>
#include <security_asn1/nssUtils.h>
#include <security_utilities/debugging.h>
#include <security_utilities/devrandom.h>
#include <Security/oidsalg.h>
#include <Security/SecKeyPriv.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <openssl/pem.h>
#include <assert.h>
#define SecPkcs8Dbg(args...) secdebug("SecPkcs8", ## args)
#pragma mark --- PKCS5 v1.5 Key Derivation ---
static CSSM_RETURN pkcs5_v15_genKey(
CSSM_CSP_HANDLE cspHand,
SecNssCoder &coder,
const SecKeyImportExportParameters *keyParams,
const CSSM_DATA ¶mData,
CSSM_ALGORITHMS keyAlg,
CSSM_ALGORITHMS pbeHashAlg,
uint32 keySizeInBits,
uint32 blockSizeInBytes,
impExpKeyUnwrapParams *unwrapParams)
{
CSSM_KEY *passKey = NULL;
CFDataRef cfPhrase = NULL;
CSSM_RETURN crtn;
OSStatus ortn;
CSSM_CRYPTO_DATA seed;
CSSM_CC_HANDLE ccHand = 0;
CSSM_ACCESS_CREDENTIALS creds;
ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, VP_Import,
(CFTypeRef *)&cfPhrase, &passKey);
if(ortn) {
return ortn;
}
memset(&seed, 0, sizeof(seed));
if(cfPhrase != NULL) {
unsigned len = CFDataGetLength(cfPhrase);
coder.allocItem(seed.Param, len);
memmove(seed.Param.Data, CFDataGetBytePtr(cfPhrase), len);
CFRelease(cfPhrase);
}
CSSM_ALGORITHMS pbeAlg;
switch(pbeHashAlg) {
case CSSM_ALGID_MD2:
pbeAlg = CSSM_ALGID_PKCS5_PBKDF1_MD2;
break;
case CSSM_ALGID_MD5:
pbeAlg = CSSM_ALGID_PKCS5_PBKDF1_MD5;
break;
case CSSM_ALGID_SHA1:
pbeAlg = CSSM_ALGID_PKCS5_PBKDF1_SHA1;
break;
default:
SecPkcs8Dbg("PKCS8: PKCS5 v1/5 bogus hash alg");
crtn = CSSMERR_CSP_INTERNAL_ERROR;
goto errOut;
}
impExpPKCS5_PBE_Parameters pbeParams;
memset(&pbeParams, 0, sizeof(pbeParams));
if(coder.decodeItem(paramData, impExpPKCS5_PBE_ParametersTemplate, &pbeParams)) {
SecPkcs8Dbg("PKCS8: PKCS5 v1.5 pbeParams decode error");
crtn = errSecUnknownFormat;
goto errOut;
}
uint32 iterCount;
if(!p12DataToInt(pbeParams.iterations, iterCount)) {
SecPkcs8Dbg("PKCS8: bad PKCS5 v1.5 iteration count");
crtn = errSecUnknownFormat;
goto errOut;
}
coder.allocItem(unwrapParams->iv, 8);
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
pbeAlg,
keyAlg,
keySizeInBits,
&creds,
passKey, iterCount,
&pbeParams.salt,
&seed,
&ccHand);
if(crtn) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_CSP_CreateDeriveKeyContext failure");
goto errOut;
}
memset(unwrapParams->unwrappingKey, 0, sizeof(CSSM_KEY));
CSSM_DATA dummyLabel;
dummyLabel.Data = (uint8 *)"temp unwrap key";
dummyLabel.Length = strlen((char *)dummyLabel.Data);
crtn = CSSM_DeriveKey(ccHand,
&unwrapParams->iv, CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
&dummyLabel,
NULL, unwrapParams->unwrappingKey);
if(crtn) {
SecPkcs8Dbg("PKCS8: PKCS5 v1.5 CSSM_DeriveKey failure");
}
errOut:
if(ccHand != 0) {
CSSM_DeleteContext(ccHand);
}
if(passKey != NULL) {
CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
free(passKey);
}
return crtn;
}
#pragma mark --- PKCS5 v2.0 Key Derivation ---
static OSStatus pkcs5_DES_params(
const CSSM_DATA ¶mData, CSSM_OID *encrOid,
impExpKeyUnwrapParams *unwrapParams,
CSSM_ALGORITHMS *keyAlg, uint32 *keySizeInBits, SecNssCoder &coder)
{
if(coder.decodeItem(paramData, kSecAsn1OctetStringTemplate, &unwrapParams->iv)) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 DES init vector decode error");
return errSecUnknownFormat;
}
if(nssCompareCssmData(encrOid, &CSSMOID_PKCS5_DES_EDE3_CBC)) {
*keyAlg = CSSM_ALGID_3DES_3KEY;
unwrapParams->encrAlg = CSSM_ALGID_3DES_3KEY_EDE;
if(*keySizeInBits == 0) {
*keySizeInBits = 3 * 64;
}
}
else {
*keyAlg = CSSM_ALGID_DES;
unwrapParams->encrAlg = CSSM_ALGID_DES;
if(*keySizeInBits == 0) {
*keySizeInBits = 64;
}
}
unwrapParams->encrPad = CSSM_PADDING_PKCS7;
unwrapParams->encrMode = CSSM_ALGMODE_CBCPadIV8;
return noErr;
}
static OSStatus pkcs5_RC2_params(
const CSSM_DATA ¶mData, impExpKeyUnwrapParams *unwrapParams,
CSSM_ALGORITHMS *keyAlg, uint32 *keySizeInBits, SecNssCoder &coder)
{
impExpPKCS5_RC2Params rc2Params;
memset(&rc2Params, 0, sizeof(rc2Params));
if(coder.decodeItem(paramData, impExpPKCS5_RC2ParamsTemplate, &rc2Params)) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 RC2 params decode error");
return errSecUnknownFormat;
}
*keyAlg = CSSM_ALGID_RC2;
unwrapParams->encrAlg = CSSM_ALGID_RC2;
unwrapParams->encrPad = CSSM_PADDING_PKCS7;
unwrapParams->encrMode = CSSM_ALGMODE_CBCPadIV8;
unwrapParams->effectiveKeySizeInBits = 32; if(rc2Params.version.Data) {
uint32 v;
if(!p12DataToInt(rc2Params.version, v)) {
SecPkcs8Dbg("PKCS8: bad PKCS5 rc2Params.version");
return errSecUnknownFormat;
}
switch(v) {
case 160:
unwrapParams->effectiveKeySizeInBits = 40;
break;
case 120:
unwrapParams->effectiveKeySizeInBits = 64;
break;
case 58:
unwrapParams->effectiveKeySizeInBits = 128;
break;
default:
if(v >= 256) {
unwrapParams->effectiveKeySizeInBits = v;
}
else {
}
break;
}
}
unwrapParams->iv = rc2Params.iv;
if(*keySizeInBits == 0) {
SecPkcs8Dbg("PKCS8: NO RC2 DEFAULT KEYSIZE!");
return errSecUnknownFormat;
}
return noErr;
}
static OSStatus pkcs5_RC5_params(
const CSSM_DATA ¶mData, impExpKeyUnwrapParams *unwrapParams,
CSSM_ALGORITHMS *keyAlg, uint32 *keySizeInBits, SecNssCoder &coder)
{
impExpPKCS5_RC5Params rc5Params;
memset(&rc5Params, 0, sizeof(rc5Params));
if(coder.decodeItem(paramData, impExpPKCS5_RC5ParamsTemplate, &rc5Params)) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 RC5 params decode error");
return errSecUnknownFormat;
}
*keyAlg = CSSM_ALGID_RC5;
unwrapParams->encrAlg = CSSM_ALGID_RC5;
unwrapParams->encrPad = CSSM_PADDING_PKCS7;
unwrapParams->encrMode = CSSM_ALGMODE_CBCPadIV8;
if(rc5Params.rounds.Data) {
if(!p12DataToInt(rc5Params.rounds, unwrapParams->rounds)) {
SecPkcs8Dbg("PKCS8: bad PKCS5 rc5Params.rounds");
return errSecUnknownFormat;
}
}
if(rc5Params.blockSizeInBits.Data) {
if(!p12DataToInt(rc5Params.blockSizeInBits, unwrapParams->blockSizeInBits)) {
SecPkcs8Dbg("PKCS8: bad PKCS5 rc5Params.blockSizeInBits");
return errSecUnknownFormat;
}
}
unwrapParams->iv = rc5Params.iv;
if(unwrapParams->iv.Length == 0) {
uint32 len = unwrapParams->blockSizeInBits / 8;
coder.allocItem(unwrapParams->iv, len);
memset(unwrapParams->iv.Data, 0, len);
}
if(*keySizeInBits == 0) {
SecPkcs8Dbg("PKCS8: NO RC5 DEFAULT KEYSIZE!");
return errSecUnknownFormat;
}
return noErr;
}
static CSSM_RETURN pbkdf2DeriveKey(
CSSM_CSP_HANDLE cspHand,
SecNssCoder &coder,
CSSM_ALGORITHMS keyAlg,
uint32 keySizeInBits,
uint32 iterationCount,
const CSSM_DATA &salt,
const SecKeyImportExportParameters *keyParams, impExpVerifyPhrase verifyPhrase, CSSM_KEY_PTR symKey) {
CSSM_KEY *passKey = NULL;
CFDataRef cfPhrase = NULL;
CSSM_PKCS5_PBKDF2_PARAMS pbeParams;
CSSM_RETURN crtn;
OSStatus ortn;
CSSM_DATA dummyLabel;
CSSM_DATA pbeData;
uint32 keyAttr;
CSSM_CC_HANDLE ccHand = 0;
CSSM_ACCESS_CREDENTIALS creds;
memset(&pbeParams, 0, sizeof(pbeParams));
ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, verifyPhrase,
(CFTypeRef *)&cfPhrase, &passKey);
if(ortn) {
return ortn;
}
if(cfPhrase != NULL) {
unsigned len = CFDataGetLength(cfPhrase);
coder.allocItem(pbeParams.Passphrase, len);
memmove(pbeParams.Passphrase.Data,
CFDataGetBytePtr(cfPhrase), len);
CFRelease(cfPhrase);
}
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
CSSM_ALGID_PKCS5_PBKDF2,
keyAlg,
keySizeInBits,
&creds,
passKey, iterationCount,
&salt,
NULL, &ccHand);
if(crtn) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_CSP_CreateDeriveKeyContext failure");
goto errOut;
}
memset(symKey, 0, sizeof(CSSM_KEY));
keyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE;
dummyLabel.Data = (uint8 *)"temp unwrap key";
dummyLabel.Length = strlen((char *)dummyLabel.Data);
pbeParams.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
pbeData.Data = (uint8 *)&pbeParams;
pbeData.Length = sizeof(pbeParams);
crtn = CSSM_DeriveKey(ccHand,
&pbeData,
CSSM_KEYUSE_ANY,
keyAttr,
&dummyLabel,
NULL, symKey);
if(crtn) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_DeriveKey failure");
}
errOut:
if(ccHand != 0) {
CSSM_DeleteContext(ccHand);
}
if(passKey != NULL) {
CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
free(passKey);
}
return crtn;
}
static CSSM_RETURN pkcs5_v2_genKey(
CSSM_CSP_HANDLE cspHand,
SecNssCoder &coder,
const CSSM_DATA ¶mData,
const SecKeyImportExportParameters *keyParams,
impExpKeyUnwrapParams *unwrapParams)
{
SecPkcs8Dbg("PKCS8: generating PKCS5 v2.0 key");
CSSM_ALGORITHMS keyAlg = CSSM_ALGID_NONE;
uint32 prf = 0;
assert(keyParams != NULL);
if(paramData.Length == 0) {
SecPkcs8Dbg("PKCS8: empty PKCS5 v2 pbes2Params");
return errSecUnknownFormat;
}
impExpPKCS5_PBES2_Params pbes2Params;
memset(&pbes2Params, 0, sizeof(pbes2Params));
if(coder.decodeItem(paramData, impExpPKCS5_PBES2_ParamsTemplate, &pbes2Params)) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 pbes2Params decode error");
return errSecUnknownFormat;
}
if(!nssCompareCssmData(&pbes2Params.keyDerivationFunc.algorithm,
&CSSMOID_PKCS5_PBKDF2)) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 unexpected keyDerivationFunc alg");
return errSecUnknownFormat;
}
impExpPKCS5_PBKDF2_Params pbkdf2Params;
memset(&pbkdf2Params, 0, sizeof(pbkdf2Params));
if(coder.decodeItem(pbes2Params.keyDerivationFunc.parameters,
impExpPKCS5_PBKDF2_ParamsTemplate, &pbkdf2Params)) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 pbkdf2Params decode error");
return errSecUnknownFormat;
}
CSSM_DATA salt = pbkdf2Params.salt;
uint32 iterCount;
if(!p12DataToInt(pbkdf2Params.iterationCount, iterCount)) {
SecPkcs8Dbg("PKCS8: bad PKCS5 v2 iteration count");
return errSecUnknownFormat;
}
uint32 keySizeInBits = 0;
if(pbkdf2Params.keyLengthInBytes.Data) {
uint32 keyLengthInBytes;
if(!p12DataToInt(pbkdf2Params.keyLengthInBytes, keyLengthInBytes)) {
SecPkcs8Dbg("PKCS8: bad PKCS5 v2 key size");
return errSecUnknownFormat;
}
keySizeInBits = keyLengthInBytes * 8;
}
if(pbkdf2Params.prf.Data) {
if(!nssCompareCssmData(&pbkdf2Params.prf, &CSSMOID_PKCS5_HMAC_SHA1)) {
SecPkcs8Dbg("PKCS8: PKCS5 v2 unexpected prf OID");
return errSecUnknownFormat;
}
}
prf = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
CSSM_X509_ALGORITHM_IDENTIFIER &encrScheme = pbes2Params.encryptionScheme;
CSSM_OID *encrOid = &encrScheme.algorithm;
OSStatus ortn;
CSSM_DATA &encrParam = encrScheme.parameters;
if(nssCompareCssmData(encrOid, &CSSMOID_PKCS5_DES_EDE3_CBC) ||
nssCompareCssmData(encrOid, &CSSMOID_DES_CBC)) {
ortn = pkcs5_DES_params(encrParam, encrOid, unwrapParams, &keyAlg,
&keySizeInBits, coder);
if(ortn) {
return ortn;
}
}
else if(nssCompareCssmData(encrOid, &CSSMOID_PKCS5_RC2_CBC)) {
ortn = pkcs5_RC2_params(encrParam, unwrapParams, &keyAlg,
&keySizeInBits, coder);
if(ortn) {
return ortn;
}
}
else if(nssCompareCssmData(encrOid, &CSSMOID_PKCS5_RC5_CBC)) {
ortn = pkcs5_RC5_params(encrParam, unwrapParams, &keyAlg,
&keySizeInBits, coder);
if(ortn) {
return ortn;
}
}
else {
SecPkcs8Dbg("PKCS8: PKCS5 v2 unknown encrScheme.algorithm");
return errSecUnknownFormat;
}
assert(keyAlg != CSSM_ALGID_NONE);
assert(unwrapParams->encrAlg != CSSM_ALGID_NONE);
return pbkdf2DeriveKey(cspHand, coder,
keyAlg, keySizeInBits,
iterCount, salt,
keyParams,
VP_Import,
unwrapParams->unwrappingKey);
}
#pragma mark --- PKCS12 Key Derivation ---
static CSSM_RETURN pkcs12_genKey(
CSSM_CSP_HANDLE cspHand,
SecNssCoder &coder,
const SecKeyImportExportParameters *keyParams,
const CSSM_DATA ¶mData, CSSM_ALGORITHMS keyAlg, CSSM_ALGORITHMS pbeHashAlg, uint32 keySizeInBits, uint32 blockSizeInBytes, impExpKeyUnwrapParams *unwrapParams)
{
SecPkcs8Dbg("PKCS8: generating PKCS12 key");
assert(keyAlg != CSSM_ALGID_NONE);
assert(pbeHashAlg != CSSM_ALGID_NONE);
assert(keySizeInBits != 0);
NSS_P12_PBE_Params pbeParams;
if(paramData.Length == 0) {
SecPkcs8Dbg("PKCS8: empty P12 pbeParams");
return errSecUnknownFormat;
}
memset(&pbeParams, 0, sizeof(pbeParams));
if(coder.decodeItem(paramData, NSS_P12_PBE_ParamsTemplate, &pbeParams)) {
SecPkcs8Dbg("PKCS8: P12 pbeParams decode error");
return errSecUnknownFormat;
}
uint32 iterCount = 0;
if(!p12DataToInt(pbeParams.iterations, iterCount)) {
SecPkcs8Dbg("PKCS8: bad P12 iteration count");
return errSecUnknownFormat;
}
CSSM_KEY *passKey = NULL;
CFStringRef phraseStr = NULL;
CSSM_DATA phraseData = {0, NULL};
CSSM_DATA *phraseDataP = NULL;
OSStatus ortn;
CSSM_RETURN crtn;
assert(keyParams != NULL);
ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_String, VP_Import,
(CFTypeRef *)&phraseStr, &passKey);
if(ortn) {
return ortn;
}
if(phraseStr != NULL) {
try {
p12ImportPassPhrase(phraseStr, coder, phraseData);
}
catch(...) {
SecPkcs8Dbg("PKCS8: p12ImportPassPhrase threw");
crtn = memFullErr;
goto errOut;
}
CFRelease(phraseStr);
phraseDataP = &phraseData;
}
if(blockSizeInBytes) {
coder.allocItem(unwrapParams->iv, blockSizeInBytes);
}
crtn = p12KeyGen(cspHand,
*unwrapParams->unwrappingKey,
true, keyAlg,
pbeHashAlg,
keySizeInBits,
iterCount,
pbeParams.salt,
phraseDataP,
passKey,
unwrapParams->iv);
if(crtn) {
SecPkcs8Dbg("PKCS8: p12KeyGen failed");
}
errOut:
if(passKey != NULL) {
CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
free(passKey);
}
return crtn;
}
#pragma mark --- Public PKCS8 import function ---
OSStatus impExpPkcs8Import(
CFDataRef inData,
SecKeychainRef importKeychain, CSSM_CSP_HANDLE cspHand, SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, CFMutableArrayRef outArray) {
CSSM_KEY wrappedKey;
CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader;
CSSM_RETURN crtn = CSSM_OK;
impExpKeyUnwrapParams unwrapParams;
memset(&unwrapParams, 0, sizeof(unwrapParams));
CSSM_ALGORITHMS keyAlg = CSSM_ALGID_NONE;
CSSM_ALGORITHMS pbeHashAlg = CSSM_ALGID_NONE; uint32 keySizeInBits;
uint32 blockSizeInBytes;
PKCS_Which pkcs = PW_None;
if( (keyParams == NULL) ||
( (keyParams->passphrase == NULL) &&
!(keyParams->flags & kSecKeySecurePassphrase) ) ) {
return errSecPassphraseRequired;
}
assert(cspHand != 0);
SecNssCoder coder;
NSS_EncryptedPrivateKeyInfo encrPrivKeyInfo;
memset(&encrPrivKeyInfo, 0, sizeof(encrPrivKeyInfo));
if(coder.decode(CFDataGetBytePtr(inData),
CFDataGetLength(inData),
kSecAsn1EncryptedPrivateKeyInfoTemplate, &encrPrivKeyInfo)) {
SecImpExpDbg("impExpPkcs8Import: error decoding top-level encrPrivKeyInfo");
return errSecUnknownFormat;
}
bool found = false;
found = pkcsOidToParams(&encrPrivKeyInfo.algorithm.algorithm,
keyAlg, unwrapParams.encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
unwrapParams.encrPad, unwrapParams.encrMode, pkcs);
if(!found) {
SecImpExpDbg("impExpPkcs8Import: unknown OID in top-level encrPrivKeyInfo");
return errSecUnknownFormat;
}
CSSM_KEY unwrappingKey;
memset(&unwrappingKey, 0, sizeof(unwrappingKey));
unwrapParams.unwrappingKey = &unwrappingKey;
CSSM_DATA ¶mData = encrPrivKeyInfo.algorithm.parameters;
switch(pkcs) {
case PW_PKCS5_v1_5:
crtn = pkcs5_v15_genKey(cspHand, coder, keyParams, paramData,
keyAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
&unwrapParams);
break;
case PW_PKCS5_v2:
crtn = pkcs5_v2_genKey(cspHand, coder, paramData, keyParams, &unwrapParams);
break;
case PW_PKCS12:
crtn = pkcs12_genKey(cspHand, coder, keyParams, paramData,
keyAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
&unwrapParams);
break;
case PW_None:
assert(0);
return errSecUnknownFormat;
}
if(crtn) {
SecPkcs8Dbg("PKCS8: key derivation failed");
return crtn;
}
assert(unwrapParams.encrAlg != CSSM_ALGID_NONE);
assert(unwrappingKey.KeyData.Data != NULL);
assert(unwrappingKey.KeyHeader.AlgorithmId != CSSM_ALGID_NONE);
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
hdr.BlobType = CSSM_KEYBLOB_WRAPPED;
hdr.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8;
hdr.AlgorithmId = CSSM_ALGID_NONE;
hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;
hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
hdr.KeyUsage = CSSM_KEYUSE_ANY;
wrappedKey.KeyData = encrPrivKeyInfo.encryptedData;
crtn = impExpImportKeyCommon(
&wrappedKey,
importKeychain,
cspHand,
flags,
keyParams,
&unwrapParams,
NULL, outArray);
CSSM_FreeKey(cspHand, NULL, &unwrappingKey, CSSM_FALSE);
return crtn;
}
#pragma mark --- Public PKCS8 export function ---
#define PKCS5_V2_SALT_LEN 8
#define PKCS5_V2_ITERATIONS 2048
#define PKCS5_V2_DES_IV_SIZE 8
OSStatus impExpPkcs8Export(
SecKeyRef secKey,
SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, CFMutableDataRef outData, const char **pemHeader)
{
DevRandomGenerator rng;
SecNssCoder coder;
impExpPKCS5_PBES2_Params pbes2Params;
CSSM_X509_ALGORITHM_IDENTIFIER &keyDeriveAlgId = pbes2Params.keyDerivationFunc;
CSSM_ATTRIBUTE_TYPE formatAttrType = CSSM_ATTRIBUTE_NONE;
CSSM_KEYBLOB_FORMAT blobForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
const CSSM_KEY *cssmKey;
if(keyParams == NULL) {
return paramErr;
}
assert(secKey != NULL);
assert(outData != NULL);
memset(&pbes2Params, 0, sizeof(pbes2Params));
keyDeriveAlgId.algorithm = CSSMOID_PKCS5_PBKDF2;
impExpPKCS5_PBKDF2_Params pbkdf2Params;
memset(&pbkdf2Params, 0, sizeof(pbkdf2Params));
coder.allocItem(pbkdf2Params.salt, PKCS5_V2_SALT_LEN);
rng.random(pbkdf2Params.salt.Data, PKCS5_V2_SALT_LEN);
p12IntToData(PKCS5_V2_ITERATIONS, pbkdf2Params.iterationCount, coder);
coder.encodeItem(&pbkdf2Params, impExpPKCS5_PBKDF2_ParamsTemplate,
keyDeriveAlgId.parameters);
CSSM_X509_ALGORITHM_IDENTIFIER &encrScheme = pbes2Params.encryptionScheme;
encrScheme.algorithm = CSSMOID_PKCS5_DES_EDE3_CBC;
CSSM_DATA rawIv = {0, NULL};
coder.allocItem(rawIv, PKCS5_V2_DES_IV_SIZE);
rng.random(rawIv.Data, PKCS5_V2_DES_IV_SIZE);
coder.encodeItem(&rawIv, kSecAsn1OctetStringTemplate,
encrScheme.parameters);
NSS_EncryptedPrivateKeyInfo encrPrivKeyInfo;
memset(&encrPrivKeyInfo, 0, sizeof(encrPrivKeyInfo));
CSSM_X509_ALGORITHM_IDENTIFIER &topAlgId = encrPrivKeyInfo.algorithm;
topAlgId.algorithm = CSSMOID_PKCS5_PBES2;
coder.encodeItem(&pbes2Params, impExpPKCS5_PBES2_ParamsTemplate,
topAlgId.parameters);
CSSM_CSP_HANDLE cspdlHand = 0;
OSStatus ortn;
bool releaseCspHand = false;
CSSM_DATA encodedKeyInfo = {0, NULL};
ortn = SecKeyGetCSPHandle(secKey, &cspdlHand);
if(ortn) {
cspdlHand = cuCspStartup(CSSM_FALSE);
if(cspdlHand == 0) {
return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
}
releaseCspHand = true;
}
CSSM_KEY wrappingKey;
memset(&wrappingKey, 0, sizeof(CSSM_KEY));
CSSM_RETURN crtn = pbkdf2DeriveKey(cspdlHand, coder,
CSSM_ALGID_3DES_3KEY, 3 * 64,
PKCS5_V2_ITERATIONS, pbkdf2Params.salt,
keyParams,
VP_Export,
&wrappingKey);
if(crtn) {
goto errOut;
}
crtn = SecKeyGetCSSMKey(secKey, &cssmKey);
if(crtn) {
SecImpExpDbg("impExpPkcs8Export SecKeyGetCSSMKey error");
goto errOut;
}
switch(cssmKey->KeyHeader.AlgorithmId) {
case CSSM_ALGID_DSA:
case CSSM_ALGID_ECDSA:
formatAttrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT;
blobForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
break;
default:
break;
}
CSSM_KEY wrappedKey;
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
crtn = impExpExportKeyCommon(cspdlHand, secKey, &wrappingKey, &wrappedKey,
CSSM_ALGID_3DES_3KEY_EDE, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS7,
CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8, formatAttrType, blobForm, NULL, &rawIv);
if(crtn) {
goto errOut;
}
encrPrivKeyInfo.encryptedData = wrappedKey.KeyData;
coder.encodeItem(&encrPrivKeyInfo, kSecAsn1EncryptedPrivateKeyInfoTemplate,
encodedKeyInfo);
CFDataAppendBytes(outData, encodedKeyInfo.Data, encodedKeyInfo.Length);
CSSM_FreeKey(cspdlHand, NULL, &wrappedKey, CSSM_FALSE);
*pemHeader = PEM_STRING_PKCS8;
errOut:
if(wrappingKey.KeyData.Data) {
CSSM_FreeKey(cspdlHand, NULL, &wrappingKey, CSSM_FALSE);
}
if(releaseCspHand) {
cuCspDetachUnload(cspdlHand, CSSM_FALSE);
}
return crtn;
}