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.cpp837
1 files changed, 711 insertions, 126 deletions
diff --git a/cpp/src/IceSSL/Util.cpp b/cpp/src/IceSSL/Util.cpp
index c61bafbac3e..52cd3c5144c 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_UWP)
# include <winsock2.h>
#endif
@@ -16,9 +16,12 @@
#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_USE_OPENSSL
# include <openssl/err.h>
@@ -30,33 +33,48 @@
using namespace std;
using namespace Ice;
+using namespace IceInternal;
using namespace IceUtil;
using namespace IceSSL;
-void
-IceSSL::readFile(const string& file, vector<char>& buffer)
+#ifdef ICE_OS_UWP
+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
{
@@ -539,7 +557,7 @@ IceSSL::getSslErrors(bool verbose)
else
{
const char* reason = ERR_reason_error_string(err);
- ostr << (reason == NULL ? "unknown reason" : reason);
+ ostr << (reason == ICE_NULLPTR ? "unknown reason" : reason);
if(flags & ERR_TXT_STRING)
{
ostr << ": " << data;
@@ -562,9 +580,8 @@ IceSSL::errorToString(CFErrorRef err)
ostringstream os;
if(err)
{
- CFStringRef s = CFErrorCopyDescription(err);
- os << "(error: " << CFErrorGetCode(err) << " description: " << fromCFString(s) << ")";
- CFRelease(s);
+ UniqueRef<CFStringRef> s(CFErrorCopyDescription(err));
+ os << "(error: " << CFErrorGetCode(err) << " description: " << fromCFString(s.get()) << ")";
}
return os.str();
}
@@ -574,12 +591,13 @@ IceSSL::errorToString(OSStatus status)
{
ostringstream os;
os << "(error: " << status;
- CFStringRef s = SecCopyErrorMessageString(status, 0);
+#if defined(ICE_USE_SECURE_TRANSPORT_MACOS)
+ UniqueRef<CFStringRef> s(SecCopyErrorMessageString(status, 0));
if(s)
{
- os << " description: " << fromCFString(s);
- CFRelease(s);
+ os << " description: " << fromCFString(s.get());
}
+#endif
os << ")";
return os.str();
}
@@ -599,13 +617,14 @@ IceSSL::fromCFString(CFStringRef v)
return s;
}
+#if defined(ICE_USE_SECURE_TRANSPORT_MACOS)
CFDictionaryRef
IceSSL::getCertificateProperty(SecCertificateRef cert, CFTypeRef key)
{
- CFArrayRef keys = CFArrayCreate(NULL, &key , 1, &kCFTypeArrayCallBacks);
- CFErrorRef err = 0;
- CFDictionaryRef values = SecCertificateCopyValues(cert, keys, &err);
- CFRelease(keys);
+ UniqueRef<CFDictionaryRef> property;
+ UniqueRef<CFArrayRef> keys(CFArrayCreate(ICE_NULLPTR, &key , 1, &kCFTypeArrayCallBacks));
+ UniqueRef<CFErrorRef> err;
+ UniqueRef<CFDictionaryRef> values(SecCertificateCopyValues(cert, keys.get(), &err.get()));
if(err)
{
ostringstream os;
@@ -614,18 +633,38 @@ IceSSL::getCertificateProperty(SecCertificateRef cert, CFTypeRef key)
}
assert(values);
- CFDictionaryRef property = (CFDictionaryRef)CFDictionaryGetValue(values, key);
- if(property)
- {
- CFRetain(property);
- }
- CFRelease(values);
- return property;
+ property.retain(static_cast<CFDictionaryRef>(CFDictionaryGetValue(values.get(), key)));
+ return property.release();
}
+#endif
namespace
{
+CFMutableDataRef
+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);
+
+ UniqueRef<CFMutableDataRef> data(CFDataCreateMutable(kCFAllocatorDefault, size));
+ CFDataSetLength(data.get(), size);
+ is.read(reinterpret_cast<char*>(CFDataGetMutableBytePtr(data.get())), size);
+ if(!is.good())
+ {
+ throw CertificateReadException(__FILE__, __LINE__, "error reading file " + file);
+ }
+ return data.release();
+}
+
+#if defined(ICE_USE_SECURE_TRANSPORT_MACOS)
//
// Check the certificate basic constraints to check if the certificate is marked as a CA.
//
@@ -635,14 +674,14 @@ isCA(SecCertificateRef cert)
UniqueRef<CFDictionaryRef> property(getCertificateProperty(cert, kSecOIDBasicConstraints));
if(property)
{
- CFArrayRef propertyValues = (CFArrayRef)CFDictionaryGetValue(property.get(), kSecPropertyKeyValue);
+ CFArrayRef propertyValues = static_cast<CFArrayRef>(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue));
for(int i = 0, size = CFArrayGetCount(propertyValues); i < size; ++i)
{
- CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(propertyValues, i);
- CFStringRef label = (CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyLabel);
+ CFDictionaryRef dict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(propertyValues, i));
+ CFStringRef label = static_cast<CFStringRef>(CFDictionaryGetValue(dict, kSecPropertyKeyLabel));
if(CFEqual(label, CFSTR("Certificate Authority")))
{
- return CFEqual((CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyValue), CFSTR("Yes"));
+ return CFEqual(static_cast<CFStringRef>(CFDictionaryGetValue(dict, kSecPropertyKeyValue)), CFSTR("Yes"));
}
}
}
@@ -657,12 +696,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<CFMutableDataRef> data(readCertFile(file));
SecItemImportExportKeyParameters params;
memset(&params, 0, sizeof(params));
@@ -673,17 +707,19 @@ loadKeychainItems(const string& file, SecExternalItemType type, SecKeychainRef k
params.passphrase = toCFString(passphrase);
}
- CFArrayRef items;
+ UniqueRef<CFArrayRef> items;
SecExternalItemType importType = type;
SecExternalFormat format = type == kSecItemTypeUnknown ? kSecFormatPKCS12 : kSecFormatUnknown;
UniqueRef<CFStringRef> path(toCFString(file));
- OSStatus err = SecItemImport(data.get(), path.get(), &format, &importType, 0, &params, keychain, &items);
+ OSStatus err = SecItemImport(data.get(), path.get(), &format, &importType, 0, &params, keychain, &items.get());
//
// If passphrase failure and no password was configured, we obtain
// the password from the given prompt or configure the import to
// prompt the user with an alert dialog.
//
+ UniqueRef<CFStringRef> passphraseHolder;
+ UniqueRef<CFStringRef> alertPromptHolder;
if(passphrase.empty() &&
(err == errSecPassphraseRequired || err == errSecInvalidData || err == errSecPkcs12VerifyFailure))
{
@@ -692,7 +728,8 @@ loadKeychainItems(const string& file, SecExternalItemType type, SecKeychainRef k
params.flags |= kSecKeySecurePassphrase;
ostringstream os;
os << "Enter the password for\n" << file;
- params.alertPrompt = toCFString(os.str());
+ alertPromptHolder.reset(toCFString(os.str()));
+ params.alertPrompt = alertPromptHolder.get();
}
int count = 0;
@@ -701,25 +738,12 @@ loadKeychainItems(const string& file, SecExternalItemType type, SecKeychainRef k
{
if(prompt)
{
- if(params.passphrase)
- {
- CFRelease(params.passphrase);
- }
- params.passphrase = toCFString(prompt->getPassword());
+ passphraseHolder.reset(toCFString(prompt->getPassword()));
+ params.passphrase = passphraseHolder.get();
}
- err = SecItemImport(data.get(), path.get(), &format, &importType, 0, &params, keychain, &items);
+ err = SecItemImport(data.get(), path.get(), &format, &importType, 0, &params, keychain, &items.get());
++count;
}
-
- if(params.alertPrompt)
- {
- CFRelease(params.alertPrompt);
- }
- }
-
- if(params.passphrase)
- {
- CFRelease(params.passphrase);
}
if(err != noErr)
@@ -732,24 +756,100 @@ loadKeychainItems(const string& file, SecExternalItemType type, SecKeychainRef k
if(type != kSecItemTypeUnknown && importType != kSecItemTypeAggregate && importType != type)
{
- CFRelease(items);
ostringstream os;
os << "IceSSL: error reading " << (type == kSecItemTypePrivateKey ? "private key" : "certificate");
os << " `" << file << "' doesn't contain the expected item";
throw CertificateReadException(__FILE__, __LINE__, os.str());
}
- return items;
+ return items.release();
}
+SecKeychainRef
+openKeychain(const std::string& path, const std::string& keychainPassword)
+{
+ string keychainPath = path;
+ UniqueRef<SecKeychainRef> keychain;
+ OSStatus err = 0;
+ if(keychainPath.empty())
+ {
+ if((err = SecKeychainCopyDefault(&keychain.get())))
+ {
+ 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.get())))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: unable to open keychain: `" +
+ keychainPath + "'\n" + errorToString(err));
+ }
+ }
+
+ SecKeychainStatus status;
+ err = SecKeychainGetStatus(keychain.get(), &status);
+ if(err == noErr)
+ {
+ const char* pass = keychainPassword.empty() ? 0 : keychainPassword.c_str();
+ if((err = SecKeychainUnlock(keychain.get(), 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();
+ keychain.reset(0);
+ if((err = SecKeychainCreate(keychainPath.c_str(), keychainPassword.size(), pass, pass == 0, 0, &keychain.get())))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: unable to create keychain:\n" + errorToString(err));
+ }
+ }
+ 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.get(), &settings)))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: error setting keychain settings:\n" + errorToString(err));
+ }
+
+ return keychain.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
@@ -758,13 +858,14 @@ IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRe
UniqueRef<CFDictionaryRef> subjectKeyProperty(getCertificateProperty(cert, kSecOIDSubjectKeyIdentifier));
if(subjectKeyProperty)
{
- CFArrayRef values = (CFArrayRef)CFDictionaryGetValue(subjectKeyProperty.get(), kSecPropertyKeyValue);
+ CFArrayRef values = static_cast<CFArrayRef>(CFDictionaryGetValue(subjectKeyProperty.get(),
+ kSecPropertyKeyValue));
for(int i = 0; i < CFArrayGetCount(values); ++i)
{
- CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(values, i);
+ CFDictionaryRef dict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(values, i));
if(CFEqual(CFDictionaryGetValue(dict, kSecPropertyKeyLabel), CFSTR("Key Identifier")))
{
- hash.retain(CFDictionaryGetValue(dict, kSecPropertyKeyValue));
+ hash.retain(static_cast<CFDataRef>(CFDictionaryGetValue(dict, kSecPropertyKeyValue)));
break;
}
}
@@ -784,24 +885,24 @@ IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRe
CFDictionarySetValue(query.get(), kSecAttrSubjectKeyID, hash.get());
CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue);
- CFTypeRef value = 0;
- OSStatus err = SecItemCopyMatching(query.get(), &value);
- UniqueRef<SecCertificateRef> item(value);
+ UniqueRef<CFTypeRef> value(0);
+ OSStatus err = SecItemCopyMatching(query.get(), &value.get());
+ UniqueRef<SecCertificateRef> item(static_cast<SecCertificateRef>(const_cast<void*>(value.release())));
if(err == noErr)
{
//
// If the certificate has already been imported, create the
// identity. The key should also have been imported.
//
- SecIdentityRef identity;
- err = SecIdentityCreateWithCertificate(keychain, item.get(), &identity);
+ UniqueRef<SecIdentityRef> identity;
+ err = SecIdentityCreateWithCertificate(keychain, item.get(), &identity.get());
if(err != noErr)
{
ostringstream os;
os << "IceSSL: error creating certificate identity:\n" << errorToString(err);
throw CertificateReadException(__FILE__, __LINE__, os.str());
}
- return identity;
+ return identity.release();
}
else if(err != errSecItemNotFound)
{
@@ -819,10 +920,11 @@ IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRe
UniqueRef<SecKeyRef> key;
for(int i = 0; i < count; ++i)
{
- SecKeychainItemRef item = (SecKeychainItemRef)CFArrayGetValueAtIndex(items.get(), 0);
+ SecKeychainItemRef item =
+ static_cast<SecKeychainItemRef>(const_cast<void*>(CFArrayGetValueAtIndex(items.get(), 0)));
if(SecKeyGetTypeID() == CFGetTypeID(item))
{
- key.retain(item);
+ key.retain(reinterpret_cast<SecKeyRef>(item));
break;
}
}
@@ -844,16 +946,16 @@ IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRe
CFDictionarySetValue(query.get(), kSecValueRef, cert);
CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue);
- value = 0;
- err = SecItemAdd(query.get(), (CFTypeRef*)&value);
- UniqueRef<CFArrayRef> added(value);
+ value.reset(0);
+ err = SecItemAdd(query.get(), static_cast<CFTypeRef*>(&value.get()));
+ UniqueRef<CFArrayRef> added(static_cast<CFArrayRef>(value.release()));
if(err != noErr)
{
ostringstream os;
os << "IceSSL: failure adding certificate to keychain\n" << errorToString(err);
throw CertificateReadException(__FILE__, __LINE__, os.str());
}
- item.retain(CFArrayGetValueAtIndex(added.get(), 0));
+ item.retain(static_cast<SecCertificateRef>(const_cast<void*>(CFArrayGetValueAtIndex(added.get(), 0))));
//
// Create the association between the private key and the certificate,
@@ -864,7 +966,7 @@ IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRe
{
SecKeychainAttribute attr;
attr.tag = kSecKeyLabel;
- attr.data = (void*)CFDataGetBytePtr(hash.get());
+ attr.data = const_cast<UInt8*>(CFDataGetBytePtr(hash.get()));
attr.length = CFDataGetLength(hash.get());
attributes.push_back(attr);
}
@@ -874,15 +976,13 @@ IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRe
// name.
//
string label;
- CFStringRef commonName = 0;
- if(SecCertificateCopyCommonName(item.get(), &commonName) == noErr)
+ UniqueRef<CFStringRef> commonName(0);
+ if(SecCertificateCopyCommonName(item.get(), &commonName.get()) == noErr)
{
- label = fromCFString(commonName);
- CFRelease(commonName);
-
+ label = fromCFString(commonName.get());
SecKeychainAttribute attr;
attr.tag = kSecKeyPrintName;
- attr.data = (void*)label.c_str();
+ attr.data = const_cast<char*>(label.c_str());
attr.length = label.size();
attributes.push_back(attr);
}
@@ -890,29 +990,147 @@ IceSSL::loadPrivateKey(const string& file, SecCertificateRef cert, SecKeychainRe
SecKeychainAttributeList attrs;
attrs.attr = &attributes[0];
attrs.count = attributes.size();
- SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)key.get(), &attrs, 0, 0);
+ SecKeychainItemModifyAttributesAndData(reinterpret_cast<SecKeychainItemRef>(key.get()), &attrs, 0, 0);
- SecIdentityRef identity;
- err = SecIdentityCreateWithCertificate(keychain, item.get(), &identity);
+ UniqueRef<SecIdentityRef> identity;
+ err = SecIdentityCreateWithCertificate(keychain, item.get(), &identity.get());
if(err != noErr)
{
ostringstream os;
os << "IceSSL: error creating certificate identity:\n" << errorToString(err);
throw CertificateReadException(__FILE__, __LINE__, os.str());
}
- return identity;
+ return identity.release();
+}
+#else
+
+CFArrayRef
+loadCerts(const string& file)
+{
+ UniqueRef<CFArrayRef> 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()));
+ UniqueRef<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(const_cast<CFMutableArrayRef>(certs.get()), cert.get());
+ first = false;
+ }
+ }
+ else
+ {
+ UniqueRef<CFDataRef> data(readCertFile(file));
+ UniqueRef<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(const_cast<CFMutableArrayRef>(certs.get()), cert.get());
+ }
+ 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)
{
+ UniqueRef<CFArrayRef> chain;
+#if defined(ICE_USE_SECURE_TRANSPORT_IOS)
+ UniqueRef<CFDataRef> cert(readCertFile(file));
+
+ UniqueRef<CFMutableDictionaryRef> settings(CFDictionaryCreateMutable(0,
+ 1,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ UniqueRef<CFArrayRef> items;
+ OSStatus err;
+ int count = 0;
+ do
+ {
+ UniqueRef<CFStringRef> pass(toCFString(password.empty() && prompt ? prompt->getPassword() : password));
+ CFDictionarySetValue(settings.get(), kSecImportExportPassphrase, pass.get());
+ err = SecPKCS12Import(cert.get(), settings.get(), &items.get());
+ ++count;
+ }
+ while(password.empty() && prompt && err == errSecAuthFailed && count < retryMax);
+
+ if(err != noErr)
+ {
+ ostringstream os;
+ os << "IceSSL: unable to import certificate from file " << file << " (error = " << err << ")";
+ throw InitializationException(__FILE__, __LINE__, os.str());
+ }
+
+ for(int i = 0; i < CFArrayGetCount(items.get()); ++i)
+ {
+ CFDictionaryRef dict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(items.get(), i));
+ SecIdentityRef identity = static_cast<SecIdentityRef>(
+ const_cast<void*>(CFDictionaryGetValue(dict, kSecImportItemIdentity)));
+ if(identity)
+ {
+ CFArrayRef certs = static_cast<CFArrayRef>(CFDictionaryGetValue(dict, kSecImportItemCertChain));
+ chain.reset(CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certs));
+ CFArraySetValueAtIndex(const_cast<CFMutableArrayRef>(chain.get()), 0, identity);
+ }
+ }
+
+ if(!chain)
+ {
+ 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);
+ chain.reset(loadKeychainItems(file, kSecItemTypeUnknown, keychain.get(), password, prompt, retryMax));
}
else
{
@@ -921,7 +1139,8 @@ IceSSL::loadCertificateChain(const string& file, const string& keyFile, SecKeych
// might already have been imported.
//
UniqueRef<CFArrayRef> items(loadKeychainItems(file, kSecItemTypeCertificate, 0, password, prompt, retryMax));
- SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(items.get(), 0);
+ SecCertificateRef cert =
+ static_cast<SecCertificateRef>(const_cast<void*>(CFArrayGetValueAtIndex(items.get(), 0)));
if(SecCertificateGetTypeID() != CFGetTypeID(cert))
{
ostringstream os;
@@ -934,43 +1153,54 @@ 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));
- CFMutableArrayRef a = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, items.get());
- CFArraySetValueAtIndex(a, 0, identity.get());
- return a;
+ UniqueRef<SecIdentityRef> identity(loadPrivateKey(keyFile, cert, keychain.get(), password, prompt, retryMax));
+ chain.reset(CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, items.get()));
+ CFArraySetValueAtIndex(const_cast<CFMutableArrayRef>(chain.get()), 0, identity.get());
}
+#endif
+ return chain.release();
}
SecCertificateRef
IceSSL::loadCertificate(const string& file)
{
- CFArrayRef items = loadKeychainItems(file, kSecItemTypeCertificate, 0, "", 0, 0);
- SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(items, 0);
- CFRetain(cert);
- CFRelease(items);
- return cert;
+ UniqueRef<SecCertificateRef> cert;
+#if defined(ICE_USE_SECURE_TRANSPORT_IOS)
+ UniqueRef<CFArrayRef> certs(loadCerts(file));
+ assert(CFArrayGetCount(certs.get()) > 0);
+ cert.retain((SecCertificateRef)CFArrayGetValueAtIndex(certs.get(), 0));
+#else
+ UniqueRef<CFArrayRef> items(loadKeychainItems(file, kSecItemTypeCertificate, 0, "", 0, 0));
+ cert.retain((SecCertificateRef)CFArrayGetValueAtIndex(items.get(), 0));
+#endif
+ return cert.release();
}
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);
+ UniqueRef<CFArrayRef> certificateAuthorities(CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
int count = CFArrayGetCount(items.get());
for(CFIndex i = 0; i < count; ++i)
{
- SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(items.get(), i);
+ SecCertificateRef cert =
+ static_cast<SecCertificateRef>(const_cast<void*>(CFArrayGetValueAtIndex(items.get(), i)));
assert(SecCertificateGetTypeID() == CFGetTypeID(cert));
if(isCA(cert))
{
- CFArrayAppendValue(certificateAuthorities, cert);
+ CFArrayAppendValue(const_cast<CFMutableArrayRef>(certificateAuthorities.get()), cert);
}
}
- return certificateAuthorities;
+ return certificateAuthorities.release();
+#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 +1217,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 +1290,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,22 +1302,101 @@ 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 + "'");
}
- SecCertificateRef cert = 0;
- OSStatus err = SecItemCopyMatching(query.get(), (CFTypeRef*)&cert);
+ UniqueRef<SecCertificateRef> cert;
+ OSStatus err = SecItemCopyMatching(query.get(), (CFTypeRef*)&cert.get());
if(err != noErr)
{
throw PluginInitializationException(__FILE__, __LINE__,
"IceSSL: find certificate `" + value + "' failed:\n" + errorToString(err));
}
- return cert;
+
+ //
+ // Retrieve the certificate chain
+ //
+ UniqueRef<SecPolicyRef> policy(SecPolicyCreateSSL(true, 0));
+ UniqueRef<SecTrustRef> trust;
+ err = SecTrustCreateWithCertificates(reinterpret_cast<CFArrayRef>(cert.get()), policy.get(), &trust.get());
+ if(err || !trust)
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: error creating trust object" +
+ (err ? ":\n" + errorToString(err) : ""));
+ }
+
+ SecTrustResultType trustResult;
+ if((err = SecTrustEvaluate(trust.get(), &trustResult)))
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: error evaluating trust:\n" + errorToString(err));
+ }
+
+ int chainLength = SecTrustGetCertificateCount(trust.get());
+ UniqueRef<CFArrayRef> items(CFArrayCreateMutable(kCFAllocatorDefault, chainLength, &kCFTypeArrayCallBacks));
+ for(int i = 0; i < chainLength; ++i)
+ {
+ CFArrayAppendValue(const_cast<CFMutableArrayRef>(items.get()), SecTrustGetCertificateAtIndex(trust.get(), i));
+ }
+
+ //
+ // Replace the first certificate in the chain with the
+ // identity.
+ //
+ UniqueRef<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.get());
+ CFDictionarySetValue(query.get(), kSecReturnAttributes, kCFBooleanTrue);
+ UniqueRef<CFDictionaryRef> attributes;
+ err = SecItemCopyMatching(query.get(), reinterpret_cast<CFTypeRef*>(&attributes.get()));
+ 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());
+ }
+
+ // 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.get(), kSecAttrLabel));
+ CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue);
+ err = SecItemCopyMatching(query.get(), (CFTypeRef*)&identity.get());
+ if(err == noErr)
+ {
+ UniqueRef<SecCertificateRef> cert2;
+ if((err = SecIdentityCopyCertificate(identity.get(), &cert2.get())) == noErr)
+ {
+ err = CFEqual(cert2.get(), cert.get()) ? noErr : errSecItemNotFound;
+ }
+ }
+#else
+ err = SecIdentityCreateWithCertificate(keychain.get(), cert.get(), &identity.get());
+#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(const_cast<CFMutableArrayRef>(items.get()), 0, identity.get());
+ return items.release();
}
#elif defined(ICE_USE_SCHANNEL)
@@ -1125,7 +1438,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 +1539,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 +1657,283 @@ IceSSL::findCertificates(const string& location, const string& name, const strin
}
return certs;
}
+#elif defined (ICE_OS_UWP)
+
+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^
+IceSSL::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^>^
+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()));
+ }
+ }
+ }
+
+ try
+ {
+ return IceInternal::runSync(CertificateStores::FindAllAsync(query));
+ }
+ catch(Platform::Exception^ ex)
+ {
+ throw PluginInitializationException(__FILE__, __LINE__,
+ "IceSSL: certificate error:\n" + wstringToString(ex->Message->Data()));
+ }
+}
#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)
+ CFBundleRef bundle = CFBundleGetMainBundle();
+ if(bundle)
+ {
+ UniqueRef<CFStringRef> resourceName(toCFString(path));
+ UniqueRef<CFStringRef> subDirName(toCFString(defaultDir));
+ UniqueRef<CFURLRef> url(CFBundleCopyResourceURL(bundle, resourceName.get(), 0, subDirName.get()));
+
+ UInt8 filePath[PATH_MAX];
+ if(CFURLGetFileSystemRepresentation(url.get(), 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)))