mirror of
https://github.com/pocoproject/poco.git
synced 2025-12-08 12:19:21 +01:00
* Find certificate from Windows store using its thumbprint * Address review comments --------- Co-authored-by: Hussein Ismail <hussein.ismail@m-files.com>
This commit is contained in:
@@ -127,11 +127,13 @@ public:
|
|||||||
OPT_LOAD_CERT_FROM_FILE = 0x10,
|
OPT_LOAD_CERT_FROM_FILE = 0x10,
|
||||||
/// Load certificate and private key from a PKCS #12 (.pfx) file,
|
/// Load certificate and private key from a PKCS #12 (.pfx) file,
|
||||||
/// and not from the certificate store.
|
/// and not from the certificate store.
|
||||||
|
OPT_USE_CERT_HASH = 0x20,
|
||||||
|
/// Find the certificate using thumbprint.
|
||||||
OPT_DEFAULTS = OPT_PERFORM_REVOCATION_CHECK | OPT_TRUST_ROOTS_WIN_CERT_STORE | OPT_USE_STRONG_CRYPTO
|
OPT_DEFAULTS = OPT_PERFORM_REVOCATION_CHECK | OPT_TRUST_ROOTS_WIN_CERT_STORE | OPT_USE_STRONG_CRYPTO
|
||||||
};
|
};
|
||||||
|
|
||||||
Context(Usage usage,
|
Context(Usage usage,
|
||||||
const std::string& certificateNameOrPath,
|
const std::string& certificateInfoOrPath,
|
||||||
VerificationMode verMode = VERIFY_RELAXED,
|
VerificationMode verMode = VERIFY_RELAXED,
|
||||||
int options = OPT_DEFAULTS,
|
int options = OPT_DEFAULTS,
|
||||||
const std::string& certificateStoreName = CERT_STORE_MY);
|
const std::string& certificateStoreName = CERT_STORE_MY);
|
||||||
@@ -139,7 +141,7 @@ public:
|
|||||||
///
|
///
|
||||||
/// * usage specifies whether the context is used by a client or server,
|
/// * usage specifies whether the context is used by a client or server,
|
||||||
/// as well as which protocol to use.
|
/// as well as which protocol to use.
|
||||||
/// * certificateNameOrPath specifies either the subject name of the certificate to use,
|
/// * certificateInfoOrPath specifies either the subject name or thumbprint of the certificate to use,
|
||||||
/// or the path of a PKCS #12 file containing the certificate and corresponding private key.
|
/// or the path of a PKCS #12 file containing the certificate and corresponding private key.
|
||||||
/// If a subject name is specified, the certificate must be located in the certificate
|
/// If a subject name is specified, the certificate must be located in the certificate
|
||||||
/// store specified by certificateStoreName. If a path is given, the OPT_LOAD_CERT_FROM_FILE
|
/// store specified by certificateStoreName. If a path is given, the OPT_LOAD_CERT_FROM_FILE
|
||||||
@@ -240,7 +242,7 @@ private:
|
|||||||
int _options;
|
int _options;
|
||||||
int _disabledProtocols;
|
int _disabledProtocols;
|
||||||
bool _extendedCertificateVerification;
|
bool _extendedCertificateVerification;
|
||||||
std::string _certNameOrPath;
|
std::string _certInfoOrPath;
|
||||||
std::string _certStoreName;
|
std::string _certStoreName;
|
||||||
HCERTSTORE _hMemCertStore;
|
HCERTSTORE _hMemCertStore;
|
||||||
HCERTSTORE _hCollectionCertStore;
|
HCERTSTORE _hCollectionCertStore;
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ class NetSSL_Win_API SSLManager
|
|||||||
/// <schannel>
|
/// <schannel>
|
||||||
/// <server|client>
|
/// <server|client>
|
||||||
/// <certificateName>cert Id</certificateName>
|
/// <certificateName>cert Id</certificateName>
|
||||||
|
/// <certificateHash>cert thumbprint</certificateHash>
|
||||||
|
/// <certificatePath>path of a certificate</certificatePath>
|
||||||
/// <certificateStore>MY</certificateStore>
|
/// <certificateStore>MY</certificateStore>
|
||||||
/// <verificationMode>none|relaxed|strict</verificationMode>
|
/// <verificationMode>none|relaxed|strict</verificationMode>
|
||||||
/// <revocationCheck>true|false</revocationCheck>
|
/// <revocationCheck>true|false</revocationCheck>
|
||||||
@@ -102,7 +104,9 @@ class NetSSL_Win_API SSLManager
|
|||||||
/// for servers.
|
/// for servers.
|
||||||
///
|
///
|
||||||
/// - certificateName (string): The subject name of the certificate to use. The certificate must
|
/// - certificateName (string): The subject name of the certificate to use. The certificate must
|
||||||
/// be available in the Windows user or machine certificate store.
|
/// be available in the Windows user or machine certificate store.
|
||||||
|
/// - certificateHash (string): The thumbprint of the certificate to use. Alternative for certificateName.
|
||||||
|
/// The certificate must be available in the Windows user or machine certificate store.
|
||||||
/// - certificatePath (string): The path of a certificate and private key file in PKCS #12 format.
|
/// - certificatePath (string): The path of a certificate and private key file in PKCS #12 format.
|
||||||
/// - certificateStore (string): The certificate store location to use.
|
/// - certificateStore (string): The certificate store location to use.
|
||||||
/// Valid values are "MY", "Root", "Trust" or "CA". Defaults to "MY".
|
/// Valid values are "MY", "Root", "Trust" or "CA". Defaults to "MY".
|
||||||
@@ -269,6 +273,8 @@ private:
|
|||||||
|
|
||||||
static const std::string CFG_CERT_NAME;
|
static const std::string CFG_CERT_NAME;
|
||||||
static const std::string VAL_CERT_NAME;
|
static const std::string VAL_CERT_NAME;
|
||||||
|
static const std::string CFG_CERT_HASH;
|
||||||
|
static const std::string VAL_CERT_HASH;
|
||||||
static const std::string CFG_CERT_PATH;
|
static const std::string CFG_CERT_PATH;
|
||||||
static const std::string VAL_CERT_PATH;
|
static const std::string VAL_CERT_PATH;
|
||||||
static const std::string CFG_CERT_STORE;
|
static const std::string CFG_CERT_STORE;
|
||||||
|
|||||||
@@ -23,8 +23,10 @@
|
|||||||
#include "Poco/MemoryStream.h"
|
#include "Poco/MemoryStream.h"
|
||||||
#include "Poco/Base64Decoder.h"
|
#include "Poco/Base64Decoder.h"
|
||||||
#include "Poco/Buffer.h"
|
#include "Poco/Buffer.h"
|
||||||
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
namespace Poco {
|
namespace Poco {
|
||||||
@@ -39,7 +41,7 @@ const std::string Context::CERT_STORE_USERDS("USERDS");
|
|||||||
|
|
||||||
|
|
||||||
Context::Context(Usage usage,
|
Context::Context(Usage usage,
|
||||||
const std::string& certNameOrPath,
|
const std::string& certificateInfoOrPath,
|
||||||
VerificationMode verMode,
|
VerificationMode verMode,
|
||||||
int options,
|
int options,
|
||||||
const std::string& certStore):
|
const std::string& certStore):
|
||||||
@@ -48,7 +50,7 @@ Context::Context(Usage usage,
|
|||||||
_options(options),
|
_options(options),
|
||||||
_disabledProtocols(0),
|
_disabledProtocols(0),
|
||||||
_extendedCertificateVerification(true),
|
_extendedCertificateVerification(true),
|
||||||
_certNameOrPath(certNameOrPath),
|
_certInfoOrPath(certificateInfoOrPath),
|
||||||
_certStoreName(certStore),
|
_certStoreName(certStore),
|
||||||
_hMemCertStore(0),
|
_hMemCertStore(0),
|
||||||
_hCollectionCertStore(0),
|
_hCollectionCertStore(0),
|
||||||
@@ -145,7 +147,7 @@ Poco::Net::X509Certificate Context::certificate()
|
|||||||
if (_pCert)
|
if (_pCert)
|
||||||
return Poco::Net::X509Certificate(_pCert, true);
|
return Poco::Net::X509Certificate(_pCert, true);
|
||||||
|
|
||||||
if (_certNameOrPath.empty())
|
if (_certInfoOrPath.empty())
|
||||||
throw NoCertificateException("Certificate requested, but no certificate name or path provided");
|
throw NoCertificateException("Certificate requested, but no certificate name or path provided");
|
||||||
|
|
||||||
if (_options & OPT_LOAD_CERT_FROM_FILE)
|
if (_options & OPT_LOAD_CERT_FROM_FILE)
|
||||||
@@ -168,36 +170,70 @@ void Context::loadCertificate()
|
|||||||
if (!_hCertStore)
|
if (!_hCertStore)
|
||||||
{
|
{
|
||||||
if (_options & OPT_USE_MACHINE_STORE)
|
if (_options & OPT_USE_MACHINE_STORE)
|
||||||
_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, wcertStoreName.c_str());
|
_hCertStore = CertOpenStore(
|
||||||
|
CERT_STORE_PROV_SYSTEM, 0, 0,
|
||||||
|
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG,
|
||||||
|
wcertStoreName.c_str()
|
||||||
|
);
|
||||||
else
|
else
|
||||||
_hCertStore = CertOpenSystemStoreW(0, wcertStoreName.c_str());
|
_hCertStore = CertOpenSystemStoreW(0, wcertStoreName.c_str());
|
||||||
}
|
}
|
||||||
if (!_hCertStore) throw CertificateException("Failed to open certificate store", _certStoreName, GetLastError());
|
if (!_hCertStore) throw CertificateException("Failed to open certificate store", _certStoreName, GetLastError());
|
||||||
|
|
||||||
|
// Find the certificate either using name or hash.
|
||||||
|
if(_options & OPT_USE_CERT_HASH)
|
||||||
|
{
|
||||||
|
// Sanity check for the hash value.
|
||||||
|
if(_certInfoOrPath.size() < 40 || _certInfoOrPath.size() % 2) throw CertificateException(Poco::format("Invalid certificate hash %s", _certInfoOrPath));
|
||||||
|
|
||||||
CERT_RDN_ATTR cert_rdn_attr;
|
// Convert hex to binary.
|
||||||
char cmnName[] = szOID_COMMON_NAME;
|
BYTE buffer[256] = {};
|
||||||
cert_rdn_attr.pszObjId = cmnName;
|
int bufferSize = 0;
|
||||||
cert_rdn_attr.dwValueType = CERT_RDN_ANY_TYPE;
|
int byte = 0;
|
||||||
cert_rdn_attr.Value.cbData = (DWORD) _certNameOrPath.size();
|
std::string szHex;
|
||||||
cert_rdn_attr.Value.pbData = (BYTE *) _certNameOrPath.c_str();
|
for(int counter = 0; counter < _certInfoOrPath.size() / 2; counter++)
|
||||||
|
{
|
||||||
|
szHex = _certInfoOrPath.substr(2 * counter, 2);
|
||||||
|
if( sscanf(szHex.c_str(), "%02x", OUT &byte ) != 1)
|
||||||
|
throw CertificateException(Poco::format("Invalid certificate hash %s", _certInfoOrPath));
|
||||||
|
buffer[counter] = (BYTE) byte;
|
||||||
|
bufferSize += 1;
|
||||||
|
}
|
||||||
|
|
||||||
CERT_RDN cert_rdn;
|
CRYPT_HASH_BLOB chBlob;
|
||||||
cert_rdn.cRDNAttr = 1;
|
chBlob.cbData = bufferSize;
|
||||||
cert_rdn.rgRDNAttr = &cert_rdn_attr;
|
chBlob.pbData = buffer;
|
||||||
|
|
||||||
_pCert = CertFindCertificateInStore(_hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_ATTR, &cert_rdn, NULL);
|
_pCert = CertFindCertificateInStore(_hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_HASH, &chBlob, NULL);
|
||||||
if (!_pCert) throw NoCertificateException(Poco::format("Failed to find certificate %s in store %s", _certNameOrPath, _certStoreName));
|
if (!_pCert) throw NoCertificateException(Poco::format("Failed to find certificate %s in store %s", _certInfoOrPath, _certStoreName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CERT_RDN_ATTR cert_rdn_attr;
|
||||||
|
char cmnName[] = szOID_COMMON_NAME;
|
||||||
|
cert_rdn_attr.pszObjId = cmnName;
|
||||||
|
cert_rdn_attr.dwValueType = CERT_RDN_ANY_TYPE;
|
||||||
|
cert_rdn_attr.Value.cbData = (DWORD) _certInfoOrPath.size();
|
||||||
|
cert_rdn_attr.Value.pbData = (BYTE *) _certInfoOrPath.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", _certInfoOrPath, _certStoreName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Context::importCertificate()
|
void Context::importCertificate()
|
||||||
{
|
{
|
||||||
Poco::File certFile(_certNameOrPath);
|
Poco::File certFile(_certInfoOrPath);
|
||||||
if (!certFile.exists()) throw Poco::FileNotFoundException(_certNameOrPath);
|
if (!certFile.exists()) throw Poco::FileNotFoundException(_certInfoOrPath);
|
||||||
Poco::File::FileSize size = certFile.getSize();
|
Poco::File::FileSize size = certFile.getSize();
|
||||||
|
if (size > 4096) throw Poco::DataFormatException("PKCS #12 certificate file too large", _certInfoOrPath);
|
||||||
Poco::Buffer<char> buffer(static_cast<std::size_t>(size));
|
Poco::Buffer<char> buffer(static_cast<std::size_t>(size));
|
||||||
Poco::FileInputStream istr(_certNameOrPath);
|
Poco::FileInputStream istr(_certInfoOrPath);
|
||||||
istr.read(buffer.begin(), buffer.size());
|
istr.read(buffer.begin(), buffer.size());
|
||||||
if (istr.gcount() != size) throw Poco::IOException("error reading PKCS #12 certificate file");
|
if (istr.gcount() != size) throw Poco::IOException("error reading PKCS #12 certificate file");
|
||||||
importCertificate(buffer.begin(), buffer.size());
|
importCertificate(buffer.begin(), buffer.size());
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ namespace Net {
|
|||||||
|
|
||||||
const std::string SSLManager::CFG_CERT_NAME("certificateName");
|
const std::string SSLManager::CFG_CERT_NAME("certificateName");
|
||||||
const std::string SSLManager::VAL_CERT_NAME("");
|
const std::string SSLManager::VAL_CERT_NAME("");
|
||||||
|
const std::string SSLManager::CFG_CERT_HASH("certificateHash");
|
||||||
|
const std::string SSLManager::VAL_CERT_HASH("");
|
||||||
const std::string SSLManager::CFG_CERT_PATH("certificatePath");
|
const std::string SSLManager::CFG_CERT_PATH("certificatePath");
|
||||||
const std::string SSLManager::VAL_CERT_PATH("");
|
const std::string SSLManager::VAL_CERT_PATH("");
|
||||||
const std::string SSLManager::CFG_CERT_STORE("certificateStore");
|
const std::string SSLManager::CFG_CERT_STORE("certificateStore");
|
||||||
@@ -188,7 +190,8 @@ void SSLManager::initDefaultContext(bool server)
|
|||||||
|
|
||||||
const std::string prefix = server ? CFG_SERVER_PREFIX : CFG_CLIENT_PREFIX;
|
const std::string prefix = server ? CFG_SERVER_PREFIX : CFG_CLIENT_PREFIX;
|
||||||
Poco::Util::AbstractConfiguration& config = appConfig();
|
Poco::Util::AbstractConfiguration& config = appConfig();
|
||||||
std::string certName = config.getString(prefix + CFG_CERT_NAME, VAL_CERT_NAME);
|
std::string certInfo = config.getString(prefix + CFG_CERT_NAME, VAL_CERT_NAME);
|
||||||
|
std::string certHash = config.getString(prefix + CFG_CERT_HASH, VAL_CERT_HASH);
|
||||||
std::string certPath = config.getString(prefix + CFG_CERT_PATH, VAL_CERT_PATH);
|
std::string certPath = config.getString(prefix + CFG_CERT_PATH, VAL_CERT_PATH);
|
||||||
std::string certStore = config.getString(prefix + CFG_CERT_STORE, VAL_CERT_STORE);
|
std::string certStore = config.getString(prefix + CFG_CERT_STORE, VAL_CERT_STORE);
|
||||||
|
|
||||||
@@ -218,7 +221,12 @@ void SSLManager::initDefaultContext(bool server)
|
|||||||
if (!certPath.empty())
|
if (!certPath.empty())
|
||||||
{
|
{
|
||||||
options |= Context::OPT_LOAD_CERT_FROM_FILE;
|
options |= Context::OPT_LOAD_CERT_FROM_FILE;
|
||||||
certName = certPath;
|
certInfo = certPath;
|
||||||
|
}
|
||||||
|
if (certInfo.empty() && !certHash.empty())
|
||||||
|
{
|
||||||
|
options |= Context::OPT_USE_CERT_HASH;
|
||||||
|
certInfo = certHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::Usage usage;
|
Context::Usage usage;
|
||||||
@@ -234,7 +242,7 @@ void SSLManager::initDefaultContext(bool server)
|
|||||||
usage = Context::TLSV1_SERVER_USE;
|
usage = Context::TLSV1_SERVER_USE;
|
||||||
else
|
else
|
||||||
usage = Context::SERVER_USE;
|
usage = Context::SERVER_USE;
|
||||||
_ptrDefaultServerContext = new Context(usage, certName, verMode, options, certStore);
|
_ptrDefaultServerContext = new Context(usage, certInfo, verMode, options, certStore);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -248,7 +256,7 @@ void SSLManager::initDefaultContext(bool server)
|
|||||||
usage = Context::TLSV1_CLIENT_USE;
|
usage = Context::TLSV1_CLIENT_USE;
|
||||||
else
|
else
|
||||||
usage = Context::CLIENT_USE;
|
usage = Context::CLIENT_USE;
|
||||||
_ptrDefaultClientContext = new Context(usage, certName, verMode, options, certStore);
|
_ptrDefaultClientContext = new Context(usage, certInfo, verMode, options, certStore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user