diff options
Diffstat (limited to 'cpp/src/IceSSL/UWPEngine.cpp')
-rw-r--r-- | cpp/src/IceSSL/UWPEngine.cpp | 300 |
1 files changed, 268 insertions, 32 deletions
diff --git a/cpp/src/IceSSL/UWPEngine.cpp b/cpp/src/IceSSL/UWPEngine.cpp index aadade9bf93..97fcbda9b1f 100644 --- a/cpp/src/IceSSL/UWPEngine.cpp +++ b/cpp/src/IceSSL/UWPEngine.cpp @@ -9,47 +9,283 @@ #include <IceSSL/Config.h> -#ifdef ICE_OS_UWP +#include <IceSSL/UWPEngineF.h> +#include <IceSSL/UWPEngine.h> +#include <IceSSL/UWPTransceiverI.h> +#include <IceSSL/Util.h> -#include <IceSSL/SSLEngine.h> #include <Ice/Communicator.h> +#include <Ice/StringConverter.h> +#include <Ice/StringUtil.h> #include <Ice/Properties.h> +#include <Ice/Network.h> #include <IceUtil/Shared.h> #include <string> -IceUtil::Shared* IceSSL::upCast(IceSSL::UWPEngine* p) { return p; } - using namespace std; +using namespace Ice; +using namespace IceUtil; using namespace IceSSL; -UWPEngine::UWPEngine(const Ice::CommunicatorPtr& communicator) : SSLEngine(communicator) +using namespace concurrency; +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::Storage; +using namespace Windows::Storage::Streams; +using namespace Windows::Security::Cryptography; +using namespace Windows::Security::Cryptography::Core; +using namespace Windows::Security::Cryptography::Certificates; + +IceUtil::Shared* UWP::upCast(UWP::SSLEngine* p) { return p; } + +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) +{ + CertificateQuery^ query = ref new CertificateQuery(); + query->IncludeDuplicates = true; + query->IncludeExpiredCertificates = true; + query->FriendlyName = friendlyName; + query->StoreName = StandardCertificateStoreNames::Personal; + + try + { + auto certificates = IceInternal::runSync(CertificateStores::FindAllAsync(query)); + return certificates->Size > 0 ? certificates->GetAt(0) : nullptr; + } + catch(Platform::Exception^ ex) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: certificate error:\n" + wstringToString(ex->Message->Data())); + } +} + +// +// 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) +{ + try + { + IceInternal::runSync(CertificateEnrollmentManager::ImportPfxDataAsync( + data, + password, + ExportOption::NotExportable, + KeyProtectionLevel::NoConsent, + InstallOptions::None, + friendlyName)); + return false; // The import succcess + } + catch(Platform::Exception^ ex) + { + if(HRESULT_CODE(ex->HResult) == ERROR_DECRYPTION_FAILED) + { + return true; // Password error + } + else + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: certificate error:\n" + wstringToString(ex->Message->Data())); + } + } +} + +Certificates::Certificate^ +importPersonalCertificate(const string& file, function<string ()> password, bool passwordPrompt, + int passwordRetryMax) +{ + auto uri = ref new Uri(ref new String(stringToWstring(file).c_str())); + try + { + auto file = IceInternal::runSync(StorageFile::GetFileFromApplicationUriAsync(uri)); + auto buffer = IceInternal::runSync(FileIO::ReadBufferAsync(file)); + + // + // 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) + { + return 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"); + } + return findPersonalCertificate(friendlyName); + } + } + catch(Platform::Exception^ ex) + { + if(HRESULT_CODE(ex->HResult) == ERROR_FILE_NOT_FOUND) + { + throw PluginInitializationException(__FILE__, __LINE__, "certificate file not found:\n" + file); + } + else + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: certificate error:\n" + wstringToString(ex->Message->Data())); + } + } +} + +IVectorView<Certificates::Certificate^>^ +findCertificates(const string& name, const string& value) +{ + CertificateQuery^ query = ref new CertificateQuery(); + query->StoreName = ref new String(stringToWstring(name).c_str()); + query->IncludeDuplicates = true; + query->IncludeExpiredCertificates = true; + + if(value != "*") + { + if(value.find(':', 0) == string::npos) + { + throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: no key in `" + value + "'"); + } + size_t start = 0; + size_t pos; + while((pos = value.find(':', start)) != string::npos) + { + string field = IceUtilInternal::toUpper(IceUtilInternal::trim(value.substr(start, pos - start))); + if(field != "ISSUER" && field != "THUMBPRINT" && field != "FRIENDLYNAME") + { + throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: unknown key in `" + value + "'"); + } + + start = pos + 1; + while(start < value.size() && (value[start] == ' ' || value[start] == '\t')) + { + ++start; + } + + if(start == value.size()) + { + throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: missing argument in `" + value + "'"); + } + + string arg; + if(value[start] == '"' || value[start] == '\'') + { + size_t end = start; + ++end; + while(end < value.size()) + { + if(value[end] == value[start] && value[end - 1] != '\\') + { + break; + } + ++end; + } + if(end == value.size() || value[end] != value[start]) + { + throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: unmatched quote in `" + value + "'"); + } + ++start; + arg = value.substr(start, end - start); + start = end + 1; + } + else + { + size_t end = value.find_first_of(" \t", start); + if(end == string::npos) + { + arg = value.substr(start); + start = value.size(); + } + else + { + arg = value.substr(start, end - start); + start = end + 1; + } + } + + if(field == "ISSUER") + { + query->IssuerName = ref new String(stringToWstring(arg).c_str()); + } + else if(field == "FRIENDLYNAME") + { + query->FriendlyName = ref new String(stringToWstring(arg).c_str()); + } + else if(field == "THUMBPRINT") + { + vector<BYTE> buffer; + if(!parseBytes(arg, buffer)) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: invalid `IceSSL.FindCert' property: can't decode the value"); + } + query->Thumbprint = ref new Array<unsigned char>(&buffer[0], static_cast<unsigned int>(buffer.size())); + } + } + } + + try + { + return IceInternal::runSync(CertificateStores::FindAllAsync(query)); + } + catch(Platform::Exception^ ex) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: certificate error:\n" + wstringToString(ex->Message->Data())); + } +} + +} // anonymous namespace end + +UWP::SSLEngine::SSLEngine(const Ice::CommunicatorPtr& communicator) : IceSSL::SSLEngine(communicator) { } void -UWPEngine::initialize() +UWP::SSLEngine::initialize() { - lock_guard<mutex> lock(_mutex); + Mutex::Lock lock(_mutex); if(_initialized) { return; } - SSLEngine::initialize(); + IceSSL::SSLEngine::initialize(); const auto properties = communicator()->getProperties(); // - // Load CAs - // - //string ca = properties->getProperty("IceSSL.CAs"); - //if(!ca.empty()) - //{ - // _ca = Certificate::load(ca); - //} - - // // Load client certificate // const int passwordRetryMax = properties->getPropertyAsIntWithDefault("IceSSL.PasswordRetryMax", 3); @@ -59,14 +295,14 @@ UWPEngine::initialize() string findCert = properties->getProperty("IceSSL.FindCert"); if(!certFile.empty()) { - _certificate = make_shared<IceSSL::Certificate>(importPersonalCertificate( + _certificate = dynamic_pointer_cast<UWP::Certificate>(UWP::Certificate::create(importPersonalCertificate( certFile, [this]() { return password(false); }, getPasswordPrompt() != nullptr, - passwordRetryMax)); + passwordRetryMax))); } else if(!findCert.empty()) { @@ -75,33 +311,33 @@ UWPEngine::initialize() { throw Ice::PluginInitializationException(__FILE__, __LINE__, "IceSSL: no certificates found"); } - _certificate = make_shared<IceSSL::Certificate>(certs->GetAt(0)); + _certificate = dynamic_pointer_cast<UWP::Certificate>(UWP::Certificate::create(certs->GetAt(0))); } _initialized = true; } bool -UWPEngine::initialized() const +UWP::SSLEngine::initialized() const { return _initialized; } -//shared_ptr<Certificate> -//UWPEngine::ca() -//{ -// return _ca; -//} - -shared_ptr<Certificate> -UWPEngine::certificate() +shared_ptr<UWP::Certificate> +UWP::SSLEngine::certificate() { return _certificate; } void -UWPEngine::destroy() +UWP::SSLEngine::destroy() { } -#endif - +IceInternal::TransceiverPtr +UWP::SSLEngine::createTransceiver(const InstancePtr& instance, + const IceInternal::TransceiverPtr& delegate, + const string& hostOrAdapterName, + bool incoming) +{ + return new UWP::TransceiverI(instance, delegate, hostOrAdapterName, incoming); +} |