Added support for loading certificates and private key pairs from PKCS #12 files, as well as loading certificates (without private key) from PEM or DER files. Some code restructuring and cleanup.

This commit is contained in:
Günter Obiltschnig
2014-10-07 23:16:58 +02:00
parent 75919178fb
commit 8bf66bb3f9
53 changed files with 1809 additions and 402 deletions

View File

@@ -1,7 +1,7 @@
//
// AcceptCertificateHandler.cpp
//
// $Id: //poco/1.4/NetSSL_Win/src/AcceptCertificateHandler.cpp#1 $
// $Id$
//
// Library: NetSSL_Win
// Package: SSLCore

View File

@@ -1,9 +1,9 @@
//
// Context.cpp
//
// $Id: //poco/1.4/NetSSL_Schannel/src/Context.cpp#1 $
// $Id$
//
// Library: NetSSL_Schannel
// Library: NetSSL_Win
// Package: SSLCore
// Module: Context
//
@@ -17,7 +17,16 @@
#include "Poco/Net/Context.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/SSLException.h"
#include "Poco/Net/Utility.h"
#include "Poco/UnicodeConverter.h"
#include "Poco/Format.h"
#include "Poco/File.h"
#include "Poco/FileStream.h"
#include "Poco/MemoryStream.h"
#include "Poco/Base64Decoder.h"
#include "Poco/Buffer.h"
#include <algorithm>
#include <cctype>
namespace Poco {
@@ -32,20 +41,54 @@ const std::string Context::CERT_STORE_USERDS("USERDS");
Context::Context(Usage usage,
const std::string& certName,
const std::string& certNameOrPath,
VerificationMode verMode,
int options,
const std::string& certStore):
_usage(usage),
_mode(verMode),
_options(options),
_certificateName(certName),
_certificateStoreName(certStore),
_certNameOrPath(certNameOrPath),
_certStoreName(certStore),
_hMemCertStore(0),
_hCollectionCertStore(0),
_hRootCertStore(0),
_mutex()
_hCertStore(0),
_pCert(0),
_securityFunctions(SSLManager::instance().securityFunctions())
{
init();
}
Context::~Context()
{
if (_pCert)
{
CertFreeCertificateContext(_pCert);
}
if (_hCertStore)
{
CertCloseStore(_hCertStore, 0);
}
CertCloseStore(_hCollectionCertStore, 0);
CertCloseStore(_hMemCertStore, 0);
if (_hRootCertStore)
{
CertCloseStore(_hRootCertStore, 0);
}
if (_hCreds.dwLower != 0 && _hCreds.dwUpper != 0)
{
_securityFunctions.FreeCredentialsHandle(&_hCreds);
}
}
void Context::init()
{
_hCreds.dwLower = 0;
_hCreds.dwUpper = 0;
_hMemCertStore = CertOpenStore(
CERT_STORE_PROV_MEMORY, // The memory provider type
0, // The encoding type is not needed
@@ -83,22 +126,229 @@ Context::Context(Usage usage,
}
Context::~Context()
void Context::addTrustedCert(const Poco::Net::X509Certificate& cert)
{
CertCloseStore(_hCollectionCertStore, 0);
CertCloseStore(_hMemCertStore, 0);
if (_hRootCertStore)
Poco::FastMutex::ScopedLock lock(_mutex);
if (!CertAddCertificateContextToStore(_hMemCertStore, cert.system(), CERT_STORE_ADD_REPLACE_EXISTING, 0))
throw CertificateException("Failed to add certificate to store", GetLastError());
}
Poco::Net::X509Certificate Context::certificate()
{
if (_pCert)
return Poco::Net::X509Certificate(_pCert, true);
if (_certNameOrPath.empty())
throw NoCertificateException("Certificate requested, but no certificate name or path provided");
if (_options & OPT_LOAD_CERT_FROM_FILE)
{
CertCloseStore(_hRootCertStore, 0);
importCertificate();
}
else
{
loadCertificate();
}
return Poco::Net::X509Certificate(_pCert, true);
}
void Context::loadCertificate()
{
std::wstring wcertStore;
Poco::UnicodeConverter::convert(_certStoreName, wcertStore);
if (!_hCertStore)
{
if (_options & OPT_USE_MACHINE_STORE)
_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, _certStoreName.c_str());
else
_hCertStore = CertOpenSystemStoreW(0, wcertStore.c_str());
}
if (!_hCertStore) throw CertificateException("Failed to open certificate store", _certStoreName, GetLastError());
CERT_RDN_ATTR cert_rdn_attr;
cert_rdn_attr.pszObjId = szOID_COMMON_NAME;
cert_rdn_attr.dwValueType = CERT_RDN_ANY_TYPE;
cert_rdn_attr.Value.cbData = (DWORD) _certNameOrPath.size();
cert_rdn_attr.Value.pbData = (BYTE *) _certNameOrPath.c_str();
CERT_RDN cert_rdn;
cert_rdn.cRDNAttr = 1;
cert_rdn.rgRDNAttr = &cert_rdn_attr;
_pCert = CertFindCertificateInStore(_hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_ATTR, &cert_rdn, NULL);
if (!_pCert) throw NoCertificateException(Poco::format("Failed to find certificate %s in store %s", _certNameOrPath, _certStoreName));
}
void Context::importCertificate()
{
Poco::File certFile(_certNameOrPath);
if (!certFile.exists()) throw Poco::FileNotFoundException(_certNameOrPath);
Poco::File::FileSize size = certFile.getSize();
if (size > 4096) throw Poco::DataFormatException("PKCS #12 certificate file too large", _certNameOrPath);
Poco::Buffer<char> buffer(static_cast<std::size_t>(size));
Poco::FileInputStream istr(_certNameOrPath);
istr.read(buffer.begin(), buffer.size());
if (istr.gcount() != size) throw Poco::IOException("error reading PKCS #12 certificate file");
importCertificate(buffer.begin(), buffer.size());
}
void Context::importCertificate(const char* pBuffer, std::size_t size)
{
std::string password;
SSLManager::instance().PrivateKeyPassphraseRequired.notify(&SSLManager::instance(), password);
std::wstring wpassword;
Poco::UnicodeConverter::toUTF16(password, wpassword);
// clear UTF-8 password
std::fill(const_cast<char*>(password.data()), const_cast<char*>(password.data() + password.size()), 'X');
CRYPT_DATA_BLOB blob;
blob.cbData = static_cast<DWORD>(size);
blob.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(pBuffer));
HCERTSTORE hTempStore = PFXImportCertStore(&blob, wpassword.data(), PKCS12_ALLOW_OVERWRITE_KEY | PKCS12_INCLUDE_EXTENDED_PROPERTIES);
// clear UTF-16 password
std::fill(const_cast<wchar_t*>(wpassword.data()), const_cast<wchar_t*>(wpassword.data() + password.size()), L'X');
if (hTempStore)
{
PCCERT_CONTEXT pCert = 0;
pCert = CertEnumCertificatesInStore(hTempStore, pCert);
while (pCert)
{
PCCERT_CONTEXT pStoreCert = 0;
BOOL res = CertAddCertificateContextToStore(_hMemCertStore, pCert, CERT_STORE_ADD_REPLACE_EXISTING, &pStoreCert);
if (res)
{
if (!_pCert)
{
_pCert = pStoreCert;
}
else
{
CertFreeCertificateContext(pStoreCert);
pStoreCert = 0;
}
}
pCert = CertEnumCertificatesInStore(hTempStore, pCert);
}
CertCloseStore(hTempStore, 0);
}
else throw CertificateException("failed to import certificate", GetLastError());
}
CredHandle& Context::credentials()
{
Poco::FastMutex::ScopedLock lock(_mutex);
if (_hCreds.dwLower == 0 && _hCreds.dwUpper == 0)
{
acquireSchannelCredentials(_hCreds);
}
return _hCreds;
}
void Context::acquireSchannelCredentials(CredHandle& credHandle) const
{
SCHANNEL_CRED schannelCred;
ZeroMemory(&schannelCred, sizeof(schannelCred));
schannelCred.dwVersion = SCHANNEL_CRED_VERSION;
if (_pCert)
{
schannelCred.cCreds = 1; // how many cred are stored in &pCertContext
schannelCred.paCred = &const_cast<PCCERT_CONTEXT>(_pCert);
}
schannelCred.grbitEnabledProtocols = proto();
// Windows NT and Windows Me/98/95: revocation checking not supported via flags
if (_options & Context::OPT_PERFORM_REVOCATION_CHECK)
schannelCred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
else
schannelCred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE;
if (isForServerUse())
{
if (_mode == Context::VERIFY_STRICT)
schannelCred.dwFlags |= SCH_CRED_NO_SYSTEM_MAPPER;
if (_mode == Context::VERIFY_NONE)
schannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
}
else
{
if (_mode == Context::VERIFY_STRICT)
schannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
else
schannelCred.dwFlags |= SCH_CRED_USE_DEFAULT_CREDS;
if (_mode == Context::VERIFY_NONE)
schannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_SERVERNAME_CHECK;
}
#if defined(SCH_USE_STRONG_CRYPTO)
if (_options & Context::OPT_USE_STRONG_CRYPTO)
schannelCred.dwFlags |= SCH_USE_STRONG_CRYPTO;
#endif
schannelCred.hRootStore = _hCollectionCertStore;
TimeStamp tsExpiry;
tsExpiry.LowPart = tsExpiry.HighPart = 0;
SECURITY_STATUS status = _securityFunctions.AcquireCredentialsHandleW(
NULL,
UNISP_NAME_W,
isForServerUse() ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
NULL,
&schannelCred,
NULL,
NULL,
&credHandle,
&tsExpiry);
if (status != SEC_E_OK)
{
throw SSLException("Failed to acquire Schannel credentials", Utility::formatError(status));
}
}
void Context::addTrustedCert(const Poco::Net::X509Certificate& cert)
DWORD Context::proto() const
{
Poco::FastMutex::ScopedLock lock(_mutex);
CertAddCertificateContextToStore(_hMemCertStore, cert.system(), CERT_STORE_ADD_REPLACE_EXISTING, 0);
switch (_usage)
{
case Context::CLIENT_USE:
return SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_CLIENT;
case Context::SERVER_USE:
return SP_PROT_SSL3_SERVER | SP_PROT_TLS1_SERVER;
case Context::TLSV1_CLIENT_USE:
return SP_PROT_TLS1_CLIENT;
case Context::TLSV1_SERVER_USE:
return SP_PROT_TLS1_SERVER;
#if defined(SP_PROT_TLS1_1)
case Context::TLSV1_1_CLIENT_USE:
return SP_PROT_TLS1_1_CLIENT;
case Context::TLSV1_1_SERVER_USE:
return SP_PROT_TLS1_1_SERVER;
#endif
#if defined(SP_PROT_TLS1_2)
case Context::TLSV1_2_CLIENT_USE:
return SP_PROT_TLS1_2_CLIENT;
case Context::TLSV1_2_SERVER_USE:
return SP_PROT_TLS1_2_SERVER;
#endif
default:
throw Poco::InvalidArgumentException("Unsupported SSL/TLS protocol version");
}
}
} } // namespace Poco::Net

