//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: certhlpr.cpp // // Contents: import and export of private keys // // Functions: ImportExoprtDllMain // CryptImportPKCS8 // CryptExportPKCS8 // // History: //-------------------------------------------------------------------------- #include "global.hxx" #ifdef __cplusplus extern "C" { #endif #include "prvtkey.h" #ifdef __cplusplus } // Balance extern "C" above #endif #define INFO_LEN_ALIGN(Len) ((Len + 7) & ~7) static const BYTE NullDer[2] = {0x05, 0x00}; static const CRYPT_OBJID_BLOB NullDerBlob = {2, (BYTE *)&NullDer[0]}; static HCRYPTASN1MODULE hPrivateKeyAsn1Module; static HCRYPTOIDFUNCSET hEncodePrivKeyFuncSet; static HCRYPTOIDFUNCSET hDecodePrivKeyFuncSet; //+------------------------------------------------------------------------- // OSS ASN.1 PKCS#8 PrivateKey Encode / Decode functions //-------------------------------------------------------------------------- static BOOL WINAPI OssRSAPrivateKeyStrucEncode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN BLOBHEADER *pBlobHeader, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ); static BOOL WINAPI OssRSAPrivateKeyStrucDecode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, OUT BLOBHEADER *pBlobHeader, IN OUT DWORD *pcbBlobHeader ); static BOOL WINAPI OssPrivateKeyInfoEncode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN PCRYPT_PRIVATE_KEY_INFO pInfo, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ); static BOOL WINAPI OssPrivateKeyInfoDecode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, OUT PCRYPT_PRIVATE_KEY_INFO pInfo, IN OUT DWORD *pcbInfo ); static BOOL WINAPI OssEncryptedPrivateKeyInfoEncode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN PCRYPT_ENCRYPTED_PRIVATE_KEY_INFO pInfo, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ); static BOOL WINAPI OssEncryptedPrivateKeyInfoDecode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, OUT PCRYPT_ENCRYPTED_PRIVATE_KEY_INFO pInfo, IN OUT DWORD *pcbInfo ); static const CRYPT_OID_FUNC_ENTRY PrivateKeyEncodeFuncTable[] = { PKCS_RSA_PRIVATE_KEY, OssRSAPrivateKeyStrucEncode, PKCS_PRIVATE_KEY_INFO, OssPrivateKeyInfoEncode, PKCS_ENCRYPTED_PRIVATE_KEY_INFO, OssEncryptedPrivateKeyInfoEncode }; #define PRIVATEKEY_ENCODE_FUNC_COUNT (sizeof(PrivateKeyEncodeFuncTable) / \ sizeof(PrivateKeyEncodeFuncTable[0])) static const CRYPT_OID_FUNC_ENTRY PrivateKeyDecodeFuncTable[] = { PKCS_RSA_PRIVATE_KEY, OssRSAPrivateKeyStrucDecode, PKCS_PRIVATE_KEY_INFO, OssPrivateKeyInfoDecode, PKCS_ENCRYPTED_PRIVATE_KEY_INFO, OssEncryptedPrivateKeyInfoDecode, }; #define PRIVATEKEY_DECODE_FUNC_COUNT (sizeof(PrivateKeyDecodeFuncTable) / \ sizeof(PrivateKeyDecodeFuncTable[0])) BOOL WINAPI EncodeDecodeDllMain( HMODULE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { switch( ul_reason_for_call ) { case DLL_PROCESS_ATTACH: if (NULL == (hEncodePrivKeyFuncSet = CryptInitOIDFunctionSet( CRYPT_OID_ENCODE_OBJECT_FUNC, 0))) goto ErrorReturn; if (NULL == (hDecodePrivKeyFuncSet = CryptInitOIDFunctionSet( CRYPT_OID_DECODE_OBJECT_FUNC, 0))) goto ErrorReturn; if (!CryptInstallOIDFunctionAddress( NULL, // hModule X509_ASN_ENCODING, CRYPT_OID_ENCODE_OBJECT_FUNC, PRIVATEKEY_ENCODE_FUNC_COUNT, PrivateKeyEncodeFuncTable, 0)) // dwFlags goto ErrorReturn; if (!CryptInstallOIDFunctionAddress( NULL, // hModule X509_ASN_ENCODING, CRYPT_OID_DECODE_OBJECT_FUNC, PRIVATEKEY_DECODE_FUNC_COUNT, PrivateKeyDecodeFuncTable, 0)) // dwFlags goto ErrorReturn; #ifdef OSS_CRYPT_ASN1 if (0 == (hPrivateKeyAsn1Module = I_CryptInstallAsn1Module(prvtkey, 0, NULL)) ) goto ErrorReturn; #else PRVTKEY_Module_Startup(); if (0 == (hPrivateKeyAsn1Module = I_CryptInstallAsn1Module( PRVTKEY_Module, 0, NULL))) { PRVTKEY_Module_Cleanup(); goto ErrorReturn; } #endif // OSS_CRYPT_ASN1 break; case DLL_PROCESS_DETACH: I_CryptUninstallAsn1Module(hPrivateKeyAsn1Module); #ifndef OSS_CRYPT_ASN1 PRVTKEY_Module_Cleanup(); #endif // OSS_CRYPT_ASN1 break; default: break; } return TRUE; ErrorReturn: return FALSE; } static inline ASN1encoding_t GetEncoder(void) { return I_CryptGetAsn1Encoder(hPrivateKeyAsn1Module); } static inline ASN1decoding_t GetDecoder(void) { return I_CryptGetAsn1Decoder(hPrivateKeyAsn1Module); } //+------------------------------------------------------------------------- // Encode an OSS formatted info structure // // Called by the OssX509*Encode() functions. //-------------------------------------------------------------------------- static BOOL OssInfoEncode( IN int pdunum, IN void *pOssInfo, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ) { return PkiAsn1EncodeInfo( GetEncoder(), pdunum, pOssInfo, pbEncoded, pcbEncoded); } //+------------------------------------------------------------------------- // Decode into an allocated, OSS formatted info structure // // Called by the OssX509*Decode() functions. //-------------------------------------------------------------------------- static BOOL OssInfoDecodeAndAlloc( IN int pdunum, IN const BYTE *pbEncoded, IN DWORD cbEncoded, OUT void **ppOssInfo ) { return PkiAsn1DecodeAndAllocInfo( GetDecoder(), pdunum, pbEncoded, cbEncoded, ppOssInfo); } //+------------------------------------------------------------------------- // Free an allocated, OSS formatted info structure // // Called by the OssX509*Decode() functions. //-------------------------------------------------------------------------- static void OssInfoFree( IN int pdunum, IN void *pOssInfo ) { if (pOssInfo) { DWORD dwErr = GetLastError(); // TlsGetValue globbers LastError PkiAsn1FreeInfo(GetDecoder(), pdunum, pOssInfo); SetLastError(dwErr); } } //+------------------------------------------------------------------------- // Set/Get CRYPT_DATA_BLOB (Octet String) //-------------------------------------------------------------------------- static inline void OssX509SetOctetString( IN PCRYPT_DATA_BLOB pInfo, OUT OCTETSTRING *pOss ) { pOss->value = pInfo->pbData; pOss->length = pInfo->cbData; } static inline void OssX509GetOctetString( IN OCTETSTRING *pOss, IN DWORD dwFlags, OUT PCRYPT_DATA_BLOB pInfo, IN OUT BYTE **ppbExtra, IN OUT LONG *plRemainExtra ) { PkiAsn1GetOctetString(pOss->length, pOss->value, dwFlags, pInfo, ppbExtra, plRemainExtra); } //+------------------------------------------------------------------------- // Set/Get Object Identifier string //-------------------------------------------------------------------------- static BOOL OssX509SetObjId( IN LPSTR pszObjId, OUT ObjectID *pOss ) { pOss->count = sizeof(pOss->value) / sizeof(pOss->value[0]); if (PkiAsn1ToObjectIdentifier(pszObjId, &pOss->count, pOss->value)) return TRUE; else { SetLastError((DWORD) CRYPT_E_BAD_ENCODE); return FALSE; } } static void OssX509GetObjId( IN ObjectID *pOss, IN DWORD dwFlags, OUT LPSTR *ppszObjId, IN OUT BYTE **ppbExtra, IN OUT LONG *plRemainExtra ) { LONG lRemainExtra = *plRemainExtra; BYTE *pbExtra = *ppbExtra; LONG lAlignExtra; DWORD cbObjId; cbObjId = lRemainExtra > 0 ? lRemainExtra : 0; PkiAsn1FromObjectIdentifier( pOss->count, pOss->value, (LPSTR) pbExtra, &cbObjId ); lAlignExtra = INFO_LEN_ALIGN(cbObjId); lRemainExtra -= lAlignExtra; if (lRemainExtra >= 0) { if(cbObjId) { *ppszObjId = (LPSTR) pbExtra; } else *ppszObjId = NULL; pbExtra += lAlignExtra; } *plRemainExtra = lRemainExtra; *ppbExtra = pbExtra; } //+------------------------------------------------------------------------- // Set/Get "Any" DER BLOB //-------------------------------------------------------------------------- static inline void OssX509SetAny( IN PCRYPT_OBJID_BLOB pInfo, OUT NOCOPYANY *pOss ) { #ifdef OSS_CRYPT_ASN1 PkiAsn1SetAny(pInfo, (OpenType *) pOss); #else PkiAsn1SetAny(pInfo, pOss); #endif // OSS_CRYPT_ASN1 } static inline void OssX509GetAny( IN NOCOPYANY *pOss, IN DWORD dwFlags, OUT PCRYPT_OBJID_BLOB pInfo, IN OUT BYTE **ppbExtra, IN OUT LONG *plRemainExtra ) { #ifdef OSS_CRYPT_ASN1 PkiAsn1GetAny((OpenType *) pOss, dwFlags, pInfo, ppbExtra, plRemainExtra); #else PkiAsn1GetAny(pOss, dwFlags, pInfo, ppbExtra, plRemainExtra); #endif // OSS_CRYPT_ASN1 } //+------------------------------------------------------------------------- // Set/Free/Get SeqOfAny //-------------------------------------------------------------------------- static BOOL WINAPI OssX509SetSeqOfAny( IN DWORD cValue, IN PCRYPT_DER_BLOB pValue, #ifdef OSS_CRYPT_ASN1 OUT unsigned int *pOssCount, #else OUT ASN1uint32_t *pOssCount, #endif // OSS_CRYPT_ASN1 OUT NOCOPYANY **ppOssValue ) { *pOssCount = 0; *ppOssValue = NULL; if (cValue > 0) { NOCOPYANY *pOssValue; pOssValue = (NOCOPYANY *) SSAlloc(cValue * sizeof(NOCOPYANY)); if (pOssValue == NULL) return FALSE; memset(pOssValue, 0, cValue * sizeof(NOCOPYANY)); *pOssCount = cValue; *ppOssValue = pOssValue; for ( ; cValue > 0; cValue--, pValue++, pOssValue++) OssX509SetAny(pValue, pOssValue); } return TRUE; } static void OssX509FreeSeqOfAny( IN NOCOPYANY *pOssValue ) { if (pOssValue) SSFree(pOssValue); } static void OssX509GetSeqOfAny( IN unsigned int OssCount, IN NOCOPYANY *pOssValue, IN DWORD dwFlags, OUT DWORD *pcValue, OUT PCRYPT_DER_BLOB *ppValue, IN OUT BYTE **ppbExtra, IN OUT LONG *plRemainExtra ) { LONG lAlignExtra; PCRYPT_ATTR_BLOB pValue; lAlignExtra = INFO_LEN_ALIGN(OssCount * sizeof(CRYPT_DER_BLOB)); *plRemainExtra -= lAlignExtra; if (*plRemainExtra >= 0) { *pcValue = OssCount; pValue = (PCRYPT_DER_BLOB) *ppbExtra; *ppValue = pValue; *ppbExtra += lAlignExtra; } else pValue = NULL; for (; OssCount > 0; OssCount--, pOssValue++, pValue++) OssX509GetAny(pOssValue, dwFlags, pValue, ppbExtra, plRemainExtra); } //+------------------------------------------------------------------------- // Set/Free/Get CRYPT_ATTRIBUTE //-------------------------------------------------------------------------- static BOOL WINAPI OssX509SetAttribute( IN PCRYPT_ATTRIBUTE pInfo, OUT Attribute *pOss ) { memset(pOss, 0, sizeof(*pOss)); if (!OssX509SetObjId(pInfo->pszObjId, &pOss->type)) return FALSE; return OssX509SetSeqOfAny( pInfo->cValue, pInfo->rgValue, &pOss->values.count, &pOss->values.value); } static void OssX509FreeAttribute( IN OUT Attribute *pOss ) { OssX509FreeSeqOfAny(pOss->values.value); } static void OssX509GetAttribute( IN Attribute *pOss, IN DWORD dwFlags, OUT PCRYPT_ATTRIBUTE pInfo, IN OUT BYTE **ppbExtra, IN OUT LONG *plRemainExtra ) { OssX509GetObjId(&pOss->type, dwFlags, &pInfo->pszObjId, ppbExtra, plRemainExtra); OssX509GetSeqOfAny(pOss->values.count, pOss->values.value, dwFlags, &pInfo->cValue, &pInfo->rgValue, ppbExtra, plRemainExtra); } //+------------------------------------------------------------------------- // Set/Get CRYPT_ALGORITHM_IDENTIFIER //-------------------------------------------------------------------------- static BOOL OssX509SetAlgorithm( IN PCRYPT_ALGORITHM_IDENTIFIER pInfo, OUT AlgorithmIdentifier *pOss ) { memset(pOss, 0, sizeof(*pOss)); if (pInfo->pszObjId) { if (!OssX509SetObjId(pInfo->pszObjId, &pOss->algorithm)) return FALSE; if (pInfo->Parameters.cbData) OssX509SetAny(&pInfo->Parameters, &pOss->parameters); else // Per PKCS #1: default to the ASN.1 type NULL. OssX509SetAny((PCRYPT_OBJID_BLOB) &NullDerBlob, &pOss->parameters); pOss->bit_mask |= parameters_present; } return TRUE; } static void OssX509GetAlgorithm( IN AlgorithmIdentifier *pOss, IN DWORD dwFlags, OUT PCRYPT_ALGORITHM_IDENTIFIER pInfo, IN OUT BYTE **ppbExtra, IN OUT LONG *plRemainExtra ) { if (*plRemainExtra >= 0) memset(pInfo, 0, sizeof(*pInfo)); OssX509GetObjId(&pOss->algorithm, dwFlags, &pInfo->pszObjId, ppbExtra, plRemainExtra); if (pOss->bit_mask & parameters_present) OssX509GetAny(&pOss->parameters, dwFlags, &pInfo->Parameters, ppbExtra, plRemainExtra); } //+------------------------------------------------------------------------- // Helper function for Encode RSA Private Key //-------------------------------------------------------------------------- static BOOL WINAPI AllocAndCopyHugeInteger( IN BYTE *pbHugeInteger, IN DWORD cbHugeInteger, OUT HUGEINTEGER *pHugeInteger ) { BYTE *pbAllocBuffer = NULL; if (NULL == (pbAllocBuffer = (BYTE *) SSAlloc(cbHugeInteger + 1))) return FALSE; *pbAllocBuffer = 0; memcpy(pbAllocBuffer + 1, pbHugeInteger, cbHugeInteger); PkiAsn1ReverseBytes(pbAllocBuffer + 1, cbHugeInteger); pHugeInteger->length = cbHugeInteger + 1; pHugeInteger->value = pbAllocBuffer; return TRUE; } #ifndef RSA2 #define RSA2 ((DWORD)'R'+((DWORD)'S'<<8)+((DWORD)'A'<<16)+((DWORD)'2'<<24)) #endif //+------------------------------------------------------------------------- // Encode RSA Private Key //-------------------------------------------------------------------------- static BOOL WINAPI OssRSAPrivateKeyStrucEncode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN BLOBHEADER *pBlobHeader, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; BYTE *pbKeyBlob; RSAPUBKEY *pRsaPubKey; BYTE *pbCurrentHugeInteger; DWORD cbHugeInteger; BYTE *pbAllocBuffer = NULL; RSAPrivateKey OssRSAPrivateKey; memset(&OssRSAPrivateKey, 0, (size_t) sizeof(RSAPrivateKey)); // The CAPI RSA private key representation consists of the following sequence: // - BLOBHEADER blobheader; // - RSAPUBKEY rsapubkey; // - BYTE modulus[rsapubkey.bitlen/8]; // - BYTE prime1[rsapubkey.bitlen/16]; // - BYTE prime2[rsapubkey.bitlen/16]; // - BYTE exponent1[rsapubkey.bitlen/16]; // - BYTE exponent2[rsapubkey.bitlen/16]; // - BYTE coefficient[rsapubkey.bitlen/16]; // - BYTE privateExponent[rsapubkey.bitlen/8]; pbKeyBlob = (BYTE *) pBlobHeader; pRsaPubKey = (RSAPUBKEY *) (pbKeyBlob + sizeof(BLOBHEADER)); assert(pRsaPubKey->bitlen / 8 > 0); assert(pBlobHeader->bType == PRIVATEKEYBLOB); assert(pBlobHeader->bVersion == CUR_BLOB_VERSION); assert(pBlobHeader->aiKeyAlg == CALG_RSA_SIGN || pBlobHeader->aiKeyAlg == CALG_RSA_KEYX); assert(pRsaPubKey->magic == RSA2); assert(pRsaPubKey->bitlen % 8 == 0); if (pBlobHeader->bType != PRIVATEKEYBLOB) goto InvalidArg; // PKCS #1 ASN.1 encode // // ASN.1 isn't reversing HUGE_INTEGERs. Also, after doing the // reversal insert a leading 0 byte to force it to always be treated // as an unsigned integer OssRSAPrivateKey.version = 0; // currently on version 0 // MODULUS pbCurrentHugeInteger = pbKeyBlob + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY); cbHugeInteger = pRsaPubKey->bitlen / 8; if (!AllocAndCopyHugeInteger(pbCurrentHugeInteger, cbHugeInteger, &(OssRSAPrivateKey.modulus))) goto ErrorReturn; // PUBLIC EXPONENT OssRSAPrivateKey.publicExponent = pRsaPubKey->pubexp; // PRIME1 pbCurrentHugeInteger += cbHugeInteger; cbHugeInteger = pRsaPubKey->bitlen / 16; if (!AllocAndCopyHugeInteger(pbCurrentHugeInteger, cbHugeInteger, &(OssRSAPrivateKey.prime1))) goto ErrorReturn; // PRIME2 pbCurrentHugeInteger += cbHugeInteger; if (!AllocAndCopyHugeInteger(pbCurrentHugeInteger, cbHugeInteger, &(OssRSAPrivateKey.prime2))) goto ErrorReturn; // EXPONENT1 pbCurrentHugeInteger += cbHugeInteger; if (!AllocAndCopyHugeInteger(pbCurrentHugeInteger, cbHugeInteger, &(OssRSAPrivateKey.exponent1))) goto ErrorReturn; // EXPONENT2 pbCurrentHugeInteger += cbHugeInteger; if (!AllocAndCopyHugeInteger(pbCurrentHugeInteger, cbHugeInteger, &(OssRSAPrivateKey.exponent2))) goto ErrorReturn; // COEFFICIENT pbCurrentHugeInteger += cbHugeInteger; if (!AllocAndCopyHugeInteger(pbCurrentHugeInteger, cbHugeInteger, &(OssRSAPrivateKey.coefficient))) goto ErrorReturn; // PRIVATE EXPONENT pbCurrentHugeInteger += cbHugeInteger; cbHugeInteger = pRsaPubKey->bitlen / 8; if (!AllocAndCopyHugeInteger(pbCurrentHugeInteger, cbHugeInteger, &(OssRSAPrivateKey.privateExponent))) goto ErrorReturn; fResult = OssInfoEncode( RSAPrivateKey_PDU, &OssRSAPrivateKey, pbEncoded, pcbEncoded ); goto CommonReturn; InvalidArg: SetLastError((DWORD) E_INVALIDARG); ErrorReturn: *pcbEncoded = 0; fResult = FALSE; CommonReturn: if (OssRSAPrivateKey.modulus.value) SSFree(OssRSAPrivateKey.modulus.value); if (OssRSAPrivateKey.prime1.value) SSFree(OssRSAPrivateKey.prime1.value); if (OssRSAPrivateKey.prime2.value) SSFree(OssRSAPrivateKey.prime2.value); if (OssRSAPrivateKey.exponent1.value) SSFree(OssRSAPrivateKey.exponent1.value); if (OssRSAPrivateKey.exponent2.value) SSFree(OssRSAPrivateKey.exponent2.value); if (OssRSAPrivateKey.coefficient.value) SSFree(OssRSAPrivateKey.coefficient.value); if (OssRSAPrivateKey.privateExponent.value) SSFree(OssRSAPrivateKey.privateExponent.value); return fResult; } //+------------------------------------------------------------------------- // Helper function for Decode RSA Private Key //-------------------------------------------------------------------------- static BOOL WINAPI CopyHugeIntegerToByteArray( HUGEINTEGER *pHugeInteger, BYTE *pbBuffer, DWORD cbBuffer, BOOL fGetRidOfLeading0) { memset(pbBuffer, 0, (size_t) cbBuffer); DWORD cbHugeInteger = pHugeInteger->length; BYTE *pbHugeInteger = pHugeInteger->value; // get rid of leading zero on the huge integer if ((cbHugeInteger > 1) && (*pbHugeInteger == 0) && (fGetRidOfLeading0)) { pbHugeInteger++; cbHugeInteger--; } else if ((cbHugeInteger > cbBuffer) && (*pbHugeInteger != 0) && (fGetRidOfLeading0)) { // // THIS IS A UNSUPPORTED KEY FORMAT PROBLEM!! // SetLastError((DWORD) ERROR_UNSUPPORTED_TYPE); assert(0); return FALSE; } // verify there is enough space in pbBuffer to receive // the huge integer if (cbHugeInteger > cbBuffer) { SetLastError((DWORD) CRYPT_E_BAD_ENCODE); assert(0); return FALSE; } // advance pbBuffer to the correct place within itself, // this will leave leading zeros at the beginning of the buffer /*else pbBuffer += (cbBuffer - cbHugeInteger);*/ if (cbHugeInteger > 0) { memcpy(pbBuffer, pbHugeInteger, cbHugeInteger); // ASN.1 isn't reversing HUGEINTEGERs PkiAsn1ReverseBytes(pbBuffer, cbHugeInteger); } return TRUE; } //+------------------------------------------------------------------------- // Decode RSA Private Key //-------------------------------------------------------------------------- static BOOL WINAPI OssRSAPrivateKeyStrucDecode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, OUT BLOBHEADER *pBlobHeader, IN OUT DWORD *pcbBlobHeader ) { BOOL fResult; RSAPrivateKey *pOssPrivateKey = NULL; if (pBlobHeader == NULL) *pcbBlobHeader = 0; if ((fResult = OssInfoDecodeAndAlloc( RSAPrivateKey_PDU, pbEncoded, cbEncoded, (void **) &pOssPrivateKey))) { DWORD cbPrivateKeyStruc; BYTE *pbOssModulus; DWORD cbModulus; DWORD cbNonModulus; // Now convert the OSS RSA private key into CAPI's representation which // consists of the following sequence: // - BLOBHEADER blobheader; // - RSAPUBKEY rsapubkey; // - BYTE modulus[rsapubkey.bitlen/8]; // - BYTE prime1[rsapubkey.bitlen/16]; // - BYTE prime2[rsapubkey.bitlen/16]; // - BYTE exponent1[rsapubkey.bitlen/16]; // - BYTE exponent2[rsapubkey.bitlen/16]; // - BYTE coefficient[rsapubkey.bitlen/16]; // - BYTE privateExponent[rsapubkey.bitlen/8]; cbModulus = pOssPrivateKey->modulus.length; pbOssModulus = pOssPrivateKey->modulus.value; // Strip off a leading 0 byte. Its there in the decoded ASN // integer for an unsigned integer with the leading bit set. if (cbModulus > 1 && *pbOssModulus == 0) { pbOssModulus++; cbModulus--; } cbNonModulus = (cbModulus / 2) + (cbModulus % 2); cbPrivateKeyStruc = sizeof(BLOBHEADER) + // length of BLOBHEADER sizeof(RSAPUBKEY) + // length of RSAPUBKEY (cbModulus * 2) + // length of modulus and privateExponent (cbNonModulus * 5); // length of prime1&2, exponent1&2, and coefficient if (*pcbBlobHeader < cbPrivateKeyStruc) { if (pBlobHeader) { fResult = FALSE; SetLastError((DWORD) ERROR_MORE_DATA); } } else { BYTE *pbKeyBlob = (BYTE *) pBlobHeader; RSAPUBKEY *pRsaPubKey = (RSAPUBKEY *) (pbKeyBlob + sizeof(PUBLICKEYSTRUC)); BYTE *pbModulus = pbKeyBlob + sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY); BYTE *pbCurrentPosition = NULL; pBlobHeader->bType = PRIVATEKEYBLOB; pBlobHeader->bVersion = CUR_BLOB_VERSION; pBlobHeader->reserved = 0; // Note: KEYX can also be used for doing a signature pBlobHeader->aiKeyAlg = CALG_RSA_KEYX; pRsaPubKey->magic = RSA2; pRsaPubKey->bitlen = cbModulus * 8; pRsaPubKey->pubexp = pOssPrivateKey->publicExponent; // MODULUS if (cbModulus > 0) { memcpy(pbModulus, pbOssModulus, cbModulus); // ASN.1 isn't reversing HUGEINTEGERs PkiAsn1ReverseBytes(pbModulus, cbModulus); } // PRIME1 pbCurrentPosition = pbModulus + cbModulus; if (!CopyHugeIntegerToByteArray(&pOssPrivateKey->prime1, pbCurrentPosition, cbNonModulus, (pOssPrivateKey->prime1.length - 1) == cbNonModulus)) goto ErrorReturn; // PRIME2 pbCurrentPosition += cbNonModulus; if (!CopyHugeIntegerToByteArray(&pOssPrivateKey->prime2, pbCurrentPosition, cbNonModulus, (pOssPrivateKey->prime2.length - 1) == cbNonModulus)) goto ErrorReturn; // EXPONENT1 pbCurrentPosition += cbNonModulus; if (!CopyHugeIntegerToByteArray(&pOssPrivateKey->exponent1, pbCurrentPosition, cbNonModulus, (pOssPrivateKey->exponent1.length - 1) == cbNonModulus)) goto ErrorReturn; // EXPONENT2 pbCurrentPosition += cbNonModulus; if (!CopyHugeIntegerToByteArray(&pOssPrivateKey->exponent2, pbCurrentPosition, cbNonModulus, (pOssPrivateKey->exponent2.length - 1) == cbNonModulus)) goto ErrorReturn; // COEFFICIENT pbCurrentPosition += cbNonModulus; if (!CopyHugeIntegerToByteArray(&pOssPrivateKey->coefficient, pbCurrentPosition, cbNonModulus, (pOssPrivateKey->coefficient.length - 1) == cbNonModulus)) goto ErrorReturn; // PRIVATE EXPONENT pbCurrentPosition += cbNonModulus; if (!CopyHugeIntegerToByteArray(&pOssPrivateKey->privateExponent, pbCurrentPosition, cbModulus, (pOssPrivateKey->privateExponent.length - 1) == cbModulus)) goto ErrorReturn; } *pcbBlobHeader = cbPrivateKeyStruc; } else *pcbBlobHeader = 0; OssInfoFree(RSAPrivateKey_PDU, pOssPrivateKey); goto CommonReturn; ErrorReturn: *pcbBlobHeader = 0; fResult = FALSE; CommonReturn: return fResult; } //+------------------------------------------------------------------------- // Encode Private Key Info //-------------------------------------------------------------------------- static BOOL WINAPI OssPrivateKeyInfoEncode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN PCRYPT_PRIVATE_KEY_INFO pInfo, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; PrivateKeyInfo OssPrivateKeyInfo; Attribute *pOssAttr = NULL; DWORD cAttr; PCRYPT_ATTRIBUTE pAttr; memset(&OssPrivateKeyInfo, 0, sizeof(PrivateKeyInfo)); OssPrivateKeyInfo.version = pInfo->Version; if (!OssX509SetAlgorithm(&pInfo->Algorithm, &OssPrivateKeyInfo.privateKeyAlgorithm)) goto ErrorReturn; OssX509SetOctetString(&pInfo->PrivateKey, &OssPrivateKeyInfo.privateKey); if (pInfo->pAttributes) { cAttr = pInfo->pAttributes->cAttr; pAttr = pInfo->pAttributes->rgAttr; OssPrivateKeyInfo.privateKeyAttributes.count = cAttr; OssPrivateKeyInfo.privateKeyAttributes.value = NULL; if (cAttr > 0) { pOssAttr = (Attribute *) SSAlloc(cAttr * sizeof(Attribute)); if (pOssAttr == NULL) goto ErrorReturn; memset(pOssAttr, 0, cAttr * sizeof(Attribute)); OssPrivateKeyInfo.privateKeyAttributes.value= pOssAttr; } for ( ; cAttr > 0; cAttr--, pAttr++, pOssAttr++) { if (!OssX509SetAttribute(pAttr, pOssAttr)) goto ErrorReturn; } OssPrivateKeyInfo.bit_mask |= privateKeyAttributes_present; } fResult = OssInfoEncode( PrivateKeyInfo_PDU, &OssPrivateKeyInfo, pbEncoded, pcbEncoded ); goto CommonReturn; ErrorReturn: *pcbEncoded = 0; fResult = FALSE; CommonReturn: if (OssPrivateKeyInfo.privateKeyAttributes.value) { pOssAttr = OssPrivateKeyInfo.privateKeyAttributes.value; cAttr = OssPrivateKeyInfo.privateKeyAttributes.count; for ( ; cAttr > 0; cAttr--, pOssAttr++) OssX509FreeAttribute(pOssAttr); SSFree(OssPrivateKeyInfo.privateKeyAttributes.value); } return fResult; } //+------------------------------------------------------------------------- // Decode Private Key Info //-------------------------------------------------------------------------- static BOOL WINAPI OssPrivateKeyInfoDecode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, OUT PCRYPT_PRIVATE_KEY_INFO pInfo, IN OUT DWORD *pcbInfo ) { BOOL fResult; PrivateKeyInfo *pPrivateKeyInfo = NULL; BYTE *pbExtra; LONG lRemainExtra; if (pInfo == NULL) *pcbInfo = 0; if (!OssInfoDecodeAndAlloc( PrivateKeyInfo_PDU, pbEncoded, cbEncoded, (void **) &pPrivateKeyInfo)) goto EncodeError; // for lRemainExtra < 0, LENGTH_ONLY calculation lRemainExtra = (LONG) *pcbInfo - sizeof(CRYPT_PRIVATE_KEY_INFO); if (lRemainExtra < 0) { pbExtra = NULL; } else { // Default all optional fields to zero memset(pInfo, 0, sizeof(CRYPT_PRIVATE_KEY_INFO)); // Update fields not needing extra memory after the CRYPT_PRIVATE_KEY_INFO pInfo->Version = pPrivateKeyInfo->version; pbExtra = (BYTE *) pInfo + sizeof(CRYPT_PRIVATE_KEY_INFO); } OssX509GetAlgorithm(&pPrivateKeyInfo->privateKeyAlgorithm, dwFlags, &pInfo->Algorithm, &pbExtra, &lRemainExtra); OssX509GetOctetString(&pPrivateKeyInfo->privateKey, dwFlags, &pInfo->PrivateKey, &pbExtra, &lRemainExtra); if (pPrivateKeyInfo->bit_mask & privateKeyAttributes_present) { DWORD cAttr; PCRYPT_ATTRIBUTE pAttr; Attribute *pOssAttr; LONG lAlignExtra; // put the CRYPT_ATTRIBUTES structure in the extra buffer space // and point pInfo->pAttributes to it if ((pbExtra) && (lRemainExtra >= sizeof(CRYPT_ATTRIBUTES))) { memset(pbExtra, 0, sizeof(CRYPT_ATTRIBUTES)); pInfo->pAttributes = (PCRYPT_ATTRIBUTES) pbExtra; pbExtra += sizeof(CRYPT_ATTRIBUTES); } lRemainExtra -= sizeof(CRYPT_ATTRIBUTES); cAttr = pPrivateKeyInfo->privateKeyAttributes.count; lAlignExtra = INFO_LEN_ALIGN(cAttr * sizeof(CRYPT_ATTRIBUTE)); lRemainExtra -= lAlignExtra; if (lRemainExtra >= 0) { pInfo->pAttributes->cAttr = cAttr; pAttr = (PCRYPT_ATTRIBUTE) pbExtra; pInfo->pAttributes->rgAttr = pAttr; pbExtra += lAlignExtra; } else pAttr = NULL; pOssAttr = pPrivateKeyInfo->privateKeyAttributes.value; for (; cAttr > 0; cAttr--, pAttr++, pOssAttr++) OssX509GetAttribute(pOssAttr, dwFlags, pAttr, &pbExtra, &lRemainExtra); } if (lRemainExtra >= 0) *pcbInfo = *pcbInfo - (DWORD) lRemainExtra; else { *pcbInfo = *pcbInfo + (DWORD) -lRemainExtra; if (pInfo) goto LengthError; } fResult = TRUE; goto CommonReturn; LengthError: SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; goto CommonReturn; EncodeError: SetLastError((DWORD) CRYPT_E_BAD_ENCODE); // ErrorReturn: *pcbInfo = 0; fResult = FALSE; CommonReturn: OssInfoFree(PrivateKeyInfo_PDU, pPrivateKeyInfo); return fResult; } //+------------------------------------------------------------------------- // Encode Encrypted Private Key Info //-------------------------------------------------------------------------- static BOOL WINAPI OssEncryptedPrivateKeyInfoEncode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN PCRYPT_ENCRYPTED_PRIVATE_KEY_INFO pInfo, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; EncryptedPrivateKeyInfo OssEncryptedPrivateKeyInfo; memset(&OssEncryptedPrivateKeyInfo, 0, sizeof(EncryptedPrivateKeyInfo)); if (!OssX509SetAlgorithm(&pInfo->EncryptionAlgorithm, &OssEncryptedPrivateKeyInfo.encryptionAlgorithm)) goto ErrorReturn; OssX509SetOctetString(&pInfo->EncryptedPrivateKey, &OssEncryptedPrivateKeyInfo.encryptedData); fResult = OssInfoEncode( EncryptedPrivateKeyInfo_PDU, &OssEncryptedPrivateKeyInfo, pbEncoded, pcbEncoded ); goto CommonReturn; ErrorReturn: *pcbEncoded = 0; fResult = FALSE; CommonReturn: return fResult; } //+------------------------------------------------------------------------- // Decode Encrypted Private Key Info //-------------------------------------------------------------------------- static BOOL WINAPI OssEncryptedPrivateKeyInfoDecode( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, OUT PCRYPT_ENCRYPTED_PRIVATE_KEY_INFO pInfo, IN OUT DWORD *pcbInfo ) { BOOL fResult; EncryptedPrivateKeyInfo *pEncryptedPrivateKeyInfo = NULL; BYTE *pbExtra; LONG lRemainExtra; if (pInfo == NULL) *pcbInfo = 0; if (!OssInfoDecodeAndAlloc( EncryptedPrivateKeyInfo_PDU, pbEncoded, cbEncoded, (void **) &pEncryptedPrivateKeyInfo)) goto EncodeError; // for lRemainExtra < 0, LENGTH_ONLY calculation lRemainExtra = (LONG) *pcbInfo - sizeof(CRYPT_ENCRYPTED_PRIVATE_KEY_INFO); if (lRemainExtra < 0) { pbExtra = NULL; } else { memset(pInfo, 0, sizeof(CRYPT_ENCRYPTED_PRIVATE_KEY_INFO)); pbExtra = (BYTE *) pInfo + sizeof(CRYPT_ENCRYPTED_PRIVATE_KEY_INFO); } OssX509GetAlgorithm(&pEncryptedPrivateKeyInfo->encryptionAlgorithm, dwFlags, &pInfo->EncryptionAlgorithm, &pbExtra, &lRemainExtra); OssX509GetOctetString(&pEncryptedPrivateKeyInfo->encryptedData, dwFlags, &pInfo->EncryptedPrivateKey, &pbExtra, &lRemainExtra); if (lRemainExtra >= 0) *pcbInfo = *pcbInfo - (DWORD) lRemainExtra; else { *pcbInfo = *pcbInfo + (DWORD) -lRemainExtra; if (pInfo) goto LengthError; } fResult = TRUE; goto CommonReturn; LengthError: SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; goto CommonReturn; EncodeError: SetLastError((DWORD) CRYPT_E_BAD_ENCODE); //ErrorReturn: *pcbInfo = 0; fResult = FALSE; CommonReturn: OssInfoFree(EncryptedPrivateKeyInfo_PDU, pEncryptedPrivateKeyInfo); return fResult; }