Merge branch 'develop' of https://github.com/pocoproject/poco into develop

This commit is contained in:
Guenter Obiltschnig 2014-10-10 08:13:24 +02:00
commit af38d47df2
9 changed files with 207 additions and 175 deletions

View File

@ -78,7 +78,6 @@ HTTPSession::~HTTPSession()
} }
catch (...) catch (...)
{ {
poco_unexpected();
} }
delete _pException; delete _pException;
} }

View File

@ -188,7 +188,7 @@ protected:
void performServerHandshake(); void performServerHandshake();
bool serverHandshakeLoop(PCtxtHandle phContext, PCredHandle phCred, bool requireClientAuth, bool doInitialRead, bool newContext); bool serverHandshakeLoop(PCtxtHandle phContext, PCredHandle phCred, bool requireClientAuth, bool doInitialRead, bool newContext);
void clientVerifyCertificate(const std::string& hostName); void clientVerifyCertificate(const std::string& hostName);
void verifyCertificateChainClient(PCCERT_CONTEXT pServerCert, PCCERT_CHAIN_CONTEXT pChainContext); void verifyCertificateChainClient(PCCERT_CONTEXT pServerCert);
void serverVerifyCertificate(); void serverVerifyCertificate();
LONG serverDisconnect(PCredHandle phCreds, CtxtHandle* phContext); LONG serverDisconnect(PCredHandle phCreds, CtxtHandle* phContext);
LONG clientDisconnect(PCredHandle phCreds, CtxtHandle* phContext); LONG clientDisconnect(PCredHandle phCreds, CtxtHandle* phContext);

View File

@ -3,7 +3,7 @@
// //
// $Id$ // $Id$
// //
// Library: Crypto // Library: NetSSL_Win
// Package: Certificate // Package: Certificate
// Module: X509Certificate // Module: X509Certificate
// //
@ -22,8 +22,8 @@
#include "Poco/Net/NetSSL.h" #include "Poco/Net/NetSSL.h"
#include "Poco/DateTime.h" #include "Poco/DateTime.h"
#include "Poco/SharedPtr.h"
#include <set> #include <set>
#include <istream>
#include <wincrypt.h> #include <wincrypt.h>
@ -51,6 +51,10 @@ public:
/// Creates the X509Certificate object by reading /// Creates the X509Certificate object by reading
/// a certificate in PEM or DER format from a file. /// a certificate in PEM or DER format from a file.
explicit X509Certificate(std::istream& istr);
/// Creates the X509Certificate object by reading
/// a certificate in PEM or DER format from a stream.
X509Certificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore = false); X509Certificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore = false);
/// Creates the X509Certificate object by loading /// Creates the X509Certificate object by loading
/// a certificate from the specified certificate store. /// a certificate from the specified certificate store.
@ -116,9 +120,8 @@ public:
/// the issuer given by issuerCertificate. This can be /// the issuer given by issuerCertificate. This can be
/// used to validate a certificate chain. /// used to validate a certificate chain.
/// ///
/// Verifies if the certificate has been signed with the /// Verifies that the given certificate is contained in the
/// issuer's private key, using the public key from the issuer /// certificate's issuer certificate chain.
/// certificate.
/// ///
/// Returns true if verification against the issuer certificate /// Returns true if verification against the issuer certificate
/// was successful, false otherwise. /// was successful, false otherwise.
@ -140,7 +143,6 @@ public:
/// of the host. /// of the host.
/// ///
/// Returns true if verification succeeded, or false otherwise. /// Returns true if verification succeeded, or false otherwise.
const PCCERT_CONTEXT system() const; const PCCERT_CONTEXT system() const;
/// Returns the underlying WinCrypt certificate. /// Returns the underlying WinCrypt certificate.
@ -154,6 +156,7 @@ protected:
void loadCertificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore); void loadCertificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore);
void importCertificate(const std::string& certPath); void importCertificate(const std::string& certPath);
void importCertificate(std::istream& istr);
void importCertificate(const char* pBuffer, std::size_t size); void importCertificate(const char* pBuffer, std::size_t size);
void importPEMCertificate(const char* pBuffer, std::size_t size); void importPEMCertificate(const char* pBuffer, std::size_t size);
void importDERCertificate(const char* pBuffer, std::size_t size); void importDERCertificate(const char* pBuffer, std::size_t size);
@ -162,11 +165,6 @@ protected:
static bool matchWildcard(const std::string& alias, const std::string& hostName); static bool matchWildcard(const std::string& alias, const std::string& hostName);
private: private:
enum
{
NAME_BUFFER_SIZE = 256
};
std::string _issuerName; std::string _issuerName;
std::string _subjectName; std::string _subjectName;
PCCERT_CONTEXT _pCert; PCCERT_CONTEXT _pCert;

