summaryrefslogtreecommitdiff
path: root/cpp/src/IceSSL/Util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceSSL/Util.cpp')
-rwxr-xr-x[-rw-r--r--]cpp/src/IceSSL/Util.cpp792
1 files changed, 747 insertions, 45 deletions
diff --git a/cpp/src/IceSSL/Util.cpp b/cpp/src/IceSSL/Util.cpp
index 50c47f3cc29..f6fd6a92563 100644..100755
--- a/cpp/src/IceSSL/Util.cpp
+++ b/cpp/src/IceSSL/Util.cpp
@@ -8,7 +8,7 @@
// **********************************************************************
#include <IceUtil/Config.h>
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(ICE_OS_WINRT)
# include <winsock2.h>
#endif
@@ -16,9 +16,16 @@
#include <IceUtil/FileUtil.h>
#include <IceUtil/StringUtil.h>
+#include <Ice/Base64.h>
#include <Ice/LocalException.h>
#include <Ice/Network.h>
#include <Ice/Object.h>
+#include <Ice/StringConverter.h>
+#include <fstream>
+
+#ifdef ICE_OS_WINRT
+# include <ppltasks.h>
+#endif
#ifdef ICE_USE_OPENSSL
# include <openssl/err.h>
@@ -33,30 +40,44 @@ using namespace Ice;
using namespace IceUtil;
using namespace IceSSL;
-void
-IceSSL::readFile(const string& file, vector<char>& buffer)
+#ifdef ICE_OS_WINRT
+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;
+#endif
+
+#ifdef ICE_CPP11_MAPPING
+IceSSL::CertificateVerifier::CertificateVerifier(std::function<bool(const std::shared_ptr<NativeConnectionInfo>&)> v) :
+ _verify(std::move(v))
{
- IceUtilInternal::ifstream is(file, ios::in | ios::binary);
- if(!is.good())
- {
- throw CertificateReadException(__FILE__, __LINE__, "error opening file " + file);
- }
+}
- is.seekg(0, is.end);
- buffer.resize(static_cast<int>(is.tellg()));
- is.seekg(0, is.beg);
+bool
+IceSSL::CertificateVerifier::verify(const NativeConnectionInfoPtr& info)
+{
+ return _verify(info);
+}
- if(!buffer.empty())
- {
- is.read(&buffer[0], buffer.size());
- if(!is.good())
- {
- throw CertificateReadException(__FILE__, __LINE__, "error reading file " + file);
- }
- }
+IceSSL::PasswordPrompt::PasswordPrompt(std::function<std::string()> p) :
+ _prompt(std::move(p))
+{
}
-#ifndef ICE_USE_OPENSSL
+std::string
+IceSSL::PasswordPrompt::getPassword()
+{
+ return _prompt();
+}
+#endif
+
+
+#if !defined(ICE_USE_OPENSSL)
namespace
{
@@ -574,12 +595,14 @@ IceSSL::errorToString(OSStatus status)
{
ostringstream os;
os << "(error: " << status;
+#if defined(ICE_USE_SECURE_TRANSPORT_MACOS)
CFStringRef s = SecCopyErrorMessageString(status, 0);
if(s)
{
os << " description: " << fromCFString(s);
CFRelease(s);
}
+#endif
os << ")";
return os.str();
}
@@ -599,6 +622,7 @@ IceSSL::fromCFString(CFStringRef v)
return s;
}
+#if defined(ICE_USE_SECURE_TRANSPORT_MACOS)
CFDictionaryRef
IceSSL::getCertificateProperty(SecCertificateRef cert, CFTypeRef key)
{
@@ -622,10 +646,36 @@ IceSSL::getCertificateProperty(SecCertificateRef cert, CFTypeRef key)
CFRelease(values);
return property;
}
+#endif
namespace
{
+CFDataRef
+readCertFile(const string& file)
+{
+ ifstream is(IceUtilInternal::streamFilename(file).c_str(), ios::in | ios::binary);
+ if(!is.good())
+ {
+ throw CertificateReadException(__FILE__, __LINE__, "error opening file " + file);
+ }
+
+ is.seekg(0, is.end);
+ size_t size = is.tellg();
+ is.seekg(0, is.beg);
+
+ CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, size);
+ CFDataSetLength(data, size);
+ is.read(reinterpret_cast<char*>(CFDataGetMutableBytePtr(data)), size);
+ if(!is.good())
+ {
+ CFRelease(data);
+ throw CertificateReadException(__FILE__, __LINE__, "error reading file " + file);
+ }
+ return data;
+}
+
+#if defined(ICE_USE_SECURE_TRANSPORT_MACOS)
//
// Check the certificate basic constraints to check if the certificate is marked as a CA.
//
@@ -657,12 +707,7 @@ CFArrayRef
loadKeychainItems(const string& file, SecExternalItemType type, SecKeychainRef keychain, const string& passphrase,
const PasswordPromptPtr& prompt, int retryMax)
{
- vector<char> buffer;
- readFile(file, buffer);
- UniqueRef<CFDataRef> data(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
- reinterpret_cast<const UInt8*>(&buffer[0]),
- buffer.size(),
- kCFAllocatorNull));
+ UniqueRef<CFDataRef> data(readCertFile(file));
SecItemImportExportKeyParameters params;
memset(&params, 0, sizeof(params));
@@ -742,14 +787,93 @@ loadKeychainItems(const string& file, SecExternalItemType type, SecKeychainRef k
return items;
}
+SecKeychainRef
+openKeychain(const std::string& path, const std::string& keychainPassword)
+{
+ string keychainPath = path;
+ SecKeychainRef keychain = 0;
+ OSStatus err = 0;
+ if(keychainPath.empty())
+ {
+ if((err = SecKeychainCopyDefault(&keychain)))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: unable to retrieve default keychain:\n" + errorToString(err));
+ }
+ }
+ else
+ {
+ //
+ // KeyChain path is relative to the current working directory.
+ //
+ if(!IceUtilInternal::isAbsolutePath(keychainPath))
+ {
+ string cwd;
+ if(IceUtilInternal::getcwd(cwd) == 0)
+ {
+ keychainPath = string(cwd) + '/' + keychainPath;
+ }
+ }
+
+ if((err = SecKeychainOpen(keychainPath.c_str(), &keychain)))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: unable to open keychain: `" +
+ keychainPath + "'\n" + errorToString(err));
+ }
+ }
+
+ UniqueRef<SecKeychainRef> k(keychain);
+
+ SecKeychainStatus status;
+ err = SecKeychainGetStatus(keychain, &status);
+ if(err == noErr)
+ {
+ const char* pass = keychainPassword.empty() ? 0 : keychainPassword.c_str();
+ if((err = SecKeychainUnlock(keychain, keychainPassword.size(), pass, pass != 0)))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: unable to unlock keychain:\n" + errorToString(err));
+ }
+ }
+ else if(err == errSecNoSuchKeychain)
+ {
+ const char* pass = keychainPassword.empty() ? 0 : keychainPassword.c_str();
+ if((err = SecKeychainCreate(keychainPath.c_str(), keychainPassword.size(), pass, pass == 0, 0, &keychain)))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: unable to create keychain:\n" + errorToString(err));
+ }
+ k.reset(keychain);
+ }
+ else
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: unable to open keychain:\n" + errorToString(err));
+ }
+
+ //
+ // Set keychain settings to avoid keychain lock.
+ //
+ SecKeychainSettings settings;
+ settings.version = SEC_KEYCHAIN_SETTINGS_VERS1;
+ settings.lockOnSleep = FALSE;
+ settings.useLockInterval = FALSE;
+ settings.lockInterval = INT_MAX;
+ if((err = SecKeychainSetSettings(keychain, &settings)))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: error setting keychain settings:\n" + errorToString(err));
+ }
+
+ return k.release();
}
//
// Imports a certificate private key and optionally add it to a keychain.
//
SecIdentityRef
-IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRef keychain, const string& password,
- const PasswordPromptPtr& prompt, int retryMax)
+loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRef keychain, const string& password,
+ const PasswordPromptPtr& prompt, int retryMax)
{
//
// Check if we already imported the certificate
@@ -902,17 +1026,139 @@ IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRe
}
return identity;
}
+#else
+
+CFArrayRef
+loadCerts(const string& file)
+{
+ UniqueRef<CFMutableArrayRef> certs(CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+ if(file.find(".pem") != string::npos)
+ {
+ vector<char> buffer;
+ readFile(file, buffer);
+ string strbuf(buffer.begin(), buffer.end());
+ string::size_type size, startpos, endpos = 0;
+ bool first = true;
+ while(true)
+ {
+ startpos = strbuf.find("-----BEGIN CERTIFICATE-----", endpos);
+ if(startpos != string::npos)
+ {
+ startpos += sizeof("-----BEGIN CERTIFICATE-----");
+ endpos = strbuf.find("-----END CERTIFICATE-----", startpos);
+ if(endpos == string::npos)
+ {
+ InitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: certificate " + file + " is not a valid PEM-encoded certificate";
+ throw ex;
+ }
+ size = endpos - startpos;
+ }
+ else if(first)
+ {
+ startpos = 0;
+ endpos = string::npos;
+ size = strbuf.size();
+ }
+ else
+ {
+ break;
+ }
+
+ vector<unsigned char> data(IceInternal::Base64::decode(string(&buffer[startpos], size)));
+ UniqueRef<CFDataRef> certdata(CFDataCreate(kCFAllocatorDefault, &data[0], data.size()));
+ SecCertificateRef cert = SecCertificateCreateWithData(0, certdata.get());
+ if(!cert)
+ {
+ InitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: certificate " + file + " is not a valid PEM-encoded certificate";
+ throw ex;
+ }
+ CFArrayAppendValue(certs.get(), cert);
+ CFRelease(cert);
+ first = false;
+ }
+ }
+ else
+ {
+ UniqueRef<CFDataRef> data(readCertFile(file));
+ SecCertificateRef cert = SecCertificateCreateWithData(0, data.get());
+ if(!cert)
+ {
+ InitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: certificate " + file + " is not a valid DER-encoded certificate";
+ throw ex;
+ }
+ CFArrayAppendValue(certs.get(), cert);
+ CFRelease(cert);
+ }
+ return certs.release();
+}
+#endif
+
+}
//
// Imports a certificate (it might contain an identity or certificate depending on the format).
//
CFArrayRef
-IceSSL::loadCertificateChain(const string& file, const string& keyFile, SecKeychainRef keychain,
- const string& password, const PasswordPromptPtr& prompt, int retryMax)
+IceSSL::loadCertificateChain(const string& file, const string& keyFile, const std::string& keychainPath,
+ const string& keychainPassword, const string& password, const PasswordPromptPtr& prompt,
+ int retryMax)
{
+#if defined(ICE_USE_SECURE_TRANSPORT_IOS)
+ UniqueRef<CFDataRef> cert(readCertFile(file));
+
+ UniqueRef<CFMutableDictionaryRef> settings(CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ CFArrayRef items = 0;
+ OSStatus err;
+ if(password.empty() && prompt)
+ {
+ int count = 0;
+ do
+ {
+ UniqueRef<CFStringRef> pass(toCFString(prompt->getPassword()));
+ CFDictionarySetValue(settings.get(), kSecImportExportPassphrase, pass.get());
+ err = SecPKCS12Import(cert.get(), settings.get(), &items);
+ ++count;
+ }
+ while(err == errSecAuthFailed && count < retryMax);
+ }
+ else
+ {
+ UniqueRef<CFStringRef> pass(toCFString(password));
+ CFDictionarySetValue(settings.get(), kSecImportExportPassphrase, pass.get());
+ err = SecPKCS12Import(cert.get(), settings.get(), &items);
+ }
+ if(err != noErr)
+ {
+ ostringstream os;
+ os << "IceSSL: unable to import certificate from file " << file << " (error = " << err << ")";
+ throw InitializationException(__FILE__, __LINE__, os.str());
+ }
+
+ UniqueRef<CFArrayRef> itemsHolder(items);;
+ for(int i = 0; i < CFArrayGetCount(items); ++i)
+ {
+ CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(items, i);
+ SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(dict, kSecImportItemIdentity);
+ if(identity)
+ {
+ CFArrayRef certs = (CFArrayRef)CFDictionaryGetValue(dict, kSecImportItemCertChain);
+ CFMutableArrayRef a = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certs);
+ CFArraySetValueAtIndex(a, 0, identity);
+ return a;
+ }
+ }
+ ostringstream os;
+ os << "IceSSL: couldn't find identity in file " << file;
+ throw InitializationException(__FILE__, __LINE__, os.str());
+#else
+ UniqueRef<SecKeychainRef> keychain(openKeychain(keychainPath, keychainPassword));
if(keyFile.empty())
{
- return loadKeychainItems(file, kSecItemTypeUnknown, keychain, password, prompt, retryMax);
+ return loadKeychainItems(file, kSecItemTypeUnknown, keychain.get(), password, prompt, retryMax);
}
else
{
@@ -934,26 +1180,37 @@ IceSSL::loadCertificateChain(const string& file, const string& keyFile, SecKeych
// add the certificate/key to the keychain if they aren't
// already present in the keychain.
//
- UniqueRef<SecIdentityRef> identity(loadPrivateKey(keyFile, cert, keychain, password, prompt, retryMax));
+ UniqueRef<SecIdentityRef> identity(loadPrivateKey(keyFile, cert, keychain.get(), password, prompt, retryMax));
CFMutableArrayRef a = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, items.get());
CFArraySetValueAtIndex(a, 0, identity.get());
return a;
}
+#endif
}
SecCertificateRef
IceSSL::loadCertificate(const string& file)
{
- CFArrayRef items = loadKeychainItems(file, kSecItemTypeCertificate, 0, "", 0, 0);
- SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(items, 0);
+#if defined(ICE_USE_SECURE_TRANSPORT_IOS)
+ UniqueRef<CFArrayRef> certs(loadCerts(file));
+ assert(CFArrayGetCount(certs.get()) > 0);
+ SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs.get(), 0);
CFRetain(cert);
- CFRelease(items);
return cert;
+#else
+ UniqueRef<CFArrayRef> items(loadKeychainItems(file, kSecItemTypeCertificate, 0, "", 0, 0));
+ SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(items.get(), 0);
+ CFRetain(cert);
+ return cert;
+#endif
}
CFArrayRef
IceSSL::loadCACertificates(const string& file)
{
+#if defined(ICE_USE_SECURE_TRANSPORT_IOS)
+ return loadCerts(file);
+#else
UniqueRef<CFArrayRef> items(loadKeychainItems(file, kSecItemTypeCertificate, 0, "", 0, 0));
CFMutableArrayRef certificateAuthorities = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
int count = CFArrayGetCount(items.get());
@@ -967,10 +1224,11 @@ IceSSL::loadCACertificates(const string& file)
}
}
return certificateAuthorities;
+#endif
}
-SecCertificateRef
-IceSSL::findCertificate(SecKeychainRef keychain, const string& value)
+CFArrayRef
+IceSSL::findCertificateChain(const std::string& keychainPath, const std::string& keychainPassword, const string& value)
{
//
// Search the keychain using key:value pairs. The following keys are supported:
@@ -987,17 +1245,20 @@ IceSSL::findCertificate(SecKeychainRef keychain, const string& value)
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
- const void* values[] = { keychain };
+#if defined(ICE_USE_SECURE_TRANSPORT_MACOS)
+ UniqueRef<SecKeychainRef> keychain(openKeychain(keychainPath, keychainPassword));
+ const void* values[] = { keychain.get() };
UniqueRef<CFArrayRef> searchList(CFArrayCreate(kCFAllocatorDefault, values, 1, &kCFTypeArrayCallBacks));
-
- CFDictionarySetValue(query.get(), kSecMatchLimit, kSecMatchLimitOne);
CFDictionarySetValue(query.get(), kSecMatchSearchList, searchList.get());
+#endif
+ CFDictionarySetValue(query.get(), kSecMatchLimit, kSecMatchLimitOne);
CFDictionarySetValue(query.get(), kSecClass, kSecClassCertificate);
CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue);
CFDictionarySetValue(query.get(), kSecMatchCaseInsensitive, kCFBooleanTrue);
size_t start = 0;
size_t pos;
+ bool valid = false;
while((pos = value.find(':', start)) != string::npos)
{
string field = IceUtilInternal::toUpper(IceUtilInternal::trim(value.substr(start, pos - start)));
@@ -1057,6 +1318,7 @@ IceSSL::findCertificate(SecKeychainRef keychain, const string& value)
{
UniqueRef<CFStringRef> v(toCFString(arg));
CFDictionarySetValue(query.get(), field == "LABEL" ? kSecAttrLabel : kSecMatchSubjectContains, v.get());
+ valid = true;
}
else if(field == "SUBJECTKEYID" || field == "SERIAL")
{
@@ -1068,10 +1330,11 @@ IceSSL::findCertificate(SecKeychainRef keychain, const string& value)
UniqueRef<CFDataRef> v(CFDataCreate(kCFAllocatorDefault, &buffer[0], buffer.size()));
CFDictionarySetValue(query.get(), field == "SUBJECTKEYID" ? kSecAttrSubjectKeyID : kSecAttrSerialNumber,
v.get());
+ valid = true;
}
}
- if(CFDictionaryGetCount(query.get()) == 5)
+ if(!valid)
{
throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: invalid value `" + value + "'");
}
@@ -1083,7 +1346,92 @@ IceSSL::findCertificate(SecKeychainRef keychain, const string& value)
throw PluginInitializationException(__FILE__, __LINE__,
"IceSSL: find certificate `" + value + "' failed:\n" + errorToString(err));
}
- return cert;
+
+ UniqueRef<SecCertificateRef> certHolder(cert);
+
+ //
+ // Retrieve the certificate chain
+ //
+ UniqueRef<SecPolicyRef> policy(SecPolicyCreateSSL(true, 0));
+ SecTrustRef trust = 0;
+ err = SecTrustCreateWithCertificates((CFArrayRef)cert, policy.get(), &trust);
+ if(err || !trust)
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: error creating trust object" +
+ (err ? ":\n" + errorToString(err) : ""));
+ }
+ UniqueRef<SecTrustRef> v(trust);
+
+ SecTrustResultType trustResult;
+ if((err = SecTrustEvaluate(trust, &trustResult)))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: error evaluating trust:\n" + errorToString(err));
+ }
+
+ int chainLength = SecTrustGetCertificateCount(trust);
+ CFMutableArrayRef items = CFArrayCreateMutable(kCFAllocatorDefault, chainLength, &kCFTypeArrayCallBacks);
+ UniqueRef<CFMutableArrayRef> itemsHolder(items);
+ for(int i = 0; i < chainLength; ++i)
+ {
+ CFArrayAppendValue(items, SecTrustGetCertificateAtIndex(trust, i));
+ }
+
+ //
+ // Replace the first certificate in the chain with the
+ // identity.
+ //
+ SecIdentityRef identity;
+#if defined(ICE_USE_SECURE_TRANSPORT_IOS)
+
+ //
+ // SecIdentityCreateWithCertificate isn't supported on iOS so we lookup the identity
+ // using the certicate label. If the user added the identity with SecItemAdd the
+ // identity has the same label as the certificate.
+ //
+ query.reset(CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ CFDictionarySetValue(query.get(), kSecClass, kSecClassCertificate);
+ CFDictionarySetValue(query.get(), kSecValueRef, cert);
+ CFDictionarySetValue(query.get(), kSecReturnAttributes, kCFBooleanTrue);
+ CFDictionaryRef attributes;
+ err = SecItemCopyMatching(query.get(), (CFTypeRef*)&attributes);
+ if(err != noErr)
+ {
+ ostringstream os;
+ os << "IceSSL: couldn't create identity for certificate found in the keychain:\n" << errorToString(err);
+ throw PluginInitializationException(__FILE__, __LINE__, os.str());
+ }
+ UniqueRef<CFDictionaryRef> attributesHolder(attributes);
+
+ // Now lookup the identity with the label
+ query.reset(CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ CFDictionarySetValue(query.get(), kSecMatchLimit, kSecMatchLimitOne);
+ CFDictionarySetValue(query.get(), kSecClass, kSecClassIdentity);
+ CFDictionarySetValue(query.get(), kSecAttrLabel, (CFDataRef)CFDictionaryGetValue(attributes, kSecAttrLabel));
+ CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue);
+ err = SecItemCopyMatching(query.get(), (CFTypeRef*)&identity);
+ if(err == noErr)
+ {
+ SecCertificateRef cert2 = NULL;
+ if((err = SecIdentityCopyCertificate(identity, &cert2)) == noErr)
+ {
+ err = CFEqual(cert2, cert) ? noErr : errSecItemNotFound;
+ CFRelease(cert2);
+ }
+ }
+#else
+ err = SecIdentityCreateWithCertificate(keychain.get(), cert, &identity);
+#endif
+ if(err != noErr)
+ {
+ ostringstream os;
+ os << "IceSSL: couldn't create identity for certificate found in the keychain:\n" << errorToString(err);
+ throw PluginInitializationException(__FILE__, __LINE__, os.str());
+ }
+ CFArraySetValueAtIndex(items, 0, identity);
+ CFRelease(identity);
+ return itemsHolder.release();
}
#elif defined(ICE_USE_SCHANNEL)
@@ -1125,7 +1473,7 @@ IceSSL::findCertificates(const string& location, const string& name, const strin
storeLoc = CERT_SYSTEM_STORE_LOCAL_MACHINE;
}
- HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, storeLoc, stringToWstring(name).c_str());
+ HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, storeLoc, Ice::stringToWstring(name).c_str());
if(!store)
{
throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: failed to open certificate store `" + name +
@@ -1226,13 +1574,13 @@ IceSSL::findCertificates(const string& location, const string& name, const strin
if(field == "SUBJECT" || field == "ISSUER")
{
- const wstring argW = stringToWstring(arg);
+ const wstring argW = Ice::stringToWstring(arg);
DWORD findType = field == "SUBJECT" ? CERT_FIND_SUBJECT_STR : CERT_FIND_ISSUER_STR;
addMatchingCertificates(store, tmpStore, findType, argW.c_str());
}
else if(field == "SUBJECTDN" || field == "ISSUERDN")
{
- const wstring argW = stringToWstring(arg);
+ const wstring argW = Ice::stringToWstring(arg);
DWORD flags[] = {
CERT_OID_NAME_STR,
CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
@@ -1344,11 +1692,365 @@ 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)
+{
+ 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()));
+ }
+ }
+ }
+
+ std::promise<IVectorView<Certificates::Certificate^>^> p;
+ 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
+ {
+ 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();
+}
#endif
+void
+IceSSL::readFile(const string& file, vector<char>& buffer)
+{
+ ifstream is(IceUtilInternal::streamFilename(file).c_str(), ios::in | ios::binary);
+ if(!is.good())
+ {
+ throw CertificateReadException(__FILE__, __LINE__, "error opening file " + file);
+ }
+
+ is.seekg(0, is.end);
+ buffer.resize(static_cast<int>(is.tellg()));
+ is.seekg(0, is.beg);
+
+ if(!buffer.empty())
+ {
+ is.read(&buffer[0], buffer.size());
+ if(!is.good())
+ {
+ throw CertificateReadException(__FILE__, __LINE__, "error reading file " + file);
+ }
+ }
+}
+
bool
IceSSL::checkPath(const string& path, const string& defaultDir, bool dir, string& resolved)
{
+#if defined(ICE_USE_SECURE_TRANSPORT_IOS)
+ CFURLRef url = 0;
+ CFBundleRef bundle = CFBundleGetMainBundle();
+ if(bundle)
+ {
+ CFStringRef resourceName = toCFString(path);
+ CFStringRef subDirName = toCFString(defaultDir);
+ url = CFBundleCopyResourceURL(bundle, resourceName, 0, subDirName);
+ CFRelease(resourceName);
+ CFRelease(subDirName);
+
+ UInt8 filePath[PATH_MAX];
+ if(CFURLGetFileSystemRepresentation(url, true, filePath, sizeof(filePath)))
+ {
+ string tmp = string(reinterpret_cast<char*>(filePath));
+ if((dir && IceUtilInternal::directoryExists(tmp)) || (!dir && IceUtilInternal::fileExists(tmp)))
+ {
+ resolved = tmp;
+ return true;
+ }
+ }
+ }
+#endif
if(IceUtilInternal::isAbsolutePath(path))
{
if((dir && IceUtilInternal::directoryExists(path)) || (!dir && IceUtilInternal::fileExists(path)))