summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose <jose@zeroc.com>2016-11-24 21:31:26 +0100
committerJose <jose@zeroc.com>2016-11-24 21:31:26 +0100
commit64a9231b01a869bccd3585abeb9eec75d9697bcf (patch)
tree00b9d15949c5159dce8821339e458215570ff22a
parentPort IceSSL/configuration test to UWP and fixes to UWP IceSSL implementation (diff)
downloadice-64a9231b01a869bccd3585abeb9eec75d9697bcf.tar.bz2
ice-64a9231b01a869bccd3585abeb9eec75d9697bcf.tar.xz
ice-64a9231b01a869bccd3585abeb9eec75d9697bcf.zip
UWP IceSSL implementation improvements
- Add support for IceSSL.CertFile - Add support for IceSSL password prompt
-rwxr-xr-xcpp/src/IceSSL/Util.cpp218
-rw-r--r--cpp/src/IceSSL/Util.h7
-rwxr-xr-xcpp/src/IceSSL/WinRTEngine.cpp17
-rw-r--r--cpp/test/IceSSL/configuration/AllTests.cpp101
-rw-r--r--cpp/test/uwp/msbuild/uwp.vcxproj8
-rw-r--r--cpp/test/uwp/msbuild/uwp.vcxproj.filters3
6 files changed, 292 insertions, 62 deletions
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<Certificates::Certificate^> 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::Certificate^>^ certificates)
+ {
+ if(certificates->Size > 0)
+ {
+ p.set_value(certificates->GetAt(0));
+ }
+ else
+ {
+ p.set_value(nullptr);
+ }
+ },
+ task_continuation_context::use_arbitrary())
+
+ .then([&](task<void> 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<bool> 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<void> 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<string ()> password, bool passwordPrompt,
+ int passwordRetryMax)
+{
+ std::promise<Certificates::Certificate^> 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<void> 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<Certificates::Certificate^>^
IceSSL::findCertificates(const string& name, const string& value)
{
@@ -1786,26 +1975,25 @@ IceSSL::findCertificates(const string& name, const string& value)
}
std::promise<IVectorView<Certificates::Certificate^>^> p;
- HRESULT error = 0;
- create_task(CertificateStores::FindAllAsync(query)).then(
- [&](task<IVectorView<Certificates::Certificate^>^> previous)
+ create_task(CertificateStores::FindAllAsync(query))
+
+ .then([&p](IVectorView<Certificates::Certificate^>^ certificates)
+ {
+ p.set_value(certificates);
+ },
+ task_continuation_context::use_arbitrary())
+
+ .then([&p](task<void> 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());
diff --git a/cpp/src/IceSSL/Util.h b/cpp/src/IceSSL/Util.h
index 59ff813a1a8..f10f5f25a72 100644
--- a/cpp/src/IceSSL/Util.h
+++ b/cpp/src/IceSSL/Util.h
@@ -181,12 +181,12 @@ toCFString(const std::string& s)
std::string errorToString(CFErrorRef);
std::string errorToString(OSStatus);
-#if !defined(__APPLE__) || TARGET_OS_IPHONE == 0
+# if defined(ICE_USE_SECURE_TRANSPORT_MACOS)
//
// Retrieve a certificate property
//
CFDictionaryRef getCertificateProperty(SecCertificateRef, CFTypeRef);
-#endif
+# endif
//
// Read certificate from a file.
@@ -203,6 +203,9 @@ CFArrayRef findCertificateChain(const std::string&, const std::string&, const st
std::vector<PCCERT_CONTEXT>
findCertificates(const std::string&, const std::string&, const std::string&, std::vector<HCERTSTORE>&);
#elif defined(ICE_OS_WINRT)
+Windows::Security::Cryptography::Certificates::Certificate^
+importPersonalCertificate(const std::string&, std::function<std::string()>, bool, int);
+
Windows::Foundation::Collections::IVectorView<Windows::Security::Cryptography::Certificates::Certificate^>^
findCertificates(const std::string&, const std::string&);
#endif
diff --git a/cpp/src/IceSSL/WinRTEngine.cpp b/cpp/src/IceSSL/WinRTEngine.cpp
index b2bbf069e34..b955c8ce4a1 100755
--- a/cpp/src/IceSSL/WinRTEngine.cpp
+++ b/cpp/src/IceSSL/WinRTEngine.cpp
@@ -52,8 +52,23 @@ WinRTEngine::initialize()
//
// Load client certificate
//
+ const int passwordRetryMax = properties->getPropertyAsIntWithDefault("IceSSL.PasswordRetryMax", 3);
+ setPassword(properties->getProperty("IceSSL.Password"));
+
+ string certFile = properties->getProperty("IceSSL.CertFile");
string findCert = properties->getProperty("IceSSL.FindCert");
- if(!findCert.empty())
+ if(!certFile.empty())
+ {
+ _certificate = make_shared<IceSSL::Certificate>(importPersonalCertificate(
+ certFile,
+ [this]()
+ {
+ return password(false);
+ },
+ getPasswordPrompt != nullptr,
+ passwordRetryMax));
+ }
+ else if(!findCert.empty())
{
auto certs = findCertificates(properties->getPropertyWithDefault("IceSSL.CertStore", "My"), findCert);
if(certs->Size == 0)
diff --git a/cpp/test/IceSSL/configuration/AllTests.cpp b/cpp/test/IceSSL/configuration/AllTests.cpp
index 302ae15e846..24047abfabf 100644
--- a/cpp/test/IceSSL/configuration/AllTests.cpp
+++ b/cpp/test/IceSSL/configuration/AllTests.cpp
@@ -95,13 +95,15 @@ importPersonalCertificate(const string& friendlyName, const string& file, const
{
std::promise<bool> p;
auto uri = ref new Uri(ref new String(stringToWstring(file).c_str()));
- create_task(StorageFile::GetFileFromApplicationUriAsync(uri)).then(
- [](StorageFile^ file)
+ create_task(StorageFile::GetFileFromApplicationUriAsync(uri))
+
+ .then([](StorageFile^ file)
{
return FileIO::ReadBufferAsync(file);
- }
- ).then(
- [&password, &friendlyName](IBuffer^ buffer)
+ },
+ task_continuation_context::use_arbitrary())
+
+ .then([&password, &friendlyName](IBuffer^ buffer)
{
return CertificateEnrollmentManager::ImportPfxDataAsync(CryptographicBuffer::EncodeToBase64String(buffer),
ref new String(stringToWstring(password).c_str()),
@@ -109,59 +111,76 @@ importPersonalCertificate(const string& friendlyName, const string& file, const
KeyProtectionLevel::NoConsent,
InstallOptions::None,
ref new String(stringToWstring(friendlyName).c_str()));
- }
- ).then([&p](task<void> previous)
+ },
+ task_continuation_context::use_arbitrary())
+
+ .then([&p]()
+ {
+ p.set_value(true);
+ },
+ task_continuation_context::use_arbitrary())
+
+ .then([&p](task<void> t)
{
try
{
- previous.get();
- p.set_value(true);
+ t.get();
}
catch(...)
{
p.set_exception(current_exception());
}
- });
+ },
+ task_continuation_context::use_arbitrary());
+
return p.get_future().get();
}
bool
-removeCertificate(String^ storeName, const string& friendlyName)
+removeCertificate(String^ storeName, const string& friendlyName = "")
{
promise<bool> p;
CertificateQuery^ query = ref new CertificateQuery();
query->IncludeDuplicates = true;
query->IncludeExpiredCertificates = true;
- query->FriendlyName = ref new String(stringToWstring(friendlyName).c_str());
+ if(!friendlyName.empty())
+ {
+ query->FriendlyName = ref new String(stringToWstring(friendlyName).c_str());
+ }
query->StoreName = storeName;
- create_task(CertificateStores::FindAllAsync(query)).then(
- [](IVectorView<Certificate^>^ certs)
+
+ create_task(CertificateStores::FindAllAsync(query))
+
+ .then([&p](IVectorView<Certificate^>^ certs)
{
for(unsigned int i = 0; i < certs->Size; ++i)
{
Certificate^ cert = certs->GetAt(i);
- CertificateStore^ store = CertificateStores::GetStoreByName(cert->StoreName);
- store->Delete(cert);
+ CertificateStores::GetStoreByName(cert->StoreName)->Delete(cert);
}
- }
- ).then(
- [&p](task<void> previous)
+ p.set_value(true);
+ },
+ task_continuation_context::use_arbitrary())
+
+ .then(
+ [&p](task<void> t)
{
try
{
- previous.get();
- p.set_value(true);
+ t.get();
}
catch(...)
{
p.set_exception(current_exception());
}
- });
+ },
+ task_continuation_context::use_arbitrary());
+
return p.get_future().get();
}
bool
-removePersonalCertificate(const string& friendlyName)
+removePersonalCertificate(const string& friendlyName = "")
{
return removeCertificate(StandardCertificateStoreNames::Personal, friendlyName);
}
@@ -618,14 +637,9 @@ createClientProps(const Ice::PropertiesPtr& defaultProps, const string& defaultD
importCaCertificate(ca, "ms-appx:///" + ca + ".pem");
}
- removePersonalCertificate("c_rsa_ca1");
- removePersonalCertificate("c_rsa_ca1_exp");
- removePersonalCertificate("c_rsa_ca2");
-
if(!cert.empty())
{
- importPersonalCertificate(cert, "ms-appx:///" + cert + ".p12", "password");
- properties->setProperty("IceSSL.FindCert", "FRIENDLYNAME:'" + cert + "'");
+ properties->setProperty("IceSSL.CertFile", "ms-appx:///" + cert + ".p12");
}
#else
if(!ca.empty())
@@ -2096,16 +2110,12 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12)
#endif
//
- // IceSSL.Password is not supported with WinRT
- //
-# ifndef ICE_OS_WINRT
- //
// SChannel doesn't support PEM Password protected certificates certificates
//
-# ifdef ICE_USE_SCHANNEL
+#ifdef ICE_USE_SCHANNEL
if(p12)
{
-# endif
+#endif
cout << "testing password prompt... " << flush;
{
//
@@ -2122,11 +2132,11 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12)
test(plugin);
PasswordPromptIPtr prompt = ICE_MAKE_SHARED(PasswordPromptI, "client");
-# ifdef ICE_CPP11_MAPPING
+#ifdef ICE_CPP11_MAPPING
plugin->setPasswordPrompt([prompt]{ return prompt->getPassword(); });
-# else
+#else
plugin->setPasswordPrompt(prompt);
-# endif
+#endif
pm->initializePlugins();
test(prompt->count() == 1);
Test::ServerFactoryPrxPtr fact = ICE_CHECKED_CAST(Test::ServerFactoryPrx, comm->stringToProxy(factoryRef));
@@ -2148,7 +2158,10 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12)
//
// Use an incorrect password and check that retries are attempted.
//
- initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12, "c_rsa_pass_ca1","cacert1");
+#ifdef ICE_OS_WINRT
+ removePersonalCertificate();
+#endif
+ initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12, "c_rsa_pass_ca1", "cacert1");
initData.properties->setProperty("IceSSL.Password", ""); // Clear password
initData.properties->setProperty("IceSSL.PasswordRetryMax", "4");
initData.properties->setProperty("Ice.InitPlugins", "0");
@@ -2158,11 +2171,11 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12)
test(plugin);
prompt = ICE_MAKE_SHARED(PasswordPromptI, "invalid");
-# ifdef ICE_CPP11_MAPPING
+#ifdef ICE_CPP11_MAPPING
plugin->setPasswordPrompt([prompt]{ return prompt->getPassword(); });
-# else
+#else
plugin->setPasswordPrompt(prompt);
-# endif
+#endif
try
{
pm->initializePlugins();
@@ -2181,9 +2194,8 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12)
comm->destroy();
}
cout << "ok" << endl;
-# ifdef ICE_USE_SCHANNEL
+#ifdef ICE_USE_SCHANNEL
}
-# endif
#endif
//
@@ -3538,6 +3550,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12)
0
};
+ removePersonalCertificate();
importPersonalCertificate("c_rsa_ca1", "ms-appx:///c_rsa_ca1.p12", "password");
for(int i = 0; clientFindCertProperties[i] != 0; i++)
diff --git a/cpp/test/uwp/msbuild/uwp.vcxproj b/cpp/test/uwp/msbuild/uwp.vcxproj
index 8afb079a43c..29bf9f7396c 100644
--- a/cpp/test/uwp/msbuild/uwp.vcxproj
+++ b/cpp/test/uwp/msbuild/uwp.vcxproj
@@ -350,6 +350,14 @@
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent>
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent>
</None>
+ <None Include="..\..\IceSSL\certs\c_rsa_pass_ca1.p12">
+ <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent>
+ <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent>
+ <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent>
+ <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent>
+ <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent>
+ <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent>
+ </None>
<None Include="..\..\IceSSL\certs\s_rsa_ca1_exp_pub.pem">
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent>
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent>
diff --git a/cpp/test/uwp/msbuild/uwp.vcxproj.filters b/cpp/test/uwp/msbuild/uwp.vcxproj.filters
index 2180b8057fc..c39cf594af4 100644
--- a/cpp/test/uwp/msbuild/uwp.vcxproj.filters
+++ b/cpp/test/uwp/msbuild/uwp.vcxproj.filters
@@ -1481,5 +1481,8 @@
<None Include="..\..\IceSSL\certs\cacerts.pem">
<Filter>certs</Filter>
</None>
+ <None Include="..\..\IceSSL\certs\c_rsa_pass_ca1.p12">
+ <Filter>certs</Filter>
+ </None>
</ItemGroup>
</Project> \ No newline at end of file