View File

@@ -37,10 +37,17 @@ InvalidCertificateHandler::InvalidCertificateHandler(bool handleErrorsOnServerSi
InvalidCertificateHandler::~InvalidCertificateHandler()
{
if (_handleErrorsOnServerSide)
SSLManager::instance().ServerVerificationError -= Delegate<InvalidCertificateHandler, VerificationErrorArgs>(this, &InvalidCertificateHandler::onInvalidCertificate);
else
SSLManager::instance().ClientVerificationError -= Delegate<InvalidCertificateHandler, VerificationErrorArgs>(this, &InvalidCertificateHandler::onInvalidCertificate);
try
{
if (_handleErrorsOnServerSide)
SSLManager::instance().ServerVerificationError -= Delegate<InvalidCertificateHandler, VerificationErrorArgs>(this, &InvalidCertificateHandler::onInvalidCertificate);
else
SSLManager::instance().ClientVerificationError -= Delegate<InvalidCertificateHandler, VerificationErrorArgs>(this, &InvalidCertificateHandler::onInvalidCertificate);
}
catch (...)
{
poco_unexpected();
}
}

View File

@@ -0,0 +1,42 @@
//
// KeyConsoleHandler.cpp
//
// $Id$
//
// Library: NetSSL_Win
// Package: SSLCore
// Module: KeyConsoleHandler
//
// Copyright (c) 2006-2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/KeyConsoleHandler.h"
#include <iostream>
namespace Poco {
namespace Net {
KeyConsoleHandler::KeyConsoleHandler(bool server):PrivateKeyPassphraseHandler(server)
{
}
KeyConsoleHandler::~KeyConsoleHandler()
{
}
void KeyConsoleHandler::onPrivateKeyRequested(const void* pSender, std::string& privateKey)
{
std::cout << "Please enter the passphrase for the private key: ";
std::cin >> privateKey;
}
} } // namespace Poco::Net

View File

@@ -0,0 +1,63 @@
//
// KeyFileHandler.cpp
//
// $Id$
//
// Library: NetSSL_Win
// Package: SSLCore
// Module: KeyFileHandler
//
// Copyright (c) 2006-2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/KeyFileHandler.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/File.h"
#include "Poco/Util/AbstractConfiguration.h"
#include "Poco/Util/Application.h"
#include "Poco/Util/OptionException.h"
namespace Poco {
namespace Net {
const std::string KeyFileHandler::CFG_PRIV_KEY_FILE("privateKeyPassphraseHandler.options.password");
KeyFileHandler::KeyFileHandler(bool server):PrivateKeyPassphraseHandler(server)
{
}
KeyFileHandler::~KeyFileHandler()
{
}
void KeyFileHandler::onPrivateKeyRequested(const void* pSender, std::string& privateKey)
{
try
{
Poco::Util::AbstractConfiguration& config = Poco::Util::Application::instance().config();
std::string prefix = serverSide() ? SSLManager::CFG_SERVER_PREFIX : SSLManager::CFG_CLIENT_PREFIX;
if (!config.hasProperty(prefix + CFG_PRIV_KEY_FILE))
throw Poco::Util::EmptyOptionException(std::string("Missing Configuration Entry: ") + prefix + CFG_PRIV_KEY_FILE);
privateKey = config.getString(prefix + CFG_PRIV_KEY_FILE);
}
catch (Poco::NullPointerException&)
{
throw Poco::IllegalStateException(
"An application configuration is required to obtain the private key passphrase, "
"but no Poco::Util::Application instance is available."
);
}
}
} } // namespace Poco::Net

View File

@@ -0,0 +1,46 @@
//
// PrivateKeyFactory.cpp
//
// $Id$
//
// Library: NetSSL_Win
// Package: SSLCore
// Module: PrivateKeyFactory
//
// Copyright (c) 2006-2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/PrivateKeyFactory.h"
#include "Poco/Net/SSLManager.h"
namespace Poco {
namespace Net {
PrivateKeyFactory::PrivateKeyFactory()
{
}
PrivateKeyFactory::~PrivateKeyFactory()
{
}
PrivateKeyFactoryRegistrar::PrivateKeyFactoryRegistrar(const std::string& name, PrivateKeyFactory* pFactory)
{
SSLManager::instance().privateKeyFactoryMgr().setFactory(name, pFactory);
}
PrivateKeyFactoryRegistrar::~PrivateKeyFactoryRegistrar()
{
}
} } // namespace Poco::Net

View File

@@ -0,0 +1,69 @@
//
// PrivateKeyFactoryMgr.cpp
//
// $Id$
//
// Library: NetSSL_Win
// Package: SSLCore
// Module: PrivateKeyFactoryMgr
//
// Copyright (c) 2006-2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/PrivateKeyFactoryMgr.h"
#include "Poco/Net/KeyFileHandler.h"
#include "Poco/Net/KeyConsoleHandler.h"
namespace Poco {
namespace Net {
PrivateKeyFactoryMgr::PrivateKeyFactoryMgr()
{
setFactory("KeyFileHandler", new PrivateKeyFactoryImpl<KeyFileHandler>());
setFactory("KeyConsoleHandler", new PrivateKeyFactoryImpl<KeyConsoleHandler>());
}
PrivateKeyFactoryMgr::~PrivateKeyFactoryMgr()
{
}
void PrivateKeyFactoryMgr::setFactory(const std::string& name, PrivateKeyFactory* pFactory)
{
bool success = _factories.insert(make_pair(name, Poco::SharedPtr<PrivateKeyFactory>(pFactory))).second;
if (!success)
delete pFactory;
poco_assert(success);
}
bool PrivateKeyFactoryMgr::hasFactory(const std::string& name) const
{
return _factories.find(name) != _factories.end();
}
const PrivateKeyFactory* PrivateKeyFactoryMgr::getFactory(const std::string& name) const
{
FactoriesMap::const_iterator it = _factories.find(name);
if (it != _factories.end())
return it->second;
else
return 0;
}
void PrivateKeyFactoryMgr::removeFactory(const std::string& name)
{
_factories.erase(name);
}
} } // namespace Poco::Net

View File

@@ -0,0 +1,48 @@
//
// PrivateKeyPassphraseHandler.cpp
//
// $Id$
//
// Library: NetSSL_Win
// Package: SSLCore
// Module: PrivateKeyPassphraseHandler
//
// Copyright (c) 2006-2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/PrivateKeyPassphraseHandler.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Delegate.h"
using Poco::Delegate;
namespace Poco {
namespace Net {
PrivateKeyPassphraseHandler::PrivateKeyPassphraseHandler(bool onServerSide): _serverSide(onServerSide)
{
SSLManager::instance().PrivateKeyPassphraseRequired += Delegate<PrivateKeyPassphraseHandler, std::string>(this, &PrivateKeyPassphraseHandler::onPrivateKeyRequested);
}
PrivateKeyPassphraseHandler::~PrivateKeyPassphraseHandler()
{
try
{
SSLManager::instance().PrivateKeyPassphraseRequired -= Delegate<PrivateKeyPassphraseHandler, std::string>(this, &PrivateKeyPassphraseHandler::onPrivateKeyRequested);
}
catch (...)
{
poco_unexpected();
}
}
} } // namespace Poco::Net

View File

@@ -1,7 +1,7 @@
//
// SSLException.cpp
//
// $Id: //poco/1.4/NetSSL_Win/src/SSLException.cpp#1 $
// $Id$
//
// Library: NetSSL_Win
// Package: SSLCore
@@ -24,8 +24,10 @@ namespace Net {
POCO_IMPLEMENT_EXCEPTION(SSLException, NetException, "SSL Exception")
POCO_IMPLEMENT_EXCEPTION(SSLContextException, SSLException, "SSL context exception")
POCO_IMPLEMENT_EXCEPTION(InvalidCertificateException, SSLException, "Invalid certficate")
POCO_IMPLEMENT_EXCEPTION(CertificateValidationException, SSLException, "Certificate validation error")
POCO_IMPLEMENT_EXCEPTION(CertificateException, SSLException, "Certificate exception")
POCO_IMPLEMENT_EXCEPTION(NoCertificateException, CertificateException, "No certificate")
POCO_IMPLEMENT_EXCEPTION(InvalidCertificateException, CertificateException, "Invalid certficate")
POCO_IMPLEMENT_EXCEPTION(CertificateValidationException, CertificateException, "Certificate validation error")
POCO_IMPLEMENT_EXCEPTION(SSLConnectionUnexpectedlyClosedException, SSLException, "SSL connection unexpectedly closed")

View File

@@ -1,9 +1,9 @@
//
// SSLManager.cpp
//
// $Id: //poco/1.4/NetSSL_Schannel/src/SSLManager.cpp#1 $
// $Id$
//
// Library: NetSSL_Schannel
// Library: NetSSL_Win
// Package: SSLCore
// Module: SSLManager
//
@@ -17,11 +17,11 @@
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/Context.h"
#include "Poco/Net/Utility.h"
#include "Poco/Net/PrivateKeyPassphraseHandler.h"
#include "Poco/SingletonHolder.h"
#include "Poco/Delegate.h"
#include "Poco/Util/Application.h"
#include "Poco/Util/OptionException.h"
#include "Poco/Util/LayeredConfiguration.h"
namespace Poco {
@@ -30,6 +30,8 @@ namespace Net {
const std::string SSLManager::CFG_CERT_NAME("certificateName");
const std::string SSLManager::VAL_CERT_NAME("");
const std::string SSLManager::CFG_CERT_PATH("certificatePath");
const std::string SSLManager::VAL_CERT_PATH("");
const std::string SSLManager::CFG_CERT_STORE("certificateStore");
const std::string SSLManager::VAL_CERT_STORE("MY");
const std::string SSLManager::CFG_VER_MODE("verificationMode");
@@ -42,6 +44,8 @@ const std::string SSLManager::CFG_USE_MACHINE_STORE("useMachineStore");
const bool SSLManager::VAL_USE_MACHINE_STORE(false);
const std::string SSLManager::CFG_USE_STRONG_CRYPTO("useStrongCrypto");
const bool SSLManager::VAL_USE_STRONG_CRYPTO(true);
const std::string SSLManager::CFG_DELEGATE_HANDLER("privateKeyPassphraseHandler.name");
const std::string SSLManager::VAL_DELEGATE_HANDLER("KeyConsoleHandler");
const std::string SSLManager::CFG_CERTIFICATE_HANDLER("invalidCertificateHandler.name");
const std::string SSLManager::VAL_CERTIFICATE_HANDLER("ConsoleCertificateHandler");
const std::string SSLManager::CFG_SERVER_PREFIX("schannel.server.");
@@ -60,7 +64,15 @@ SSLManager::SSLManager():
SSLManager::~SSLManager()
{
shutdown();
try
{
shutdown();
}
catch (...)
{
poco_unexpected();
}
}
@@ -76,17 +88,19 @@ SSLManager& SSLManager::instance()
}
void SSLManager::initializeServer(InvalidCertificateHandlerPtr& ptrHandler, Context::Ptr ptrContext)
void SSLManager::initializeServer(PrivateKeyPassphraseHandlerPtr pPassphraseHandler, InvalidCertificateHandlerPtr pCertHandler, Context::Ptr pContext)
{
_ptrServerCertificateHandler = ptrHandler;
_ptrDefaultServerContext = ptrContext;
_ptrServerPassphraseHandler = pPassphraseHandler;
_ptrServerCertificateHandler = pCertHandler;
_ptrDefaultServerContext = pContext;
}
void SSLManager::initializeClient(InvalidCertificateHandlerPtr& ptrHandler, Context::Ptr ptrContext)
void SSLManager::initializeClient(PrivateKeyPassphraseHandlerPtr pPassphraseHandler, InvalidCertificateHandlerPtr pCertHandler, Context::Ptr pContext)
{
_ptrClientCertificateHandler = ptrHandler;
_ptrDefaultClientContext = ptrContext;
_ptrClientPassphraseHandler = pPassphraseHandler;
_ptrClientCertificateHandler = pCertHandler;
_ptrDefaultClientContext = pContext;
}
@@ -112,6 +126,28 @@ Context::Ptr SSLManager::defaultClientContext()
}
SSLManager::PrivateKeyPassphraseHandlerPtr SSLManager::serverPassphraseHandler()
{
Poco::FastMutex::ScopedLock lock(_mutex);
if (!_ptrServerPassphraseHandler)
initPassphraseHandler(true);
return _ptrServerPassphraseHandler;
}
SSLManager::PrivateKeyPassphraseHandlerPtr SSLManager::clientPassphraseHandler()
{
Poco::FastMutex::ScopedLock lock(_mutex);
if (!_ptrClientPassphraseHandler)
initPassphraseHandler(false);
return _ptrClientPassphraseHandler;
}
SSLManager::InvalidCertificateHandlerPtr SSLManager::serverCertificateHandler()
{
Poco::FastMutex::ScopedLock lock(_mutex);
@@ -142,8 +178,9 @@ void SSLManager::initDefaultContext(bool server)
initEvents(server);
const std::string prefix = server ? CFG_SERVER_PREFIX : CFG_CLIENT_PREFIX;
Poco::Util::LayeredConfiguration& config = Poco::Util::Application::instance().config();
Poco::Util::AbstractConfiguration& config = appConfig();
std::string certName = config.getString(prefix + CFG_CERT_NAME, VAL_CERT_NAME);
std::string certPath = config.getString(prefix + CFG_CERT_PATH, VAL_CERT_PATH);
std::string certStore = config.getString(prefix + CFG_CERT_STORE, VAL_CERT_STORE);
bool requireTLSv1 = config.getBool(prefix + CFG_REQUIRE_TLSV1, false);
@@ -168,6 +205,11 @@ void SSLManager::initDefaultContext(bool server)
if (trustRoots) options |= Context::OPT_TRUST_ROOTS_WIN_CERT_STORE;
if (useMachineStore) options |= Context::OPT_USE_MACHINE_STORE;
if (useStrongCrypto) options |= Context::OPT_USE_STRONG_CRYPTO;
if (!certPath.empty())
{
options |= Context::OPT_LOAD_CERT_FROM_FILE;
certName = certPath;
}
Context::Usage usage;
if (server)
@@ -199,17 +241,45 @@ void SSLManager::initDefaultContext(bool server)
void SSLManager::initEvents(bool server)
{
initPassphraseHandler(server);
initCertificateHandler(server);
}
void SSLManager::initPassphraseHandler(bool server)
{
if (server && _ptrServerPassphraseHandler) return;
if (!server && _ptrClientPassphraseHandler) return;
std::string prefix = server ? CFG_SERVER_PREFIX : CFG_CLIENT_PREFIX;
Poco::Util::AbstractConfiguration& config = appConfig();
std::string className(config.getString(prefix + CFG_DELEGATE_HANDLER, VAL_DELEGATE_HANDLER));
const PrivateKeyFactory* pFactory = 0;
if (privateKeyFactoryMgr().hasFactory(className))
{
pFactory = privateKeyFactoryMgr().getFactory(className);
}
if (pFactory)
{
if (server)
_ptrServerPassphraseHandler = pFactory->create(server);
else
_ptrClientPassphraseHandler = pFactory->create(server);
}
else throw Poco::Util::UnknownOptionException(std::string("No passphrase handler known with the name ") + className);
}
void SSLManager::initCertificateHandler(bool server)
{
if (server && _ptrServerCertificateHandler) return;
if (!server && _ptrClientCertificateHandler) return;
std::string prefix = server ? CFG_SERVER_PREFIX : CFG_CLIENT_PREFIX;
Poco::Util::LayeredConfiguration& config = Poco::Util::Application::instance().config();
Poco::Util::AbstractConfiguration& config = appConfig();
std::string className(config.getString(prefix + CFG_CERTIFICATE_HANDLER, VAL_CERTIFICATE_HANDLER));
@@ -234,8 +304,10 @@ void SSLManager::shutdown()
{
ClientVerificationError.clear();
ServerVerificationError.clear();
_ptrServerPassphraseHandler = 0;
_ptrServerCertificateHandler = 0;
_ptrDefaultServerContext = 0;
_ptrClientPassphraseHandler = 0;
_ptrClientCertificateHandler = 0;
_ptrDefaultClientContext = 0;
@@ -321,6 +393,22 @@ void SSLManager::unloadSecurityLibrary()
}
Poco::Util::AbstractConfiguration& SSLManager::appConfig()
{
try
{
return Poco::Util::Application::instance().config();
}
catch (Poco::NullPointerException&)
{
throw Poco::IllegalStateException(
"An application configuration is required to initialize the Poco::Net::SSLManager, "
"but no Poco::Util::Application instance is available."
);
}
}
void initializeSSL()
{
}

View File

@@ -1,9 +1,9 @@
//
// SecureSocketImpl.cpp
//
// $Id: //poco/1.4/NetSSL_Schannel/src/SecureSocketImpl.cpp#1 $
// $Id$
//
// Library: NetSSL_Schannel
// Library: NetSSL_Win
// Package: SSLSockets
// Module: SecureSocketImpl
//
@@ -75,9 +75,7 @@ SecureSocketImpl::SecureSocketImpl(Poco::AutoPtr<SocketImpl> pSocketImpl, Contex
_pSocket(pSocketImpl),
_pContext(pContext),
_mode(pContext->isForServerUse() ? MODE_SERVER : MODE_CLIENT),
_useMachineStore((pContext->options() & Context::OPT_USE_MACHINE_STORE) != 0),
_clientAuthRequired(pContext->verificationMode() >= Context::VERIFY_STRICT),
_hCertificateStore(0),
_pServerCertificate(0),
_pPeerCertificate(0),
_hCreds(),
@@ -155,7 +153,6 @@ void SecureSocketImpl::cleanup()
if (_hCreds.dwLower != 0 && _hCreds.dwUpper != 0)
{
_securityFunctions.FreeCredentialsHandle(&_hCreds);
_hCreds.dwLower = 0;
_hCreds.dwUpper = 0;
}
@@ -179,12 +176,6 @@ void SecureSocketImpl::cleanup()
_pPeerCertificate = 0;
}
if (_hCertificateStore)
{
CertCloseStore(_hCertificateStore, 0);
_hCertificateStore = 0;
}
// must release buffers before unloading library
_outSecBuffer.release();
_inSecBuffer.release();
@@ -657,116 +648,22 @@ void SecureSocketImpl::setPeerHostName(const std::string& peerHostName)
}
PCCERT_CONTEXT SecureSocketImpl::loadCertificate(const std::string& certStore, const std::string& certName, bool useMachineStore, bool mustFindCertificate)
PCCERT_CONTEXT SecureSocketImpl::loadCertificate(bool mustFindCertificate)
{
if (mustFindCertificate && certName.empty())
throw SSLException("Certificate required, but no certificate name provided");
PCCERT_CONTEXT pCert = 0;
std::wstring wcertStore;
Poco::UnicodeConverter::convert(certStore, wcertStore);
if (!_hCertificateStore)
try
{
if (useMachineStore)
_hCertificateStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, certStore.c_str());
Poco::Net::X509Certificate cert = _pContext->certificate();
PCCERT_CONTEXT pCert = cert.system();
// TODO: avoid use of PCCERT_CONTEXT
CertDuplicateCertificateContext(pCert);
return pCert;
}
catch (...)
{
if (mustFindCertificate)
throw;
else
_hCertificateStore = CertOpenSystemStoreW(0, wcertStore.c_str());
}
if (!_hCertificateStore)
{
throw SSLException("Failed to open certificate store", certStore, GetLastError());
}
if (!certName.empty())
{
CERT_RDN_ATTR cert_rdn_attr;
cert_rdn_attr.pszObjId = szOID_COMMON_NAME;
cert_rdn_attr.dwValueType = CERT_RDN_ANY_TYPE;
cert_rdn_attr.Value.cbData = (DWORD) certName.size();
cert_rdn_attr.Value.pbData = (BYTE *) certName.c_str();
CERT_RDN cert_rdn;
cert_rdn.cRDNAttr = 1;
cert_rdn.rgRDNAttr = &cert_rdn_attr;
pCert = CertFindCertificateInStore(_hCertificateStore, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_ATTR, &cert_rdn, NULL);
if (!pCert)
{
// we throw independently of mustFind. If a name is specified we consider it an error
// if we don't find the given certificate
throw SSLException(Poco::format("Failed to find certificate %s in store %s", certName, certStore));
}
}
if (mustFindCertificate && !pCert)
throw SSLException(Poco::format("Failed to find certificate %s in store %s", certName, certStore));
return pCert;
}
void SecureSocketImpl::acquireSchannelContext(Mode mode, PCCERT_CONTEXT pCertContext, CredHandle& outHandle)
{
SCHANNEL_CRED schannelCred;
ZeroMemory(&schannelCred, sizeof(schannelCred));
schannelCred.dwVersion = SCHANNEL_CRED_VERSION;
if (pCertContext != 0)
{
schannelCred.cCreds = 1; // how many cred are stored in &pCertContext
schannelCred.paCred = &pCertContext;
}
schannelCred.grbitEnabledProtocols = proto();
// Windows NT and Windows Me/98/95: revocation checking not supported via flags
if (_pContext->options() & Context::OPT_PERFORM_REVOCATION_CHECK)
schannelCred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
else
schannelCred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE;
if (mode == MODE_SERVER)
{
if (_pContext->verificationMode() == Context::VERIFY_STRICT)
schannelCred.dwFlags |= SCH_CRED_NO_SYSTEM_MAPPER;
if (_pContext->verificationMode() == Context::VERIFY_NONE)
schannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
}
else
{
if (_pContext->verificationMode() == Context::VERIFY_STRICT)
schannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
else
schannelCred.dwFlags |= SCH_CRED_USE_DEFAULT_CREDS;
if (_pContext->verificationMode() == Context::VERIFY_NONE)
schannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_SERVERNAME_CHECK;
}
#if defined(SCH_USE_STRONG_CRYPTO)
if (_pContext->options() & Context::OPT_USE_STRONG_CRYPTO)
schannelCred.dwFlags |= SCH_USE_STRONG_CRYPTO;
#endif
schannelCred.hRootStore = _pContext->certificateStore();
TimeStamp tsExpiry;
tsExpiry.LowPart = tsExpiry.HighPart = 0;
SECURITY_STATUS status = _securityFunctions.AcquireCredentialsHandleW(
NULL,
UNISP_NAME_W,
mode == MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
NULL,
&schannelCred,
NULL,
NULL,
&outHandle,
&tsExpiry);
if (status != SEC_E_OK)
{
throw SSLException("Failed to acquire Schannel credentials", Utility::formatError(status));
return 0;
}
}
@@ -818,8 +715,8 @@ void SecureSocketImpl::clientConnectVerify()
void SecureSocketImpl::initClientContext()
{
_pServerCertificate = loadCertificate(_pContext->certificateStoreName(), _pContext->certificateName(), _useMachineStore, false);
acquireSchannelContext(MODE_CLIENT, _pServerCertificate, _hCreds);
_pServerCertificate = loadCertificate(false);
_hCreds = _pContext->credentials();
}
@@ -1104,8 +1001,8 @@ void SecureSocketImpl::performClientHandshakeLoopIncompleteMessage()
void SecureSocketImpl::initServerContext()
{
_pServerCertificate = loadCertificate(_pContext->certificateStoreName(), _pContext->certificateName(), _useMachineStore, true);
acquireSchannelContext(MODE_SERVER, _pServerCertificate, _hCreds);
_pServerCertificate = loadCertificate(true);
_hCreds = _pContext->credentials();
}
@@ -1593,36 +1490,6 @@ LONG SecureSocketImpl::serverDisconnect(PCredHandle phCreds, CtxtHandle *phConte
}
DWORD SecureSocketImpl::proto() const
{
switch (_pContext->usage())
{
case Context::CLIENT_USE:
return SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_CLIENT;
case Context::SERVER_USE:
return SP_PROT_SSL3_SERVER | SP_PROT_TLS1_SERVER;
case Context::TLSV1_CLIENT_USE:
return SP_PROT_TLS1_CLIENT;
case Context::TLSV1_SERVER_USE:
return SP_PROT_TLS1_SERVER;
#if defined(SP_PROT_TLS1_1)
case Context::TLSV1_1_CLIENT_USE:
return SP_PROT_TLS1_1_CLIENT;
case Context::TLSV1_1_SERVER_USE:
return SP_PROT_TLS1_1_SERVER;
#endif
#if defined(SP_PROT_TLS1_2)
case Context::TLSV1_2_CLIENT_USE:
return SP_PROT_TLS1_2_CLIENT;
case Context::TLSV1_2_SERVER_USE:
return SP_PROT_TLS1_2_SERVER;
#endif
default:
throw Poco::InvalidArgumentException("Unsupported SSL/TLS protocol version");
}
}
void SecureSocketImpl::stateIllegal()
{
throw Poco::IllegalStateException("SSL state machine");

View File

@@ -1,9 +1,9 @@
//
// Utility.cpp
//
// $Id: //poco/1.4/NetSSL_Schannel/src/Utility.cpp#1 $
// $Id$
//
// Library: NetSSL_Schannel
// Library: NetSSL_Win
// Package: SSLCore
// Module: Utility
//

View File

@@ -1,7 +1,7 @@
//
// X509Certificate.cpp
//
// $Id: //poco/1.4/Crypto/src/X509Certificate.cpp#1 $
// $Id$
//
// Library: Crypto
// Package: Certificate
@@ -15,13 +15,18 @@
#include "Poco/Net/X509Certificate.h"
#include "Poco/Net/SSLException.h"
#include "Poco/StreamCopier.h"
#include "Poco/String.h"
#include "Poco/DateTimeParser.h"
#include "Poco/Base64Encoder.h"
#include "Poco/Base64Decoder.h"
#include "Poco/File.h"
#include "Poco/FileStream.h"
#include "Poco/MemoryStream.h"
#include "Poco/Buffer.h"
#include "Poco/UnicodeConverter.h"
#include "Poco/Format.h"
#include <sstream>
@@ -29,17 +34,17 @@ namespace Poco {
namespace Net {
X509Certificate::X509Certificate(std::istream& istr):
_pCert(0)
{
load(istr);
}
X509Certificate::X509Certificate(const std::string& path):
_pCert(0)
{
load(path);
importCertificate(path);
}
X509Certificate::X509Certificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore):
_pCert(0)
{
loadCertificate(certName, certStoreName, useMachineStore);
}
@@ -98,36 +103,6 @@ X509Certificate::~X509Certificate()
}
void X509Certificate::load(std::istream& istr)
{
poco_assert (!_pCert);
// TODO
init();
}
void X509Certificate::load(const std::string& path)
{
Poco::FileInputStream istr(path);
load(istr);
}
void X509Certificate::save(std::ostream& stream) const
{
// TODO
}
void X509Certificate::save(const std::string& path) const
{
Poco::FileOutputStream ostr(path);
save(ostr);
}
void X509Certificate::init()
{
wchar_t data[256];
@@ -253,4 +228,97 @@ void* X509Certificate::nid2oid(NID nid)
}
void X509Certificate::loadCertificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore)
{
std::wstring wcertStore;
Poco::UnicodeConverter::convert(certStoreName, wcertStore);
HCERTSTORE hCertStore;
if (useMachineStore)
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, certStoreName.c_str());
else
hCertStore = CertOpenSystemStoreW(0, wcertStore.c_str());
if (!hCertStore) throw CertificateException("Failed to open certificate store", certStoreName, GetLastError());
CERT_RDN_ATTR cert_rdn_attr;
cert_rdn_attr.pszObjId = szOID_COMMON_NAME;
cert_rdn_attr.dwValueType = CERT_RDN_ANY_TYPE;
cert_rdn_attr.Value.cbData = (DWORD) certName.size();
cert_rdn_attr.Value.pbData = (BYTE *) certName.c_str();
CERT_RDN cert_rdn;
cert_rdn.cRDNAttr = 1;
cert_rdn.rgRDNAttr = &cert_rdn_attr;
_pCert = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_ATTR, &cert_rdn, NULL);
if (!_pCert)
{
CertCloseStore(hCertStore, 0);
throw NoCertificateException(Poco::format("Failed to find certificate %s in store %s", certName, certStoreName));
}
CertCloseStore(hCertStore, 0);
}
void X509Certificate::importCertificate(const std::string& certPath)
{
Poco::File certFile(certPath);
if (!certFile.exists()) throw Poco::FileNotFoundException(certPath);
Poco::File::FileSize size = certFile.getSize();
if (size > 4096) throw Poco::DataFormatException("certificate file too large", certPath);
if (size < 32) throw Poco::DataFormatException("certificate file too small", certPath);
Poco::Buffer<char> buffer(static_cast<std::size_t>(size));
Poco::FileInputStream istr(certPath);
istr.read(buffer.begin(), buffer.size());
if (istr.gcount() != size) throw Poco::IOException("error reading certificate file");
importCertificate(buffer.begin(), buffer.size());
}
void X509Certificate::importCertificate(const char* pBuffer, std::size_t size)
{
if (std::memcmp(pBuffer, "-----BEGIN CERTIFICATE-----", 27) == 0)
importPEMCertificate(pBuffer + 27, size - 27);
else
importDERCertificate(pBuffer, size);
}
void X509Certificate::importPEMCertificate(const char* pBuffer, std::size_t size)
{
Poco::Buffer<char> derBuffer(size);
std::size_t derSize = 0;
const char* pemBegin = pBuffer;
const char* pemEnd = pemBegin + (size - 25);
while (pemEnd > pemBegin && std::memcmp(pemEnd, "-----END CERTIFICATE-----", 25) != 0) --pemEnd;
if (pemEnd == pemBegin) throw Poco::DataFormatException("Not a valid PEM file - end marker missing");
Poco::MemoryInputStream istr(pemBegin, pemEnd - pemBegin);
Poco::Base64Decoder dec(istr);
char* derBegin = derBuffer.begin();
char* derEnd = derBegin;
int ch = dec.get();
while (ch != -1)
{
*derEnd++ = static_cast<char>(ch);
ch = dec.get();
}
importDERCertificate(derBegin, derEnd - derBegin);
}
void X509Certificate::importDERCertificate(const char* pBuffer, std::size_t size)
{
_pCert = CertCreateCertificateContext(X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(pBuffer), static_cast<DWORD>(size));
if (!_pCert)
{
throw Poco::DataFormatException("Failed to load certificate from file", GetLastError());
}
}
} } // namespace Poco::Net