View File

@ -3,7 +3,9 @@
HTTPSTimeServer.format = %W, %e %b %y %H:%M:%S %Z HTTPSTimeServer.format = %W, %e %b %y %H:%M:%S %Z
HTTPSTimeServer.port = 9443 HTTPSTimeServer.port = 9443
schannel.server.certificateName = ${system.nodeName} schannel.server.certificatePath = ${application.configDir}any.pfx
schannel.server.privateKeyPassphraseHandler.name = KeyFileHandler
schannel.server.privateKeyPassphraseHandler.options.password = secret
schannel.server.verificationMode = none schannel.server.verificationMode = none
schannel.server.useMachineStore = false schannel.server.useMachineStore = false
schannel.server.useStrongCrypto = true schannel.server.useStrongCrypto = true

Binary file not shown.

View File

@ -18,6 +18,7 @@
#include "Poco/Net/StringPartSource.h" #include "Poco/Net/StringPartSource.h"
#include "Poco/Net/SSLManager.h" #include "Poco/Net/SSLManager.h"
#include "Poco/Net/ConsoleCertificateHandler.h" #include "Poco/Net/ConsoleCertificateHandler.h"
#include "Poco/Net/PrivateKeyPassphraseHandler.h"
#include "Poco/SharedPtr.h" #include "Poco/SharedPtr.h"
#include "Poco/Path.h" #include "Poco/Path.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
@ -83,7 +84,7 @@ int main(int argc, char** argv)
// Note: we must create the passphrase handler prior Context // Note: we must create the passphrase handler prior Context
SharedPtr<InvalidCertificateHandler> pCert = new ConsoleCertificateHandler(false); // ask the user via console SharedPtr<InvalidCertificateHandler> pCert = new ConsoleCertificateHandler(false); // ask the user via console
Context::Ptr pContext = new Context(Context::CLIENT_USE, ""); Context::Ptr pContext = new Context(Context::CLIENT_USE, "");
SSLManager::instance().initializeClient(pCert, pContext); SSLManager::instance().initializeClient(0, pCert, pContext);
MailMessage message; MailMessage message;
message.setSender(sender); message.setSender(sender);

View File

