From 64a9231b01a869bccd3585abeb9eec75d9697bcf Mon Sep 17 00:00:00 2001 From: Jose Date: Thu, 24 Nov 2016 21:31:26 +0100 Subject: UWP IceSSL implementation improvements - Add support for IceSSL.CertFile - Add support for IceSSL password prompt --- cpp/src/IceSSL/Util.cpp | 218 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 203 insertions(+), 15 deletions(-) (limited to 'cpp/src/IceSSL/Util.cpp') diff --git a/cpp/src/IceSSL/Util.cpp b/cpp/src/IceSSL/Util.cpp index 6979e34cd7b..0c227c7e279 100755 --- a/cpp/src/IceSSL/Util.cpp +++ b/cpp/src/IceSSL/Util.cpp @@ -1693,6 +1693,195 @@ IceSSL::findCertificates(const string& location, const string& name, const strin return certs; } #elif defined (ICE_OS_WINRT) + +namespace +{ + +// +// Find a certificate in the Application Personal certificate store +// with the given friendly name. Returns the matching certificate or +// nullptr if none is found. +// +Certificates::Certificate^ +findPersonalCertificate(String^ friendlyName) +{ + std::promise p; + + CertificateQuery^ query = ref new CertificateQuery(); + query->IncludeDuplicates = true; + query->IncludeExpiredCertificates = true; + query->FriendlyName = friendlyName; + query->StoreName = StandardCertificateStoreNames::Personal; + + create_task(CertificateStores::FindAllAsync(query)) + + .then([&p](IVectorView^ certificates) + { + if(certificates->Size > 0) + { + p.set_value(certificates->GetAt(0)); + } + else + { + p.set_value(nullptr); + } + }, + task_continuation_context::use_arbitrary()) + + .then([&](task t) + { + try + { + t.get(); + } + catch(Platform::Exception^ ex) + { + p.set_exception(make_exception_ptr( + PluginInitializationException(__FILE__, __LINE__, "IceSSL: certificate error:\n" + + wstringToString(ex->Message->Data())))); + } + }, + task_continuation_context::use_arbitrary()); + + return p.get_future().get(); +} + +// +// Import a certificate in the Application Personal certificate store +// with the given friendly name. Returns true if there was a password +// error and false otherwise. If the import fails because a different +// error PluginInitializationException exception is throw. +// +bool +importPfxData(String^ friendlyName, String^ data, String^ password) +{ + promise p; + + create_task(CertificateEnrollmentManager::ImportPfxDataAsync( + data, + password, + ExportOption::NotExportable, + KeyProtectionLevel::NoConsent, + InstallOptions::None, + friendlyName)) + + .then([&p]() + { + p.set_value(false); // The import succcess + }, + task_continuation_context::use_arbitrary()) + + .then([&p](task t) + { + try + { + t.get(); + } + catch(Platform::Exception^ ex) + { + if(HRESULT_CODE(ex->HResult) == ERROR_DECRYPTION_FAILED) + { + p.set_value(true); // Password error + } + else + { + p.set_exception(make_exception_ptr( + PluginInitializationException(__FILE__, __LINE__, "IceSSL: certificate error:\n" + + wstringToString(ex->Message->Data())))); + } + } + }, + task_continuation_context::use_arbitrary()); + + return p.get_future().get(); +} + +} + +Certificates::Certificate^ +IceSSL::importPersonalCertificate(const string& file, function password, bool passwordPrompt, + int passwordRetryMax) +{ + std::promise p; + auto uri = ref new Uri(ref new String(stringToWstring(file).c_str())); + create_task(StorageFile::GetFileFromApplicationUriAsync(uri)) + + .then([](StorageFile^ file) + { + return FileIO::ReadBufferAsync(file); + }, + task_continuation_context::use_arbitrary()) + + .then([&file, &password, &p, passwordPrompt, passwordRetryMax](IBuffer^ buffer) + { + // + // Create a hash of the certificate to use as a friendly name, this will allow us + // to uniquely identify the certificate in the store. + // + auto hasher = HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1); + auto hash = hasher->CreateHash(); + + hash->Append(buffer); + String^ friendlyName = CryptographicBuffer::EncodeToBase64String(hash->GetValueAndReset()); + + // + // If the certificate is already in the store we avoid importing it. + // + Certificates::Certificate^ cert = findPersonalCertificate(friendlyName); + if(cert) + { + p.set_value(cert); + } + else + { + String^ data = CryptographicBuffer::EncodeToBase64String(buffer); + int count = 0; + bool passwordErr = false; + do + { + passwordErr = importPfxData(friendlyName, data, + ref new String(stringToWstring(password()).c_str())); + } + while(passwordPrompt && passwordErr && ++count < passwordRetryMax); + if(passwordErr) + { + throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error decoding certificate"); + } + p.set_value(findPersonalCertificate(friendlyName)); + } + }, + task_continuation_context::use_arbitrary()) + + .then([&p, &file](task t) + { + try + { + t.get(); + } + catch(Platform::Exception^ ex) + { + if(HRESULT_CODE(ex->HResult) == ERROR_FILE_NOT_FOUND) + { + p.set_exception(make_exception_ptr( + PluginInitializationException(__FILE__, __LINE__, "certificate file not found:\n" + file))); + } + else + { + p.set_exception(make_exception_ptr( + PluginInitializationException(__FILE__, __LINE__, "IceSSL: certificate error:\n" + + wstringToString(ex->Message->Data())))); + } + } + catch(...) + { + p.set_exception(current_exception()); + } + }, + task_continuation_context::use_arbitrary()); + + return p.get_future().get(); +} + IVectorView^ IceSSL::findCertificates(const string& name, const string& value) { @@ -1786,26 +1975,25 @@ IceSSL::findCertificates(const string& name, const string& value) } std::promise^> p; - HRESULT error = 0; - create_task(CertificateStores::FindAllAsync(query)).then( - [&](task^> previous) + create_task(CertificateStores::FindAllAsync(query)) + + .then([&p](IVectorView^ certificates) + { + p.set_value(certificates); + }, + task_continuation_context::use_arbitrary()) + + .then([&p](task t) { try { - p.set_value(previous.get()); + t.get(); } - catch(Platform::Exception^ err) + catch(Platform::Exception^ ex) { - try - { - Ice::SyscallException ex(__FILE__, __LINE__); - ex.error = err->HResult; - throw ex; - } - catch(...) - { - p.set_exception(current_exception()); - } + p.set_exception( + make_exception_ptr(PluginInitializationException(__FILE__, __LINE__, "IceSSL: certificate error:\n" + + wstringToString(ex->Message->Data())))); } }, task_continuation_context::use_arbitrary()); -- cgit v1.2.3