Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find certificate from Windows store using its thumbprint #2123

Open
wants to merge 2 commits into
base: poco-1.8.2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions NetSSL_Win/include/Poco/Net/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,21 @@ class NetSSL_Win_API Context: public Poco::RefCountedObject
OPT_LOAD_CERT_FROM_FILE = 0x10,
/// Load certificate and private key from a PKCS #12 (.pfx) file,
/// 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
};

Context(Usage usage,
const std::string& certificateNameOrPath,
const std::string& certificateInfoOrPath,
VerificationMode verMode = VERIFY_RELAXED,
int options = OPT_DEFAULTS,
const std::string& certificateStoreName = CERT_STORE_MY);
/// Creates a Context.
///
/// * usage specifies whether the context is used by a client or server,
/// 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.
/// 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
Expand Down Expand Up @@ -208,7 +210,7 @@ class NetSSL_Win_API Context: public Poco::RefCountedObject
Context::VerificationMode _mode;
int _options;
bool _extendedCertificateVerification;
std::string _certNameOrPath;
std::string _certInfoOrPath;
std::string _certStoreName;
HCERTSTORE _hMemCertStore;
HCERTSTORE _hCollectionCertStore;
Expand Down
6 changes: 6 additions & 0 deletions NetSSL_Win/include/Poco/Net/SSLManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class NetSSL_Win_API SSLManager
/// <schannel>
/// <server|client>
/// <certificateName>cert Id</certificateName>
/// <certificateHash>cert thumbprint</certificateHash>
/// <certificatePath>path of a certificate</certificatePath>
/// <certificateStore>MY</certificateStore>
/// <verificationMode>none|relaxed|strict</verificationMode>
/// <revocationCheck>true|false</revocationCheck>
Expand Down Expand Up @@ -101,6 +103,8 @@ class NetSSL_Win_API SSLManager
///
/// - certificateName (string): The subject name of the certificate to use. The certificate must
/// 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.
/// - certificateStore (string): The certificate store location to use.
/// Valid values are "MY", "Root", "Trust" or "CA". Defaults to "MY".
Expand Down Expand Up @@ -267,6 +271,8 @@ class NetSSL_Win_API SSLManager

static const std::string CFG_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 VAL_CERT_PATH;
static const std::string CFG_CERT_STORE;
Expand Down
69 changes: 51 additions & 18 deletions NetSSL_Win/src/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
#include "Poco/MemoryStream.h"
#include "Poco/Base64Decoder.h"
#include "Poco/Buffer.h"
#include "Poco/HexBinaryDecoder.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HexBinaryDecoder is included but not used.

#include <sstream>
#include <algorithm>
#include <cctype>
#include <stdio.h>


namespace Poco {
Expand All @@ -39,15 +42,15 @@ const std::string Context::CERT_STORE_USERDS("USERDS");


Context::Context(Usage usage,
const std::string& certNameOrPath,
const std::string& certificateInfoOrPath,
VerificationMode verMode,
int options,
const std::string& certStore):
_usage(usage),
_mode(verMode),
_options(options),
_extendedCertificateVerification(true),
_certNameOrPath(certNameOrPath),
_certInfoOrPath(certificateInfoOrPath),
_certStoreName(certStore),
_hMemCertStore(0),
_hCollectionCertStore(0),
Expand Down Expand Up @@ -144,7 +147,7 @@ Poco::Net::X509Certificate Context::certificate()
if (_pCert)
return Poco::Net::X509Certificate(_pCert, true);

if (_certNameOrPath.empty())
if (_certInfoOrPath.empty())
throw NoCertificateException("Certificate requested, but no certificate name or path provided");

if (_options & OPT_LOAD_CERT_FROM_FILE)
Expand All @@ -167,35 +170,65 @@ void Context::loadCertificate()
if (!_hCertStore)
{
if (_options & OPT_USE_MACHINE_STORE)
_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, _certStoreName.c_str());
_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG, wcertStore.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();
// Find the certificate either using name or hash.
if(_options & OPT_USE_CERT_HASH)
{
// Sanity check for the hash value.
if(_certInfoOrPath.size() % 2) throw CertificateException(Poco::format("Invalid certificate hash %s", _certInfoOrPath));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The length of _certInfoOrPath should be checked here as well to prevent a potential buffer overrun later on.

// Convert hex to binary.
BYTE buffer[256] = {};
int bufferSize = 0;
int byte = 0;
std::string szHex;
for(int counter = 0; counter < _certInfoOrPath.size() / 2; counter++)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly use HexBinaryDecoder here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to use it, but for some reason it's not working as I expected.

{
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;
cert_rdn.cRDNAttr = 1;
cert_rdn.rgRDNAttr = &cert_rdn_attr;
CRYPT_HASH_BLOB chBlob;
chBlob.cbData = bufferSize;
chBlob.pbData = buffer;

_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));
_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", _certInfoOrPath, _certStoreName));
}
else
{
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) _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()
{
Poco::File certFile(_certNameOrPath);
if (!certFile.exists()) throw Poco::FileNotFoundException(_certNameOrPath);
Poco::File certFile(_certInfoOrPath);
if (!certFile.exists()) throw Poco::FileNotFoundException(_certInfoOrPath);
Poco::File::FileSize size = certFile.getSize();
if (size > 4096) throw Poco::DataFormatException("PKCS #12 certificate file too large", _certNameOrPath);
if (size > 4096) throw Poco::DataFormatException("PKCS #12 certificate file too large", _certInfoOrPath);
Poco::Buffer<char> buffer(static_cast<std::size_t>(size));
Poco::FileInputStream istr(_certNameOrPath);
Poco::FileInputStream istr(_certInfoOrPath);
istr.read(buffer.begin(), buffer.size());
if (istr.gcount() != size) throw Poco::IOException("error reading PKCS #12 certificate file");
importCertificate(buffer.begin(), buffer.size());
Expand Down
16 changes: 12 additions & 4 deletions NetSSL_Win/src/SSLManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ namespace Net {

const std::string SSLManager::CFG_CERT_NAME("certificateName");
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::VAL_CERT_PATH("");
const std::string SSLManager::CFG_CERT_STORE("certificateStore");
Expand Down Expand Up @@ -188,7 +190,8 @@ void SSLManager::initDefaultContext(bool server)

const std::string prefix = server ? CFG_SERVER_PREFIX : CFG_CLIENT_PREFIX;
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 certStore = config.getString(prefix + CFG_CERT_STORE, VAL_CERT_STORE);

Expand Down Expand Up @@ -217,7 +220,12 @@ void SSLManager::initDefaultContext(bool server)
if (!certPath.empty())
{
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;
Expand All @@ -231,7 +239,7 @@ void SSLManager::initDefaultContext(bool server)
usage = Context::TLSV1_SERVER_USE;
else
usage = Context::SERVER_USE;
_ptrDefaultServerContext = new Context(usage, certName, verMode, options, certStore);
_ptrDefaultServerContext = new Context(usage, certInfo, verMode, options, certStore);
}
else
{
Expand All @@ -243,7 +251,7 @@ void SSLManager::initDefaultContext(bool server)
usage = Context::TLSV1_CLIENT_USE;
else
usage = Context::CLIENT_USE;
_ptrDefaultClientContext = new Context(usage, certName, verMode, options, certStore);
_ptrDefaultClientContext = new Context(usage, certInfo, verMode, options, certStore);
}
}

Expand Down