@ -23,6 +23,7 @@
#include "Poco/Net/FTPStreamFactory.h" #include "Poco/Net/FTPStreamFactory.h"
#include "Poco/Net/SSLManager.h" #include "Poco/Net/SSLManager.h"
#include "Poco/Net/ConsoleCertificateHandler.h" #include "Poco/Net/ConsoleCertificateHandler.h"
#include "Poco/Net/PrivateKeyPassphraseHandler.h"
#include <memory> #include <memory>
#include <iostream> #include <iostream>
@ -75,7 +76,7 @@ int main(int argc, char** argv)
SharedPtr<InvalidCertificateHandler> pCertHandler = new ConsoleCertificateHandler(false); // ask the user via console SharedPtr<InvalidCertificateHandler> pCertHandler = new ConsoleCertificateHandler(false); // ask the user via console
Context::Ptr pContext = new Context(Context::CLIENT_USE, ""); Context::Ptr pContext = new Context(Context::CLIENT_USE, "");
SSLManager::instance().initializeClient(pCertHandler, pContext); SSLManager::instance().initializeClient(0, pCertHandler, pContext);
try try
{ {

View File

@ -1175,15 +1175,24 @@ void SecureSocketImpl::clientVerifyCertificate(const std::string& hostName)
if (!args.getIgnoreError()) if (!args.getIgnoreError())
throw InvalidCertificateException("Host name verification failed"); throw InvalidCertificateException("Host name verification failed");
} }
int iRc = CertVerifyTimeValidity(NULL, _pPeerCertificate->pCertInfo);
if (iRc != 0) LONG rc = CertVerifyTimeValidity(0, _pPeerCertificate->pCertInfo);
if (rc != 0)
{ {
VerificationErrorArgs args(cert, 0, SEC_E_CERT_EXPIRED, "The certificate is expired"); VerificationErrorArgs args(cert, 0, SEC_E_CERT_EXPIRED, "The certificate is not yet, or no longer valid");
SSLManager::instance().ClientVerificationError(this, args); SSLManager::instance().ClientVerificationError(this, args);
if (!args.getIgnoreError()) if (!args.getIgnoreError())
throw InvalidCertificateException("Expired certificate"); throw InvalidCertificateException("Expired certificate");
} }
verifyCertificateChainClient(_pPeerCertificate);
}
void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert)
{
X509Certificate cert(pServerCert, true);
CERT_CHAIN_PARA chainPara; CERT_CHAIN_PARA chainPara;
PCCERT_CHAIN_CONTEXT pChainContext = NULL; PCCERT_CHAIN_CONTEXT pChainContext = NULL;
std::memset(&chainPara, 0, sizeof(chainPara)); std::memset(&chainPara, 0, sizeof(chainPara));
@ -1199,24 +1208,15 @@ void SecureSocketImpl::clientVerifyCertificate(const std::string& hostName)
NULL, NULL,
&pChainContext)) &pChainContext))
{ {
CertFreeCertificateChain(pChainContext); throw SSLException("Cannot get certificate chain", GetLastError());
throw SSLException("Failed to get certificate chain", GetLastError());
} }
verifyCertificateChainClient(_pPeerCertificate, pChainContext);
}
void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert, PCCERT_CHAIN_CONTEXT pChainContext)
{
X509Certificate cert(pServerCert, true);
HTTPSPolicyCallbackData polHttps; HTTPSPolicyCallbackData polHttps;
std::memset(&polHttps, 0, sizeof(HTTPSPolicyCallbackData)); std::memset(&polHttps, 0, sizeof(HTTPSPolicyCallbackData));
polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
polHttps.dwAuthType = AUTHTYPE_SERVER; polHttps.dwAuthType = AUTHTYPE_SERVER;
polHttps.fdwChecks = SECURITY_FLAG_IGNORE_UNKNOWN_CA; // we do our own CA verification! polHttps.fdwChecks = SECURITY_FLAG_IGNORE_UNKNOWN_CA; // we do our own check later on
polHttps.pwszServerName = 0;// not supported on Win98, ME! but ignored on client side anyway polHttps.pwszServerName = 0;
CERT_CHAIN_POLICY_PARA polPara; CERT_CHAIN_POLICY_PARA polPara;
std::memset(&polPara, 0, sizeof(polPara)); std::memset(&polPara, 0, sizeof(polPara));
@ -1238,12 +1238,11 @@ void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert,
if (!args.getIgnoreError()) if (!args.getIgnoreError())
{ {
CertFreeCertificateChain(pChainContext); CertFreeCertificateChain(pChainContext);
throw SSLException("Failed to verify certificate chain"); throw SSLException("Cannot verify certificate chain");
} }
else return; else return;
} }
else if (polStatus.dwError)
if (polStatus.dwError)
{ {
VerificationErrorArgs args(cert, polStatus.lElementIndex, polStatus.dwError, Utility::formatError(polStatus.dwError)); VerificationErrorArgs args(cert, polStatus.lElementIndex, polStatus.dwError, Utility::formatError(polStatus.dwError));
SSLManager::instance().ClientVerificationError(this, args); SSLManager::instance().ClientVerificationError(this, args);
@ -1257,13 +1256,15 @@ void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert,
// now verify CA's // now verify CA's
HCERTSTORE trustedCerts = _pContext->certificateStore(); HCERTSTORE trustedCerts = _pContext->certificateStore();
Poco::Buffer<PCERT_CONTEXT> certs(pChainContext->cChain);
for (DWORD i = 0; i < pChainContext->cChain; i++) for (DWORD i = 0; i < pChainContext->cChain; i++)
{ {
certs[i] = (PCERT_CONTEXT)(pChainContext->rgpChain[i]->rgpElement[0]->pCertContext); std::vector<PCCERT_CONTEXT> certs;
// each issuer of the pCert must be a member of the trustedCerts store for (DWORD k = 0; k < pChainContext->rgpChain[i]->cElement; k++)
PCCERT_CONTEXT pResult = CertFindCertificateInStore(trustedCerts, certs[i]->dwCertEncodingType, 0, CERT_FIND_ISSUER_OF, certs[i], 0); {
certs.push_back(pChainContext->rgpChain[i]->rgpElement[k]->pCertContext);
}
// verify that the root of the chain can be found in the trusted store
PCCERT_CONTEXT pResult = CertFindCertificateInStore(trustedCerts, certs.back()->dwCertEncodingType, 0, CERT_FIND_ISSUER_OF, certs.back(), 0);
if (!pResult) if (!pResult)
{ {
poco_assert_dbg (GetLastError() == CRYPT_E_NOT_FOUND); poco_assert_dbg (GetLastError() == CRYPT_E_NOT_FOUND);
@ -1276,34 +1277,36 @@ void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert,
return; return;
} }
CertFreeCertificateContext(pResult); CertFreeCertificateContext(pResult);
}
#if !defined(_WIN32_WCE) #if !defined(_WIN32_WCE)
// check if cert is revoked // check if cert is revoked
if (_pContext->options() & Context::OPT_PERFORM_REVOCATION_CHECK) if (_pContext->options() & Context::OPT_PERFORM_REVOCATION_CHECK)
{
CERT_REVOCATION_STATUS revStat;
revStat.cbSize = sizeof(CERT_REVOCATION_STATUS);
PCERT_CONTEXT* pCerts = certs.begin();
BOOL rc = CertVerifyRevocation(
X509_ASN_ENCODING,
CERT_CONTEXT_REVOCATION_TYPE,
pChainContext->cChain,
(void **)pCerts,
CERT_VERIFY_REV_CHAIN_FLAG,
NULL,
&revStat);
if (!rc)
{ {
VerificationErrorArgs args(cert, revStat.dwIndex, revStat.dwReason, Utility::formatError(revStat.dwError)); CERT_REVOCATION_STATUS revStat;
SSLManager::instance().ClientVerificationError(this, args); revStat.cbSize = sizeof(CERT_REVOCATION_STATUS);
if (!args.getIgnoreError())
BOOL ok = CertVerifyRevocation(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CERT_CONTEXT_REVOCATION_TYPE,
certs.size(),
(void**) &certs[0],
CERT_VERIFY_REV_CHAIN_FLAG,
NULL,
&revStat);
// Revocation check of the root certificate may fail due to missing CRL points, etc.
// We ignore all errors checking the root certificate except CRYPT_E_REVOKED.
if (!ok && (revStat.dwIndex < certs.size() - 1 || revStat.dwError == CRYPT_E_REVOKED))
{ {
CertFreeCertificateChain(pChainContext); VerificationErrorArgs args(cert, revStat.dwIndex, revStat.dwReason, Utility::formatError(revStat.dwError));
throw SSLException("Failed to verify revoked certificate chain"); SSLManager::instance().ClientVerificationError(this, args);
if (!args.getIgnoreError())
{
CertFreeCertificateChain(pChainContext);
throw SSLException("Failed to verify revoked certificate chain");
}
} }
else break;
} }
} }
#endif #endif
@ -1321,10 +1324,10 @@ void SecureSocketImpl::serverVerifyCertificate()
DWORD status = SEC_E_OK; DWORD status = SEC_E_OK;
X509Certificate cert(_pPeerCertificate, true); X509Certificate cert(_pPeerCertificate, true);
int iRc = CertVerifyTimeValidity(NULL, _pPeerCertificate->pCertInfo); LONG rc = CertVerifyTimeValidity(0, _pPeerCertificate->pCertInfo);
if (iRc != 0) if (rc != 0)
{ {
VerificationErrorArgs args(cert, 0, SEC_E_CERT_EXPIRED, "The certificate is expired"); VerificationErrorArgs args(cert, 0, SEC_E_CERT_EXPIRED, "The certificate is not yet, or no longer valid");
SSLManager::instance().ServerVerificationError(this, args); SSLManager::instance().ServerVerificationError(this, args);
if (!args.getIgnoreError()) if (!args.getIgnoreError())
@ -1348,14 +1351,7 @@ void SecureSocketImpl::serverVerifyCertificate()
NULL, NULL,
&pChainContext)) &pChainContext))
{ {
VerificationErrorArgs args(cert, 0, GetLastError(), "The certificate chain is expired"); throw SSLException("Cannot get certificate chain", GetLastError());
SSLManager::instance().ServerVerificationError(this, args);
if (pChainContext) CertFreeCertificateChain(pChainContext);
if (!args.getIgnoreError())
{
throw SSLException("The certificate chain is expired");
}
else return;
} }
HTTPSPolicyCallbackData polHttps; HTTPSPolicyCallbackData polHttps;
@ -1363,7 +1359,7 @@ void SecureSocketImpl::serverVerifyCertificate()
polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
polHttps.dwAuthType = AUTHTYPE_CLIENT; polHttps.dwAuthType = AUTHTYPE_CLIENT;
polHttps.fdwChecks = 0; polHttps.fdwChecks = 0;
polHttps.pwszServerName = NULL; polHttps.pwszServerName = 0;
CERT_CHAIN_POLICY_PARA policyPara; CERT_CHAIN_POLICY_PARA policyPara;
std::memset(&policyPara, 0, sizeof(policyPara)); std::memset(&policyPara, 0, sizeof(policyPara));
@ -1378,18 +1374,17 @@ void SecureSocketImpl::serverVerifyCertificate()
{ {
VerificationErrorArgs args(cert, 0, GetLastError(), "Failed to verify certificate chain"); VerificationErrorArgs args(cert, 0, GetLastError(), "Failed to verify certificate chain");
SSLManager::instance().ServerVerificationError(this, args); SSLManager::instance().ServerVerificationError(this, args);
if (pChainContext) CertFreeCertificateChain(pChainContext); CertFreeCertificateChain(pChainContext);
if (!args.getIgnoreError()) if (!args.getIgnoreError())
throw SSLException("Failed to verify certificate chain"); throw SSLException("Cannot verify certificate chain");
else else
return; return;
} }
else if (policyStatus.dwError)
if (policyStatus.dwError)
{ {
VerificationErrorArgs args(cert, policyStatus.lElementIndex, status, Utility::formatError(policyStatus.dwError)); VerificationErrorArgs args(cert, policyStatus.lElementIndex, status, Utility::formatError(policyStatus.dwError));
SSLManager::instance().ServerVerificationError(this, args); SSLManager::instance().ServerVerificationError(this, args);
if (pChainContext) CertFreeCertificateChain(pChainContext); CertFreeCertificateChain(pChainContext);
if (!args.getIgnoreError()) if (!args.getIgnoreError())
throw SSLException("Failed to verify certificate chain"); throw SSLException("Failed to verify certificate chain");
else else
@ -1397,36 +1392,37 @@ void SecureSocketImpl::serverVerifyCertificate()
} }
#if !defined(_WIN32_WCE) #if !defined(_WIN32_WCE)
PCERT_CONTEXT *pCerts = new PCERT_CONTEXT[pChainContext->cChain]; // perform revocation checking
for (DWORD i = 0; i < pChainContext->cChain; i++) for (DWORD i = 0; i < pChainContext->cChain; i++)
{ {
pCerts[i] = (PCERT_CONTEXT)(pChainContext->rgpChain[i]->rgpElement[0]->pCertContext); std::vector<PCCERT_CONTEXT> certs;
} for (DWORD k = 0; k < pChainContext->rgpChain[i]->cElement; k++)
CERT_REVOCATION_STATUS revStat;
revStat.cbSize = sizeof(CERT_REVOCATION_STATUS);
BOOL bRc = CertVerifyRevocation(
X509_ASN_ENCODING,
CERT_CONTEXT_REVOCATION_TYPE,
pChainContext->cChain,
(void **)pCerts,
CERT_VERIFY_REV_CHAIN_FLAG,
NULL,
&revStat);
if (!bRc)
{
VerificationErrorArgs args(cert, revStat.dwIndex, revStat.dwReason, Utility::formatError(revStat.dwReason));
SSLManager::instance().ServerVerificationError(this, args);
if (!args.getIgnoreError())
{ {
CertFreeCertificateChain(pChainContext); certs.push_back(pChainContext->rgpChain[i]->rgpElement[k]->pCertContext);
delete [] pCerts; }
throw SSLException("Failed to verify certificate chain");
CERT_REVOCATION_STATUS revStat;
revStat.cbSize = sizeof(CERT_REVOCATION_STATUS);
BOOL ok = CertVerifyRevocation(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CERT_CONTEXT_REVOCATION_TYPE,
certs.size(),
(void**) &certs[0],
CERT_VERIFY_REV_CHAIN_FLAG,
NULL,
&revStat);
if (!ok && (revStat.dwIndex < certs.size() - 1 || revStat.dwError == CRYPT_E_REVOKED))
{
VerificationErrorArgs args(cert, revStat.dwIndex, revStat.dwReason, Utility::formatError(revStat.dwReason));
SSLManager::instance().ServerVerificationError(this, args);
if (!args.getIgnoreError())
{
CertFreeCertificateChain(pChainContext);
throw SSLException("Failed to verify certificate chain");
}
} }
} }
delete [] pCerts;
#endif #endif
if (pChainContext) if (pChainContext)
{ {
@ -1439,8 +1435,6 @@ LONG SecureSocketImpl::clientDisconnect(PCredHandle phCreds, CtxtHandle* phConte
{ {
if (phContext->dwLower == 0 && phContext->dwUpper == 0) if (phContext->dwLower == 0 && phContext->dwUpper == 0)
{ {
// handshake has never been done
poco_assert_dbg (_needHandshake);
return SEC_E_OK; return SEC_E_OK;
} }

View File

@ -3,7 +3,7 @@
// //
// $Id$ // $Id$
// //
// Library: Crypto // Library: NetSSL_Win
// Package: Certificate // Package: Certificate
// Module: X509Certificate // Module: X509Certificate
// //
@ -42,6 +42,13 @@ X509Certificate::X509Certificate(const std::string& path):
} }
X509Certificate::X509Certificate(std::istream& istr):
_pCert(0)
{
importCertificate(istr);
}
X509Certificate::X509Certificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore): X509Certificate::X509Certificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore):
_pCert(0) _pCert(0)
{ {
@ -106,11 +113,55 @@ X509Certificate::~X509Certificate()
void X509Certificate::init() void X509Certificate::init()
{ {
wchar_t data[256]; std::string name = issuerName(NID_COUNTRY);
CertGetNameStringW(_pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, data, 256); if (!name.empty())
Poco::UnicodeConverter::convert(data, _issuerName); {
CertGetNameStringW(_pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, data, 256); _issuerName += "/C=";
Poco::UnicodeConverter::convert(data, _subjectName); _issuerName += name;
}
name = issuerName(NID_STATE_OR_PROVINCE);
if (!name.empty())
{
_issuerName += "/ST=";
_issuerName += name;
}
name = issuerName(NID_ORGANIZATION_NAME);
if (!name.empty())
{
_issuerName += "/O=";
_issuerName += name;
}
name = issuerName(NID_COMMON_NAME);
if (!name.empty())
{
_issuerName += "/CN=";
_issuerName += name;
}
name = subjectName(NID_COUNTRY);
if (!name.empty())
{
_subjectName += "/C=";
_subjectName += name;
}
name = subjectName(NID_STATE_OR_PROVINCE);
if (!name.empty())
{
_subjectName += "/ST=";
_subjectName += name;
}
name = subjectName(NID_ORGANIZATION_NAME);
if (!name.empty())
{
_subjectName += "/O=";
_subjectName += name;
}
name = subjectName(NID_COMMON_NAME);
if (!name.empty())
{
_subjectName += "/CN=";
_subjectName += name;
}
} }
@ -123,9 +174,10 @@ std::string X509Certificate::commonName() const
std::string X509Certificate::issuerName(NID nid) const std::string X509Certificate::issuerName(NID nid) const
{ {
std::string result; std::string result;
wchar_t data[256]; DWORD size = CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, CERT_NAME_ISSUER_FLAG, nid2oid(nid), 0, 0);
CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, CERT_NAME_ISSUER_FLAG, nid2oid(nid), data, 256); Poco::Buffer<wchar_t> data(size);
Poco::UnicodeConverter::convert(data, result); CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, CERT_NAME_ISSUER_FLAG, nid2oid(nid), data.begin(), size);
Poco::UnicodeConverter::convert(data.begin(), result);
return result; return result;
} }
@ -133,9 +185,10 @@ std::string X509Certificate::issuerName(NID nid) const
std::string X509Certificate::subjectName(NID nid) const std::string X509Certificate::subjectName(NID nid) const
{ {
std::string result; std::string result;
wchar_t data[256]; DWORD size = CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, 0, nid2oid(nid), 0, 0);
CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, 0, nid2oid(nid), data, 256); Poco::Buffer<wchar_t> data(size);
Poco::UnicodeConverter::convert(data, result); CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, 0, nid2oid(nid), data.begin(), size);
Poco::UnicodeConverter::convert(data.begin(), result);
return result; return result;
} }
@ -182,7 +235,7 @@ void X509Certificate::extractNames(std::string& cmnName, std::set<std::string>&
PCERT_ALT_NAME_INFO pNameInfo = reinterpret_cast<PCERT_ALT_NAME_INFO>(buffer.begin()); PCERT_ALT_NAME_INFO pNameInfo = reinterpret_cast<PCERT_ALT_NAME_INFO>(buffer.begin());
for (int i = 0; i < pNameInfo->cAltEntry; i++) for (int i = 0; i < pNameInfo->cAltEntry; i++)
{ {
std::wstring waltName(pNameInfo->rgAltEntry->pwszDNSName); std::wstring waltName(pNameInfo->rgAltEntry[i].pwszDNSName);
std::string altName; std::string altName;
Poco::UnicodeConverter::toUTF8(waltName, altName); Poco::UnicodeConverter::toUTF8(waltName, altName);
domainNames.insert(altName); domainNames.insert(altName);
@ -213,68 +266,40 @@ Poco::DateTime X509Certificate::expiresOn() const
bool X509Certificate::issuedBy(const X509Certificate& issuerCertificate) const bool X509Certificate::issuedBy(const X509Certificate& issuerCertificate) const
{ {
class CertStoreHandle CERT_CHAIN_PARA chainPara;
PCCERT_CHAIN_CONTEXT pChainContext = 0;
std::memset(&chainPara, 0, sizeof(chainPara));
chainPara.cbSize = sizeof(chainPara);
if (!CertGetCertificateChain(
NULL,
_pCert,
NULL,
NULL,
&chainPara,
0,
NULL,
&pChainContext))
{ {
public: throw SSLException("Cannot get certificate chain", subjectName(), GetLastError());
CertStoreHandle(HCERTSTORE hStore): }
_hStore(hStore)
{
}
~CertStoreHandle()
{
if (_hStore) CertCloseStore(_hStore, 0);
}
HCERTSTORE handle() const
{
return _hStore;
}
private:
HCERTSTORE _hStore;
};
CertStoreHandle caStore(CertOpenSystemStoreW(0, L"CA"));
CertStoreHandle rootStore(CertOpenSystemStoreW(0, L"ROOT"));
bool result = false; bool result = false;
PCCERT_CONTEXT pIssuer; for (DWORD i = 0; i < pChainContext->cChain && !result; i++)
PCCERT_CONTEXT pIssued = _pCert;
do
{ {
DWORD flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG; for (DWORD k = 0; k < pChainContext->rgpChain[i]->cElement && !result; k++)
pIssuer = 0;
if (caStore.handle())
{ {
pIssuer = CertGetIssuerCertificateFromStore(caStore.handle(), pIssued, 0, &flags); PCCERT_CONTEXT pChainCert = pChainContext->rgpChain[i]->rgpElement[k]->pCertContext;
} if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, issuerCertificate.system()->pCertInfo, pChainCert->pCertInfo))
if (!pIssuer && rootStore.handle())
{
pIssuer = CertGetIssuerCertificateFromStore(rootStore.handle(), pIssued, 0, &flags);
}
if (pIssuer)
{
X509Certificate issuer(pIssuer);
if (flags & CERT_STORE_NO_CRL_FLAG)
flags &= ~(CERT_STORE_NO_CRL_FLAG | CERT_STORE_REVOCATION_FLAG);
if (flags)
break;
if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, issuerCertificate.system()->pCertInfo, issuer.system()->pCertInfo))
{ {
result = true; if (CertComparePublicKeyInfo(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &issuerCertificate.system()->pCertInfo->SubjectPublicKeyInfo, &pChainCert->pCertInfo->SubjectPublicKeyInfo))
break; {
} result = true;
if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pIssuer->pCertInfo, pIssued->pCertInfo)) }
{
// reached self-signed certificate
break;
} }
} }
else break; }
} CertFreeCertificateChain(pChainContext);
while (pIssuer);
return result; return result;
} }
@ -326,8 +351,8 @@ void X509Certificate::loadCertificate(const std::string& certName, const std::st
CERT_RDN_ATTR cert_rdn_attr; CERT_RDN_ATTR cert_rdn_attr;
cert_rdn_attr.pszObjId = szOID_COMMON_NAME; cert_rdn_attr.pszObjId = szOID_COMMON_NAME;
cert_rdn_attr.dwValueType = CERT_RDN_ANY_TYPE; cert_rdn_attr.dwValueType = CERT_RDN_ANY_TYPE;
cert_rdn_attr.Value.cbData = (DWORD) certName.size(); cert_rdn_attr.Value.cbData = static_cast<DWORD>(certName.size());
cert_rdn_attr.Value.pbData = (BYTE *) certName.c_str(); cert_rdn_attr.Value.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(certName.c_str()));
CERT_RDN cert_rdn; CERT_RDN cert_rdn;
cert_rdn.cRDNAttr = 1; cert_rdn.cRDNAttr = 1;
@ -358,6 +383,18 @@ void X509Certificate::importCertificate(const std::string& certPath)
} }
void X509Certificate::importCertificate(std::istream& istr)
{
std::string data;
Poco::StreamCopier::copyToString(istr, data);
if (!data.empty())
{
importCertificate(data.data(), data.size());
}
else throw Poco::IOException("failed to read certificate from stream");
}
void X509Certificate::importCertificate(const char* pBuffer, std::size_t size) void X509Certificate::importCertificate(const char* pBuffer, std::size_t size)
{ {
if (std::memcmp(pBuffer, "-----BEGIN CERTIFICATE-----", 27) == 0) if (std::memcmp(pBuffer, "-----BEGIN CERTIFICATE-----", 27) == 0)