PKIFBCryptPublicKey.cpp

Go to the documentation of this file.
00001 
00010 #include "PKIFBCryptPublicKey.h"
00011 
00012 #include <wincrypt.h>
00013 #include <iostream>
00014 #include <sstream>
00015 
00016 #include "PKIFKeyMaterial.h"
00017 #include "PKIFCryptoPPKeyMaterial.h"
00018 #include "Buffer.h"
00019 
00020 #include "Certificate.h"
00021 #include "AlgorithmIdentifier.h"
00022 #include "SubjectPublicKeyInfo.h"
00023 
00024 #include "PKIFException.h"
00025 #include "PKIFCryptoException.h"
00026 #include "ToolkitUtils.h"
00027 #include "components.h"
00028 #include "PKIFCommonErrors.h"
00029 #include "PKIFCNGUtils.h"
00030 #include "PKIFCAPIErrors.h"
00031 
00032 #ifndef NT_SUCCESS
00033 #define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)
00034 #endif
00035 
00036 using namespace std;
00037 
00045 bool GetCAPI1ProvType(
00047     PCERT_PUBLIC_KEY_INFO spki,
00049     DWORD & prov)
00050 {
00051     //inspect the subject public key algorithm to determine the type of provider to create
00052     if(0 == strcmp(szOID_X957_DSA, spki->Algorithm.pszObjId) ||
00053         0 == strcmp(szOID_X957_SHA1DSA, spki->Algorithm.pszObjId))
00054         prov = PROV_DSS;
00055     else if(0 == strcmp(szOID_RSA_RSA, spki->Algorithm.pszObjId) ||
00056         0 == strcmp(szOID_RSA_MD5RSA, spki->Algorithm.pszObjId) ||
00057         0 == strcmp(szOID_RSA_SHA1RSA, spki->Algorithm.pszObjId)) 
00058         prov =  PROV_RSA_FULL;
00059     else
00060         return false;
00061     return true;
00062 }
00070 CPKIFBCryptPublicKey::CPKIFBCryptPublicKey()
00071 :m_hKey(NULL),m_hAlg(NULL),m_spki((CPKIFSubjectPublicKeyInfo *)0)
00072 {
00073 }
00081 CPKIFBCryptPublicKey::~CPKIFBCryptPublicKey()
00082 {
00083     this->clear();
00084 }
00085 
00097 BCRYPT_KEY_HANDLE CPKIFBCryptPublicKey::Initialize(const CPKIFBufferPtr & km)
00098 {
00099     LOG_STRING_DEBUG("CPKIFBCryptPublicKey::Initialize(const CPKIFKeyMaterial& key)", TOOLKIT_CRYPTO_CAPIRAW, 0, NULL);
00100     this->clear();
00101 
00102 
00103     SECURITY_STATUS cngStatus = ERROR_SUCCESS;
00104     NTSTATUS ntrv = 0L;
00105 
00106     // these _PKIF CAPI structures are scoped pointers that know how to free 
00107     // themselves. interface is like std::auto_ptr, with implicit casts
00108     PCERT_PUBLIC_KEY_INFO_PKIF capiSpki(0);
00109     DWORD capiSpkiLen = 0;
00110     if(!CryptDecodeObjectEx(X509_ASN_ENCODING,X509_PUBLIC_KEY_INFO,
00111                         km->GetBuffer(),(DWORD)km->GetLength(),
00112                         CRYPT_DECODE_ALLOC_FLAG,
00113                         NULL,
00114                         &capiSpki,&capiSpkiLen))
00115     {
00116         std::ostringstream os;
00117         os << "CryptDecodeObjectEx failed to decode subjectPublicKeyInfo structure: " << GetLastError();;
00118         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00119     }
00120     CPKIFOIDPtr spkiOID(new CPKIFOID(CPKIFStringPtr(new string(capiSpki->Algorithm.pszObjId))));
00121     CPKIFBufferPtr spkiParams(new CPKIFBuffer(false,capiSpki->Algorithm.Parameters.pbData,capiSpki->Algorithm.Parameters.cbData));
00122     CPKIFAlgorithmIdentifierPtr algID(new CPKIFAlgorithmIdentifier(spkiOID,spkiParams));
00123     CPKIFBufferPtr keyBuf(new CPKIFBuffer(false,capiSpki->PublicKey.pbData,capiSpki->PublicKey.cbData));
00124     CPKIFSubjectPublicKeyInfoPtr decodedSPKI(new CPKIFSubjectPublicKeyInfo(algID,keyBuf));
00125     CPKIFCryptoPPKeyMaterial cppKM(decodedSPKI);
00126     return Initialize(cppKM);
00127 
00128 }
00129 
00130 
00142 BCRYPT_KEY_HANDLE CPKIFBCryptPublicKey::Initialize(const CPKIFKeyMaterial & km)
00143 {
00144     LOG_STRING_DEBUG("CPKIFBCryptPublicKey::Initialize(const CPKIFKeyMaterial& key)", TOOLKIT_CRYPTO_CAPIRAW, 0, NULL);
00145     this->clear();
00146 
00147 
00148     SECURITY_STATUS cngStatus = ERROR_SUCCESS;
00149     NTSTATUS ntrv = 0L;
00150 
00151 
00152     LPCWSTR cngAlg = NULL;
00153     // these _PKIF CAPI structures are scoped pointers that know how to free 
00154     // themselves. interface is like std::auto_ptr, with implicit casts
00155     PCERT_PUBLIC_KEY_INFO_PKIF capiSpki(0);
00156     // this is very much like a boost::scoped_array_ptr with implicit casts
00157     pkif_byte_array paramsGuard(0);
00158 
00159     // the neatest way (and "neat" is admittedly pushing it a bit...) to get
00160     // a key from a cert into CAPI NG is with the ASN.1 subjectPublicKeyInfo structure.
00161     // Unless it's a DSA key. Then you're simply facing a mess.
00162     CPKIFSubjectPublicKeyInfoPtr spki = km.GetSubjectPublicKeyInfo();
00163     
00164     // If the key material object didn't have that populated, look for a cert and
00165     // decode it.
00166     if(!spki) {
00167         CPKIFCertificatePtr keyCert(new CPKIFCertificate());
00168         keyCert->Decode(km.GetCertificate(),km.GetCertificateLength());
00169         spki = keyCert->GetSubjectPublicKeyInfo();
00170     }
00171     
00172     // If there's still no luck, we don't have key material we can use for a signature
00173     // verification operation. Bail.
00174     if(!spki)
00175     {
00176         RAISE_CRYPTO_EXCEPTION("A CPKIFKeyMaterial object without a public key was passed to Verify()",
00177             TOOLKIT_CRYPTO, COMMON_INVALID_INPUT, NULL);
00178     }
00179     m_spki = spki;
00180 
00181     // a minor hack... the Crypto++ key material object's handling of
00182     // subjectPublicKeyInfo makes this code more substantially more readable
00183     CPKIFCryptoPPKeyMaterialPtr cppkm(new CPKIFCryptoPPKeyMaterial(spki));
00184     CPKIFBufferPtr rawSPKI = cppkm->GetRawSPKI();
00185     DWORD capiSpkiLen = 0;
00186     if(!CryptDecodeObjectEx(X509_ASN_ENCODING,X509_PUBLIC_KEY_INFO,
00187                         rawSPKI->GetBuffer(),(DWORD)rawSPKI->GetLength(),
00188                         CRYPT_DECODE_ALLOC_FLAG,
00189                         NULL,
00190                         &capiSpki,&capiSpkiLen))
00191     {
00192         std::ostringstream os;
00193         os << "CryptDecodeObjectEx failed to decode subjectPublicKeyInfo structure: " << GetLastError();;
00194         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00195     }
00196 
00197     // XXX *** Is there any case where we don't want to inherit?
00198     CPKIFAlgorithmIdentifierPtr workingParams = km.GetWorkingParameters();
00199     if(0 == capiSpki->Algorithm.Parameters.cbData &&
00200         workingParams != (CPKIFAlgorithmIdentifier*)NULL)
00201     {
00202         CPKIFBufferPtr params = workingParams->parameters();
00203         capiSpki->Algorithm.Parameters.cbData = params->GetLength();
00204         capiSpki->Algorithm.Parameters.pbData = new unsigned char[params->GetLength()];
00205         memcpy(capiSpki->Algorithm.Parameters.pbData, (BYTE*)params->GetBuffer(), params->GetLength());
00206         paramsGuard.reset(capiSpki->Algorithm.Parameters.pbData);
00207     }
00208 
00209     if(!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING,capiSpki,0,0,&m_hKey))
00210     {
00211         // Having failed the "neat" way, there are a couple hoops to jump through
00212         DWORD provType;
00213         if(!GetCAPI1ProvType(capiSpki,provType))
00214         {
00215             std::ostringstream os;
00216             os << "CryptImportPublicKeyInfoEx2 failed: " << GetLastError();
00217             os << ", then we were unable to get provider type for " << capiSpki->Algorithm.pszObjId << endl;
00218             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00219         }
00220         LPCWSTR oldBlobType = 0;
00221         LPCWSTR newProvAlgId = 0;
00222         switch(provType)
00223         {
00224         case PROV_DSS:
00225             oldBlobType = LEGACY_DSA_PUBLIC_BLOB;
00226             newProvAlgId = BCRYPT_DSA_ALGORITHM;
00227             break;
00228         case PROV_RSA_FULL:
00229             oldBlobType = LEGACY_RSAPUBLIC_BLOB;
00230             newProvAlgId = BCRYPT_RSA_ALGORITHM;
00231             break;
00232         default:
00233         {
00234             std::ostringstream os;
00235             os << "CryptImportPublicKeyInfoEx2 failed: " << GetLastError();
00236             os << ", then we got an unanticipated provider type for " << capiSpki->Algorithm.pszObjId << endl;
00237             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00238         }
00239             break;
00240         }
00241 
00242         // 1. Import the key using the old CAPI interface
00243         HCRYPTPROV_PKIF hProv;
00244         if(!CryptAcquireContext(&hProv,NULL,NULL,provType,CRYPT_VERIFYCONTEXT)) {
00245             std::ostringstream os;
00246             os << "Unable to acquire context for " << capiSpki->Algorithm.pszObjId << " provider: " << GetLastError() << endl;
00247             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00248         }
00249         HCRYPTKEY_PKIF hcapikey;
00250         if(!CryptImportPublicKeyInfo(hProv,X509_ASN_ENCODING,capiSpki,&hcapikey))
00251         {
00252             std::ostringstream os;
00253             os << "CAPI1 was unable to import a key with alg ID " << capiSpki->Algorithm.pszObjId << " after CNG failed: " << GetLastError() << endl;
00254             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00255         }
00256         // 2. Now attempt to export from CAPI.
00257         DWORD cbBlob = 0;
00258         // CRYPT_BLOB_VER3 is absolutely required on Vista and 2008.
00259         if(!CryptExportKey(hcapikey,0,PUBLICKEYBLOB,CRYPT_BLOB_VER3,NULL,&cbBlob))
00260         {
00261             std::ostringstream os;
00262             os << "CAPI1 was unable to export a key with alg ID " << capiSpki->Algorithm.pszObjId << " for CNG import: " << GetLastError() << endl;
00263             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00264         }
00265         pkif_byte_array pbBlob(new unsigned char[cbBlob]);
00266         if(!CryptExportKey(hcapikey,0,PUBLICKEYBLOB,CRYPT_BLOB_VER3,pbBlob,&cbBlob))
00267         {
00268             std::ostringstream os;
00269             os << "CAPI1 was unable to export a key with alg ID " << capiSpki->Algorithm.pszObjId << " for CNG import: " << GetLastError() << endl;
00270             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00271         }
00272         
00273         // 3. Now attempt to import the blob as an NCRYPT_KEY_HANDLE.
00274         NCRYPT_PROV_HANDLE_PKIF hNCProv;
00275         if(FAILED(cngStatus = NCryptOpenStorageProvider(&hNCProv, MS_KEY_STORAGE_PROVIDER,0)))
00276         {
00277             std::ostringstream os;
00278             os << "Unable to Open " << MS_KEY_STORAGE_PROVIDER << " for CNG import: (" 
00279                 << GetLastError() << ")" << "(SECURITY_STATUS code " << cngStatus << ")" << endl;
00280             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00281         }
00282         NCRYPT_KEY_HANDLE_PKIF hTmpKey;
00283         if(FAILED(cngStatus = NCryptImportKey(hNCProv,NULL,oldBlobType,NULL,&hTmpKey,
00284                                         pbBlob,cbBlob,0)))
00285         {
00286             std::ostringstream os;
00287             os << "Unable to import blob into " << MS_KEY_STORAGE_PROVIDER << ": (" 
00288                 << GetLastError() << ")" << "(SECURITY_STATUS code " << cngStatus << ")" << endl;
00289             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00290         }
00291 
00292         // 4. Now export as a BCRYPT blob
00293         DWORD cbNewBlob = 0;
00294         if(FAILED(cngStatus = NCryptExportKey(hTmpKey,NULL,BCRYPT_PUBLIC_KEY_BLOB,NULL,NULL,0,&cbNewBlob,0)))
00295         {
00296             std::ostringstream os;
00297             os << "Unable to export blob from " << MS_KEY_STORAGE_PROVIDER << " for CNG import: (" 
00298                 << GetLastError() << ")" << "(SECURITY_STATUS code " << cngStatus << ")" << endl;
00299             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00300         }
00301         pkif_byte_array pbNewBlob(new unsigned char[cbNewBlob]);
00302         if(FAILED(cngStatus = NCryptExportKey(hTmpKey,NULL,BCRYPT_PUBLIC_KEY_BLOB,NULL,pbNewBlob,cbNewBlob,&cbNewBlob,0)))
00303         {
00304             std::ostringstream os;
00305             os << "Unable to export blob from " << MS_KEY_STORAGE_PROVIDER << " for CNG import: (" 
00306                 << GetLastError() << ")" << "(SECURITY_STATUS code " << cngStatus << ")" << endl;
00307             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00308         }
00309 
00310         // 5. Now it *should* be importable to a BCRYPT_KEY_HANDLE
00311         if(!NT_SUCCESS(ntrv = BCryptOpenAlgorithmProvider(&m_hAlg,newProvAlgId,NULL,0)))
00312         {
00313             std::ostringstream os;
00314             os << "Couldn't open BCrypt algorithm provider for " << capiSpki->Algorithm.pszObjId << " :(" 
00315                 << GetLastError() << ")" << "(NTSTATUS code " << ntrv << ")" << endl;
00316             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00317         }
00318         if(!NT_SUCCESS(ntrv = BCryptImportKeyPair(m_hAlg,NULL,BCRYPT_PUBLIC_KEY_BLOB,&m_hKey,pbNewBlob,cbNewBlob,0))) {
00319             std::ostringstream os;
00320             os << "Couldn't import key for algorithm " << capiSpki->Algorithm.pszObjId << " into CNG:(" 
00321                 << GetLastError() << ")" << "(NTSTATUS code " << cngStatus << ")" << endl;
00322             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_KEY_IMPORT_FAILED, NULL);
00323         }
00324         // having reached around my elbow, I can now scratch my rear end.
00325 
00326     }
00327     return this->GetCNGHandle();
00328 
00329 }
00337 CPKIFAlgorithmIdentifierPtr CPKIFBCryptPublicKey::GetAlgId() const
00338 {
00339     CPKIFAlgorithmIdentifierPtr empty;
00340     if(!m_spki) return empty;
00341     return m_spki->alg();
00342 }
00350 BCRYPT_KEY_HANDLE CPKIFBCryptPublicKey::GetCNGHandle() const
00351 {
00352     return m_hKey;
00353 }
00361 void CPKIFBCryptPublicKey::clear()
00362 {
00363     if(m_hKey) {
00364         BCryptDestroyKey(m_hKey);
00365         m_hKey = NULL;
00366     }
00367     if(m_hAlg) {
00368         BCryptCloseAlgorithmProvider(m_hAlg,0);
00369         m_hAlg = NULL;
00370     }
00371     CPKIFSubjectPublicKeyInfoPtr empty;
00372     m_spki = empty;
00373 }

Generated on Mon Nov 15 11:15:53 2010 for PublicKeyInfrastructureFramework(PKIF) by  doxygen 1.5.6