From f65d98f9c1b7582e929832f96e490fa7d213e143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Thu, 9 Oct 2014 23:56:00 +0200 Subject: [PATCH 1/2] fixed cert chain verification and samples --- Net/src/HTTPSession.cpp | 1 - .../include/Poco/Net/SecureSocketImpl.h | 2 +- .../HTTPSTimeServer.properties | 4 +- NetSSL_Win/samples/HTTPSTimeServer/any.pfx | Bin 0 -> 2653 bytes NetSSL_Win/samples/Mail/src/Mail.cpp | 3 +- NetSSL_Win/samples/download/src/download.cpp | 2 +- NetSSL_Win/src/SecureSocketImpl.cpp | 174 +++++++++--------- 7 files changed, 91 insertions(+), 95 deletions(-) create mode 100644 NetSSL_Win/samples/HTTPSTimeServer/any.pfx diff --git a/Net/src/HTTPSession.cpp b/Net/src/HTTPSession.cpp index 1abcaf996..030ec7246 100644 --- a/Net/src/HTTPSession.cpp +++ b/Net/src/HTTPSession.cpp @@ -78,7 +78,6 @@ HTTPSession::~HTTPSession() } catch (...) { - poco_unexpected(); } delete _pException; } diff --git a/NetSSL_Win/include/Poco/Net/SecureSocketImpl.h b/NetSSL_Win/include/Poco/Net/SecureSocketImpl.h index da9ccb1f3..cc280cba7 100644 --- a/NetSSL_Win/include/Poco/Net/SecureSocketImpl.h +++ b/NetSSL_Win/include/Poco/Net/SecureSocketImpl.h @@ -188,7 +188,7 @@ protected: void performServerHandshake(); bool serverHandshakeLoop(PCtxtHandle phContext, PCredHandle phCred, bool requireClientAuth, bool doInitialRead, bool newContext); void clientVerifyCertificate(const std::string& hostName); - void verifyCertificateChainClient(PCCERT_CONTEXT pServerCert, PCCERT_CHAIN_CONTEXT pChainContext); + void verifyCertificateChainClient(PCCERT_CONTEXT pServerCert); void serverVerifyCertificate(); LONG serverDisconnect(PCredHandle phCreds, CtxtHandle* phContext); LONG clientDisconnect(PCredHandle phCreds, CtxtHandle* phContext); diff --git a/NetSSL_Win/samples/HTTPSTimeServer/HTTPSTimeServer.properties b/NetSSL_Win/samples/HTTPSTimeServer/HTTPSTimeServer.properties index 3cf34a9f8..1786a8bfa 100644 --- a/NetSSL_Win/samples/HTTPSTimeServer/HTTPSTimeServer.properties +++ b/NetSSL_Win/samples/HTTPSTimeServer/HTTPSTimeServer.properties @@ -3,7 +3,9 @@ HTTPSTimeServer.format = %W, %e %b %y %H:%M:%S %Z 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.useMachineStore = false schannel.server.useStrongCrypto = true diff --git a/NetSSL_Win/samples/HTTPSTimeServer/any.pfx b/NetSSL_Win/samples/HTTPSTimeServer/any.pfx new file mode 100644 index 0000000000000000000000000000000000000000..cc0ad5d17ffb7165f9990d7ffb0c7a9aa6172544 GIT binary patch literal 2653 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 Fe*!8&;bi~- literal 0 HcmV?d00001 diff --git a/NetSSL_Win/samples/Mail/src/Mail.cpp b/NetSSL_Win/samples/Mail/src/Mail.cpp index cca8036c2..cb37e0961 100644 --- a/NetSSL_Win/samples/Mail/src/Mail.cpp +++ b/NetSSL_Win/samples/Mail/src/Mail.cpp @@ -18,6 +18,7 @@ #include "Poco/Net/StringPartSource.h" #include "Poco/Net/SSLManager.h" #include "Poco/Net/ConsoleCertificateHandler.h" +#include "Poco/Net/PrivateKeyPassphraseHandler.h" #include "Poco/SharedPtr.h" #include "Poco/Path.h" #include "Poco/Exception.h" @@ -83,7 +84,7 @@ int main(int argc, char** argv) // Note: we must create the passphrase handler prior Context SharedPtr pCert = new ConsoleCertificateHandler(false); // ask the user via console Context::Ptr pContext = new Context(Context::CLIENT_USE, ""); - SSLManager::instance().initializeClient(pCert, pContext); + SSLManager::instance().initializeClient(0, pCert, pContext); MailMessage message; message.setSender(sender); diff --git a/NetSSL_Win/samples/download/src/download.cpp b/NetSSL_Win/samples/download/src/download.cpp index 5fbe0470e..c6df7d200 100644 --- a/NetSSL_Win/samples/download/src/download.cpp +++ b/NetSSL_Win/samples/download/src/download.cpp @@ -75,7 +75,7 @@ int main(int argc, char** argv) SharedPtr pCertHandler = new ConsoleCertificateHandler(false); // ask the user via console Context::Ptr pContext = new Context(Context::CLIENT_USE, ""); - SSLManager::instance().initializeClient(pCertHandler, pContext); + SSLManager::instance().initializeClient(0, pCertHandler, pContext); try { diff --git a/NetSSL_Win/src/SecureSocketImpl.cpp b/NetSSL_Win/src/SecureSocketImpl.cpp index e2de60045..8d35b053f 100644 --- a/NetSSL_Win/src/SecureSocketImpl.cpp +++ b/NetSSL_Win/src/SecureSocketImpl.cpp @@ -1175,15 +1175,24 @@ void SecureSocketImpl::clientVerifyCertificate(const std::string& hostName) if (!args.getIgnoreError()) 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); if (!args.getIgnoreError()) throw InvalidCertificateException("Expired certificate"); } + verifyCertificateChainClient(_pPeerCertificate); +} + + +void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert) +{ + X509Certificate cert(pServerCert, true); + CERT_CHAIN_PARA chainPara; PCCERT_CHAIN_CONTEXT pChainContext = NULL; std::memset(&chainPara, 0, sizeof(chainPara)); @@ -1199,24 +1208,15 @@ void SecureSocketImpl::clientVerifyCertificate(const std::string& hostName) NULL, &pChainContext)) { - CertFreeCertificateChain(pChainContext); - throw SSLException("Failed to get certificate chain", GetLastError()); + throw SSLException("Cannot get certificate chain", GetLastError()); } - verifyCertificateChainClient(_pPeerCertificate, pChainContext); -} - - -void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert, PCCERT_CHAIN_CONTEXT pChainContext) -{ - X509Certificate cert(pServerCert, true); - HTTPSPolicyCallbackData polHttps; std::memset(&polHttps, 0, sizeof(HTTPSPolicyCallbackData)); polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); polHttps.dwAuthType = AUTHTYPE_SERVER; - polHttps.fdwChecks = SECURITY_FLAG_IGNORE_UNKNOWN_CA; // we do our own CA verification! - polHttps.pwszServerName = 0;// not supported on Win98, ME! but ignored on client side anyway + polHttps.fdwChecks = SECURITY_FLAG_IGNORE_UNKNOWN_CA; // we do our own check later on + polHttps.pwszServerName = 0; CERT_CHAIN_POLICY_PARA polPara; std::memset(&polPara, 0, sizeof(polPara)); @@ -1238,12 +1238,11 @@ void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert, if (!args.getIgnoreError()) { CertFreeCertificateChain(pChainContext); - throw SSLException("Failed to verify certificate chain"); + throw SSLException("Cannot verify certificate chain"); } else return; } - - if (polStatus.dwError) + else if (polStatus.dwError) { VerificationErrorArgs args(cert, polStatus.lElementIndex, polStatus.dwError, Utility::formatError(polStatus.dwError)); SSLManager::instance().ClientVerificationError(this, args); @@ -1257,13 +1256,15 @@ void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert, // now verify CA's HCERTSTORE trustedCerts = _pContext->certificateStore(); - Poco::Buffer certs(pChainContext->cChain); for (DWORD i = 0; i < pChainContext->cChain; i++) { - certs[i] = (PCERT_CONTEXT)(pChainContext->rgpChain[i]->rgpElement[0]->pCertContext); - // each issuer of the pCert must be a member of the trustedCerts store - PCCERT_CONTEXT pResult = CertFindCertificateInStore(trustedCerts, certs[i]->dwCertEncodingType, 0, CERT_FIND_ISSUER_OF, certs[i], 0); - + std::vector certs; + for (DWORD k = 0; k < pChainContext->rgpChain[i]->cElement; k++) + { + 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) { poco_assert_dbg (GetLastError() == CRYPT_E_NOT_FOUND); @@ -1276,34 +1277,36 @@ void SecureSocketImpl::verifyCertificateChainClient(PCCERT_CONTEXT pServerCert, return; } CertFreeCertificateContext(pResult); - } #if !defined(_WIN32_WCE) - // check if cert is revoked - 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) + // check if cert is revoked + if (_pContext->options() & Context::OPT_PERFORM_REVOCATION_CHECK) { - VerificationErrorArgs args(cert, revStat.dwIndex, revStat.dwReason, Utility::formatError(revStat.dwError)); - SSLManager::instance().ClientVerificationError(this, args); - if (!args.getIgnoreError()) + 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); + + // 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); - throw SSLException("Failed to verify revoked certificate chain"); + VerificationErrorArgs args(cert, revStat.dwIndex, revStat.dwReason, Utility::formatError(revStat.dwError)); + SSLManager::instance().ClientVerificationError(this, args); + if (!args.getIgnoreError()) + { + CertFreeCertificateChain(pChainContext); + throw SSLException("Failed to verify revoked certificate chain"); + } } + else break; } } #endif @@ -1321,10 +1324,10 @@ void SecureSocketImpl::serverVerifyCertificate() DWORD status = SEC_E_OK; X509Certificate cert(_pPeerCertificate, true); - 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().ServerVerificationError(this, args); if (!args.getIgnoreError()) @@ -1348,14 +1351,7 @@ void SecureSocketImpl::serverVerifyCertificate() NULL, &pChainContext)) { - VerificationErrorArgs args(cert, 0, GetLastError(), "The certificate chain is expired"); - SSLManager::instance().ServerVerificationError(this, args); - if (pChainContext) CertFreeCertificateChain(pChainContext); - if (!args.getIgnoreError()) - { - throw SSLException("The certificate chain is expired"); - } - else return; + throw SSLException("Cannot get certificate chain", GetLastError()); } HTTPSPolicyCallbackData polHttps; @@ -1363,7 +1359,7 @@ void SecureSocketImpl::serverVerifyCertificate() polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); polHttps.dwAuthType = AUTHTYPE_CLIENT; polHttps.fdwChecks = 0; - polHttps.pwszServerName = NULL; + polHttps.pwszServerName = 0; CERT_CHAIN_POLICY_PARA policyPara; std::memset(&policyPara, 0, sizeof(policyPara)); @@ -1378,18 +1374,17 @@ void SecureSocketImpl::serverVerifyCertificate() { VerificationErrorArgs args(cert, 0, GetLastError(), "Failed to verify certificate chain"); SSLManager::instance().ServerVerificationError(this, args); - if (pChainContext) CertFreeCertificateChain(pChainContext); + CertFreeCertificateChain(pChainContext); if (!args.getIgnoreError()) - throw SSLException("Failed to verify certificate chain"); + throw SSLException("Cannot verify certificate chain"); else return; } - - if (policyStatus.dwError) + else if (policyStatus.dwError) { VerificationErrorArgs args(cert, policyStatus.lElementIndex, status, Utility::formatError(policyStatus.dwError)); SSLManager::instance().ServerVerificationError(this, args); - if (pChainContext) CertFreeCertificateChain(pChainContext); + CertFreeCertificateChain(pChainContext); if (!args.getIgnoreError()) throw SSLException("Failed to verify certificate chain"); else @@ -1397,36 +1392,37 @@ void SecureSocketImpl::serverVerifyCertificate() } #if !defined(_WIN32_WCE) - PCERT_CONTEXT *pCerts = new PCERT_CONTEXT[pChainContext->cChain]; + // perform revocation checking for (DWORD i = 0; i < pChainContext->cChain; i++) { - pCerts[i] = (PCERT_CONTEXT)(pChainContext->rgpChain[i]->rgpElement[0]->pCertContext); - } - - 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()) + std::vector certs; + for (DWORD k = 0; k < pChainContext->rgpChain[i]->cElement; k++) { - CertFreeCertificateChain(pChainContext); - delete [] pCerts; - throw SSLException("Failed to verify certificate chain"); + certs.push_back(pChainContext->rgpChain[i]->rgpElement[k]->pCertContext); + } + + 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 if (pChainContext) { @@ -1439,8 +1435,6 @@ LONG SecureSocketImpl::clientDisconnect(PCredHandle phCreds, CtxtHandle* phConte { if (phContext->dwLower == 0 && phContext->dwUpper == 0) { - // handshake has never been done - poco_assert_dbg (_needHandshake); return SEC_E_OK; } From 2d5827d332bd4344545dfeb0f27a1b3e435ddf52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Fri, 10 Oct 2014 08:12:04 +0200 Subject: [PATCH 2/2] fixed certificate name verification --- NetSSL_Win/include/Poco/Net/X509Certificate.h | 20 +- NetSSL_Win/samples/download/src/download.cpp | 1 + NetSSL_Win/src/X509Certificate.cpp | 175 +++++++++++------- 3 files changed, 116 insertions(+), 80 deletions(-) diff --git a/NetSSL_Win/include/Poco/Net/X509Certificate.h b/NetSSL_Win/include/Poco/Net/X509Certificate.h index f73f7219a..fe0a0208b 100644 --- a/NetSSL_Win/include/Poco/Net/X509Certificate.h +++ b/NetSSL_Win/include/Poco/Net/X509Certificate.h @@ -3,7 +3,7 @@ // // $Id$ // -// Library: Crypto +// Library: NetSSL_Win // Package: Certificate // Module: X509Certificate // @@ -22,8 +22,8 @@ #include "Poco/Net/NetSSL.h" #include "Poco/DateTime.h" -#include "Poco/SharedPtr.h" #include +#include #include @@ -51,6 +51,10 @@ public: /// Creates the X509Certificate object by reading /// 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); /// Creates the X509Certificate object by loading /// a certificate from the specified certificate store. @@ -116,9 +120,8 @@ public: /// the issuer given by issuerCertificate. This can be /// used to validate a certificate chain. /// - /// Verifies if the certificate has been signed with the - /// issuer's private key, using the public key from the issuer - /// certificate. + /// Verifies that the given certificate is contained in the + /// certificate's issuer certificate chain. /// /// Returns true if verification against the issuer certificate /// was successful, false otherwise. @@ -140,7 +143,6 @@ public: /// of the host. /// /// Returns true if verification succeeded, or false otherwise. - const PCCERT_CONTEXT system() const; /// Returns the underlying WinCrypt certificate. @@ -154,6 +156,7 @@ protected: void loadCertificate(const std::string& certName, const std::string& certStoreName, bool useMachineStore); void importCertificate(const std::string& certPath); + void importCertificate(std::istream& istr); void importCertificate(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); @@ -162,11 +165,6 @@ protected: static bool matchWildcard(const std::string& alias, const std::string& hostName); private: - enum - { - NAME_BUFFER_SIZE = 256 - }; - std::string _issuerName; std::string _subjectName; PCCERT_CONTEXT _pCert; diff --git a/NetSSL_Win/samples/download/src/download.cpp b/NetSSL_Win/samples/download/src/download.cpp index c6df7d200..90284a235 100644 --- a/NetSSL_Win/samples/download/src/download.cpp +++ b/NetSSL_Win/samples/download/src/download.cpp @@ -23,6 +23,7 @@ #include "Poco/Net/FTPStreamFactory.h" #include "Poco/Net/SSLManager.h" #include "Poco/Net/ConsoleCertificateHandler.h" +#include "Poco/Net/PrivateKeyPassphraseHandler.h" #include #include diff --git a/NetSSL_Win/src/X509Certificate.cpp b/NetSSL_Win/src/X509Certificate.cpp index ff209e19d..07ab7fab0 100644 --- a/NetSSL_Win/src/X509Certificate.cpp +++ b/NetSSL_Win/src/X509Certificate.cpp @@ -3,7 +3,7 @@ // // $Id$ // -// Library: Crypto +// Library: NetSSL_Win // Package: Certificate // 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): _pCert(0) { @@ -106,11 +113,55 @@ X509Certificate::~X509Certificate() void X509Certificate::init() { - wchar_t data[256]; - CertGetNameStringW(_pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, data, 256); - Poco::UnicodeConverter::convert(data, _issuerName); - CertGetNameStringW(_pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, data, 256); - Poco::UnicodeConverter::convert(data, _subjectName); + std::string name = issuerName(NID_COUNTRY); + if (!name.empty()) + { + _issuerName += "/C="; + _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 result; - wchar_t data[256]; - CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, CERT_NAME_ISSUER_FLAG, nid2oid(nid), data, 256); - Poco::UnicodeConverter::convert(data, result); + DWORD size = CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, CERT_NAME_ISSUER_FLAG, nid2oid(nid), 0, 0); + Poco::Buffer data(size); + CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, CERT_NAME_ISSUER_FLAG, nid2oid(nid), data.begin(), size); + Poco::UnicodeConverter::convert(data.begin(), result); return result; } @@ -133,9 +185,10 @@ std::string X509Certificate::issuerName(NID nid) const std::string X509Certificate::subjectName(NID nid) const { std::string result; - wchar_t data[256]; - CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, 0, nid2oid(nid), data, 256); - Poco::UnicodeConverter::convert(data, result); + DWORD size = CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, 0, nid2oid(nid), 0, 0); + Poco::Buffer data(size); + CertGetNameStringW(_pCert, CERT_NAME_ATTR_TYPE, 0, nid2oid(nid), data.begin(), size); + Poco::UnicodeConverter::convert(data.begin(), result); return result; } @@ -182,7 +235,7 @@ void X509Certificate::extractNames(std::string& cmnName, std::set& PCERT_ALT_NAME_INFO pNameInfo = reinterpret_cast(buffer.begin()); for (int i = 0; i < pNameInfo->cAltEntry; i++) { - std::wstring waltName(pNameInfo->rgAltEntry->pwszDNSName); + std::wstring waltName(pNameInfo->rgAltEntry[i].pwszDNSName); std::string altName; Poco::UnicodeConverter::toUTF8(waltName, altName); domainNames.insert(altName); @@ -213,68 +266,40 @@ Poco::DateTime X509Certificate::expiresOn() 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: - 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")); + throw SSLException("Cannot get certificate chain", subjectName(), GetLastError()); + } bool result = false; - PCCERT_CONTEXT pIssuer; - PCCERT_CONTEXT pIssued = _pCert; - do + for (DWORD i = 0; i < pChainContext->cChain && !result; i++) { - DWORD flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG; - pIssuer = 0; - if (caStore.handle()) + for (DWORD k = 0; k < pChainContext->rgpChain[i]->cElement && !result; k++) { - pIssuer = CertGetIssuerCertificateFromStore(caStore.handle(), pIssued, 0, &flags); - } - 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)) + PCCERT_CONTEXT pChainCert = pChainContext->rgpChain[i]->rgpElement[k]->pCertContext; + if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, issuerCertificate.system()->pCertInfo, pChainCert->pCertInfo)) { - result = true; - break; - } - if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pIssuer->pCertInfo, pIssued->pCertInfo)) - { - // reached self-signed certificate - break; + if (CertComparePublicKeyInfo(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &issuerCertificate.system()->pCertInfo->SubjectPublicKeyInfo, &pChainCert->pCertInfo->SubjectPublicKeyInfo)) + { + result = true; + } } } - else break; - } - while (pIssuer); - + } + CertFreeCertificateChain(pChainContext); 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.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_attr.Value.cbData = static_cast(certName.size()); + cert_rdn_attr.Value.pbData = reinterpret_cast(const_cast(certName.c_str())); CERT_RDN cert_rdn; 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) { if (std::memcmp(pBuffer, "-----BEGIN CERTIFICATE-----", 27) == 0)