diff --git a/cpp/include/Ice/SSL/Certificate.h b/cpp/include/Ice/SSL/Certificate.h index 3b287ec8295..83117b62f2d 100644 --- a/cpp/include/Ice/SSL/Certificate.h +++ b/cpp/include/Ice/SSL/Certificate.h @@ -17,71 +17,6 @@ namespace Ice::SSL { - /** - * The key usage "digitalSignature" bit is set - */ - const unsigned int KEY_USAGE_DIGITAL_SIGNATURE = 1u << 0; - /** - * The key usage "nonRepudiation" bit is set - */ - const unsigned int KEY_USAGE_NON_REPUDIATION = 1u << 1; - /** - * The key usage "keyEncipherment" bit is set - */ - const unsigned int KEY_USAGE_KEY_ENCIPHERMENT = 1u << 2; - /** - * The key usage "dataEncipherment" bit is set - */ - const unsigned int KEY_USAGE_DATA_ENCIPHERMENT = 1u << 3; - /** - * The key usage "keyAgreement" bit is set - */ - const unsigned int KEY_USAGE_KEY_AGREEMENT = 1u << 4; - /** - * The key usage "keyCertSign" bit is set - */ - const unsigned int KEY_USAGE_KEY_CERT_SIGN = 1u << 5; - /** - * The key usage "cRLSign" bit is set - */ - const unsigned int KEY_USAGE_CRL_SIGN = 1u << 6; - /** - * The key usage "encipherOnly" bit is set - */ - const unsigned int KEY_USAGE_ENCIPHER_ONLY = 1u << 7; - /** - * The key usage "decipherOnly" bit is set - */ - const unsigned int KEY_USAGE_DECIPHER_ONLY = 1u << 8; - /** - * The extended key usage "anyKeyUsage" bit is set - */ - const unsigned int EXTENDED_KEY_USAGE_ANY_KEY_USAGE = 1u << 0; - /** - * The extended key usage "serverAuth" bit is set - */ - const unsigned int EXTENDED_KEY_USAGE_SERVER_AUTH = 1u << 1; - /** - * The extended key usage "clientAuth" bit is set - */ - const unsigned int EXTENDED_KEY_USAGE_CLIENT_AUTH = 1u << 2; - /** - * The extended key usage "codeSigning" bit is set - */ - const unsigned int EXTENDED_KEY_USAGE_CODE_SIGNING = 1u << 3; - /** - * The extended key usage "emailProtection" bit is set - */ - const unsigned int EXTENDED_KEY_USAGE_EMAIL_PROTECTION = 1u << 4; - /** - * The extended key usage "timeStamping" bit is set - */ - const unsigned int EXTENDED_KEY_USAGE_TIME_STAMPING = 1u << 5; - /** - * The extended key usage "OCSPSigning" bit is set - */ - const unsigned int EXTENDED_KEY_USAGE_OCSP_SIGNING = 1u << 6; - /** * Thrown if the certificate cannot be read. */ @@ -237,220 +172,6 @@ namespace Ice::SSL * Performs an exact match. The order of the RDN components is important. */ inline bool operator!=(const DistinguishedName& lhs, const DistinguishedName& rhs) { return !(lhs == rhs); } - - /** - * Represents an X509 Certificate extension. - */ - class ICE_API X509Extension - { - public: - /** - * Determines whether the information in this extension is important. - * @return True if if information is important, false otherwise. - */ - virtual bool isCritical() const = 0; - - /** - * Obtains the object ID of this extension. - * @return The object ID. - */ - virtual std::string getOID() const = 0; - - /** - * Obtains the data associated with this extension. - * @return The extension data. - */ - virtual std::vector getData() const = 0; - }; - using X509ExtensionPtr = std::shared_ptr; - - class Certificate; - using CertificatePtr = std::shared_ptr; - - /** - * This convenience class is a wrapper around a native certificate. - * The interface is inspired by java.security.cert.X509Certificate. - */ - class ICE_API Certificate : public std::enable_shared_from_this - { - public: - /** - * Compares the certificates for equality using the native certificate comparison method. - */ - virtual bool operator==(const Certificate&) const = 0; - - /** - * Compares the certificates for equality using the native certificate comparison method. - */ - virtual bool operator!=(const Certificate&) const = 0; - - /** - * Obtains the authority key identifier. - * @return The identifier. - */ - virtual std::vector getAuthorityKeyIdentifier() const = 0; - - /** - * Obtains the subject key identifier. - * @return The identifier. - */ - virtual std::vector getSubjectKeyIdentifier() const = 0; - - /** - * Verifies that this certificate was signed by the given certificate - * public key. - * @param cert A certificate containing the public key. - * @return True if signed, false otherwise. - */ - virtual bool verify(const CertificatePtr& cert) const = 0; - - /** - * Obtains a string encoding of the certificate in PEM format. - * @return The encoded certificate. - * @throws CertificateEncodingException if an error occurs. - */ - virtual std::string encode() const = 0; - - /** - * Checks that the certificate is currently valid, that is, the current - * date falls between the validity period given in the certificate. - * @return True if the certificate is valid, false otherwise. - */ - virtual bool checkValidity() const = 0; - - /** - * Checks that the certificate is valid at the given time. - * @param t The target time. - * @return True if the certificate is valid, false otherwise. - */ - virtual bool checkValidity(const std::chrono::system_clock::time_point& t) const = 0; - - /** - * Returns the value of the key usage extension. The flags KEY_USAGE_DIGITAL_SIGNATURE, - * KEY_USAGE_NON_REPUDIATION, KEY_USAGE_KEY_ENCIPHERMENT, KEY_USAGE_DATA_ENCIPHERMENT - * KEY_USAGE_KEY_AGREEMENT, KEY_USAGE_KEY_CERT_SIGN, KEY_USAGE_CRL_SIGN, - * KEY_USAGE_ENCIPHER_ONLY and KEY_USAGE_DECIPHER_ONLY can be used to check what - * key usage bits are set. - */ - virtual unsigned int getKeyUsage() const = 0; - - /** - * Returns the value of the extended key usage extension. The flags EXTENDED_KEY_USAGE_ANY_KEY_USAGE, - * EXTENDED_KEY_USAGE_SERVER_AUTH, EXTENDED_KEY_USAGE_CLIENT_AUTH, - * EXTENDED_KEY_USAGE_CODE_SIGNING, EXTENDED_KEY_USAGE_EMAIL_PROTECTION, - * EXTENDED_KEY_USAGE_TIME_STAMPING and EXTENDED_KEY_USAGE_OCSP_SIGNING can be used to check what - * extended key usage bits are set. - */ - virtual unsigned int getExtendedKeyUsage() const = 0; - - /** - * Obtains the not-after validity time. - * @return The time after which this certificate is invalid. - */ - virtual std::chrono::system_clock::time_point getNotAfter() const = 0; - - /** - * Obtains the not-before validity time. - * @return The time at which this certificate is valid. - */ - virtual std::chrono::system_clock::time_point getNotBefore() const = 0; - - /** - * Obtains the serial number. This is an arbitrarily large number. - * @return The certificate's serial number. - */ - virtual std::string getSerialNumber() const = 0; - - /** - * Obtains the issuer's distinguished name (DN). - * @return The distinguished name. - */ - virtual DistinguishedName getIssuerDN() const = 0; - - /** - * Obtains the values in the issuer's alternative names extension. - * - * The returned list contains a pair of int, string. - * - * otherName [0] OtherName - * rfc822Name [1] IA5String - * dNSName [2] IA5String - * x400Address [3] ORAddress - * directoryName [4] Name - * ediPartyName [5] EDIPartyName - * uniformResourceIdentifier [6] IA5String - * iPAddress [7] OCTET STRING - * registeredID [8] OBJECT IDENTIFIER - * - * rfc822Name, dNSName, directoryName and - * uniformResourceIdentifier data is returned as a string. - * - * iPAddress is returned in dotted quad notation. IPv6 is not - * currently supported. - * - * All distinguished names are encoded in RFC2253 format. - * - * The remainder of the data will result in an empty string. Use the raw - * X509* certificate to obtain these values. - * - * @return The issuer's alternative names. - */ - virtual std::vector> getIssuerAlternativeNames() const = 0; - - /** - * Obtains the subject's distinguished name (DN). - * @return The distinguished name. - */ - virtual DistinguishedName getSubjectDN() const = 0; - - /** - * See the comment for Plugin::getIssuerAlternativeNames. - * @return The subject's alternative names. - */ - virtual std::vector> getSubjectAlternativeNames() const = 0; - - /** - * Obtains the certificate version number. - * @return The version number. - */ - virtual int getVersion() const = 0; - - /** - * Stringifies the certificate. This is a human readable version of - * the certificate, not a DER or PEM encoding. - * @return A string version of the certificate. - */ - virtual std::string toString() const = 0; - - /** - * Obtains a list of the X509v3 extensions contained in the certificate. - * @return The extensions. - */ - virtual std::vector getX509Extensions() const = 0; - - /** - * Obtains the extension with the given OID. - * @return The extension, or null if the certificate - * does not contain a extension with the given OID. - */ - virtual X509ExtensionPtr getX509Extension(const std::string& oid) const = 0; - - /** - * Loads the certificate from a file. The certificate must use the - * PEM encoding format. - * @param file The certificate file. - * @return The new certificate instance. - * @throws CertificateReadException if the file cannot be read. - */ - static CertificatePtr load(const std::string& file); - - /** - * Decodes a certificate from a string that uses the PEM encoding format. - * @param str A string containing the encoded certificate. - * @throws CertificateEncodingException if an error occurs. - */ - static CertificatePtr decode(const std::string& str); - }; } #endif diff --git a/cpp/include/Ice/SSL/Config.h b/cpp/include/Ice/SSL/Config.h index 106501ca917..4d9d2cbb6c3 100644 --- a/cpp/include/Ice/SSL/Config.h +++ b/cpp/include/Ice/SSL/Config.h @@ -28,12 +28,20 @@ # include # undef SECURITY_WIN32 #elif defined(__APPLE__) +# include # define ICE_USE_SECURE_TRANSPORT +# if TARGET_OS_IPHONE != 0 +# define ICE_USE_SECURE_TRANSPORT_IOS 1 +# else +# define ICE_USE_SECURE_TRANSPORT_MACOS 1 +# endif # include # include #else # define ICE_USE_OPENSSL +# include # include +# include #endif #endif diff --git a/cpp/include/Ice/SSL/ConnectionInfo.h b/cpp/include/Ice/SSL/ConnectionInfo.h index a55970d0206..2444c20f963 100644 --- a/cpp/include/Ice/SSL/ConnectionInfo.h +++ b/cpp/include/Ice/SSL/ConnectionInfo.h @@ -5,7 +5,7 @@ #ifndef ICE_SSL_CONNECTION_INFO_H #define ICE_SSL_CONNECTION_INFO_H -#include "Certificate.h" +#include "Config.h" #include "ConnectionInfoF.h" #include "Ice/Connection.h" @@ -19,13 +19,14 @@ namespace Ice::SSL { +#if defined(ICE_USE_SCHANNEL) /** * Provides access to the connection details of an SSL connection. */ - class ICE_API ConnectionInfo : public Ice::ConnectionInfo + class ICE_API SchannelConnectionInfo final : public Ice::ConnectionInfo { public: - ConnectionInfo() = default; + SchannelConnectionInfo() = default; /** * One-shot constructor to initialize all data members. @@ -33,29 +34,112 @@ namespace Ice::SSL * @param incoming Whether or not the connection is an incoming or outgoing connection. * @param adapterName The name of the adapter associated with the connection. * @param connectionId The connection id. - * @param certs The certificate chain. + * @param peerCertificate The peer certificate. */ - ConnectionInfo( + SchannelConnectionInfo( const Ice::ConnectionInfoPtr& underlying, bool incoming, const std::string& adapterName, const std::string& connectionId, - const std::vector& certs) + PCCERT_CONTEXT peerCertificate) : Ice::ConnectionInfo(underlying, incoming, adapterName, connectionId), - certs(certs) + peerCertificate(peerCertificate) { } - ~ConnectionInfo() override; + ~SchannelConnectionInfo() override; - ConnectionInfo(const ConnectionInfo&) = delete; - ConnectionInfo& operator=(const ConnectionInfo&) = delete; + SchannelConnectionInfo(const SchannelConnectionInfo&) = delete; + SchannelConnectionInfo& operator=(const SchannelConnectionInfo&) = delete; /** - * The certificate chain. + * The peer certificate. */ - std::vector certs; + PCCERT_CONTEXT peerCertificate = nullptr; }; + // Alias for portable code + using ConnectionInfo = SchannelConnectionInfo; +#elif defined(ICE_USE_SECURE_TRANSPORT) + /** + * Provides access to the connection details of an SSL connection. + */ + class ICE_API SecureTransportConnectionInfo final : public Ice::ConnectionInfo + { + public: + SecureTransportConnectionInfo() = default; + + /** + * One-shot constructor to initialize all data members. + * @param underlying The information of the underlying transport or null if there's no underlying transport. + * @param incoming Whether or not the connection is an incoming or outgoing connection. + * @param adapterName The name of the adapter associated with the connection. + * @param connectionId The connection id. + * @param peerCertificate The peer certificate. + */ + SecureTransportConnectionInfo( + const Ice::ConnectionInfoPtr& underlying, + bool incoming, + const std::string& adapterName, + const std::string& connectionId, + SecCertificateRef peerCertificate) + : Ice::ConnectionInfo(underlying, incoming, adapterName, connectionId), + peerCertificate(peerCertificate) + { + } + + ~SecureTransportConnectionInfo() override; + + SecureTransportConnectionInfo(const SecureTransportConnectionInfo&) = delete; + SecureTransportConnectionInfo& operator=(const SecureTransportConnectionInfo&) = delete; + + /** + * The peer certificate. + */ + SecCertificateRef peerCertificate = nullptr; + }; + // Alias for portable code + using ConnectionInfo = SecureTransportConnectionInfo; +#else // ICE_USE_OPENSSL + /** + * Provides access to the connection details of an SSL connection. + */ + class ICE_API OpenSSLConnectionInfo final : public Ice::ConnectionInfo + { + public: + OpenSSLConnectionInfo() = default; + + /** + * One-shot constructor to initialize all data members. + * @param underlying The information of the underlying transport or null if there's no underlying transport. + * @param incoming Whether or not the connection is an incoming or outgoing connection. + * @param adapterName The name of the adapter associated with the connection. + * @param connectionId The connection id. + * @param peerCertificate The peer certificate. + */ + OpenSSLConnectionInfo( + const Ice::ConnectionInfoPtr& underlying, + bool incoming, + const std::string& adapterName, + const std::string& connectionId, + X509* peerCertificate) + : Ice::ConnectionInfo(underlying, incoming, adapterName, connectionId), + peerCertificate(peerCertificate) + { + } + + ~OpenSSLConnectionInfo() override; + + OpenSSLConnectionInfo(const OpenSSLConnectionInfo&) = delete; + OpenSSLConnectionInfo& operator=(const OpenSSLConnectionInfo&) = delete; + + /** + * The peer certificate. + */ + X509* peerCertificate = nullptr; + }; + // Alias for portable code + using ConnectionInfo = OpenSSLConnectionInfo; +#endif } #if defined(__clang__) diff --git a/cpp/include/Ice/SSL/ConnectionInfoF.h b/cpp/include/Ice/SSL/ConnectionInfoF.h index 29137290bfb..d98c107e548 100644 --- a/cpp/include/Ice/SSL/ConnectionInfoF.h +++ b/cpp/include/Ice/SSL/ConnectionInfoF.h @@ -5,12 +5,28 @@ #ifndef ICE_SSL_CONNECTION_INFO_F_H #define ICE_SSL_CONNECTION_INFO_F_H +#include "Config.h" + #include namespace Ice::SSL { - class ConnectionInfo; - using ConnectionInfoPtr = std::shared_ptr; +#if defined(ICE_USE_SCHANNEL) + class SchannelConnectionInfo; + // Alias for portable code + using ConnectionInfo = SchannelConnectionInfo; + using ConnectionInfoPtr = std::shared_ptr; +#elif defined(ICE_USE_SECURE_TRANSPORT) + class SecureTransportConnectionInfo; + // Alias for portable code + using ConnectionInfo = SecureTransportConnectionInfo; + using ConnectionInfoPtr = std::shared_ptr; +#else + class OpenSSLConnectionInfo; + // Alias for portable code + using ConnectionInfo = OpenSSLConnectionInfo; + using ConnectionInfoPtr = std::shared_ptr; +#endif } #endif diff --git a/cpp/include/Ice/SSL/OpenSSL.h b/cpp/include/Ice/SSL/OpenSSL.h deleted file mode 100644 index 180b066d6e2..00000000000 --- a/cpp/include/Ice/SSL/OpenSSL.h +++ /dev/null @@ -1,58 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#ifndef ICE_SSL_OPENSSL_H -#define ICE_SSL_OPENSSL_H - -#include "Certificate.h" - -#include -#include - -namespace Ice::SSL::OpenSSL -{ - class Certificate; - using CertificatePtr = std::shared_ptr; - - /** - * Encapsulates an OpenSSL X.509 certificate. - */ - class ICE_API Certificate : public virtual Ice::SSL::Certificate - { - public: - /** - * Construct a certificate using a native certificate. - * The Certificate class assumes ownership of the given native - * certificate. - * @param cert The native certificate. - * @return A new certificate object. - */ - static CertificatePtr create(x509_st* cert); - - /** - * Load the certificate from a file. The certificate must use the - * PEM encoding format. - * @param file The certificate file. - * @return A new certificate object. - * @throws CertificateReadException if the file cannot be read. - */ - static CertificatePtr load(const std::string& file); - - /** - * Decode a certificate from a string that uses the PEM encoding format. - * @param cert A string containing the PEM-encoded certificate. - * @return A new certificate object. - * @throws CertificateEncodingException if an error occurs. - */ - static CertificatePtr decode(const std::string& cert); - - /** - * Retrieve the native X509 certificate value wrapped by this object. - * @return The native certificate. The returned reference is only valid for the lifetime of this - * object. You can increment it with X509_dup. - */ - virtual x509_st* getCert() const = 0; - }; -} -#endif diff --git a/cpp/include/Ice/SSL/Schannel.h b/cpp/include/Ice/SSL/Schannel.h deleted file mode 100644 index 8759e170ef7..00000000000 --- a/cpp/include/Ice/SSL/Schannel.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#ifndef ICE_SSL_SCHANNEL_H -#define ICE_SSL_SCHANNEL_H - -#ifdef _WIN32 -# include "Certificate.h" -# include "Config.h" - -namespace Ice::SSL::Schannel -{ - class Certificate; - using CertificatePtr = std::shared_ptr; - - /** - * This convenience class is a wrapper around a native certificate. - */ - class ICE_API Certificate : public virtual Ice::SSL::Certificate - { - public: - /** - * Constructs a certificate using a native certificate. - * The Certificate class assumes ownership of the given native - * certificate. - * @param info The certificate data. - * @return The new certificate instance. - */ - static CertificatePtr create(CERT_SIGNED_CONTENT_INFO* info); - - /** - * Loads the certificate from a file. The certificate must use the - * PEM encoding format. - * @param file The certificate file. - * @return The new certificate instance. - * @throws CertificateReadException if the file cannot be read. - */ - static CertificatePtr load(const std::string& file); - - /** - * Decodes a certificate from a string that uses the PEM encoding format. - * @param str A string containing the encoded certificate. - * @return The new certificate instance. - * @throws CertificateEncodingException if an error occurs. - */ - static CertificatePtr decode(const std::string& str); - - /** - * Obtains the native X509 certificate value wrapped by this object. - * @return A reference to the native certificate. - * The returned reference is only valid for the lifetime of this - * object. The returned reference is a pointer to a struct. - */ - virtual CERT_SIGNED_CONTENT_INFO* getCert() const = 0; - }; -} -#endif - -#endif diff --git a/cpp/include/Ice/SSL/SecureTransport.h b/cpp/include/Ice/SSL/SecureTransport.h deleted file mode 100644 index c9f5677495f..00000000000 --- a/cpp/include/Ice/SSL/SecureTransport.h +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#ifndef ICE_SSL_SECURE_TRANSPORT_H -#define ICE_SSL_SECURE_TRANSPORT_H - -#ifdef __APPLE__ - -# include "Certificate.h" -# include "Config.h" - -namespace Ice::SSL::SecureTransport -{ - class Certificate; - using CertificatePtr = std::shared_ptr; - - /** - * This convenience class is a wrapper around a native certificate. - */ - class ICE_API Certificate : public virtual Ice::SSL::Certificate - { - public: - /** - * Constructs a certificate using a native certificate. - * The Certificate class assumes ownership of the given native - * certificate. - * @param cert The certificate cert. - * @return The new certificate instance. - */ - static CertificatePtr create(SecCertificateRef cert); - - /** - * Loads the certificate from a file. The certificate must use the - * PEM encoding format. - * @param file The certificate file. - * @return The new certificate instance. - * @throws CertificateReadException if the file cannot be read. - */ - static CertificatePtr load(const std::string& file); - - /** - * Decodes a certificate from a string that uses the PEM encoding format. - * @param str A string containing the encoded certificate. - * @return The new certificate instance. - * @throws CertificateEncodingException if an error occurs. - */ - static CertificatePtr decode(const std::string& str); - - /** - * Obtains the native X509 certificate value wrapped by this object. - * @return A reference to the native certificate. - * The returned reference is only valid for the lifetime of this - * object. You can increment the reference count of the returned - * object with CFRetain. - */ - virtual SecCertificateRef getCert() const = 0; - }; -} - -#endif - -#endif diff --git a/cpp/src/Glacier2/SessionRouterI.cpp b/cpp/src/Glacier2/SessionRouterI.cpp index fb3cc8919aa..c51997388a4 100644 --- a/cpp/src/Glacier2/SessionRouterI.cpp +++ b/cpp/src/Glacier2/SessionRouterI.cpp @@ -4,6 +4,7 @@ // #include "SessionRouterI.h" +#include "../Ice/SSL/SSLUtil.h" #include "FilterManager.h" #include "Glacier2/PermissionsVerifier.h" #include "Ice/Ice.h" @@ -303,12 +304,9 @@ CreateSession::CreateSession(shared_ptr sessionRouter, const str } { auto info = dynamic_pointer_cast(current.con->getInfo()); - if (info) + if (info && info->peerCertificate) { - if (info->certs.size() > 0) - { - _context["_con.peerCert"] = info->certs[0]->encode(); - } + _context["_con.peerCert"] = Ice::SSL::encodeCertificate(info->peerCertificate); } } } @@ -650,13 +648,10 @@ SessionRouterI::createSessionFromSecureConnectionAsync( sslinfo.localPort = ipInfo->localPort; sslinfo.localHost = ipInfo->localAddress; - for (const auto& cert : info->certs) - { - sslinfo.certs.push_back(cert->encode()); - } - if (info->certs.size() > 0) + if (info->peerCertificate) { - userDN = info->certs[0]->getSubjectDN(); + sslinfo.certs.push_back(Ice::SSL::encodeCertificate(info->peerCertificate)); + userDN = Ice::SSL::getSubjectName(info->peerCertificate); } } catch (const SSL::CertificateEncodingException&) diff --git a/cpp/src/Ice/SSL/CertificateI.cpp b/cpp/src/Ice/SSL/CertificateI.cpp deleted file mode 100644 index 322b101efd9..00000000000 --- a/cpp/src/Ice/SSL/CertificateI.cpp +++ /dev/null @@ -1,214 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#include "CertificateI.h" -#include "../Base64.h" -#include "Ice/LocalException.h" -#include "Ice/Object.h" -#include "Ice/StringConverter.h" -#include "IceUtil/DisableWarnings.h" -#include "IceUtil/StringUtil.h" -#include "RFC2253.h" -#include "SSLUtil.h" - -using namespace std; -using namespace Ice; -using namespace IceInternal; -using namespace Ice::SSL; - -// -// Map a certificate OID to its alias -// -const CertificateOID Ice::SSL::certificateOIDS[] = { - {"2.5.4.3", "CN"}, - {"2.5.4.4", "SN"}, - {"2.5.4.5", "DeviceSerialNumber"}, - {"2.5.4.6", "C"}, - {"2.5.4.7", "L"}, - {"2.5.4.8", "ST"}, - {"2.5.4.9", "STREET"}, - {"2.5.4.10", "O"}, - {"2.5.4.11", "OU"}, - {"2.5.4.12", "T"}, - {"2.5.4.42", "G"}, - {"2.5.4.43", "I"}, - {"1.2.840.113549.1.9.8", "unstructuredAddress"}, - {"1.2.840.113549.1.9.2", "unstructuredName"}, - {"1.2.840.113549.1.9.1", "emailAddress"}, - {"0.9.2342.19200300.100.1.25", "DC"}}; -const int Ice::SSL::certificateOIDSSize = sizeof(Ice::SSL::certificateOIDS) / sizeof(CertificateOID); - -CertificateReadException::CertificateReadException(const char* file, int line, string r) noexcept - : Exception(file, line), - reason(std::move(r)) -{ -} - -string -CertificateReadException::ice_id() const -{ - return "::Ice::SSL::CertificateReadException"; -} - -CertificateEncodingException::CertificateEncodingException(const char* file, int line, string r) noexcept - : Exception(file, line), - reason(std::move(r)) -{ -} - -string -CertificateEncodingException::ice_id() const -{ - return "::Ice::SSL::CertificateEncodingException"; -} - -ParseException::ParseException(const char* file, int line, string r) noexcept - : Exception(file, line), - reason(std::move(r)) -{ -} - -string -ParseException::ice_id() const -{ - return "::Ice::SSL::ParseException"; -} - -DistinguishedName::DistinguishedName(const string& dn) : _rdns(RFC2253::parseStrict(dn)) { unescape(); } - -DistinguishedName::DistinguishedName(const list>& rdns) : _rdns(rdns) { unescape(); } - -namespace Ice::SSL -{ - bool operator==(const DistinguishedName& lhs, const DistinguishedName& rhs) - { - return lhs._unescaped == rhs._unescaped; - } - - bool operator<(const DistinguishedName& lhs, const DistinguishedName& rhs) - { - return lhs._unescaped < rhs._unescaped; - } -} - -bool -DistinguishedName::match(const DistinguishedName& other) const -{ - for (list>::const_iterator p = other._unescaped.begin(); p != other._unescaped.end(); ++p) - { - bool found = false; - for (list>::const_iterator q = _unescaped.begin(); q != _unescaped.end(); ++q) - { - if (p->first == q->first) - { - found = true; - if (p->second != q->second) - { - return false; - } - } - } - if (!found) - { - return false; - } - } - return true; -} - -bool -DistinguishedName::match(const string& other) const -{ - return match(DistinguishedName(other)); -} - -// -// This always produces the same output as the input DN -- the type of -// escaping is not changed. -// -std::string -DistinguishedName::toString() const -{ - ostringstream os; - bool first = true; - for (list>::const_iterator p = _rdns.begin(); p != _rdns.end(); ++p) - { - if (!first) - { - os << ","; - } - first = false; - os << p->first << "=" << p->second; - } - return os.str(); -} - -void -DistinguishedName::unescape() -{ - for (list>::const_iterator q = _rdns.begin(); q != _rdns.end(); ++q) - { - pair rdn = *q; - rdn.second = RFC2253::unescape(rdn.second); - _unescaped.push_back(rdn); - } -} - -bool -CertificateI::operator!=(const Ice::SSL::Certificate& other) const -{ - return !operator==(other); -} - -vector -CertificateI::getX509Extensions() const -{ - loadX509Extensions(); // Lazzy initialize the extensions - return _extensions; -} - -X509ExtensionPtr -CertificateI::getX509Extension(const string& oid) const -{ - loadX509Extensions(); // Lazzy initialize the extensions - X509ExtensionPtr ext; - for (vector::const_iterator i = _extensions.begin(); i != _extensions.end(); ++i) - { - if ((*i)->getOID() == oid) - { - ext = *i; - break; - } - } - return ext; -} - -void -CertificateI::loadX509Extensions() const -{ - throw FeatureNotSupportedException(__FILE__, __LINE__); -} - -bool -CertificateI::checkValidity() const -{ - auto now = chrono::system_clock::now(); - return now > getNotBefore() && now <= getNotAfter(); -} - -bool -CertificateI::checkValidity(const chrono::system_clock::time_point& now) const -{ - return now > getNotBefore() && now <= getNotAfter(); -} - -string -CertificateI::toString() const -{ - ostringstream os; - os << "serial: " << getSerialNumber() << "\n"; - os << "issuer: " << string(getIssuerDN()) << "\n"; - os << "subject: " << string(getSubjectDN()) << "\n"; - return os.str(); -} diff --git a/cpp/src/Ice/SSL/CertificateI.h b/cpp/src/Ice/SSL/CertificateI.h deleted file mode 100644 index ffa034cebdf..00000000000 --- a/cpp/src/Ice/SSL/CertificateI.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#ifndef ICE_SSL_CERTIFICATE_I_H -#define ICE_SSL_CERTIFICATE_I_H - -#include "Ice/SSL/Certificate.h" - -#include -#include - -namespace Ice::SSL -{ - // - // Map a certificate OID to its alias - // - struct ICE_API CertificateOID - { - const char* name; - const char* alias; - }; - - extern const ICE_API CertificateOID certificateOIDS[]; - extern const ICE_API int certificateOIDSSize; - - // - // Certificate common implementation - // - class ICE_API CertificateI : public virtual Ice::SSL::Certificate - { - public: - virtual bool operator!=(const Ice::SSL::Certificate&) const; - - virtual std::vector getX509Extensions() const; - virtual X509ExtensionPtr getX509Extension(const std::string&) const; - - virtual bool checkValidity() const; - virtual bool checkValidity(const std::chrono::system_clock::time_point& now) const; - virtual std::string toString() const; - - protected: - // Implementations that support retrieving X509 extensions must reimplement this method to lazzy initialize - // the extensions list. The default implementation just throw FeatureNotSupportedException. - virtual void loadX509Extensions() const; - mutable std::vector _extensions; - }; - -} // IceSSL namespace end - -#endif diff --git a/cpp/src/Ice/SSL/DistinguishedName.cpp b/cpp/src/Ice/SSL/DistinguishedName.cpp new file mode 100644 index 00000000000..31494d3297c --- /dev/null +++ b/cpp/src/Ice/SSL/DistinguishedName.cpp @@ -0,0 +1,93 @@ +// +// Copyright (c) ZeroC, Inc. All rights reserved. +// + +#include "Ice/SSL/Certificate.h" +#include "RFC2253.h" + +#include + +using namespace std; +using namespace Ice; +using namespace IceInternal; +using namespace Ice::SSL; + +DistinguishedName::DistinguishedName(const string& dn) : _rdns(RFC2253::parseStrict(dn)) { unescape(); } + +DistinguishedName::DistinguishedName(const list>& rdns) : _rdns(rdns) { unescape(); } + +namespace Ice::SSL +{ + bool operator==(const DistinguishedName& lhs, const DistinguishedName& rhs) + { + return lhs._unescaped == rhs._unescaped; + } + + bool operator<(const DistinguishedName& lhs, const DistinguishedName& rhs) + { + return lhs._unescaped < rhs._unescaped; + } +} + +bool +DistinguishedName::match(const DistinguishedName& other) const +{ + for (list>::const_iterator p = other._unescaped.begin(); p != other._unescaped.end(); ++p) + { + bool found = false; + for (list>::const_iterator q = _unescaped.begin(); q != _unescaped.end(); ++q) + { + if (p->first == q->first) + { + found = true; + if (p->second != q->second) + { + return false; + } + } + } + if (!found) + { + return false; + } + } + return true; +} + +bool +DistinguishedName::match(const string& other) const +{ + return match(DistinguishedName(other)); +} + +// +// This always produces the same output as the input DN -- the type of +// escaping is not changed. +// +std::string +DistinguishedName::toString() const +{ + ostringstream os; + bool first = true; + for (list>::const_iterator p = _rdns.begin(); p != _rdns.end(); ++p) + { + if (!first) + { + os << ","; + } + first = false; + os << p->first << "=" << p->second; + } + return os.str(); +} + +void +DistinguishedName::unescape() +{ + for (list>::const_iterator q = _rdns.begin(); q != _rdns.end(); ++q) + { + pair rdn = *q; + rdn.second = RFC2253::unescape(rdn.second); + _unescaped.push_back(rdn); + } +} diff --git a/cpp/src/Ice/SSL/OpenSSLCertificateI.cpp b/cpp/src/Ice/SSL/OpenSSLCertificateI.cpp deleted file mode 100644 index 885da8b0f87..00000000000 --- a/cpp/src/Ice/SSL/OpenSSLCertificateI.cpp +++ /dev/null @@ -1,622 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#include "CertificateI.h" -#include "Ice/SSL/OpenSSL.h" -#include "OpenSSLUtil.h" -#include "RFC2253.h" - -#include -#include -#include -#include - -#include -#include - -using namespace Ice::SSL; -using namespace std; - -// -// Avoid old style cast warnings from OpenSSL macros -// -#if defined(__GNUC__) -# pragma GCC diagnostic ignored "-Wold-style-cast" -#endif - -#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L -# define X509_get_extension_flags(x) (x->ex_flags) -# define X509_get_key_usage(x) (x->ex_kusage) -# define X509_get_extended_key_usage(x) (x->ex_xkusage) -#endif - -namespace -{ - static string convertX509NameToString(X509_name_st* name) - { - BIO* out = BIO_new(BIO_s_mem()); - X509_NAME_print_ex(out, name, 0, XN_FLAG_RFC2253); - BUF_MEM* p; - BIO_get_mem_ptr(out, &p); - string result = string(p->data, p->length); - BIO_free(out); - return result; - } - - static vector> convertGeneralNames(GENERAL_NAMES* gens) - { - vector> alt; - if (gens == 0) - { - return alt; - } - for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) - { - GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); - pair p; - p.first = gen->type; - switch (gen->type) - { - case GEN_EMAIL: - { - ASN1_IA5STRING* str = gen->d.rfc822Name; - if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) - { - p.second = string(reinterpret_cast(str->data), str->length); - } - break; - } - case GEN_DNS: - { - ASN1_IA5STRING* str = gen->d.dNSName; - if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) - { - p.second = string(reinterpret_cast(str->data), str->length); - } - break; - } - case GEN_DIRNAME: - { - p.second = convertX509NameToString(gen->d.directoryName); - break; - } - case GEN_URI: - { - ASN1_IA5STRING* str = gen->d.uniformResourceIdentifier; - if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) - { - p.second = string(reinterpret_cast(str->data), str->length); - } - break; - } - case GEN_IPADD: - { - ASN1_OCTET_STRING* addr = gen->d.iPAddress; - // TODO: Support IPv6 someday. - if (addr && addr->type == V_ASN1_OCTET_STRING && addr->data && addr->length == 4) - { - ostringstream ostr; - for (int j = 0; j < 4; ++j) - { - if (j > 0) - { - ostr << '.'; - } - ostr << static_cast(addr->data[j]); - } - p.second = ostr.str(); - } - break; - } - case GEN_OTHERNAME: - case GEN_EDIPARTY: - case GEN_X400: - case GEN_RID: - { - // - // TODO: These types are not supported. If the user wants - // them, they have to get at the certificate data. Another - // alternative is to DER encode the data (as the Java - // certificate does). - // - break; - } - } - if (!p.second.empty()) - { - alt.push_back(p); - } - } - sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); - return alt; - } - - class DistinguishedNameI : public DistinguishedName - { - public: - DistinguishedNameI(X509_name_st* name) : DistinguishedName(RFC2253::parseStrict(convertX509NameToString(name))) - { - unescape(); - } - }; - - mutex globalMutex; - - chrono::system_clock::time_point ASMUtcTimeToTime(const ASN1_UTCTIME* s) - { - struct tm tm; - int offset; - - memset(&tm, '\0', sizeof tm); - -#define g2(p) (((p)[0] - '0') * 10 + (p)[1] - '0') - tm.tm_year = g2(s->data); - if (tm.tm_year < 50) - { - tm.tm_year += 100; - } - tm.tm_mon = g2(s->data + 2) - 1; - tm.tm_mday = g2(s->data + 4); - tm.tm_hour = g2(s->data + 6); - tm.tm_min = g2(s->data + 8); - tm.tm_sec = g2(s->data + 10); - if (s->data[12] == 'Z') - { - offset = 0; - } - else - { - offset = g2(s->data + 13) * 60 + g2(s->data + 15); - if (s->data[12] == '-') - { - offset = -offset; - } - } -#undef g2 - - time_t tzone; - { - lock_guard lock(globalMutex); - time_t now = time(0); - struct tm localTime; - struct tm gmTime; -#if defined(_MSC_VER) - localtime_s(&localTime, &now); - gmtime_s(&gmTime, &now); -#else - localtime_r(&now, &localTime); - gmtime_r(&now, &gmTime); -#endif - tzone = mktime(&localTime) - mktime(&gmTime); - } - return chrono::system_clock::time_point(chrono::seconds(mktime(&tm) - int64_t{offset} * 60 + tzone)); - } - - class OpenSSLX509ExtensionI : public X509Extension - { - public: - OpenSSLX509ExtensionI(struct X509_extension_st*, const string&, x509_st*); - ~OpenSSLX509ExtensionI(); - virtual bool isCritical() const; - virtual string getOID() const; - virtual vector getData() const; - - private: - struct X509_extension_st* _extension; - string _oid; - x509_st* _cert; - }; - - class OpenSSLCertificateI : public OpenSSL::Certificate, public CertificateI - { - public: - OpenSSLCertificateI(x509_st*); - ~OpenSSLCertificateI(); - - virtual bool operator==(const Ice::SSL::Certificate&) const; - - virtual vector getAuthorityKeyIdentifier() const; - virtual vector getSubjectKeyIdentifier() const; - virtual bool verify(const Ice::SSL::CertificatePtr&) const; - virtual string encode() const; - - virtual chrono::system_clock::time_point getNotAfter() const; - virtual chrono::system_clock::time_point getNotBefore() const; - virtual string getSerialNumber() const; - virtual DistinguishedName getIssuerDN() const; - virtual vector> getIssuerAlternativeNames() const; - virtual DistinguishedName getSubjectDN() const; - virtual vector> getSubjectAlternativeNames() const; - virtual int getVersion() const; - virtual x509_st* getCert() const; - virtual unsigned int getKeyUsage() const; - virtual unsigned int getExtendedKeyUsage() const; - - protected: - virtual void loadX509Extensions() const; - - private: - x509_st* _cert; - mutable std::mutex _mutex; - }; - -} // end anonymous namespace - -OpenSSLX509ExtensionI::OpenSSLX509ExtensionI(struct X509_extension_st* extension, const string& oid, x509_st* cert) - : _extension(extension), - _oid(oid), - _cert(cert) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - CRYPTO_add(&_cert->references, 1, CRYPTO_LOCK_X509); -#else - X509_up_ref(_cert); -#endif -} - -OpenSSLX509ExtensionI::~OpenSSLX509ExtensionI() { X509_free(_cert); } - -bool -OpenSSLX509ExtensionI::isCritical() const -{ - return X509_EXTENSION_get_critical(_extension) == 1; -} - -string -OpenSSLX509ExtensionI::getOID() const -{ - return _oid; -} - -vector -OpenSSLX509ExtensionI::getData() const -{ - vector data; - ASN1_OCTET_STRING* buffer = X509_EXTENSION_get_data(_extension); - assert(buffer); - data.resize(buffer->length); - memcpy(&data[0], buffer->data, buffer->length); - return data; -} - -// -// The caller is responsible for incrementing the reference count. -// -OpenSSLCertificateI::OpenSSLCertificateI(x509_st* cert) : _cert(cert) -{ - if (!_cert) - { - throw invalid_argument("Invalid certificate reference"); - } -} - -OpenSSLCertificateI::~OpenSSLCertificateI() -{ - if (_cert) - { - X509_free(_cert); - } -} - -bool -OpenSSLCertificateI::operator==(const Ice::SSL::Certificate& r) const -{ - const OpenSSLCertificateI* p = dynamic_cast(&r); - if (!p) - { - return false; - } - - return X509_cmp(_cert, p->_cert) == 0; -} - -vector -OpenSSLCertificateI::getAuthorityKeyIdentifier() const -{ - vector keyid; - int index = X509_get_ext_by_NID(_cert, NID_authority_key_identifier, -1); - if (index >= 0) - { - X509_EXTENSION* ext = X509_get_ext(_cert, index); - if (ext) - { - AUTHORITY_KEYID* decoded = (AUTHORITY_KEYID*)X509V3_EXT_d2i(ext); - if (!decoded) - { - throw CertificateEncodingException(__FILE__, __LINE__, "the extension could not be decoded"); - } - keyid.resize(decoded->keyid->length); - memcpy(&keyid[0], decoded->keyid->data, decoded->keyid->length); - AUTHORITY_KEYID_free(decoded); - } - } - return keyid; -} - -vector -OpenSSLCertificateI::getSubjectKeyIdentifier() const -{ - vector keyid; - int index = X509_get_ext_by_NID(_cert, NID_subject_key_identifier, -1); - if (index >= 0) - { - X509_EXTENSION* ext = X509_get_ext(_cert, index); - if (ext) - { - ASN1_OCTET_STRING* decoded = static_cast(X509V3_EXT_d2i(ext)); - if (!decoded) - { - throw CertificateEncodingException(__FILE__, __LINE__, "the extension could not be decoded"); - } - keyid.resize(decoded->length); - memcpy(&keyid[0], decoded->data, decoded->length); - ASN1_OCTET_STRING_free(decoded); - } - } - return keyid; -} - -bool -OpenSSLCertificateI::verify(const Ice::SSL::CertificatePtr& cert) const -{ - OpenSSLCertificateI* c = dynamic_cast(cert.get()); - if (c) - { - EVP_PKEY* key = X509_get_pubkey(c->_cert); - bool verified = X509_verify(_cert, key) > 0; - EVP_PKEY_free(key); - return verified; - } - return false; -} - -string -OpenSSLCertificateI::encode() const -{ - BIO* out = BIO_new(BIO_s_mem()); - int i = PEM_write_bio_X509(out, _cert); - if (i <= 0) - { - BIO_free(out); - throw CertificateEncodingException(__FILE__, __LINE__, OpenSSL::getErrors(false)); - } - BUF_MEM* p; - BIO_get_mem_ptr(out, &p); - string result = string(p->data, p->length); - BIO_free(out); - return result; -} - -chrono::system_clock::time_point -OpenSSLCertificateI::getNotAfter() const -{ - return ASMUtcTimeToTime(X509_get_notAfter(_cert)); -} - -chrono::system_clock::time_point -OpenSSLCertificateI::getNotBefore() const -{ - return ASMUtcTimeToTime(X509_get_notBefore(_cert)); -} - -string -OpenSSLCertificateI::getSerialNumber() const -{ - BIGNUM* bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(_cert), 0); - char* dec = BN_bn2dec(bn); - string result = dec; - OPENSSL_free(dec); - BN_free(bn); - return result; -} - -DistinguishedName -OpenSSLCertificateI::getIssuerDN() const -{ - return DistinguishedName(RFC2253::parseStrict(convertX509NameToString(X509_get_issuer_name(_cert)))); -} - -vector> -OpenSSLCertificateI::getIssuerAlternativeNames() const -{ - return convertGeneralNames(reinterpret_cast(X509_get_ext_d2i(_cert, NID_issuer_alt_name, 0, 0))); -} - -DistinguishedName -OpenSSLCertificateI::getSubjectDN() const -{ - return DistinguishedName(RFC2253::parseStrict(convertX509NameToString(X509_get_subject_name(_cert)))); -} - -vector> -OpenSSLCertificateI::getSubjectAlternativeNames() const -{ - return convertGeneralNames(reinterpret_cast(X509_get_ext_d2i(_cert, NID_subject_alt_name, 0, 0))); -} - -int -OpenSSLCertificateI::getVersion() const -{ - return static_cast(X509_get_version(_cert)); -} - -x509_st* -OpenSSLCertificateI::getCert() const -{ - return _cert; -} - -void -OpenSSLCertificateI::loadX509Extensions() const -{ - lock_guard lock(_mutex); - if (_extensions.empty()) - { - int sz = X509_get_ext_count(_cert); - for (int i = 0; i < sz; i++) - { - X509_EXTENSION* ext = X509_get_ext(_cert, i); - ASN1_OBJECT* obj = X509_EXTENSION_get_object(ext); - string oid; - // - // According to OBJ_obj2txt doc a buffer of length 80 should be more than enough to - // handle any OID encountered in practice. - // - int len = 80; - oid.resize(len); - len = OBJ_obj2txt(&oid[0], len, obj, 1); - oid.resize(len); - _extensions.push_back( - dynamic_pointer_cast(make_shared(ext, oid, _cert))); - } - } -} - -unsigned int -OpenSSLCertificateI::getKeyUsage() const -{ - unsigned int keyUsage = 0; - int flags = X509_get_extension_flags(_cert); - if (flags & EXFLAG_KUSAGE) - { - unsigned int kusage = X509_get_key_usage(_cert); - if (kusage & KU_DIGITAL_SIGNATURE) - { - keyUsage |= KEY_USAGE_DIGITAL_SIGNATURE; - } - if (kusage & KU_NON_REPUDIATION) - { - keyUsage |= KEY_USAGE_NON_REPUDIATION; - } - if (kusage & KU_KEY_ENCIPHERMENT) - { - keyUsage |= KEY_USAGE_KEY_ENCIPHERMENT; - } - if (kusage & KU_DATA_ENCIPHERMENT) - { - keyUsage |= KEY_USAGE_DATA_ENCIPHERMENT; - } - if (kusage & KU_KEY_AGREEMENT) - { - keyUsage |= KEY_USAGE_KEY_AGREEMENT; - } - if (kusage & KU_KEY_CERT_SIGN) - { - keyUsage |= KEY_USAGE_KEY_CERT_SIGN; - } - if (kusage & KU_CRL_SIGN) - { - keyUsage |= KEY_USAGE_CRL_SIGN; - } - if (kusage & KU_ENCIPHER_ONLY) - { - keyUsage |= KEY_USAGE_ENCIPHER_ONLY; - } - if (kusage & KU_DECIPHER_ONLY) - { - keyUsage |= KEY_USAGE_DECIPHER_ONLY; - } - } - return keyUsage; -} - -unsigned int -OpenSSLCertificateI::getExtendedKeyUsage() const -{ - unsigned int extendedKeyUsage = 0; - int flags = X509_get_extension_flags(_cert); - if (flags & EXFLAG_XKUSAGE) - { - unsigned int xkusage = X509_get_extended_key_usage(_cert); - if (xkusage & XKU_ANYEKU) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_ANY_KEY_USAGE; - } - if (xkusage & XKU_SSL_SERVER) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_SERVER_AUTH; - } - if (xkusage & XKU_SSL_CLIENT) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_CLIENT_AUTH; - } - if (xkusage & XKU_CODE_SIGN) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_CODE_SIGNING; - } - if (xkusage & XKU_SMIME) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_EMAIL_PROTECTION; - } - if (xkusage & XKU_TIMESTAMP) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_TIME_STAMPING; - } - if (xkusage & XKU_OCSP_SIGN) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_OCSP_SIGNING; - } - } - return extendedKeyUsage; -} - -OpenSSL::CertificatePtr -OpenSSL::Certificate::create(x509_st* cert) -{ - return make_shared(cert); -} - -OpenSSL::CertificatePtr -OpenSSL::Certificate::load(const std::string& file) -{ - BIO* cert = BIO_new(BIO_s_file()); - if (BIO_read_filename(cert, file.c_str()) <= 0) - { - BIO_free(cert); - throw CertificateReadException(__FILE__, __LINE__, "error opening file"); - } - - x509_st* x = PEM_read_bio_X509(cert, nullptr, nullptr, nullptr); - BIO_free(cert); - if (x == nullptr) - { - throw CertificateReadException(__FILE__, __LINE__, "error reading file:\n" + getErrors(false)); - } - // Calling it with -1 for the side effects, this ensure that the extensions info is loaded - if (X509_check_purpose(x, -1, -1) == -1) - { - throw CertificateReadException(__FILE__, __LINE__, "error loading certificate:\n" + getErrors(false)); - } - return make_shared(x); -} - -OpenSSL::CertificatePtr -OpenSSL::Certificate::decode(const std::string& encoding) -{ - BIO* cert = BIO_new_mem_buf(static_cast(const_cast(&encoding[0])), static_cast(encoding.size())); - x509_st* x = PEM_read_bio_X509(cert, nullptr, nullptr, nullptr); - BIO_free(cert); - if (x == nullptr) - { - throw CertificateEncodingException(__FILE__, __LINE__, getErrors(false)); - } - // Calling it with -1 for the side effects, this ensure that the extensions info is loaded - if (X509_check_purpose(x, -1, -1) == -1) - { - throw CertificateReadException(__FILE__, __LINE__, "error loading certificate:\n" + getErrors(false)); - } - return make_shared(x); -} - -CertificatePtr -Certificate::load(const std::string& file) -{ - return OpenSSL::Certificate::load(file); -} - -CertificatePtr -Certificate::decode(const std::string& encoding) -{ - return OpenSSL::Certificate::decode(encoding); -} diff --git a/cpp/src/Ice/SSL/OpenSSLEngine.cpp b/cpp/src/Ice/SSL/OpenSSLEngine.cpp index 0b22d9f5673..658654b98ae 100644 --- a/cpp/src/Ice/SSL/OpenSSLEngine.cpp +++ b/cpp/src/Ice/SSL/OpenSSLEngine.cpp @@ -9,7 +9,6 @@ #include "Ice/Logger.h" #include "Ice/LoggerUtil.h" #include "Ice/Properties.h" -#include "Ice/SSL/OpenSSL.h" #include "IceUtil/FileUtil.h" #include "IceUtil/StringUtil.h" #include "OpenSSLEngineF.h" @@ -503,22 +502,10 @@ OpenSSL::SSLEngine::validationCallback(bool ok, X509_STORE_CTX* ctx, const Ice:: { // At this point before the SSL handshake is completed, the connection info doesn't contain the peer's // certificate chain required for verifyPeer. We set it here. - - if (ok) + int depth = X509_STORE_CTX_get_error_depth(ctx); + if (ok && depth == 0) { - // TODO we should refactor verifyPeer to not depend on the Certificate API in a follow-up PR. - vector certs; - STACK_OF(X509)* chain = X509_STORE_CTX_get1_chain(ctx); - if (chain != 0) - { - for (int i = 0; i < sk_X509_num(chain); ++i) - { - CertificatePtr cert = OpenSSL::Certificate::create(X509_dup(sk_X509_value(chain, i))); - certs.push_back(cert); - } - sk_X509_pop_free(chain, X509_free); - } - const_cast(info)->certs = certs; + // Only called for the peer certificate. verifyPeer(info); } return ok; diff --git a/cpp/src/Ice/SSL/OpenSSLEngine.h b/cpp/src/Ice/SSL/OpenSSLEngine.h index dbb0e858f42..f2deae96fc0 100644 --- a/cpp/src/Ice/SSL/OpenSSLEngine.h +++ b/cpp/src/Ice/SSL/OpenSSLEngine.h @@ -9,9 +9,9 @@ #include "Ice/BuiltinSequences.h" #include "Ice/SSL/ClientAuthenticationOptions.h" #include "Ice/SSL/ServerAuthenticationOptions.h" -#include "OpenSSLUtil.h" #include "SSLEngine.h" #include "SSLInstanceF.h" +#include "SSLUtil.h" namespace Ice::SSL::OpenSSL { diff --git a/cpp/src/Ice/SSL/OpenSSLTransceiverI.cpp b/cpp/src/Ice/SSL/OpenSSLTransceiverI.cpp index 9a389238b84..ce7bb64315f 100644 --- a/cpp/src/Ice/SSL/OpenSSLTransceiverI.cpp +++ b/cpp/src/Ice/SSL/OpenSSLTransceiverI.cpp @@ -8,7 +8,6 @@ #include "Ice/LocalException.h" #include "Ice/LoggerUtil.h" #include "Ice/SSL/ConnectionInfo.h" -#include "Ice/SSL/OpenSSL.h" #include "OpenSSLEngine.h" #include "SSLEngine.h" #include "SSLInstance.h" @@ -225,28 +224,26 @@ OpenSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal:: rethrow_exception(_verificationException); } - // Retrieve the certificate chain. - - // When calling on the server side the peer certificate is not included in SSL_get_peer_cert_chain and must be - // obtabined separately using SSL_get_peer_certificate. - if (_incoming) + // Retrieve the certificate chain if the verification callback has not already fill it. + if (!_peerCertificate) { - X509* peerCertificate = SSL_get_peer_certificate(_ssl); - if (peerCertificate) + // When calling on the server side the peer certificate is not included in SSL_get_peer_cert_chain and must be + // obtained separately using SSL_get_peer_certificate. + if (_incoming) { - _certs.clear(); - CertificatePtr cert = OpenSSL::Certificate::create(peerCertificate); - _certs.push_back(cert); + X509* peerCertificate = SSL_get_peer_certificate(_ssl); + if (peerCertificate) + { + _peerCertificate = X509_dup(peerCertificate); + } } - } - - STACK_OF(X509)* chain = SSL_get_peer_cert_chain(_ssl); - if (chain) - { - for (int i = 0; i < sk_X509_num(chain); ++i) + else { - CertificatePtr cert = OpenSSL::Certificate::create(X509_dup(sk_X509_value(chain, i))); - _certs.push_back(cert); + STACK_OF(X509)* chain = SSL_get_peer_cert_chain(_ssl); + if (chain && sk_X509_num(chain) > 0) + { + _peerCertificate = X509_dup(sk_X509_value(chain, 0)); + } } } @@ -309,6 +306,12 @@ OpenSSL::TransceiverI::close() _sslCtx = nullptr; } + if (_peerCertificate) + { + X509_free(_peerCertificate); + _peerCertificate = nullptr; + } + if (_memBio) { BIO_free(_memBio); @@ -612,7 +615,14 @@ OpenSSL::TransceiverI::getInfo() const info->underlying = _delegate->getInfo(); info->incoming = _incoming; info->adapterName = _adapterName; - info->certs = _certs; + if (_peerCertificate) + { + info->peerCertificate = X509_dup(_peerCertificate); + } + else + { + info->peerCertificate = nullptr; + } return info; } @@ -633,6 +643,16 @@ OpenSSL::TransceiverI::verifyCallback(int ok, X509_STORE_CTX* ctx) assert(_remoteCertificateVerificationCallback); try { + if (!_peerCertificate) + { + // Retrieve the peer certificate if not already set by a previous call to the verification callback. + STACK_OF(X509)* chain = X509_STORE_CTX_get1_chain(ctx); + if (chain && sk_X509_num(chain) > 0) + { + _peerCertificate = X509_dup(sk_X509_value(chain, 0)); + sk_X509_pop_free(chain, X509_free); + } + } bool verified = _remoteCertificateVerificationCallback(ok, ctx, dynamic_pointer_cast(getInfo())); if (!verified) @@ -680,6 +700,7 @@ OpenSSL::TransceiverI::TransceiverI( _incoming(true), _delegate(delegate), _connected(false), + _peerCertificate(nullptr), _ssl(nullptr), _sslCtx(nullptr), _memBio(nullptr), @@ -707,6 +728,7 @@ OpenSSL::TransceiverI::TransceiverI( _incoming(false), _delegate(delegate), _connected(false), + _peerCertificate(nullptr), _ssl(nullptr), _sslCtx(nullptr), _memBio(nullptr), diff --git a/cpp/src/Ice/SSL/OpenSSLTransceiverI.h b/cpp/src/Ice/SSL/OpenSSLTransceiverI.h index b004d01d209..6a22c88cf9d 100644 --- a/cpp/src/Ice/SSL/OpenSSLTransceiverI.h +++ b/cpp/src/Ice/SSL/OpenSSLTransceiverI.h @@ -70,7 +70,7 @@ namespace Ice::SSL::OpenSSL const IceInternal::TransceiverPtr _delegate; bool _connected; std::string _cipher; - std::vector _certs; + X509* _peerCertificate; ::SSL* _ssl; SSL_CTX* _sslCtx; BIO* _memBio; diff --git a/cpp/src/Ice/SSL/OpenSSLUtil.cpp b/cpp/src/Ice/SSL/OpenSSLUtil.cpp deleted file mode 100644 index 7ab74bb35c4..00000000000 --- a/cpp/src/Ice/SSL/OpenSSLUtil.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#include "OpenSSLUtil.h" - -#include - -#include - -// -// Avoid old style cast warnings from OpenSSL macros -// -#if defined(__GNUC__) -# pragma GCC diagnostic ignored "-Wold-style-cast" -# // Ignore OpenSSL 3.0 deprecation warning -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -using namespace std; - -string -Ice::SSL::OpenSSL::getErrors(bool verbose) -{ - ostringstream ostr; - - const char* file; - const char* data; - int line; - int flags; - unsigned long err; - int count = 0; - while ((err = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) - { - if (count > 0) - { - ostr << endl; - } - - if (verbose) - { - if (count > 0) - { - ostr << endl; - } - - char buf[200]; - ERR_error_string_n(err, buf, sizeof(buf)); - - ostr << "error # = " << err << endl; - ostr << "message = " << buf << endl; - ostr << "location = " << file << ", " << line; - if (flags & ERR_TXT_STRING) - { - ostr << endl; - ostr << "data = " << data; - } - } - else - { - const char* reason = ERR_reason_error_string(err); - ostr << (reason == nullptr ? "unknown reason" : reason); - if (flags & ERR_TXT_STRING) - { - ostr << ": " << data; - } - } - - ++count; - } - - ERR_clear_error(); - - return ostr.str(); -} diff --git a/cpp/src/Ice/SSL/OpenSSLUtil.h b/cpp/src/Ice/SSL/OpenSSLUtil.h deleted file mode 100644 index 5cbf4e309e1..00000000000 --- a/cpp/src/Ice/SSL/OpenSSLUtil.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#ifndef ICE_SSL_OPENSSL_UTIL_I_H -#define ICE_SSL_OPENSSL_UTIL_I_H - -#include - -#include - -namespace Ice::SSL::OpenSSL -{ - // Accumulate the OpenSSL error stack into a string. - std::string getErrors(bool); - -} -#endif diff --git a/cpp/src/Ice/SSL/SSLEndpointI.cpp b/cpp/src/Ice/SSL/SSLEndpointI.cpp index 151736947ef..30fd85bb5ff 100644 --- a/cpp/src/Ice/SSL/SSLEndpointI.cpp +++ b/cpp/src/Ice/SSL/SSLEndpointI.cpp @@ -58,7 +58,35 @@ namespace } } // Implement virtual destructors out of line to avoid weak vtables. -Ice::SSL::ConnectionInfo::~ConnectionInfo() {} +#if defined(ICE_USE_SCHANNEL) +Ice::SSL::SchannelConnectionInfo::~SchannelConnectionInfo() +{ + if (peerCertificate) + { + CertFreeCertificateContext(peerCertificate); + peerCertificate = nullptr; + } +} +#elif defined(ICE_USE_SECURE_TRANSPORT) +Ice::SSL::SecureTransportConnectionInfo::~SecureTransportConnectionInfo() +{ + if (peerCertificate) + { + CFRelease(peerCertificate); + peerCertificate = nullptr; + } +} +#else +Ice::SSL::OpenSSLConnectionInfo::~OpenSSLConnectionInfo() +{ + if (peerCertificate) + { + X509_free(peerCertificate); + peerCertificate = nullptr; + } +} +#endif + Ice::SSL::EndpointInfo::~EndpointInfo() {} Ice::SSL::EndpointI::EndpointI(const InstancePtr& instance, const IceInternal::EndpointIPtr& del) diff --git a/cpp/src/Ice/SSL/SSLEngine.cpp b/cpp/src/Ice/SSL/SSLEngine.cpp index 76b6c12d63d..124b6a74145 100644 --- a/cpp/src/Ice/SSL/SSLEngine.cpp +++ b/cpp/src/Ice/SSL/SSLEngine.cpp @@ -80,86 +80,6 @@ Ice::SSL::SSLEngine::initialize() const_cast(_revocationCheck) = properties->getPropertyAsIntWithDefault("IceSSL.RevocationCheck", 0); } -void -Ice::SSL::SSLEngine::verifyPeerCertName(const ConnectionInfoPtr& info, const string& address) const -{ - // For an outgoing connection, we compare the proxy address (if any) against fields in the server's certificate - // (if any). - if (_checkCertName && !info->certs.empty() && !address.empty()) - { - const CertificatePtr cert = info->certs[0]; - - // Extract the IP addresses and the DNS names from the subject alternative names. - vector> subjectAltNames = cert->getSubjectAlternativeNames(); - vector ipAddresses; - vector dnsNames; - for (vector>::const_iterator p = subjectAltNames.begin(); p != subjectAltNames.end(); ++p) - { - if (p->first == AltNAmeIP) - { - ipAddresses.push_back(IceUtilInternal::toLower(p->second)); - } - else if (p->first == AltNameDNS) - { - dnsNames.push_back(IceUtilInternal::toLower(p->second)); - } - } - - bool certNameOK = false; - string addrLower = IceUtilInternal::toLower(address); - bool isIpAddress = IceInternal::isIpAddress(address); - - // If address is an IP address, compare it to the subject alternative names IP address - if (isIpAddress) - { - certNameOK = find(ipAddresses.begin(), ipAddresses.end(), addrLower) != ipAddresses.end(); - } - else - { - // If subjectAlt is empty compare it to the subject CN, otherwise compare it to the to the subject alt - // name dnsNames. - if (dnsNames.empty()) - { - DistinguishedName d = cert->getSubjectDN(); - string dn = IceUtilInternal::toLower(string(d)); - string cn = "cn=" + addrLower; - string::size_type pos = dn.find(cn); - if (pos != string::npos) - { - // Ensure we match the entire common name. - certNameOK = (pos + cn.size() == dn.size()) || (dn[pos + cn.size()] == ','); - } - } - else - { - certNameOK = find(dnsNames.begin(), dnsNames.end(), addrLower) != dnsNames.end(); - } - } - - if (!certNameOK) - { - ostringstream ostr; - ostr << "SSL transport: "; - if (_verifyPeer > 0) - { - ostr << "ignoring "; - } - ostr << "certificate verification failure " << (isIpAddress ? "IP address mismatch" : "Hostname mismatch"); - string msg = ostr.str(); - if (_securityTraceLevel >= 1) - { - Trace out(getLogger(), _securityTraceCategory); - out << msg; - } - - if (_verifyPeer > 0) - { - throw SecurityException(__FILE__, __LINE__, msg); - } - } - } -} - void Ice::SSL::SSLEngine::verifyPeer(const ConnectionInfoPtr& info) const { diff --git a/cpp/src/Ice/SSL/SSLEngine.h b/cpp/src/Ice/SSL/SSLEngine.h index bf195c3c05f..39fd8384c33 100644 --- a/cpp/src/Ice/SSL/SSLEngine.h +++ b/cpp/src/Ice/SSL/SSLEngine.h @@ -42,7 +42,6 @@ namespace Ice::SSL // Verify peer certificate. virtual void verifyPeer(const ConnectionInfoPtr&) const; - void verifyPeerCertName(const ConnectionInfoPtr&, const std::string&) const; virtual ClientAuthenticationOptions createClientAuthenticationOptions(const std::string&) const = 0; virtual ServerAuthenticationOptions createServerAuthenticationOptions() const = 0; diff --git a/cpp/src/Ice/SSL/SSLException.cpp b/cpp/src/Ice/SSL/SSLException.cpp new file mode 100644 index 00000000000..1b0343f1273 --- /dev/null +++ b/cpp/src/Ice/SSL/SSLException.cpp @@ -0,0 +1,46 @@ +// +// Copyright (c) ZeroC, Inc. All rights reserved. +// + +#include "Ice/SSL/Certificate.h" + +#include + +using namespace std; +using namespace Ice::SSL; + +CertificateReadException::CertificateReadException(const char* file, int line, string r) noexcept + : Exception(file, line), + reason(std::move(r)) +{ +} + +string +CertificateReadException::ice_id() const +{ + return "::Ice::SSL::CertificateReadException"; +} + +CertificateEncodingException::CertificateEncodingException(const char* file, int line, string r) noexcept + : Exception(file, line), + reason(std::move(r)) +{ +} + +string +CertificateEncodingException::ice_id() const +{ + return "::Ice::SSL::CertificateEncodingException"; +} + +ParseException::ParseException(const char* file, int line, string r) noexcept + : Exception(file, line), + reason(std::move(r)) +{ +} + +string +ParseException::ice_id() const +{ + return "::Ice::SSL::ParseException"; +} diff --git a/cpp/src/Ice/SSL/SSLUtil.cpp b/cpp/src/Ice/SSL/SSLUtil.cpp index 3f8c24fad57..53e85e6a333 100644 --- a/cpp/src/Ice/SSL/SSLUtil.cpp +++ b/cpp/src/Ice/SSL/SSLUtil.cpp @@ -15,6 +15,7 @@ #include "Ice/UniqueRef.h" #include "IceUtil/FileUtil.h" #include "IceUtil/StringUtil.h" +#include "RFC2253.h" #include "SSLUtil.h" #include @@ -27,7 +28,7 @@ using namespace Ice::SSL; #if defined(__APPLE__) -std::string +string Ice::SSL::fromCFString(CFStringRef v) { string s; @@ -153,3 +154,490 @@ Ice::SSL::checkPath(const string& path, const string& defaultDir, bool dir, stri } return false; } + +namespace +{ + const pair certificateOIDS[] = { + {"2.5.4.3", "CN"}, + {"2.5.4.4", "SN"}, + {"2.5.4.5", "DeviceSerialNumber"}, + {"2.5.4.6", "C"}, + {"2.5.4.7", "L"}, + {"2.5.4.8", "ST"}, + {"2.5.4.9", "STREET"}, + {"2.5.4.10", "O"}, + {"2.5.4.11", "OU"}, + {"2.5.4.12", "T"}, + {"2.5.4.42", "G"}, + {"2.5.4.43", "I"}, + {"1.2.840.113549.1.9.8", "unstructuredAddress"}, + {"1.2.840.113549.1.9.2", "unstructuredName"}, + {"1.2.840.113549.1.9.1", "emailAddress"}, + {"0.9.2342.19200300.100.1.25", "DC"}}; + const int certificateOIDSSize = sizeof(certificateOIDS) / sizeof(pair); +} + +#if defined(ICE_USE_SCHANNEL) +namespace +{ + string certNameToString(CERT_NAME_BLOB* certName) + { + assert(certName); + DWORD length = 0; + if ((length = + CertNameToStr(X509_ASN_ENCODING, certName, CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, 0, 0)) == 0) + { + throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); + } + + vector buffer(length); + if (!CertNameToStr( + X509_ASN_ENCODING, + certName, + CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, + &buffer[0], + length)) + { + throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); + } + + string s(&buffer[0]); + for (int i = 0; i < certificateOIDSSize; ++i) + { + const pair* certificateOID = &certificateOIDS[i]; + assert(certificateOID); + const string name = string(certificateOID->first) + "="; + const string alias = string(certificateOID->second) + "="; + size_t pos = 0; + while ((pos = s.find(name, pos)) != string::npos) + { + s.replace(pos, name.size(), alias); + } + } + return s; + } +} + +Ice::SSL::ScopedCertificate::~ScopedCertificate() +{ + if (_certificate) + { + CertFreeCertificateContext(_certificate); + } +} + +DistinguishedName +Ice::SSL::getSubjectName(PCCERT_CONTEXT cert) +{ + return DistinguishedName(certNameToString(&cert->pCertInfo->Subject)); +} + +vector> +Ice::SSL::getSubjectAltNames(PCCERT_CONTEXT cert) +{ + vector> altNames; + + PCERT_EXTENSION extension = + CertFindExtension(szOID_SUBJECT_ALT_NAME2, cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension); + if (extension) + { + CERT_ALT_NAME_INFO* altName; + DWORD length = 0; + if (!CryptDecodeObjectEx( + X509_ASN_ENCODING, + X509_ALTERNATE_NAME, + extension->Value.pbData, + extension->Value.cbData, + CRYPT_DECODE_ALLOC_FLAG, + 0, + &altName, + &length)) + { + throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); + } + + for (DWORD i = 0; i < altName->cAltEntry; ++i) + { + CERT_ALT_NAME_ENTRY* entry = &altName->rgAltEntry[i]; + + switch (entry->dwAltNameChoice) + { + case CERT_ALT_NAME_RFC822_NAME: + { + altNames.push_back(make_pair(AltNameEmail, Ice::wstringToString(entry->pwszRfc822Name))); + break; + } + case CERT_ALT_NAME_DNS_NAME: + { + altNames.push_back(make_pair(AltNameDNS, Ice::wstringToString(entry->pwszDNSName))); + break; + } + case CERT_ALT_NAME_DIRECTORY_NAME: + { + altNames.push_back(make_pair(AltNameDirectory, certNameToString(&entry->DirectoryName))); + break; + } + case CERT_ALT_NAME_URL: + { + altNames.push_back(make_pair(AltNameURL, Ice::wstringToString(entry->pwszURL))); + break; + } + case CERT_ALT_NAME_IP_ADDRESS: + { + if (entry->IPAddress.cbData == 4) + { + // + // IPv4 address + // + ostringstream os; + uint8_t* src = reinterpret_cast(entry->IPAddress.pbData); + for (int j = 0; j < 4;) + { + int value = 0; + uint8_t* dest = reinterpret_cast(&value); + *dest = *src++; + os << value; + if (++j < 4) + { + os << "."; + } + } + altNames.push_back(make_pair(AltNAmeIP, os.str())); + } + // + // TODO IPv6 Address support. + // + break; + } + default: + { + // Not supported + break; + } + } + } + LocalFree(altName); + } + return altNames; +} + +string +Ice::SSL::encodeCertificate(PCCERT_CONTEXT cert) +{ + string s; + DWORD encodedLength = 0; + if (!CryptBinaryToString( + cert->pbCertEncoded, + cert->cbCertEncoded, + CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, + 0, + &encodedLength)) + { + throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); + } + + std::vector encoded; + encoded.resize(encodedLength); + if (!CryptBinaryToString( + cert->pbCertEncoded, + cert->cbCertEncoded, + CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, + &encoded[0], + &encodedLength)) + { + throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); + } + s.assign(&encoded[0]); + return s; +} + +PCCERT_CONTEXT +Ice::SSL::decodeCertificate(const std::string& data) +{ + CERT_SIGNED_CONTENT_INFO* cert = nullptr; + DWORD derLength = static_cast(data.size()); + vector derBuffer; + derBuffer.resize(derLength); + + if (!CryptStringToBinary( + &data.c_str()[0], + static_cast(data.size()), + CRYPT_STRING_BASE64HEADER, + &derBuffer[0], + &derLength, + nullptr, + nullptr)) + { + // Base64 data should always be bigger than binary + assert(GetLastError() != ERROR_MORE_DATA); + throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); + } + + DWORD decodedLeng = 0; + if (!CryptDecodeObjectEx( + X509_ASN_ENCODING, + X509_CERT, + &derBuffer[0], + derLength, + CRYPT_DECODE_ALLOC_FLAG, + nullptr, + &cert, + &decodedLeng)) + { + throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); + } + PCCERT_CONTEXT certContext = + CertCreateCertificateContext(X509_ASN_ENCODING, cert->ToBeSigned.pbData, cert->ToBeSigned.cbData); + LocalFree(cert); + if (!certContext) + { + throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); + } + return certContext; +} +#elif defined(ICE_USE_SECURE_TRANSPORT) +string +Ice::SSL::certificateOIDAlias(const string& name) +{ + for (int i = 0; i < certificateOIDSSize; ++i) + { + const pair* certificateOID = &certificateOIDS[i]; + assert(certificateOID); + if (name == certificateOID->first) + { + return certificateOID->second; + } + } + return name; +} + +Ice::SSL::ScopedCertificate::~ScopedCertificate() +{ + if (_certificate) + { + CFRelease(_certificate); + } +} +#elif defined(ICE_USE_OPENSSL) + +// +// Avoid old style cast warnings from OpenSSL macros +// +# if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wold-style-cast" +# // Ignore OpenSSL 3.0 deprecation warning +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# endif + +namespace +{ + string convertX509NameToString(X509_name_st* name) + { + BIO* out = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(out, name, 0, XN_FLAG_RFC2253); + BUF_MEM* p; + BIO_get_mem_ptr(out, &p); + string result = string(p->data, p->length); + BIO_free(out); + return result; + } + + vector> convertGeneralNames(GENERAL_NAMES* gens) + { + vector> alt; + if (gens == 0) + { + return alt; + } + for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) + { + GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); + pair p; + p.first = gen->type; + switch (gen->type) + { + case GEN_EMAIL: + { + ASN1_IA5STRING* str = gen->d.rfc822Name; + if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) + { + p.second = string(reinterpret_cast(str->data), str->length); + } + break; + } + case GEN_DNS: + { + ASN1_IA5STRING* str = gen->d.dNSName; + if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) + { + p.second = string(reinterpret_cast(str->data), str->length); + } + break; + } + case GEN_DIRNAME: + { + p.second = convertX509NameToString(gen->d.directoryName); + break; + } + case GEN_URI: + { + ASN1_IA5STRING* str = gen->d.uniformResourceIdentifier; + if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) + { + p.second = string(reinterpret_cast(str->data), str->length); + } + break; + } + case GEN_IPADD: + { + ASN1_OCTET_STRING* addr = gen->d.iPAddress; + // TODO: Support IPv6 someday. + if (addr && addr->type == V_ASN1_OCTET_STRING && addr->data && addr->length == 4) + { + ostringstream ostr; + for (int j = 0; j < 4; ++j) + { + if (j > 0) + { + ostr << '.'; + } + ostr << static_cast(addr->data[j]); + } + p.second = ostr.str(); + } + break; + } + case GEN_OTHERNAME: + case GEN_EDIPARTY: + case GEN_X400: + case GEN_RID: + { + // + // TODO: These types are not supported. If the user wants + // them, they have to get at the certificate data. Another + // alternative is to DER encode the data (as the Java + // certificate does). + // + break; + } + } + if (!p.second.empty()) + { + alt.push_back(p); + } + } + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return alt; + } +} + +Ice::SSL::ScopedCertificate::~ScopedCertificate() +{ + if (_certificate) + { + X509_free(_certificate); + } +} + +string +Ice::SSL::getErrors(bool verbose) +{ + ostringstream ostr; + + const char* file; + const char* data; + int line; + int flags; + unsigned long err; + int count = 0; + while ((err = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) + { + if (count > 0) + { + ostr << endl; + } + + if (verbose) + { + if (count > 0) + { + ostr << endl; + } + + char buf[200]; + ERR_error_string_n(err, buf, sizeof(buf)); + + ostr << "error # = " << err << endl; + ostr << "message = " << buf << endl; + ostr << "location = " << file << ", " << line; + if (flags & ERR_TXT_STRING) + { + ostr << endl; + ostr << "data = " << data; + } + } + else + { + const char* reason = ERR_reason_error_string(err); + ostr << (reason == nullptr ? "unknown reason" : reason); + if (flags & ERR_TXT_STRING) + { + ostr << ": " << data; + } + } + + ++count; + } + + ERR_clear_error(); + + return ostr.str(); +} + +Ice::SSL::DistinguishedName +Ice::SSL::getSubjectName(X509* certificate) +{ + return DistinguishedName(RFC2253::parseStrict(convertX509NameToString(X509_get_subject_name(certificate)))); +} + +std::vector> +Ice::SSL::getSubjectAltNames(X509* certificate) +{ + return convertGeneralNames( + reinterpret_cast(X509_get_ext_d2i(certificate, NID_subject_alt_name, 0, 0))); +} + +string +Ice::SSL::encodeCertificate(X509* certificate) +{ + BIO* out = BIO_new(BIO_s_mem()); + int i = PEM_write_bio_X509(out, certificate); + if (i <= 0) + { + BIO_free(out); + throw CertificateEncodingException(__FILE__, __LINE__, getErrors(false)); + } + BUF_MEM* p; + BIO_get_mem_ptr(out, &p); + string result = string(p->data, p->length); + BIO_free(out); + return result; +} +ICE_API X509* +Ice::SSL::decodeCertificate(const string& data) +{ + BIO* cert = BIO_new_mem_buf(static_cast(const_cast(&data[0])), static_cast(data.size())); + x509_st* x = PEM_read_bio_X509(cert, nullptr, nullptr, nullptr); + BIO_free(cert); + if (x == nullptr) + { + throw CertificateEncodingException(__FILE__, __LINE__, getErrors(false)); + } + // Calling it with -1 for the side effects, this ensure that the extensions info is loaded + if (X509_check_purpose(x, -1, -1) == -1) + { + throw CertificateReadException(__FILE__, __LINE__, "error loading certificate:\n" + getErrors(false)); + } + return x; +} +#endif diff --git a/cpp/src/Ice/SSL/SSLUtil.h b/cpp/src/Ice/SSL/SSLUtil.h index 0fabf3937c9..989e341d2f7 100644 --- a/cpp/src/Ice/SSL/SSLUtil.h +++ b/cpp/src/Ice/SSL/SSLUtil.h @@ -2,22 +2,18 @@ // Copyright (c) ZeroC, Inc. All rights reserved. // -#ifndef ICE_SSL_UTIL_H -#define ICE_SSL_UTIL_H +#ifndef ICE_SSL_SSL_UTIL_H +#define ICE_SSL_SSL_UTIL_H + +#include "Ice/Config.h" +#include "Ice/SSL/Certificate.h" +#include "Ice/SSL/Config.h" #include #include +#include #include -#if defined(__APPLE__) -# include -# if TARGET_OS_IPHONE != 0 -# define ICE_USE_SECURE_TRANSPORT_IOS 1 -# else -# define ICE_USE_SECURE_TRANSPORT_MACOS 1 -# endif -#endif - namespace Ice::SSL { #if defined(__APPLE__) @@ -46,12 +42,64 @@ namespace Ice::SSL // const AltNameObjectIdentifier = 8; // Read a file into memory buffer. - ICE_API void readFile(const std::string&, std::vector&); + void readFile(const std::string&, std::vector&); // Determine if a file or directory exists, with an optional default directory. - ICE_API bool checkPath(const std::string&, const std::string&, bool, std::string&); + bool checkPath(const std::string&, const std::string&, bool, std::string&); + + bool parseBytes(const std::string&, std::vector&); + +#if defined(ICE_USE_SCHANNEL) + ICE_API DistinguishedName getSubjectName(PCCERT_CONTEXT); + ICE_API std::vector> getSubjectAltNames(PCCERT_CONTEXT); + ICE_API std::string encodeCertificate(PCCERT_CONTEXT); + + class ICE_API ScopedCertificate + { + public: + ScopedCertificate(PCCERT_CONTEXT certificate) : _certificate(certificate) {} + ~ScopedCertificate(); + PCCERT_CONTEXT get() const { return _certificate; } + + private: + PCCERT_CONTEXT _certificate; + }; + ICE_API PCCERT_CONTEXT decodeCertificate(const std::string&); +#elif defined(ICE_USE_SECURE_TRANSPORT) + std::string certificateOIDAlias(const std::string&); + ICE_API DistinguishedName getSubjectName(SecCertificateRef); + ICE_API std::vector> getSubjectAltNames(SecCertificateRef); + ICE_API std::string encodeCertificate(SecCertificateRef); + + class ICE_API ScopedCertificate + { + public: + ScopedCertificate(SecCertificateRef certificate) : _certificate(certificate) {} + ~ScopedCertificate(); + SecCertificateRef get() const { return _certificate; } - ICE_API bool parseBytes(const std::string&, std::vector&); + private: + SecCertificateRef _certificate; + }; + ICE_API SecCertificateRef decodeCertificate(const std::string&); +#elif defined(ICE_USE_OPENSSL) + // Accumulate the OpenSSL error stack into a string. + std::string getErrors(bool); + ICE_API DistinguishedName getSubjectName(X509*); + ICE_API std::vector> getSubjectAltNames(X509*); + ICE_API std::string encodeCertificate(X509*); + class ICE_API ScopedCertificate + { + public: + ScopedCertificate(X509* certificate) : _certificate(certificate) {} + ~ScopedCertificate(); + X509* get() const { return _certificate; } + + private: + X509* _certificate; + }; + ICE_API X509* decodeCertificate(const std::string&); +#endif } #endif diff --git a/cpp/src/Ice/SSL/SchannelCertificateI.cpp b/cpp/src/Ice/SSL/SchannelCertificateI.cpp deleted file mode 100644 index db9c8daae46..00000000000 --- a/cpp/src/Ice/SSL/SchannelCertificateI.cpp +++ /dev/null @@ -1,733 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -#include "../StringUtil.h" -#include "CertificateI.h" -#include "Ice/SSL/Certificate.h" -#include "Ice/SSL/Schannel.h" -#include "Ice/StringConverter.h" -#include "SSLUtil.h" - -#include - -#include -#include -#include -#include - -using namespace std; -using namespace Ice; -using namespace Ice::SSL; - -namespace -{ - class CertInfoHolder - { - public: - CertInfoHolder(CERT_INFO* v) : _certInfo(v) {} - - virtual ~CertInfoHolder() { LocalFree(_certInfo); } - - private: - CERT_INFO* _certInfo; - }; - using CertInfoHolderPtr = shared_ptr; - - class SchannelX509ExtensionI : public X509Extension - { - public: - SchannelX509ExtensionI(CERT_EXTENSION, const string&, const CertInfoHolderPtr&); - virtual bool isCritical() const; - virtual string getOID() const; - virtual vector getData() const; - - private: - CERT_EXTENSION _extension; - string _oid; - CertInfoHolderPtr _certInfo; // Keep a reference on the CERT_INFO struct that holds the extension - }; - - class SchannelCertificateI : public Schannel::Certificate, public CertificateI - { - public: - SchannelCertificateI(CERT_SIGNED_CONTENT_INFO*); - ~SchannelCertificateI(); - - virtual bool operator==(const Ice::SSL::Certificate&) const; - - virtual vector getAuthorityKeyIdentifier() const; - virtual vector getSubjectKeyIdentifier() const; - virtual bool verify(const CertificatePtr&) const; - virtual string encode() const; - - virtual chrono::system_clock::time_point getNotAfter() const; - virtual chrono::system_clock::time_point getNotBefore() const; - virtual string getSerialNumber() const; - virtual DistinguishedName getIssuerDN() const; - virtual vector> getIssuerAlternativeNames() const; - virtual DistinguishedName getSubjectDN() const; - virtual vector> getSubjectAlternativeNames() const; - virtual int getVersion() const; - virtual CERT_SIGNED_CONTENT_INFO* getCert() const; - - protected: - virtual void loadX509Extensions() const; - - private: - virtual unsigned int getKeyUsage() const; - virtual unsigned int getExtendedKeyUsage() const; - - CERT_SIGNED_CONTENT_INFO* _cert; - CERT_INFO* _certInfo; - CertInfoHolderPtr _certInfoHolder; - mutable std::mutex _mutex; - }; - - const int64_t TICKS_PER_MSECOND = INT64_C(10000); - const int64_t MSECS_TO_EPOCH = INT64_C(11644473600000); - - void loadCertificate(PCERT_SIGNED_CONTENT_INFO* cert, const char* buffer, DWORD length) - { - DWORD outLength = length; - vector outBuffer; - outBuffer.resize(outLength); - - if (!CryptStringToBinary(buffer, length, CRYPT_STRING_BASE64HEADER, &outBuffer[0], &outLength, 0, 0)) - { - // - // Base64 data should always be bigger than binary - // - assert(GetLastError() != ERROR_MORE_DATA); - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - - DWORD decodedLeng = 0; - if (!CryptDecodeObjectEx( - X509_ASN_ENCODING, - X509_CERT, - &outBuffer[0], - outLength, - CRYPT_DECODE_ALLOC_FLAG, - 0, - cert, - &decodedLeng)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - } - - void loadCertificate(PCERT_SIGNED_CONTENT_INFO* cert, const string& file) - { - vector buffer; - readFile(file, buffer); - if (buffer.empty()) - { - throw CertificateReadException(__FILE__, __LINE__, "certificate file " + file + " is empty"); - } - loadCertificate(cert, &buffer[0], static_cast(buffer.size())); - } - - chrono::system_clock::time_point filetimeToTime(FILETIME ftime) - { - int64_t value = 0; - DWORD* dest = reinterpret_cast(&value); - *dest++ = ftime.dwLowDateTime; - *dest = ftime.dwHighDateTime; - - return chrono::system_clock::time_point(chrono::milliseconds((value / TICKS_PER_MSECOND) - MSECS_TO_EPOCH)); - } - - string certNameToString(CERT_NAME_BLOB* certName) - { - assert(certName); - DWORD length = 0; - if ((length = - CertNameToStr(X509_ASN_ENCODING, certName, CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, 0, 0)) == 0) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - - vector buffer(length); - if (!CertNameToStr( - X509_ASN_ENCODING, - certName, - CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, - &buffer[0], - length)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - - string s(&buffer[0]); - for (int i = 0; i < certificateOIDSSize; ++i) - { - const CertificateOID* certificateOID = &certificateOIDS[i]; - assert(certificateOID); - const string name = string(certificateOID->name) + "="; - const string alias = string(certificateOID->alias) + "="; - size_t pos = 0; - while ((pos = s.find(name, pos)) != string::npos) - { - s.replace(pos, name.size(), alias); - } - } - return s; - } - - vector> certificateAltNames(CERT_INFO* certInfo, LPCSTR altNameOID) - { - vector> altNames; - - PCERT_EXTENSION extension = CertFindExtension(altNameOID, certInfo->cExtension, certInfo->rgExtension); - if (extension) - { - CERT_ALT_NAME_INFO* altName; - DWORD length = 0; - if (!CryptDecodeObjectEx( - X509_ASN_ENCODING, - X509_ALTERNATE_NAME, - extension->Value.pbData, - extension->Value.cbData, - CRYPT_DECODE_ALLOC_FLAG, - 0, - &altName, - &length)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - - for (DWORD i = 0; i < altName->cAltEntry; ++i) - { - CERT_ALT_NAME_ENTRY* entry = &altName->rgAltEntry[i]; - - switch (entry->dwAltNameChoice) - { - case CERT_ALT_NAME_RFC822_NAME: - { - altNames.push_back(make_pair(AltNameEmail, Ice::wstringToString(entry->pwszRfc822Name))); - break; - } - case CERT_ALT_NAME_DNS_NAME: - { - altNames.push_back(make_pair(AltNameDNS, Ice::wstringToString(entry->pwszDNSName))); - break; - } - case CERT_ALT_NAME_DIRECTORY_NAME: - { - altNames.push_back(make_pair(AltNameDirectory, certNameToString(&entry->DirectoryName))); - break; - } - case CERT_ALT_NAME_URL: - { - altNames.push_back(make_pair(AltNameURL, Ice::wstringToString(entry->pwszURL))); - break; - } - case CERT_ALT_NAME_IP_ADDRESS: - { - if (entry->IPAddress.cbData == 4) - { - // - // IPv4 address - // - ostringstream os; - uint8_t* src = reinterpret_cast(entry->IPAddress.pbData); - for (int j = 0; j < 4;) - { - int value = 0; - uint8_t* dest = reinterpret_cast(&value); - *dest = *src++; - os << value; - if (++j < 4) - { - os << "."; - } - } - altNames.push_back(make_pair(AltNAmeIP, os.str())); - } - // - // TODO IPv6 Address support. - // - break; - } - default: - { - // Not supported - break; - } - } - } - LocalFree(altName); - } - return altNames; - } - -} // End anonymous namespace - -SchannelX509ExtensionI::SchannelX509ExtensionI(CERT_EXTENSION extension, const string& oid, const CertInfoHolderPtr& ci) - : _extension(extension), - _oid(oid), - _certInfo(ci) -{ -} - -bool -SchannelX509ExtensionI::isCritical() const -{ - return _extension.fCritical != 0; -} - -string -SchannelX509ExtensionI::getOID() const -{ - return _oid; -} - -vector -SchannelX509ExtensionI::getData() const -{ - vector data; - data.resize(_extension.Value.cbData); - memcpy(&data[0], _extension.Value.pbData, _extension.Value.cbData); - return data; -} - -SchannelCertificateI::SchannelCertificateI(CERT_SIGNED_CONTENT_INFO* cert) : _cert(cert) -{ - if (!_cert) - { - throw invalid_argument("Invalid certificate reference"); - } - - try - { - // - // Decode certificate info - // - DWORD length = 0; - if (!CryptDecodeObjectEx( - X509_ASN_ENCODING, - X509_CERT_TO_BE_SIGNED, - _cert->ToBeSigned.pbData, - _cert->ToBeSigned.cbData, - CRYPT_DECODE_ALLOC_FLAG, - 0, - &_certInfo, - &length)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - _certInfoHolder = make_shared(_certInfo); - } - catch (...) - { - LocalFree(_cert); - _cert = 0; - throw; - } -} - -SchannelCertificateI::~SchannelCertificateI() -{ - if (_cert) - { - LocalFree(_cert); - } -} - -bool -SchannelCertificateI::operator==(const Ice::SSL::Certificate& r) const -{ - const SchannelCertificateI* p = dynamic_cast(&r); - if (!p) - { - return false; - } - - return CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, _certInfo, p->_certInfo) != 0; -} - -vector -SchannelCertificateI::getAuthorityKeyIdentifier() const -{ - vector keyid; - PCERT_EXTENSION extension = - CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2, _certInfo->cExtension, _certInfo->rgExtension); - if (extension) - { - CERT_AUTHORITY_KEY_ID2_INFO* decoded; - DWORD length = 0; - if (!CryptDecodeObjectEx( - X509_ASN_ENCODING, - X509_AUTHORITY_KEY_ID2, - extension->Value.pbData, - extension->Value.cbData, - CRYPT_DECODE_ALLOC_FLAG, - 0, - &decoded, - &length)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - - if (decoded->KeyId.pbData && decoded->KeyId.cbData) - { - keyid.resize(decoded->KeyId.cbData); - memcpy(&keyid[0], decoded->KeyId.pbData, decoded->KeyId.cbData); - LocalFree(decoded); - } - } - return keyid; -} - -vector -SchannelCertificateI::getSubjectKeyIdentifier() const -{ - vector keyid; - PCERT_EXTENSION extension = - CertFindExtension(szOID_SUBJECT_KEY_IDENTIFIER, _certInfo->cExtension, _certInfo->rgExtension); - if (extension) - { - CRYPT_DATA_BLOB* decoded; - DWORD length = 0; - if (!CryptDecodeObjectEx( - X509_ASN_ENCODING, - szOID_SUBJECT_KEY_IDENTIFIER, - extension->Value.pbData, - extension->Value.cbData, - CRYPT_DECODE_ALLOC_FLAG, - 0, - &decoded, - &length)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - - if (decoded->pbData && decoded->cbData) - { - keyid.resize(decoded->cbData); - memcpy(&keyid[0], decoded->pbData, decoded->cbData); - LocalFree(decoded); - } - } - return keyid; -} - -bool -SchannelCertificateI::verify(const CertificatePtr& cert) const -{ - bool result = false; - SchannelCertificateI* c = dynamic_cast(cert.get()); - if (c) - { - BYTE* buffer = 0; - DWORD length = 0; - if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT, _cert, CRYPT_ENCODE_ALLOC_FLAG, 0, &buffer, &length)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - result = CryptVerifyCertificateSignature( - 0, - X509_ASN_ENCODING, - buffer, - length, - &c->_certInfo->SubjectPublicKeyInfo) != 0; - LocalFree(buffer); - } - return result; -} - -string -SchannelCertificateI::encode() const -{ - string s; - DWORD length = 0; - BYTE* buffer = 0; - try - { - if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT, _cert, CRYPT_ENCODE_ALLOC_FLAG, 0, &buffer, &length)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - - DWORD encodedLength = 0; - if (!CryptBinaryToString(buffer, length, CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, 0, &encodedLength)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - - std::vector encoded; - encoded.resize(encodedLength); - if (!CryptBinaryToString( - buffer, - length, - CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, - &encoded[0], - &encodedLength)) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - LocalFree(buffer); - buffer = 0; - s.assign(&encoded[0]); - } - catch (...) - { - if (buffer) - { - LocalFree(buffer); - } - throw; - } - return s; -} - -chrono::system_clock::time_point -SchannelCertificateI::getNotAfter() const -{ - return filetimeToTime(_certInfo->NotAfter); -} - -chrono::system_clock::time_point -SchannelCertificateI::getNotBefore() const -{ - return filetimeToTime(_certInfo->NotBefore); -} - -string -SchannelCertificateI::getSerialNumber() const -{ - ostringstream os; - for (int i = _certInfo->SerialNumber.cbData - 1; i >= 0; --i) - { - unsigned char c = _certInfo->SerialNumber.pbData[i]; - os.fill('0'); - os.width(2); - os << hex << static_cast(c); - if (i) - { - os << ' '; - } - } - return IceUtilInternal::toUpper(os.str()); -} - -DistinguishedName -SchannelCertificateI::getIssuerDN() const -{ - return DistinguishedName(certNameToString(&_certInfo->Issuer)); -} - -vector> -SchannelCertificateI::getIssuerAlternativeNames() const -{ - return certificateAltNames(_certInfo, szOID_ISSUER_ALT_NAME2); -} - -DistinguishedName -SchannelCertificateI::getSubjectDN() const -{ - return DistinguishedName(certNameToString(&_certInfo->Subject)); -} - -vector> -SchannelCertificateI::getSubjectAlternativeNames() const -{ - return certificateAltNames(_certInfo, szOID_SUBJECT_ALT_NAME2); -} - -int -SchannelCertificateI::getVersion() const -{ - return _certInfo->dwVersion; -} - -CERT_SIGNED_CONTENT_INFO* -SchannelCertificateI::getCert() const -{ - return _cert; -} - -void -SchannelCertificateI::loadX509Extensions() const -{ - lock_guard lock(_mutex); - if (_extensions.empty()) - { - for (size_t i = 0; i < _certInfo->cExtension; ++i) - { - CERT_EXTENSION ext = _certInfo->rgExtension[i]; - _extensions.push_back(std::make_shared(ext, ext.pszObjId, _certInfoHolder)); - } - } -} - -unsigned int -SchannelCertificateI::getKeyUsage() const -{ - unsigned int keyUsage = 0; - BYTE usage[2]; - if (CertGetIntendedKeyUsage(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, _certInfo, usage, 2)) - { - if (usage[0] & CERT_DIGITAL_SIGNATURE_KEY_USAGE) - { - keyUsage |= KEY_USAGE_DIGITAL_SIGNATURE; - } - if (usage[0] & CERT_NON_REPUDIATION_KEY_USAGE) - { - keyUsage |= KEY_USAGE_NON_REPUDIATION; - } - if (usage[0] & CERT_KEY_ENCIPHERMENT_KEY_USAGE) - { - keyUsage |= KEY_USAGE_KEY_ENCIPHERMENT; - } - if (usage[0] & CERT_DATA_ENCIPHERMENT_KEY_USAGE) - { - keyUsage |= KEY_USAGE_DATA_ENCIPHERMENT; - } - if (usage[0] & CERT_KEY_AGREEMENT_KEY_USAGE) - { - keyUsage |= KEY_USAGE_KEY_AGREEMENT; - } - if (usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE) - { - keyUsage |= KEY_USAGE_KEY_CERT_SIGN; - } - if (usage[0] & CERT_CRL_SIGN_KEY_USAGE) - { - keyUsage |= KEY_USAGE_CRL_SIGN; - } - if (usage[0] & CERT_ENCIPHER_ONLY_KEY_USAGE) - { - keyUsage |= KEY_USAGE_ENCIPHER_ONLY; - } - if (usage[1] & CERT_DECIPHER_ONLY_KEY_USAGE) - { - keyUsage |= KEY_USAGE_DECIPHER_ONLY; - } - } - else if (GetLastError()) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - return keyUsage; -} - -unsigned int -SchannelCertificateI::getExtendedKeyUsage() const -{ - unsigned int extendedKeyUsage = 0; - const CERT_CONTEXT* certContext = - CertCreateCertificateContext(X509_ASN_ENCODING, _cert->ToBeSigned.pbData, _cert->ToBeSigned.cbData); - if (certContext == 0) - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - try - { - DWORD cbUsage; - if (!CertGetEnhancedKeyUsage(certContext, 0, 0, &cbUsage)) - { - if (GetLastError() == CRYPT_E_NOT_FOUND) - { - return 0; - } - else - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - } - - if (cbUsage > 0) - { - vector pUsage; - pUsage.resize(cbUsage); - if (!CertGetEnhancedKeyUsage(certContext, 0, reinterpret_cast(&pUsage[0]), &cbUsage)) - { - if (GetLastError() == CRYPT_E_NOT_FOUND) - { - return 0; - } - else - { - throw CertificateEncodingException(__FILE__, __LINE__, IceUtilInternal::lastErrorToString()); - } - } - - CERT_ENHKEY_USAGE* enkeyUsage = reinterpret_cast(&pUsage[0]); - for (DWORD i = 0; i < enkeyUsage->cUsageIdentifier; i++) - { - LPSTR oid = enkeyUsage->rgpszUsageIdentifier[i]; - if (strcmp(oid, szOID_ANY_ENHANCED_KEY_USAGE) == 0) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_ANY_KEY_USAGE; - } - if (strcmp(oid, szOID_PKIX_KP_SERVER_AUTH) == 0) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_SERVER_AUTH; - } - if (strcmp(oid, szOID_PKIX_KP_CLIENT_AUTH) == 0) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_CLIENT_AUTH; - } - if (strcmp(oid, szOID_PKIX_KP_CODE_SIGNING) == 0) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_CODE_SIGNING; - } - if (strcmp(oid, szOID_PKIX_KP_EMAIL_PROTECTION) == 0) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_EMAIL_PROTECTION; - } - if (strcmp(oid, szOID_PKIX_KP_TIMESTAMP_SIGNING) == 0) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_TIME_STAMPING; - } - if (strcmp(oid, szOID_PKIX_KP_OCSP_SIGNING) == 0) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_OCSP_SIGNING; - } - } - } - CertFreeCertificateContext(certContext); - } - catch (...) - { - CertFreeCertificateContext(certContext); - throw; - } - return extendedKeyUsage; -} - -Schannel::CertificatePtr -Schannel::Certificate::create(CERT_SIGNED_CONTENT_INFO* cert) -{ - return make_shared(cert); -} - -Schannel::CertificatePtr -Schannel::Certificate::load(const std::string& file) -{ - CERT_SIGNED_CONTENT_INFO* cert; - loadCertificate(&cert, file); - return make_shared(cert); -} - -Schannel::CertificatePtr -Schannel::Certificate::decode(const std::string& encoding) -{ - CERT_SIGNED_CONTENT_INFO* cert; - loadCertificate(&cert, encoding.c_str(), static_cast(encoding.size())); - return make_shared(cert); -} - -Ice::SSL::CertificatePtr -Ice::SSL::Certificate::load(const std::string& file) -{ - return Ice::SSL::Schannel::Certificate::load(file); -} - -Ice::SSL::CertificatePtr -Ice::SSL::Certificate::decode(const std::string& encoding) -{ - return Ice::SSL::Schannel::Certificate::decode(encoding); -} diff --git a/cpp/src/Ice/SSL/SchannelEngine.cpp b/cpp/src/Ice/SSL/SchannelEngine.cpp index 80fb4ed6967..f347ba08585 100644 --- a/cpp/src/Ice/SSL/SchannelEngine.cpp +++ b/cpp/src/Ice/SSL/SchannelEngine.cpp @@ -400,6 +400,78 @@ namespace first = false; } } + + void verifyPeerCertName( + PCCERT_CONTEXT cert, + const string& address, + const Ice::LoggerPtr& logger, + int traceLevel, + const string& traceCategory) + { + // For an outgoing connection, we compare the proxy address (if any) against fields in the server's certificate + // (if any). + + // Extract the IP addresses and the DNS names from the subject alternative names. + vector> subjectAltNames = getSubjectAltNames(cert); + vector ipAddresses; + vector dnsNames; + for (vector>::const_iterator p = subjectAltNames.begin(); p != subjectAltNames.end(); ++p) + { + if (p->first == AltNAmeIP) + { + ipAddresses.push_back(IceUtilInternal::toLower(p->second)); + } + else if (p->first == AltNameDNS) + { + dnsNames.push_back(IceUtilInternal::toLower(p->second)); + } + } + + bool certNameOK = false; + string addrLower = IceUtilInternal::toLower(address); + bool isIpAddress = IceInternal::isIpAddress(address); + + // If address is an IP address, compare it to the subject alternative names IP address + if (isIpAddress) + { + certNameOK = find(ipAddresses.begin(), ipAddresses.end(), addrLower) != ipAddresses.end(); + } + else + { + // If subjectAlt is empty compare it to the subject CN, otherwise compare it to the to the subject alt + // name dnsNames. + if (dnsNames.empty()) + { + DistinguishedName d = getSubjectName(cert); + string dn = IceUtilInternal::toLower(string(d)); + string cn = "cn=" + addrLower; + string::size_type pos = dn.find(cn); + if (pos != string::npos) + { + // Ensure we match the entire common name. + certNameOK = (pos + cn.size() == dn.size()) || (dn[pos + cn.size()] == ','); + } + } + else + { + certNameOK = find(dnsNames.begin(), dnsNames.end(), addrLower) != dnsNames.end(); + } + } + + if (!certNameOK) + { + ostringstream os; + os << "SSL transport: certificate verification failure " + << (isIpAddress ? "IP address mismatch" : "Hostname mismatch"); + string msg = os.str(); + if (traceLevel >= 1) + { + Trace out(logger, traceCategory); + out << msg; + } + throw SecurityException(__FILE__, __LINE__, msg); + } + } } Schannel::SSLEngine::SSLEngine(const IceInternal::InstancePtr& instance) @@ -1044,13 +1116,6 @@ namespace ~ScopedCertChainContext() { CertFreeCertificateChain(_chain); } PCCERT_CHAIN_CONTEXT _chain; }; - - struct ScopedCertContext - { - ScopedCertContext(PCCERT_CONTEXT cert) : _cert(cert) {} - ~ScopedCertContext() { CertFreeCertificateContext(_cert); } - PCCERT_CONTEXT _cert; - }; } bool @@ -1073,7 +1138,7 @@ Schannel::SSLEngine::validationCallback( if (cert) // Verify the remote certificate { - ScopedCertContext scopedCertContext(cert); + ScopedCertificate scopedCertificate(cert); CERT_CHAIN_PARA chainP; memset(&chainP, 0, sizeof(chainP)); chainP.cbSize = sizeof(chainP); @@ -1140,9 +1205,9 @@ Schannel::SSLEngine::validationCallback( throw SecurityException(__FILE__, __LINE__, os.str()); } - if (!incoming) + if (!incoming && getCheckCertName() && info->peerCertificate && !host.empty()) { - verifyPeerCertName(info, host); + verifyPeerCertName(info->peerCertificate, host, getLogger(), securityTraceLevel(), securityTraceCategory()); } verifyPeer(info); } diff --git a/cpp/src/Ice/SSL/SchannelTransceiverI.cpp b/cpp/src/Ice/SSL/SchannelTransceiverI.cpp index d3dc4f995a1..667dab8f402 100644 --- a/cpp/src/Ice/SSL/SchannelTransceiverI.cpp +++ b/cpp/src/Ice/SSL/SchannelTransceiverI.cpp @@ -8,7 +8,6 @@ #include "Ice/LocalException.h" #include "Ice/LoggerUtil.h" #include "Ice/SSL/ConnectionInfo.h" -#include "Ice/SSL/Schannel.h" #include "IceUtil/StringUtil.h" #include "SSLInstance.h" #include "SSLUtil.h" @@ -389,8 +388,8 @@ Schannel::TransceiverI::sslHandshake() throw SecurityException(__FILE__, __LINE__, os.str()); } - PCCERT_CONTEXT cert = nullptr; - err = QueryContextAttributes(&_ssl, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &cert); + assert(!_peerCertificate); + err = QueryContextAttributes(&_ssl, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &_peerCertificate); if (err != SEC_E_OK && err != SEC_E_NO_CREDENTIALS) { ostringstream os; @@ -398,29 +397,6 @@ Schannel::TransceiverI::sslHandshake() throw SecurityException(__FILE__, __LINE__, os.str()); } - if (cert) - { - PCERT_SIGNED_CONTENT_INFO pvStructInfo = nullptr; - DWORD pvStructInfoSize = 0; - if (!CryptDecodeObjectEx( - X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - X509_CERT, - cert->pbCertEncoded, - cert->cbCertEncoded, - CRYPT_DECODE_ALLOC_FLAG, - 0, - &pvStructInfo, - &pvStructInfoSize)) - { - ostringstream os; - os << "SSL transport: error decoding peer certificate:\n" << IceUtilInternal::lastErrorToString(); - CertFreeCertificateContext(cert); - throw SecurityException(__FILE__, __LINE__, os.str()); - } - _peerCertificates.push_back(Schannel::Certificate::create(pvStructInfo)); - CertFreeCertificateContext(cert); - } - size_t pos = _readBuffer.i - _readBuffer.b.begin(); if (pos <= (_sizes.cbHeader + _sizes.cbMaximumMessage + _sizes.cbTrailer)) { @@ -639,6 +615,12 @@ Schannel::TransceiverI::close() _certificate = nullptr; } + if (_peerCertificate) + { + CertFreeCertificateContext(_peerCertificate); + _peerCertificate = nullptr; + } + _delegate->close(); // Clear the buffers now instead of waiting for destruction. @@ -833,7 +815,7 @@ Schannel::TransceiverI::getInfo() const info->underlying = _delegate->getInfo(); info->incoming = _incoming; info->adapterName = _adapterName; - info->certs = _peerCertificates; + info->peerCertificate = CertDuplicateCertificateContext(_peerCertificate); return info; } @@ -864,6 +846,7 @@ Schannel::TransceiverI::TransceiverI( _clientCertificateRequired(serverAuthenticationOptions.clientCertificateRequired), _localCertificateSelectionCallback(serverAuthenticationOptions.serverCertificateSelectionCallback), _sslNewSessionCallback(serverAuthenticationOptions.sslNewSessionCallback), + _peerCertificate(nullptr), _remoteCertificateValidationCallback(serverAuthenticationOptions.clientCertificateValidationCallback), _certificate(nullptr), _rootStore(nullptr), @@ -888,6 +871,7 @@ Schannel::TransceiverI::TransceiverI( _clientCertificateRequired(false), _localCertificateSelectionCallback(clientAuthenticationOptions.clientCertificateSelectionCallback), _sslNewSessionCallback(clientAuthenticationOptions.sslNewSessionCallback), + _peerCertificate(nullptr), _remoteCertificateValidationCallback(clientAuthenticationOptions.serverCertificateValidationCallback), _certificate(nullptr), _rootStore(nullptr), diff --git a/cpp/src/Ice/SSL/SchannelTransceiverI.h b/cpp/src/Ice/SSL/SchannelTransceiverI.h index fe6675a54e3..528f0784fdd 100644 --- a/cpp/src/Ice/SSL/SchannelTransceiverI.h +++ b/cpp/src/Ice/SSL/SchannelTransceiverI.h @@ -115,7 +115,7 @@ namespace Ice::SSL::Schannel std::function _sslNewSessionCallback; SecPkgContext_StreamSizes _sizes; std::string _cipher; - std::vector _peerCertificates; + PCCERT_CONTEXT _peerCertificate; std::function _remoteCertificateValidationCallback; bool _clientCertificateRequired; PCCERT_CONTEXT _certificate; diff --git a/cpp/src/Ice/SSL/SecureTransportCertificateI.cpp b/cpp/src/Ice/SSL/SecureTransportCertificateI.cpp deleted file mode 100644 index d1195b51b15..00000000000 --- a/cpp/src/Ice/SSL/SecureTransportCertificateI.cpp +++ /dev/null @@ -1,963 +0,0 @@ -// -// Copyright (c) ZeroC, Inc. All rights reserved. -// - -// -// Disable deprecation warnings for SecCertificateCopyNormalizedIssuerContent and -// SecCertificateCopyNormalizedSubjectContent -// -#include "IceUtil/DisableWarnings.h" - -#include "../Base64.h" -#include "CertificateI.h" -#include "Ice/LocalException.h" -#include "Ice/SSL/SecureTransport.h" -#include "Ice/UniqueRef.h" -#include "RFC2253.h" -#include "SecureTransportUtil.h" - -#include - -#include -#include -#include - -using namespace Ice; -using namespace IceInternal; -using namespace Ice::SSL; -using namespace Ice::SSL::SecureTransport; - -using namespace std; - -namespace -{ - static unsigned char _ekuAnyKeyUsage[4] = {0x55, 0x1d, 0x25, 0x00}; - static unsigned char _ekuServerAuthentication[8] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01}; - static unsigned char _ekuClientAuthentication[8] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02}; - static unsigned char _ekuCodeSigning[8] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03}; - static unsigned char _ekuEmailProtection[8] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04}; - static unsigned char _ekuTimeStamping[8] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x08}; - static unsigned char _ekuOCSPSigning[8] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09}; - - static CFDataRef ekuAnyKeyUsage = - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _ekuAnyKeyUsage, 4, kCFAllocatorNull); - static CFDataRef ekuServerAuthentication = - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _ekuServerAuthentication, 8, kCFAllocatorNull); - static CFDataRef ekuClientAuthentication = - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _ekuClientAuthentication, 8, kCFAllocatorNull); - static CFDataRef ekuCodeSigning = - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _ekuCodeSigning, 8, kCFAllocatorNull); - static CFDataRef ekuEmailProtection = - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _ekuEmailProtection, 8, kCFAllocatorNull); - static CFDataRef ekuTimeStamping = - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _ekuTimeStamping, 8, kCFAllocatorNull); - static CFDataRef ekuOCSPSigning = - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _ekuOCSPSigning, 8, kCFAllocatorNull); - - string certificateOIDAlias(const string& name) - { - for (int i = 0; i < certificateOIDSSize; ++i) - { - const CertificateOID* certificateOID = &certificateOIDS[i]; - assert(certificateOID); - if (name == certificateOID->name) - { - return certificateOID->alias; - } - } - return name; - } - - string escapeX509Name(const string& name) - { - ostringstream os; - for (string::const_iterator i = name.begin(); i != name.end(); ++i) - { - switch (*i) - { - case ',': - case '=': - case '+': - case '<': - case '>': - case '#': - case ';': - { - os << '\\'; - } - default: - { - break; - } - } - os << *i; - } - return os.str(); - } - -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - // - // ASN1Parser to pase the subject/issuer ASN.1 DER encoded attributes on iOS. - // - class ASN1Parser - { - public: - ASN1Parser(CFDataRef data) - : _data(CFDataGetBytePtr(data)), - _length(static_cast(CFDataGetLength(data))), - _p(_data), - _next(0) - { - } - - list> parse() - { - list> rdns; - while (_p < _data + _length) - { - switch (parseByte()) - { - case 0x06: // OID - { - _rdn.first = parseOID(); - break; - } - case 0x12: // NumericString - case 0x13: // PrintableString - case 0x0C: // UTF8String - case 0x16: // IA5String - { - _rdn.second = escapeX509Name(parseUTF8String()); - break; - } - case 0x30: // SEQUENCE - case 0x31: // SET - { - int length = parseLength(0); - _next = _p + length; - if (_next > _data + _length) - { - throw CertificateEncodingException(__FILE__, __LINE__, "invalid length"); - } - break; - } - default: - { - // Unsupported tag, skip the SET. - if (!_next) - { - return rdns; - } - _p = _next; - _next = 0; - break; - } - } - if (_p == _next) - { - rdns.push_back(_rdn); - } - } - return rdns; - } - - string parseOID() - { - int length = parseLength(1); - ostringstream oid; - unsigned char c = parseByte(); - oid << c / 40 << "." << c % 40; - while (--length > 0) - { - if ((*_p & 0x80) == 0) - { - oid << "." << static_cast(parseByte()); - } - else - { - uint64_t result = (uint64_t)(*_p & 127); - while (parseByte() & 128) - { - result = (result << 7) | (uint64_t)(*_p & 127); - --length; - } - oid << "." << result; - } - } - return certificateOIDAlias(oid.str()); - } - - string parseUTF8String() - { - int length = parseLength(0); - string v(reinterpret_cast(_p), static_cast(length)); - _p += length; - return v; - } - - int parseLength(int required) - { - int length = 0; - if ((*_p & 0x80) == 0) - { - length = static_cast(parseByte()); - } - else - { - int nbytes = static_cast(parseByte()); - for (int i = 0; i < nbytes; ++i) - { - length = length * 256 + parseByte(); - } - } - if ((required > 0 && length < required) || (_p + length > _data + _length)) - { - throw CertificateEncodingException(__FILE__, __LINE__, "invalid length"); - } - return length; - } - - unsigned char parseByte() - { - if (_p >= _data + _length) - { - throw CertificateEncodingException(__FILE__, __LINE__, "invalid length"); - } - unsigned char b = *_p++; - return b; - } - - private: - const unsigned char* _data; - const size_t _length; - const unsigned char* _p; - const unsigned char* _next; - pair _rdn; - list> _rdns; - }; - -#endif - - class SecureTransportCertificateI final : public Ice::SSL::SecureTransport::Certificate, - public Ice::SSL::CertificateI - { - public: - SecureTransportCertificateI(SecCertificateRef); - - virtual bool operator==(const Ice::SSL::Certificate&) const; - - virtual vector getAuthorityKeyIdentifier() const; - virtual vector getSubjectKeyIdentifier() const; - virtual bool verify(const Ice::SSL::CertificatePtr&) const; - virtual string encode() const; - - virtual chrono::system_clock::time_point getNotAfter() const; - virtual chrono::system_clock::time_point getNotBefore() const; - - virtual string getSerialNumber() const; - virtual DistinguishedName getIssuerDN() const; - virtual vector> getIssuerAlternativeNames() const; - virtual DistinguishedName getSubjectDN() const; - virtual vector> getSubjectAlternativeNames() const; - virtual int getVersion() const; - virtual SecCertificateRef getCert() const; - virtual unsigned int getKeyUsage() const; - virtual unsigned int getExtendedKeyUsage() const; - - private: - IceInternal::UniqueRef _cert; - -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - void initializeAttributes() const; - - mutable IceInternal::UniqueRef _subject; - mutable IceInternal::UniqueRef _issuer; - mutable std::string _serial; - mutable int _version; -#endif - }; - -#ifndef ICE_USE_SECURE_TRANSPORT_IOS - - // - // Map alternative name alias to its types. - // - const char* certificateAlternativeNameTypes[] = - {"", "Email Address", "DNS Name", "", "Directory Name", "", "URI", "IP Address"}; - const int certificateAlternativeNameTypesSize = sizeof(certificateAlternativeNameTypes) / sizeof(char*); - - int certificateAlternativeNameType(const string& alias) - { - if (!alias.empty()) - { - for (int i = 0; i < certificateAlternativeNameTypesSize; ++i) - { - if (alias == certificateAlternativeNameTypes[i]) - { - return i; - } - } - } - return -1; // Not supported - } - - DistinguishedName getX509Name(SecCertificateRef cert, CFTypeRef key) - { - assert(key == kSecOIDX509V1IssuerName || key == kSecOIDX509V1SubjectName); - list> rdnPairs; - UniqueRef property(getCertificateProperty(cert, key)); - if (property) - { - CFArrayRef dn = static_cast(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); - CFIndex size = CFArrayGetCount(dn); - for (CFIndex i = 0; i < size; ++i) - { - CFDictionaryRef dict = static_cast(CFArrayGetValueAtIndex(dn, i)); - rdnPairs.push_front(make_pair( - certificateOIDAlias( - fromCFString((static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyLabel))))), - escapeX509Name( - fromCFString(static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyValue)))))); - } - } - return DistinguishedName(rdnPairs); - } - - vector> getX509AltName(SecCertificateRef cert, CFTypeRef key) - { - assert(key == kSecOIDIssuerAltName || key == kSecOIDSubjectAltName); - UniqueRef property(getCertificateProperty(cert, key)); - - vector> pairs; - if (property) - { - CFArrayRef names = static_cast(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); - CFIndex size = CFArrayGetCount(names); - - for (CFIndex i = 0; i < size; ++i) - { - CFDictionaryRef dict = static_cast(CFArrayGetValueAtIndex(names, i)); - - int type = certificateAlternativeNameType( - fromCFString(static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyLabel)))); - if (type != -1) - { - CFStringRef v = static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyValue)); - CFStringRef t = static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyType)); - if (CFEqual(t, kSecPropertyTypeString) || CFEqual(t, kSecPropertyTypeTitle)) - { - pairs.push_back(make_pair(type, fromCFString(v))); - } - else if (CFEqual(t, kSecPropertyTypeURL)) - { - pairs.push_back(make_pair(type, fromCFString(CFURLGetString((CFURLRef)v)))); - } - else if (CFEqual(t, kSecPropertyTypeSection)) - { - CFArrayRef section = (CFArrayRef)v; - ostringstream os; - for (CFIndex j = 0, count = CFArrayGetCount(section); j < count;) - { - CFDictionaryRef d = (CFDictionaryRef)CFArrayGetValueAtIndex(section, j); - - CFStringRef sectionLabel = - static_cast(CFDictionaryGetValue(d, kSecPropertyKeyLabel)); - CFStringRef sectionValue = - static_cast(CFDictionaryGetValue(d, kSecPropertyKeyValue)); - - os << certificateOIDAlias(fromCFString(sectionLabel)) << "=" << fromCFString(sectionValue); - if (++j < count) - { - os << ","; - } - } - pairs.push_back(make_pair(type, os.str())); - } - } - } - } - return pairs; - } - - chrono::system_clock::time_point getX509Date(SecCertificateRef cert, CFTypeRef key) - { - assert(key == kSecOIDX509V1ValidityNotAfter || key == kSecOIDX509V1ValidityNotBefore); - UniqueRef property(getCertificateProperty(cert, key)); - CFAbsoluteTime seconds = 0; - if (property) - { - CFNumberRef date = static_cast(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); - CFNumberGetValue(date, kCFNumberDoubleType, &seconds); - } - - return chrono::system_clock::time_point( - chrono::seconds(static_cast(kCFAbsoluteTimeIntervalSince1970 + seconds))); - } - - string getX509String(SecCertificateRef cert, CFTypeRef key) - { - assert(key == kSecOIDX509V1SerialNumber || key == kSecOIDX509V1Version); - UniqueRef property(getCertificateProperty(cert, key)); - return property - ? fromCFString(static_cast(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue))) - : ""; - } -#endif - -} // end anonymous namespace - -SecureTransportCertificateI::SecureTransportCertificateI(SecCertificateRef cert) : _cert(cert) -{ - if (!_cert) - { - throw invalid_argument("Invalid certificate reference"); - } -} - -bool -SecureTransportCertificateI::operator==(const Ice::SSL::Certificate& r) const -{ - const SecureTransportCertificateI* p = dynamic_cast(&r); - if (!p) - { - return false; - } - return CFEqual(_cert.get(), p->_cert.get()); -} - -vector -SecureTransportCertificateI::getAuthorityKeyIdentifier() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); -#else // macOS - vector keyid; - - UniqueRef property(getCertificateProperty(_cert.get(), kSecOIDAuthorityKeyIdentifier)); - if (property) - { - CFTypeRef type = 0; - CFTypeRef value = 0; - if (CFDictionaryGetValueIfPresent(property.get(), kSecPropertyKeyType, &type)) - { - if (CFEqual(type, kSecPropertyTypeSection)) - { - if (CFDictionaryGetValueIfPresent(property.get(), kSecPropertyKeyValue, &value)) - { - if (CFArrayGetCount(static_cast(value)) >= 0) - { - value = CFArrayGetValueAtIndex(static_cast(value), 1); - type = CFDictionaryGetValue(static_cast(value), kSecPropertyKeyType); - } - } - } - - if (CFEqual(type, kSecPropertyTypeData)) - { - CFDataRef data = static_cast( - CFDictionaryGetValue(static_cast(value), kSecPropertyKeyValue)); - keyid.resize(static_cast(CFDataGetLength(data))); - memcpy(&keyid[0], CFDataGetBytePtr(data), static_cast(CFDataGetLength(data))); - } - } - } - return keyid; -#endif -} - -vector -SecureTransportCertificateI::getSubjectKeyIdentifier() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); -#else // macOS - vector keyid; - UniqueRef property(getCertificateProperty(_cert.get(), kSecOIDSubjectKeyIdentifier)); - if (property) - { - CFTypeRef type = 0; - CFTypeRef value = 0; - if (CFDictionaryGetValueIfPresent(property.get(), kSecPropertyKeyType, &type)) - { - if (CFEqual(type, kSecPropertyTypeSection)) - { - if (CFDictionaryGetValueIfPresent(property.get(), kSecPropertyKeyValue, &value)) - { - if (CFArrayGetCount(static_cast(value)) >= 0) - { - value = CFArrayGetValueAtIndex(static_cast(value), 1); - type = CFDictionaryGetValue(static_cast(value), kSecPropertyKeyType); - } - } - } - - if (CFEqual(type, kSecPropertyTypeData)) - { - CFDataRef data = static_cast( - CFDictionaryGetValue(static_cast(value), kSecPropertyKeyValue)); - keyid.resize(static_cast(CFDataGetLength(data))); - memcpy(&keyid[0], CFDataGetBytePtr(data), static_cast(CFDataGetLength(data))); - } - } - } - return keyid; -#endif -} - -bool -SecureTransportCertificateI::verify(const Ice::SSL::CertificatePtr& cert) const -{ - bool valid = false; - SecureTransportCertificateI* c = dynamic_cast(cert.get()); - if (c) - { - // - // We first check if the given certificate subject match our certificate - // issuer. Otherwhise when checking a certificate against itself - // SecTrustEvaluate always returns it is valid. - // -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - initializeAttributes(); - c->initializeAttributes(); - valid = CFEqual(_issuer.get(), c->_subject.get()); -#else // macOS - UniqueRef error; - UniqueRef issuer(SecCertificateCopyNormalizedIssuerContent(_cert.get(), &error.get())); - if (error) - { - throw CertificateEncodingException( - __FILE__, - __LINE__, - "certificate error:\n" + sslErrorToString(error.get())); - } - UniqueRef subject(SecCertificateCopyNormalizedSubjectContent(c->getCert(), &error.get())); - if (error) - { - throw CertificateEncodingException( - __FILE__, - __LINE__, - "certificate error:\n" + sslErrorToString(error.get())); - } - - // - // The certificate issuer must match the CA subject. - // - valid = CFEqual(issuer.get(), subject.get()); -#endif - if (valid) - { - UniqueRef policy(SecPolicyCreateBasicX509()); - UniqueRef trust; - OSStatus err = 0; - if ((err = SecTrustCreateWithCertificates(_cert.get(), policy.get(), &trust.get()))) - { - throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); - } - - SecCertificateRef certs[1] = {c->getCert()}; - UniqueRef anchorCertificates( - CFArrayCreate(kCFAllocatorDefault, (const void**)&certs, 1, &kCFTypeArrayCallBacks)); - if ((err = SecTrustSetAnchorCertificates(trust.get(), anchorCertificates.get()))) - { - throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); - } - - SecTrustResultType trustResult = kSecTrustResultInvalid; - if ((err = SecTrustEvaluate(trust.get(), &trustResult))) - { - throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); - } - - valid = trustResult == kSecTrustResultUnspecified; - } - } - return valid; -} - -string -SecureTransportCertificateI::encode() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - UniqueRef cert(SecCertificateCopyData(_cert.get())); - auto start = CFDataGetBytePtr(cert.get()); - auto end = start + CFDataGetLength(cert.get()); - vector data(reinterpret_cast(start), reinterpret_cast(end)); - ostringstream os; - os << "-----BEGIN CERTIFICATE-----\n"; - os << IceInternal::Base64::encode(data); - os << "\n-----END CERTIFICATE-----\n"; - return os.str(); -#else // macOS - UniqueRef exported; - OSStatus err = SecItemExport(_cert.get(), kSecFormatPEMSequence, kSecItemPemArmour, 0, &exported.get()); - if (err != noErr) - { - throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); - } - return string( - reinterpret_cast(CFDataGetBytePtr(exported.get())), - static_cast(CFDataGetLength(exported.get()))); -#endif -} - -chrono::system_clock::time_point -SecureTransportCertificateI::getNotAfter() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); -#else // macOS - return getX509Date(_cert.get(), kSecOIDX509V1ValidityNotAfter); -#endif -} - -chrono::system_clock::time_point -SecureTransportCertificateI::getNotBefore() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); -#else // macOS - return getX509Date(_cert.get(), kSecOIDX509V1ValidityNotBefore); -#endif -} - -string -SecureTransportCertificateI::getSerialNumber() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - initializeAttributes(); - return _serial; -#else // macOS - return getX509String(_cert.get(), kSecOIDX509V1SerialNumber); -#endif -} - -DistinguishedName -SecureTransportCertificateI::getIssuerDN() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - initializeAttributes(); - return _issuer ? DistinguishedName(ASN1Parser(_issuer.get()).parse()) : DistinguishedName(""); -#else // macOS - return getX509Name(_cert.get(), kSecOIDX509V1IssuerName); -#endif -} - -vector> -SecureTransportCertificateI::getIssuerAlternativeNames() const -{ -#if defined(ICE_USE_SECURE_TRANSPORT_IOS) - throw FeatureNotSupportedException(__FILE__, __LINE__); -#else // macOS - return getX509AltName(_cert.get(), kSecOIDIssuerAltName); -#endif -} - -DistinguishedName -SecureTransportCertificateI::getSubjectDN() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - initializeAttributes(); - if (_subject) - { - return DistinguishedName(ASN1Parser(_subject.get()).parse()); - } - else - { - UniqueRef subjectSummary(SecCertificateCopySubjectSummary(_cert.get())); - return DistinguishedName("CN=" + fromCFString(subjectSummary.get())); - } -#else // macOS - return getX509Name(_cert.get(), kSecOIDX509V1SubjectName); -#endif -} - -vector> -SecureTransportCertificateI::getSubjectAlternativeNames() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - throw FeatureNotSupportedException(__FILE__, __LINE__); -#else // macOS - return getX509AltName(_cert.get(), kSecOIDSubjectAltName); -#endif -} - -int -SecureTransportCertificateI::getVersion() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - initializeAttributes(); - return _version; -#else // macOS - return atoi(getX509String(_cert.get(), kSecOIDX509V1Version).c_str()) - 1; -#endif -} - -SecCertificateRef -SecureTransportCertificateI::getCert() const -{ - return _cert.get(); -} - -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - -mutex globalMutex; - -void -SecureTransportCertificateI::initializeAttributes() const -{ - // - // We need to temporarily add the certificate to the keychain in order to - // retrieve its attributes. Unfortunately kSecMatchItemList doesn't work - // on iOS. We make sure only one thread adds/removes a cert at a time here. - // - lock_guard lock(globalMutex); - - if (_subject) - { - return; - } - - UniqueRef query( - CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue(query.get(), kSecValueRef, _cert.get()); - CFDictionarySetValue(query.get(), kSecReturnAttributes, kCFBooleanTrue); - - UniqueRef attributes(0); - OSStatus err; - if ((err = SecItemAdd(query.get(), reinterpret_cast(&attributes.get()))) == errSecDuplicateItem) - { - CFDictionarySetValue(query.get(), kSecClass, kSecClassCertificate); - err = SecItemCopyMatching(query.get(), reinterpret_cast(&attributes.get())); - } - else - { - query.reset(CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue(query.get(), kSecClass, kSecClassCertificate); - CFDictionarySetValue(query.get(), kSecValueRef, _cert.get()); - err = SecItemDelete(query.get()); - } - - if (err != noErr) - { - _subject.reset(0); - _issuer.reset(0); - throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); - } - - _subject.retain(static_cast(CFDictionaryGetValue(attributes.get(), kSecAttrSubject))); - _issuer.retain(static_cast(CFDictionaryGetValue(attributes.get(), kSecAttrIssuer))); - CFDataRef serial = static_cast(CFDictionaryGetValue(attributes.get(), kSecAttrSerialNumber)); - ostringstream os; - for (int i = 0; i < CFDataGetLength(serial); ++i) - { - int c = static_cast(CFDataGetBytePtr(serial)[i]); - if (i) - { - os << ' '; - } - os.fill('0'); - os.width(2); - os << hex << c; - } - _serial = os.str(); - CFNumberRef version = static_cast(CFDictionaryGetValue(attributes.get(), kSecAttrCertificateType)); - if (!CFNumberGetValue(version, kCFNumberIntType, &_version)) - { - _version = -1; - } -} -#endif - -unsigned int -SecureTransportCertificateI::getKeyUsage() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); -#else - unsigned int keyUsage = 0; - UniqueRef property(getCertificateProperty(_cert.get(), kSecOIDKeyUsage)); - if (property) - { - CFNumberRef value = static_cast(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); - if (value) - { - unsigned int usageBits = 0; - CFNumberGetValue(value, kCFNumberSInt32Type, &usageBits); - if (usageBits & kSecKeyUsageDigitalSignature) - { - keyUsage |= KEY_USAGE_DIGITAL_SIGNATURE; - } - if (usageBits & kSecKeyUsageNonRepudiation) - { - keyUsage |= KEY_USAGE_NON_REPUDIATION; - } - if (usageBits & kSecKeyUsageKeyEncipherment) - { - keyUsage |= KEY_USAGE_KEY_ENCIPHERMENT; - } - if (usageBits & kSecKeyUsageDataEncipherment) - { - keyUsage |= KEY_USAGE_DATA_ENCIPHERMENT; - } - if (usageBits & kSecKeyUsageKeyAgreement) - { - keyUsage |= KEY_USAGE_KEY_AGREEMENT; - } - if (usageBits & kSecKeyUsageKeyCertSign) - { - keyUsage |= KEY_USAGE_KEY_CERT_SIGN; - } - if (usageBits & kSecKeyUsageCRLSign) - { - keyUsage |= KEY_USAGE_CRL_SIGN; - } - if (usageBits & kSecKeyUsageEncipherOnly) - { - keyUsage |= KEY_USAGE_ENCIPHER_ONLY; - } - if (usageBits & kSecKeyUsageDecipherOnly) - { - keyUsage |= KEY_USAGE_DECIPHER_ONLY; - } - } - } - return keyUsage; -#endif -} - -unsigned int -SecureTransportCertificateI::getExtendedKeyUsage() const -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); -#else - unsigned int extendedKeyUsage = 0; - UniqueRef property(getCertificateProperty(_cert.get(), kSecOIDExtendedKeyUsage)); - if (property) - { - CFArrayRef usages = static_cast(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); - if (usages) - { - long size = CFArrayGetCount(usages); - if (CFArrayContainsValue(usages, CFRangeMake(0, size), ekuAnyKeyUsage)) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_ANY_KEY_USAGE; - } - if (CFArrayContainsValue(usages, CFRangeMake(0, size), ekuServerAuthentication)) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_SERVER_AUTH; - } - if (CFArrayContainsValue(usages, CFRangeMake(0, size), ekuClientAuthentication)) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_CLIENT_AUTH; - } - if (CFArrayContainsValue(usages, CFRangeMake(0, size), ekuCodeSigning)) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_CODE_SIGNING; - } - if (CFArrayContainsValue(usages, CFRangeMake(0, size), ekuEmailProtection)) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_EMAIL_PROTECTION; - } - if (CFArrayContainsValue(usages, CFRangeMake(0, size), ekuTimeStamping)) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_TIME_STAMPING; - } - if (CFArrayContainsValue(usages, CFRangeMake(0, size), ekuOCSPSigning)) - { - extendedKeyUsage |= EXTENDED_KEY_USAGE_OCSP_SIGNING; - } - } - } - return extendedKeyUsage; -#endif -} - -Ice::SSL::SecureTransport::CertificatePtr -Ice::SSL::SecureTransport::Certificate::create(SecCertificateRef cert) -{ - return make_shared(cert); -} - -Ice::SSL::SecureTransport::CertificatePtr -Ice::SSL::SecureTransport::Certificate::load(const std::string& file) -{ - string resolved; - if (checkPath(file, "", false, resolved)) - { - return make_shared(loadCertificate(resolved)); - } - else - { - throw CertificateReadException(__FILE__, __LINE__, "error opening file " + file); - } -} - -Ice::SSL::SecureTransport::CertificatePtr -Ice::SSL::SecureTransport::Certificate::decode(const std::string& encoding) -{ -#ifdef ICE_USE_SECURE_TRANSPORT_IOS - string::size_type size = 0; - string::size_type startpos = 0; - startpos = encoding.find("-----BEGIN CERTIFICATE-----", 0); - if (startpos != string::npos) - { - startpos += sizeof("-----BEGIN CERTIFICATE-----"); - string::size_type endpos = encoding.find("-----END CERTIFICATE-----", startpos); - size = endpos - startpos; - } - else - { - startpos = 0; - size = encoding.size(); - } - - vector data(IceInternal::Base64::decode(string(&encoding[startpos], size))); - UniqueRef certdata( - CFDataCreate(kCFAllocatorDefault, reinterpret_cast(&data[0]), static_cast(data.size()))); - SecCertificateRef cert = SecCertificateCreateWithData(0, certdata.get()); - if (!cert) - { - assert(false); - throw CertificateEncodingException(__FILE__, __LINE__, "certificate is not a valid PEM-encoded certificate"); - } - return make_shared(cert); -#else // macOS - UniqueRef data(CFDataCreateWithBytesNoCopy( - kCFAllocatorDefault, - reinterpret_cast(encoding.c_str()), - static_cast(encoding.size()), - kCFAllocatorNull)); - - SecExternalFormat format = kSecFormatUnknown; - SecExternalItemType type = kSecItemTypeCertificate; - - SecItemImportExportKeyParameters params; - memset(¶ms, 0, sizeof(params)); - params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; - - UniqueRef items; - OSStatus err = SecItemImport(data.get(), 0, &format, &type, 0, ¶ms, 0, &items.get()); - if (err) - { - throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); - } - - UniqueRef item; - item.retain(static_cast(const_cast(CFArrayGetValueAtIndex(items.get(), 0)))); - assert(SecCertificateGetTypeID() == CFGetTypeID(item.get())); - return make_shared(reinterpret_cast(item.release())); -#endif -} - -Ice::SSL::CertificatePtr -Ice::SSL::Certificate::load(const std::string& file) -{ - return Ice::SSL::SecureTransport::Certificate::load(file); -} - -Ice::SSL::CertificatePtr -Ice::SSL::Certificate::decode(const std::string& encoding) -{ - return Ice::SSL::SecureTransport::Certificate::decode(encoding); -} diff --git a/cpp/src/Ice/SSL/SecureTransportTransceiverI.cpp b/cpp/src/Ice/SSL/SecureTransportTransceiverI.cpp index ca10ce1fedb..fc254ab7b0d 100644 --- a/cpp/src/Ice/SSL/SecureTransportTransceiverI.cpp +++ b/cpp/src/Ice/SSL/SecureTransportTransceiverI.cpp @@ -202,9 +202,9 @@ Ice::SSL::SecureTransport::TransceiverI::initialize(IceInternal::Buffer& readBuf { for (CFIndex i = 0, count = SecTrustGetCertificateCount(_trust.get()); i < count; ++i) { - SecCertificateRef cert = SecTrustGetCertificateAtIndex(_trust.get(), i); - CFRetain(cert); - _peerCerts.push_back(Ice::SSL::SecureTransport::Certificate::create(cert)); + SecCertificateRef peerCertificate = SecTrustGetCertificateAtIndex(_trust.get(), i); + CFRetain(peerCertificate); + _peerCertificate.reset(peerCertificate); } if (_trustedRootCertificates) @@ -512,7 +512,17 @@ Ice::SSL::SecureTransport::TransceiverI::getInfo() const info->underlying = _delegate->getInfo(); info->incoming = _incoming; info->adapterName = _adapterName; - info->certs = _peerCerts; + if (_peerCertificate) + { + SecCertificateRef peerCertificate = _peerCertificate.get(); + CFRetain(peerCertificate); + info->peerCertificate = peerCertificate; + } + else + { + info->peerCertificate = nullptr; + } + return info; } diff --git a/cpp/src/Ice/SSL/SecureTransportTransceiverI.h b/cpp/src/Ice/SSL/SecureTransportTransceiverI.h index 335e12d9ba7..a52c760b2e0 100644 --- a/cpp/src/Ice/SSL/SecureTransportTransceiverI.h +++ b/cpp/src/Ice/SSL/SecureTransportTransceiverI.h @@ -77,7 +77,7 @@ namespace Ice::SSL::SecureTransport mutable std::uint8_t _tflags; size_t _maxSendPacketSize; size_t _maxRecvPacketSize; - std::vector _peerCerts; + IceInternal::UniqueRef _peerCertificate; size_t _buffered; std::function _sslNewSessionCallback; std::function diff --git a/cpp/src/Ice/SSL/SecureTransportUtil.cpp b/cpp/src/Ice/SSL/SecureTransportUtil.cpp index 51b0ed22521..13097c9b2d5 100644 --- a/cpp/src/Ice/SSL/SecureTransportUtil.cpp +++ b/cpp/src/Ice/SSL/SecureTransportUtil.cpp @@ -5,6 +5,7 @@ #include "SecureTransportUtil.h" #include "../Base64.h" #include "Ice/LocalException.h" +#include "Ice/SSL/Certificate.h" #include "Ice/UniqueRef.h" #include "IceUtil/FileUtil.h" #include "IceUtil/StringUtil.h" @@ -48,6 +49,151 @@ namespace } return data.release(); } + +#if defined(ICE_USE_SECURE_TRANSPORT_MACOS) + // Map alternative name alias to its types. + const char* certificateAlternativeNameTypes[] = + {"", "Email Address", "DNS Name", "", "Directory Name", "", "URI", "IP Address"}; + const int certificateAlternativeNameTypesSize = sizeof(certificateAlternativeNameTypes) / sizeof(char*); + + int certificateAlternativeNameType(const string& alias) + { + if (!alias.empty()) + { + for (int i = 0; i < certificateAlternativeNameTypesSize; ++i) + { + if (alias == certificateAlternativeNameTypes[i]) + { + return i; + } + } + } + return -1; // Not supported + } + + string escapeX509Name(const string& name) + { + ostringstream os; + for (string::const_iterator i = name.begin(); i != name.end(); ++i) + { + switch (*i) + { + case ',': + case '=': + case '+': + case '<': + case '>': + case '#': + case ';': + { + os << '\\'; + } + default: + { + break; + } + } + os << *i; + } + return os.str(); + } + + CFDictionaryRef getCertificateProperty(SecCertificateRef cert, CFTypeRef key) + { + UniqueRef property; + UniqueRef keys(CFArrayCreate(nullptr, &key, 1, &kCFTypeArrayCallBacks)); + UniqueRef err; + UniqueRef values(SecCertificateCopyValues(cert, keys.get(), &err.get())); + if (err) + { + ostringstream os; + os << "SSL transport: error getting property for certificate:\n" << sslErrorToString(err); + throw CertificateReadException(__FILE__, __LINE__, os.str()); + } + + assert(values); + property.retain(static_cast(CFDictionaryGetValue(values.get(), key))); + return property.release(); + } + + DistinguishedName getX509Name(SecCertificateRef cert, CFTypeRef key) + { + assert(key == kSecOIDX509V1IssuerName || key == kSecOIDX509V1SubjectName); + list> rdnPairs; + UniqueRef property(getCertificateProperty(cert, key)); + if (property) + { + CFArrayRef dn = static_cast(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); + CFIndex size = CFArrayGetCount(dn); + for (CFIndex i = 0; i < size; ++i) + { + CFDictionaryRef dict = static_cast(CFArrayGetValueAtIndex(dn, i)); + rdnPairs.push_front(make_pair( + certificateOIDAlias( + fromCFString((static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyLabel))))), + escapeX509Name( + fromCFString(static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyValue)))))); + } + } + return DistinguishedName(rdnPairs); + } + + vector> getX509AltName(SecCertificateRef cert, CFTypeRef key) + { + assert(key == kSecOIDIssuerAltName || key == kSecOIDSubjectAltName); + UniqueRef property(getCertificateProperty(cert, key)); + + vector> pairs; + if (property) + { + CFArrayRef names = static_cast(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); + CFIndex size = CFArrayGetCount(names); + + for (CFIndex i = 0; i < size; ++i) + { + CFDictionaryRef dict = static_cast(CFArrayGetValueAtIndex(names, i)); + + int type = certificateAlternativeNameType( + fromCFString(static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyLabel)))); + if (type != -1) + { + CFStringRef v = static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyValue)); + CFStringRef t = static_cast(CFDictionaryGetValue(dict, kSecPropertyKeyType)); + if (CFEqual(t, kSecPropertyTypeString) || CFEqual(t, kSecPropertyTypeTitle)) + { + pairs.push_back(make_pair(type, fromCFString(v))); + } + else if (CFEqual(t, kSecPropertyTypeURL)) + { + pairs.push_back(make_pair(type, fromCFString(CFURLGetString((CFURLRef)v)))); + } + else if (CFEqual(t, kSecPropertyTypeSection)) + { + CFArrayRef section = (CFArrayRef)v; + ostringstream os; + for (CFIndex j = 0, count = CFArrayGetCount(section); j < count;) + { + CFDictionaryRef d = (CFDictionaryRef)CFArrayGetValueAtIndex(section, j); + + CFStringRef sectionLabel = + static_cast(CFDictionaryGetValue(d, kSecPropertyKeyLabel)); + CFStringRef sectionValue = + static_cast(CFDictionaryGetValue(d, kSecPropertyKeyValue)); + + os << certificateOIDAlias(fromCFString(sectionLabel)) << "=" << fromCFString(sectionValue); + if (++j < count) + { + os << ","; + } + } + pairs.push_back(make_pair(type, os.str())); + } + } + } + } + return pairs; + } +#endif } string @@ -840,3 +986,84 @@ Ice::SSL::SecureTransport::findCertificateChain( CFArraySetValueAtIndex(const_cast(items.get()), 0, identity.get()); return items.release(); } + +#ifdef ICE_USE_SECURE_TRANSPORT_IOS +DistinguishedName +Ice::SSL::getSubjectName(SecCertificateRef) +{ + throw FeatureNotSupportedException(__FILE__, __LINE__); +} + +std::vector> +Ice::SSL::getSubjectAltNames(SecCertificateRef) +{ + throw FeatureNotSupportedException(__FILE__, __LINE__); +} + +std::string +Ice::SSL::encodeCertificate(SecCertificateRef) +{ + throw FeatureNotSupportedException(__FILE__, __LINE__); +} + +SecCertificateRef +Ice::SSL::decodeCertificate(const std::string&) +{ + throw FeatureNotSupportedException(__FILE__, __LINE__); +} +#else // macOS +DistinguishedName +Ice::SSL::getSubjectName(SecCertificateRef certificate) +{ + return getX509Name(certificate, kSecOIDX509V1SubjectName); +} + +std::vector> +Ice::SSL::getSubjectAltNames(SecCertificateRef certificate) +{ + return getX509AltName(certificate, kSecOIDSubjectAltName); +} + +std::string +Ice::SSL::encodeCertificate(SecCertificateRef certificate) +{ + UniqueRef exported; + OSStatus err = SecItemExport(certificate, kSecFormatPEMSequence, kSecItemPemArmour, 0, &exported.get()); + if (err != noErr) + { + throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); + } + return string( + reinterpret_cast(CFDataGetBytePtr(exported.get())), + static_cast(CFDataGetLength(exported.get()))); +} + +SecCertificateRef +Ice::SSL::decodeCertificate(const std::string& data) +{ + UniqueRef buffer(CFDataCreateWithBytesNoCopy( + kCFAllocatorDefault, + reinterpret_cast(data.c_str()), + static_cast(data.size()), + kCFAllocatorNull)); + + SecExternalFormat format = kSecFormatUnknown; + SecExternalItemType type = kSecItemTypeCertificate; + + SecItemImportExportKeyParameters params; + memset(¶ms, 0, sizeof(params)); + params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + + UniqueRef items; + OSStatus err = SecItemImport(buffer.get(), 0, &format, &type, 0, ¶ms, 0, &items.get()); + if (err) + { + throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); + } + + UniqueRef item; + item.retain(static_cast(const_cast(CFArrayGetValueAtIndex(items.get(), 0)))); + assert(SecCertificateGetTypeID() == CFGetTypeID(item.get())); + return reinterpret_cast(item.release()); +} +#endif diff --git a/cpp/src/Ice/SSL/SecureTransportUtil.h b/cpp/src/Ice/SSL/SecureTransportUtil.h index 88b99018aa4..d573af9d9d4 100644 --- a/cpp/src/Ice/SSL/SecureTransportUtil.h +++ b/cpp/src/Ice/SSL/SecureTransportUtil.h @@ -7,7 +7,6 @@ #ifdef __APPLE__ -# include "Ice/SSL/SecureTransport.h" # include "SSLUtil.h" namespace Ice::SSL::SecureTransport diff --git a/cpp/src/Ice/SSL/TrustManager.cpp b/cpp/src/Ice/SSL/TrustManager.cpp index a20d9801b16..26fa4d5c874 100644 --- a/cpp/src/Ice/SSL/TrustManager.cpp +++ b/cpp/src/Ice/SSL/TrustManager.cpp @@ -12,6 +12,7 @@ #include "Ice/Properties.h" #include "Ice/SSL/ConnectionInfo.h" #include "RFC2253.h" +#include "SSLUtil.h" using namespace std; using namespace Ice::SSL; @@ -121,9 +122,9 @@ TrustManager::verify(const ConnectionInfoPtr& info) const } // If there is no certificate then we match false. - if (info->certs.size() != 0) + if (info->peerCertificate) { - DistinguishedName subject = info->certs[0]->getSubjectDN(); + DistinguishedName subject = getSubjectName(info->peerCertificate); if (_traceLevel > 0) { Ice::Trace trace(_instance->initializationData().logger, "Security"); diff --git a/cpp/src/Ice/msbuild/ice/ice.vcxproj b/cpp/src/Ice/msbuild/ice/ice.vcxproj index a8d42bb09a4..b6ad5ab9151 100644 --- a/cpp/src/Ice/msbuild/ice/ice.vcxproj +++ b/cpp/src/Ice/msbuild/ice/ice.vcxproj @@ -173,15 +173,15 @@ - + - + @@ -985,7 +985,6 @@ - diff --git a/cpp/src/Ice/msbuild/ice/ice.vcxproj.filters b/cpp/src/Ice/msbuild/ice/ice.vcxproj.filters index 242002bc7c0..f2df188c316 100644 --- a/cpp/src/Ice/msbuild/ice/ice.vcxproj.filters +++ b/cpp/src/Ice/msbuild/ice/ice.vcxproj.filters @@ -579,15 +579,9 @@ Source Files - - Source Files\SSL - Source Files\SSL - - Source Files\SSL - Source Files\SSL @@ -615,6 +609,12 @@ Source Files\SSL + + Source Files\SSL + + + Source Files\SSL + @@ -1006,9 +1006,6 @@ Header Files - - Source Files\SSL - Source Files\SSL diff --git a/cpp/src/IceGrid/AdminSessionI.cpp b/cpp/src/IceGrid/AdminSessionI.cpp index b4f7c772ad6..c4915ed0464 100644 --- a/cpp/src/IceGrid/AdminSessionI.cpp +++ b/cpp/src/IceGrid/AdminSessionI.cpp @@ -3,6 +3,7 @@ // #include "AdminSessionI.h" +#include "../Ice/SSL/SSLUtil.h" #include "AdminI.h" #include "Database.h" #include "Ice/Ice.h" @@ -540,8 +541,8 @@ AdminSSLSessionManagerI::create(Glacier2::SSLInfo info, optionalgetSubjectDN(); + Ice::SSL::ScopedCertificate cert = Ice::SSL::decodeCertificate(info.certs[0]); + userDN = Ice::SSL::getSubjectName(cert.get()); } catch (const Ice::Exception& ex) { diff --git a/cpp/src/IceGrid/InternalRegistryI.cpp b/cpp/src/IceGrid/InternalRegistryI.cpp index 053bcd17acf..fc6affa3fda 100644 --- a/cpp/src/IceGrid/InternalRegistryI.cpp +++ b/cpp/src/IceGrid/InternalRegistryI.cpp @@ -5,6 +5,7 @@ #include "IceUtil/DisableWarnings.h" #include "../Ice/SSL/RFC2253.h" +#include "../Ice/SSL/SSLUtil.h" #include "Database.h" #include "FileCache.h" #include "Ice/Ice.h" @@ -68,7 +69,8 @@ InternalRegistryI::registerNode( auto sslConnInfo = dynamic_pointer_cast(current.con->getInfo()); if (sslConnInfo) { - if (sslConnInfo->certs.empty() || !sslConnInfo->certs[0]->getSubjectDN().match("CN=" + info->name)) + if (!sslConnInfo->peerCertificate || + !Ice::SSL::getSubjectName(sslConnInfo->peerCertificate).match("CN=" + info->name)) { if (traceLevels->node > 0) { @@ -141,7 +143,8 @@ InternalRegistryI::registerReplica( auto sslConnInfo = dynamic_pointer_cast(current.con->getInfo()); if (sslConnInfo) { - if (sslConnInfo->certs.empty() || !sslConnInfo->certs[0]->getSubjectDN().match("CN=" + info->name)) + if (!sslConnInfo->peerCertificate || + !Ice::SSL::getSubjectName(sslConnInfo->peerCertificate).match("CN=" + info->name)) { if (traceLevels->replica > 0) { diff --git a/cpp/src/IceGrid/RegistryI.cpp b/cpp/src/IceGrid/RegistryI.cpp index 37741293685..f34f2733e1e 100644 --- a/cpp/src/IceGrid/RegistryI.cpp +++ b/cpp/src/IceGrid/RegistryI.cpp @@ -5,6 +5,7 @@ #include "RegistryI.h" #include "../Ice/Network.h" #include "../Ice/ProtocolPluginFacade.h" // Just to get the hostname +#include "../Ice/SSL/SSLUtil.h" #include "../IceStorm/Service.h" #include "AdminCallbackRouter.h" #include "AdminI.h" @@ -1208,13 +1209,10 @@ RegistryI::getSSLInfo(const ConnectionPtr& connection, string& userDN) sslinfo.remoteHost = ipInfo->remoteAddress; sslinfo.localPort = ipInfo->localPort; sslinfo.localHost = ipInfo->localAddress; - for (const auto& cert : info->certs) + if (info->peerCertificate) { - sslinfo.certs.push_back(cert->encode()); - } - if (info->certs.size() > 0) - { - userDN = info->certs[0]->getSubjectDN(); + sslinfo.certs.push_back(Ice::SSL::encodeCertificate(info->peerCertificate)); + userDN = Ice::SSL::getSubjectName(info->peerCertificate); } } catch (const Ice::SSL::CertificateEncodingException&) diff --git a/cpp/src/IceGrid/SessionI.cpp b/cpp/src/IceGrid/SessionI.cpp index 14a9215b888..1b31c1fb337 100644 --- a/cpp/src/IceGrid/SessionI.cpp +++ b/cpp/src/IceGrid/SessionI.cpp @@ -3,6 +3,7 @@ // #include "SessionI.h" +#include "../Ice/SSL/SSLUtil.h" #include "Database.h" #include "Ice/Ice.h" #include "IceGrid/Admin.h" @@ -345,8 +346,8 @@ ClientSSLSessionManagerI::create( { try { - auto cert = Ice::SSL::Certificate::decode(info.certs[0]); - userDN = cert->getSubjectDN(); + Ice::SSL::ScopedCertificate cert = Ice::SSL::decodeCertificate(info.certs[0]); + userDN = Ice::SSL::getSubjectName(cert.get()); } catch (const Ice::Exception& e) { diff --git a/cpp/test/Glacier2/ssl/Server.cpp b/cpp/test/Glacier2/ssl/Server.cpp index 34e91d632c4..9404e165222 100644 --- a/cpp/test/Glacier2/ssl/Server.cpp +++ b/cpp/test/Glacier2/ssl/Server.cpp @@ -2,6 +2,7 @@ // Copyright (c) ZeroC, Inc. All rights reserved. // +#include "../../src/Ice/SSL/SSLUtil.h" #include "Glacier2/PermissionsVerifier.h" #include "Glacier2/Session.h" #include "Ice/Ice.h" @@ -52,17 +53,11 @@ class SSLPermissionsVerifierI final : public Glacier2::SSLPermissionsVerifier { testContext(true, current.adapter->getCommunicator(), current.ctx); - auto cert = Ice::SSL::Certificate::decode(info.certs[0]); + Ice::SSL::ScopedCertificate cert = Ice::SSL::decodeCertificate(info.certs[0]); test( - cert->getIssuerDN() == - Ice::SSL::DistinguishedName( - "emailAddress=info@zeroc.com,C=US,ST=Florida,L=Jupiter,O=ZeroC\\, Inc.,OU=Ice,CN=Ice Tests CA")); - test( - cert->getSubjectDN() == + Ice::SSL::getSubjectName(cert.get()) == Ice::SSL::DistinguishedName( "emailAddress=info@zeroc.com,C=US,ST=Florida,L=Jupiter,O=ZeroC\\, Inc.,OU=Ice,CN=client")); - test(cert->checkValidity()); - return true; } }; @@ -120,16 +115,11 @@ class SSLSessionManagerI final : public Glacier2::SSLSessionManager try { - auto cert = Ice::SSL::Certificate::decode(info.certs[0]); - test( - cert->getIssuerDN() == - Ice::SSL::DistinguishedName( - "emailAddress=info@zeroc.com,C=US,ST=Florida,L=Jupiter,O=ZeroC\\, Inc.,OU=Ice,CN=Ice Tests CA")); + auto cert = Ice::SSL::decodeCertificate(info.certs[0]); test( - cert->getSubjectDN() == + Ice::SSL::getSubjectName(cert) == Ice::SSL::DistinguishedName( "emailAddress=info@zeroc.com,C=US,ST=Florida,L=Jupiter,O=ZeroC\\, Inc.,OU=Ice,CN=client")); - test(cert->checkValidity()); } catch (const Ice::SSL::CertificateReadException&) { diff --git a/cpp/test/Ice/info/AllTests.cpp b/cpp/test/Ice/info/AllTests.cpp index 05114c8cd04..f6c3f9b051c 100644 --- a/cpp/test/Ice/info/AllTests.cpp +++ b/cpp/test/Ice/info/AllTests.cpp @@ -237,7 +237,7 @@ allTests(Test::TestHelper* helper) { auto wssinfo = dynamic_pointer_cast(wsinfo->underlying); #if TARGET_OS_IPHONE == 0 - test(!wssinfo->certs.empty()); + test(wssinfo->peerCertificate); #endif } diff --git a/cpp/test/IceGrid/session/Server.cpp b/cpp/test/IceGrid/session/Server.cpp index 536a8c35a1b..cfadf235d29 100644 --- a/cpp/test/IceGrid/session/Server.cpp +++ b/cpp/test/IceGrid/session/Server.cpp @@ -2,6 +2,7 @@ // Copyright (c) ZeroC, Inc. All rights reserved. // +#include "../../src/Ice/SSL/SSLUtil.h" #include "Glacier2/PermissionsVerifier.h" #include "Ice/Ice.h" #include "TestHelper.h" @@ -31,17 +32,11 @@ class SSLPermissionsVerifierI final : public Glacier2::SSLPermissionsVerifier throw Glacier2::PermissionDeniedException("reason"); } test(info.certs.size() > 0); - auto cert = Ice::SSL::Certificate::decode(info.certs[0]); + Ice::SSL::ScopedCertificate cert = Ice::SSL::decodeCertificate(info.certs[0]); test( - cert->getIssuerDN() == - Ice::SSL::DistinguishedName( - "emailAddress=info@zeroc.com,C=US,ST=Florida,L=Jupiter,O=ZeroC\\, Inc.,OU=Ice,CN=Ice Tests CA")); - test( - cert->getSubjectDN() == + Ice::SSL::getSubjectName(cert.get()) == Ice::SSL::DistinguishedName( "emailAddress=info@zeroc.com,C=US,ST=Florida,L=Jupiter,O=ZeroC\\, Inc.,OU=Ice,CN=client")); - test(cert->checkValidity()); - return true; } }; diff --git a/cpp/test/IceSSL/configuration/AllTests.cpp b/cpp/test/IceSSL/configuration/AllTests.cpp index fb0dde072b9..7a4376e3e5c 100644 --- a/cpp/test/IceSSL/configuration/AllTests.cpp +++ b/cpp/test/IceSSL/configuration/AllTests.cpp @@ -362,21 +362,6 @@ createClientProps(const Ice::PropertiesPtr& defaultProps, bool p12, const string return properties; } -void -verify(const Ice::SSL::CertificatePtr& cert, const Ice::SSL::CertificatePtr& ca) -{ - cerr << "Verify signature: "; - if (cert->verify(ca)) - { - cerr << " VALID"; - } - else - { - cerr << " INVALID"; - } - cerr << endl; -} - Test::ServerFactoryPrx #if !defined(__APPLE__) || TARGET_OS_IPHONE == 0 allTests(Test::TestHelper* helper, const string& testDir, bool p12) @@ -514,125 +499,6 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) test(false); } fact->destroyServer(server); - comm->destroy(); - // - // Test IceSSL.VerifyPeer=1. Client has a certificate. - // - // Provide "cacert1" to the client to verify the server - // certificate (without this the client connection wouldn't be - // able to provide the certificate chain). - // - initData.properties = createClientProps(defaultProps, p12, "c_rsa_ca1", "cacert1"); - - comm = initialize(initData); - fact = Test::ServerFactoryPrx(comm, factoryRef); - test(fact); - - d = createServerProps(defaultProps, p12, "s_rsa_ca1", "cacert1"); - d["IceSSL.VerifyPeer"] = "1"; - server = fact->createServer(d); - try - { -#if defined(_WIN32) && defined(ICE_USE_OPENSSL) - Ice::SSL::CertificatePtr clientCert = - Ice::SSL::OpenSSL::Certificate::load(defaultDir + "/c_rsa_ca1_pub.pem"); -#else - Ice::SSL::CertificatePtr clientCert = Ice::SSL::Certificate::load(defaultDir + "/c_rsa_ca1_pub.pem"); -#endif - server->checkCert(clientCert->getSubjectDN().toString(), clientCert->getIssuerDN().toString()); - - // - // Validate that we can get the connection info. Validate - // that the certificates have the same DN. - // - // Validate some aspects of the Certificate class. - // -#if defined(_WIN32) && defined(ICE_USE_OPENSSL) - Ice::SSL::CertificatePtr serverCert = - Ice::SSL::OpenSSL::Certificate::load(defaultDir + "/s_rsa_ca1_pub.pem"); - test(Ice::targetEqualTo(Ice::SSL::OpenSSL::Certificate::decode(serverCert->encode()), serverCert)); -#else - Ice::SSL::CertificatePtr serverCert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_pub.pem"); - test(Ice::targetEqualTo(Ice::SSL::Certificate::decode(serverCert->encode()), serverCert)); -#endif - test(Ice::targetEqualTo(serverCert, serverCert)); -#if !defined(__APPLE__) || TARGET_OS_IPHONE == 0 - test(serverCert->checkValidity()); - - test(!serverCert->checkValidity(std::chrono::system_clock::time_point())); -#endif - -#if defined(_WIN32) && defined(ICE_USE_OPENSSL) - Ice::SSL::CertificatePtr caCert = Ice::SSL::OpenSSL::Certificate::load(defaultDir + "/cacert1.pem"); - Ice::SSL::CertificatePtr caCert2 = Ice::SSL::OpenSSL::Certificate::load(defaultDir + "/cacert2.pem"); -#else - Ice::SSL::CertificatePtr caCert = Ice::SSL::Certificate::load(defaultDir + "/cacert1.pem"); - Ice::SSL::CertificatePtr caCert2 = Ice::SSL::Certificate::load(defaultDir + "/cacert2.pem"); -#endif - test(Ice::targetEqualTo(caCert, caCert)); -#if !defined(__APPLE__) || TARGET_OS_IPHONE == 0 - test(caCert->checkValidity()); - - test(!caCert->checkValidity(std::chrono::system_clock::time_point())); -#endif - - test(!serverCert->verify(serverCert)); - test(serverCert->verify(caCert)); - test(!serverCert->verify(caCert2)); - test(caCert->verify(caCert)); - - info = dynamic_pointer_cast(server->ice_getConnection()->getInfo()); - // TODO provide the peer certificate. - /*test(info->certs.size() == 2); - - test(Ice::targetEqualTo(caCert, info->certs[1])); - test(Ice::targetEqualTo(serverCert, info->certs[0])); - - test(!Ice::targetEqualTo(serverCert, info->certs[1])); - test(!Ice::targetEqualTo(caCert, info->certs[0]));*/ - -#if !defined(__APPLE__) || TARGET_OS_IPHONE == 0 - /*test(info->certs[0]->checkValidity() && info->certs[1]->checkValidity()); - - test( - !info->certs[0]->checkValidity(std::chrono::system_clock::time_point()) && - !info->certs[1]->checkValidity(std::chrono::system_clock::time_point()));*/ -#endif - - /*test( - info->certs.size() == 2 && info->certs[0]->getSubjectDN() == serverCert->getSubjectDN() && - info->certs[0]->getIssuerDN() == serverCert->getIssuerDN());*/ - } - catch (const LocalException& ex) - { - cerr << ex << endl; - test(false); - } - fact->destroyServer(server); - - // - // Test IceSSL.VerifyPeer=2. Client has a certificate. - // - d = createServerProps(defaultProps, p12, "s_rsa_ca1", "cacert1"); - d["IceSSL.VerifyPeer"] = "2"; - server = fact->createServer(d); - try - { -#if defined(_WIN32) && defined(ICE_USE_OPENSSL) - Ice::SSL::CertificatePtr clientCert = - Ice::SSL::OpenSSL::Certificate::load(defaultDir + "/c_rsa_ca1_pub.pem"); -#else - Ice::SSL::CertificatePtr clientCert = Ice::SSL::Certificate::load(defaultDir + "/c_rsa_ca1_pub.pem"); -#endif - server->checkCert(clientCert->getSubjectDN().toString(), clientCert->getIssuerDN().toString()); - } - catch (const LocalException& ex) - { - cerr << ex << endl; - test(false); - } - fact->destroyServer(server); - comm->destroy(); // @@ -965,158 +831,6 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) } cout << "ok" << endl; -#if !defined(ICE_USE_SECURE_TRANSPORT_IOS) - cout << "testing certificate info... " << flush; - { - const char* certificates[] = {"/cacert1.pem", "/c_rsa_ca1_pub.pem", "/s_rsa_ca1_pub.pem", 0}; - - const char* authorities[] = { - "", // Self signed CA cert has not X509v3 Authority Key Identifier extension - "02:FD:1B:E9:6F:4E:96:8F:0C:0E:99:61:8F:45:48:6B:2B:14:3C:31", - "02:FD:1B:E9:6F:4E:96:8F:0C:0E:99:61:8F:45:48:6B:2B:14:3C:31", - 0}; - - const char* subjects[] = { - "02:FD:1B:E9:6F:4E:96:8F:0C:0E:99:61:8F:45:48:6B:2B:14:3C:31", - "7F:4D:BF:80:65:E0:EE:A4:18:D5:6A:87:33:63:B3:76:7D:42:82:06", - "EB:4A:7A:79:09:65:0F:45:40:E8:8C:E6:A8:27:74:34:AB:EA:AF:48", - 0}; - - for (int i = 0; certificates[i] != 0; ++i) - { - Ice::SSL::CertificatePtr cert = Ice::SSL::Certificate::load(defaultDir + certificates[i]); - test(toHexString(cert->getAuthorityKeyIdentifier()) == authorities[i]); - test(toHexString(cert->getSubjectKeyIdentifier()) == subjects[i]); - } - - Ice::SSL::CertificatePtr cert = Ice::SSL::Certificate::load(defaultDir + "/cacert1.pem"); - unsigned int keyUsage = cert->getKeyUsage(); - test( - keyUsage == - (Ice::SSL::KEY_USAGE_DIGITAL_SIGNATURE | Ice::SSL::KEY_USAGE_KEY_CERT_SIGN | Ice::SSL::KEY_USAGE_CRL_SIGN)); - - // Digital Signature, Certificate Sign, CRL Sign - cert = Ice::SSL::Certificate::load(defaultDir + "/cacert3.pem"); - keyUsage = cert->getKeyUsage(); - test( - keyUsage == - (Ice::SSL::KEY_USAGE_DIGITAL_SIGNATURE | Ice::SSL::KEY_USAGE_KEY_CERT_SIGN | Ice::SSL::KEY_USAGE_CRL_SIGN)); - - // Digital Signature, Certificate Sign, CRL Sign - cert = Ice::SSL::Certificate::load(defaultDir + "/cacert4.pem"); - keyUsage = cert->getKeyUsage(); - test( - keyUsage == - (Ice::SSL::KEY_USAGE_DIGITAL_SIGNATURE | Ice::SSL::KEY_USAGE_KEY_CERT_SIGN | Ice::SSL::KEY_USAGE_CRL_SIGN)); - } - - { - Ice::SSL::CertificatePtr cert = Ice::SSL::Certificate::load(defaultDir + "/rsa_ca1_none_pub.pem"); - unsigned int keyUsage = cert->getExtendedKeyUsage(); - test(keyUsage == 0); - - cert = Ice::SSL::Certificate::load(defaultDir + "/rsa_ca1_serverAuth_pub.pem"); - keyUsage = cert->getExtendedKeyUsage(); - test(keyUsage == Ice::SSL::EXTENDED_KEY_USAGE_SERVER_AUTH); - - cert = Ice::SSL::Certificate::load(defaultDir + "/rsa_ca1_clientAuth_pub.pem"); - keyUsage = cert->getExtendedKeyUsage(); - test(keyUsage == Ice::SSL::EXTENDED_KEY_USAGE_CLIENT_AUTH); - - cert = Ice::SSL::Certificate::load(defaultDir + "/rsa_ca1_codeSigning_pub.pem"); - keyUsage = cert->getExtendedKeyUsage(); - test(keyUsage == Ice::SSL::EXTENDED_KEY_USAGE_CODE_SIGNING); - - cert = Ice::SSL::Certificate::load(defaultDir + "/rsa_ca1_emailProtection_pub.pem"); - keyUsage = cert->getExtendedKeyUsage(); - test(keyUsage == Ice::SSL::EXTENDED_KEY_USAGE_EMAIL_PROTECTION); - - cert = Ice::SSL::Certificate::load(defaultDir + "/rsa_ca1_timeStamping_pub.pem"); - keyUsage = cert->getExtendedKeyUsage(); - test(keyUsage == Ice::SSL::EXTENDED_KEY_USAGE_TIME_STAMPING); - - cert = Ice::SSL::Certificate::load(defaultDir + "/rsa_ca1_ocspSigning_pub.pem"); - keyUsage = cert->getExtendedKeyUsage(); - test(keyUsage == Ice::SSL::EXTENDED_KEY_USAGE_OCSP_SIGNING); - - cert = Ice::SSL::Certificate::load(defaultDir + "/rsa_ca1_anyExtendedKeyUsage_pub.pem"); - keyUsage = cert->getExtendedKeyUsage(); - test(keyUsage == Ice::SSL::EXTENDED_KEY_USAGE_ANY_KEY_USAGE); - } - - { -# if !defined(__APPLE__) || TARGET_OS_IPHONE == 0 - vector> expectedAltNames; - expectedAltNames.push_back(make_pair(7, "127.0.0.1")); - expectedAltNames.push_back(make_pair(2, "client")); - Ice::SSL::CertificatePtr cert = Ice::SSL::Certificate::load(defaultDir + "/c_rsa_ca1_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); - // Digital Signature, Non Repudiation, Key Encipherment - unsigned int keyUsage = cert->getKeyUsage(); - test( - keyUsage == (Ice::SSL::KEY_USAGE_DIGITAL_SIGNATURE | Ice::SSL::KEY_USAGE_NON_REPUDIATION | - Ice::SSL::KEY_USAGE_KEY_ENCIPHERMENT)); - - expectedAltNames.clear(); - expectedAltNames.push_back(make_pair(7, "127.0.0.1")); - expectedAltNames.push_back(make_pair(2, "server")); - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); - keyUsage = cert->getKeyUsage(); - test( - keyUsage == (Ice::SSL::KEY_USAGE_DIGITAL_SIGNATURE | Ice::SSL::KEY_USAGE_NON_REPUDIATION | - Ice::SSL::KEY_USAGE_KEY_ENCIPHERMENT)); - - expectedAltNames.clear(); - expectedAltNames.push_back(make_pair(2, "localhost")); - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_cn1_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); - - expectedAltNames.clear(); - expectedAltNames.push_back(make_pair(2, "localhostXX")); - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_cn2_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); - - expectedAltNames.clear(); - expectedAltNames.push_back(make_pair(7, "127.0.0.1")); - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_cn6_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); - - expectedAltNames.clear(); - expectedAltNames.push_back(make_pair(7, "127.0.0.2")); - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_cn7_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); - - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_cn8_pub.pem"); - test(cert->getSubjectAlternativeNames().empty()); - - expectedAltNames.clear(); - expectedAltNames.push_back(make_pair(7, "127.0.0.1")); -// IPv6 address parsing is not implemented with SChannel and OpenSSL IceSSL implementations -# ifdef ICE_USE_SECURE_TRANSPORT - expectedAltNames.push_back(make_pair(7, "0000:0000:0000:0000:0000:0000:0000:0001")); -# endif - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_cn9_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); - - expectedAltNames.clear(); - expectedAltNames.push_back(make_pair(2, "host1")); - expectedAltNames.push_back(make_pair(2, "host2")); - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_cn10_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); - - expectedAltNames.clear(); - expectedAltNames.push_back(make_pair(7, "127.0.0.1")); - expectedAltNames.push_back(make_pair(7, "127.0.0.2")); - expectedAltNames.push_back(make_pair(2, "host1")); - expectedAltNames.push_back(make_pair(2, "host2")); - cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_cn11_pub.pem"); - test(cert->getSubjectAlternativeNames() == expectedAltNames); -# endif - } - cout << "ok" << endl; -#endif - cout << "testing certificate chains... " << flush; { const char* certificates[] = {"/s_rsa_cai2.p12", 0}; @@ -1250,90 +964,11 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) cout << "ok" << endl; } -#if defined(ICE_USE_OPENSSL) || defined(ICE_USE_SCHANNEL) - cout << "testing certificate extensions... " << flush; - { - const string basicConstraints = "30:03:01:01:FF"; - - const string subjectKeyIdentifier = "04:14:13:FA:72:67:FE:34:05:9A:C9:3E:61:D2:91:D6:BA:03:65:1B:8A:9A"; - - const string authorityKeyIdentifier = "30:81:A2:80:14:13:FA:72:67:FE:34:05:9A:C9:3E:61:D2:91:D6:BA:03:65:" - "1B:8A:9A:A1:7F:A4:7D:30:7B:31:0B:30:09:06:03:55:04:06:13:02:55:53:" - "31:10:30:0E:06:03:55:04:08:0C:07:46:6C:6F:72:69:64:61:31:10:30:0E:" - "06:03:55:04:07:0C:07:4A:75:70:69:74:65:72:31:0E:30:0C:06:03:55:04:" - "0A:0C:05:5A:65:72:6F:43:31:0C:30:0A:06:03:55:04:0B:0C:03:49:63:65:" - "31:0B:30:09:06:03:55:04:03:0C:02:43:41:31:1D:30:1B:06:09:2A:86:48:" - "86:F7:0D:01:09:01:16:0E:69:6E:66:6F:40:7A:65:72:6F:63:2E:63:6F:6D:" - "82:09:00:EA:2A:B7:FB:3B:A3:DF:5A"; - - const string subjectAltName = "30:0B:82:09:7A:65:72:6F:63:2E:63:6F:6D"; - - const string issuerAltName = "30:0B:82:09:7A:65:72:6F:63:2E:63:6F:6D"; - - const string customExt412 = "0C:0B:43:75:73:74:6F:6D:20:64:61:74:61"; - - const string customExt413 = "30:17:01:01:FF:0C:0E:4D:79:20:55:54:46:38:20:53:74:72:69:6E:67:02:02:03:FF"; - - Ice::SSL::CertificatePtr cert = Ice::SSL::Certificate::load(defaultDir + "/cacert_custom.pem"); - vector extensions = cert->getX509Extensions(); - test(extensions.size() == 7); - - Ice::SSL::X509ExtensionPtr ext = cert->getX509Extension("2.5.29.19"); // Subject key identifier - test(ext); - test(toHexString(ext->getData()) == basicConstraints); - test(ext->getOID() == "2.5.29.19"); - test(ext->isCritical() == false); - - ext = cert->getX509Extension("2.5.29.14"); // Subject key identifier - test(ext); - test(toHexString(ext->getData()) == subjectKeyIdentifier); - test(ext->getOID() == "2.5.29.14"); - test(ext->isCritical() == false); - - ext = cert->getX509Extension("2.5.29.35"); // Authority key identifier - test(ext); - test(toHexString(ext->getData()) == authorityKeyIdentifier); - test(ext->getOID() == "2.5.29.35"); - test(ext->isCritical() == false); - - ext = cert->getX509Extension("2.5.29.17"); // Subject alternative name - test(ext); - test(toHexString(ext->getData()) == subjectAltName); - test(ext->getOID() == "2.5.29.17"); - test(ext->isCritical() == false); - - ext = cert->getX509Extension("2.5.29.18"); // Issuer alternative name - test(ext); - test(toHexString(ext->getData()) == issuerAltName); - test(ext->getOID() == "2.5.29.18"); - test(ext->isCritical() == false); - - ext = cert->getX509Extension("1.2.3.412"); // Custom extension - test(ext); - test(toHexString(ext->getData()) == customExt412); - test(ext->getOID() == "1.2.3.412"); - test(ext->isCritical() == false); - - ext = cert->getX509Extension("1.2.3.413"); // Custom extension - test(ext); - test(toHexString(ext->getData()) == customExt413); - test(ext->getOID() == "1.2.3.413"); - test(ext->isCritical() == false); - } - cout << "ok" << endl; -#endif - cout << "testing expired certificates... " << flush; { // // This should fail because the server's certificate is expired. // -#if !defined(__APPLE__) || TARGET_OS_IPHONE == 0 - { - Ice::SSL::CertificatePtr cert = Ice::SSL::Certificate::load(defaultDir + "/s_rsa_ca1_exp_pub.pem"); - test(!cert->checkValidity()); - } -#endif InitializationData initData; initData.properties = createClientProps(defaultProps, p12, "c_rsa_ca1", "cacert1"); @@ -1362,13 +997,6 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) // // This should fail because the client's certificate is expired. // -#if !defined(__APPLE__) || TARGET_OS_IPHONE == 0 - { - Ice::SSL::CertificatePtr cert = Ice::SSL::Certificate::load(defaultDir + "/c_rsa_ca1_exp_pub.pem"); - test(!cert->checkValidity()); - } -#endif - initData.properties = createClientProps(defaultProps, p12, "c_rsa_ca1_exp", "cacert1"); comm = initialize(initData); fact = Test::ServerFactoryPrx(comm, factoryRef); @@ -1484,6 +1112,7 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) cout << "ok" << endl; #endif +#ifndef ICE_USE_SECURE_TRANSPORT_IOS cout << "testing IceSSL.TrustOnly... " << flush; // // iOS support only provides access to the CN of the certificate so we @@ -2304,6 +1933,7 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) comm->destroy(); } cout << "ok" << endl; +#endif { #if defined(ICE_USE_SCHANNEL) @@ -2482,11 +2112,12 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) initData.properties->setProperty("IceSSL.Keychain", "../certs/Find.keychain"); initData.properties->setProperty("IceSSL.KeychainPassword", "password"); initData.properties->setProperty("IceSSL.FindCert", clientFindCertProperties[i]); - // - // Use TrustOnly to ensure the peer has pick the expected certificate. - // +// +// Use TrustOnly to ensure the peer has pick the expected certificate. +// +# ifndef ICE_USE_SECURE_TRANSPORT_IOS initData.properties->setProperty("IceSSL.TrustOnly", "CN=Server"); - +# endif CommunicatorPtr comm = initialize(initData); optional fact = Test::ServerFactoryPrx(comm, factoryRef); @@ -2500,8 +2131,9 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) // // Use TrustOnly to ensure the peer has pick the expected certificate. // +# ifndef ICE_USE_SECURE_TRANSPORT_IOS d["IceSSL.TrustOnly"] = "CN=Client"; - +# endif optional server = fact->createServer(d); try { @@ -2834,11 +2466,8 @@ allTests(Test::TestHelper* helper, const string& /*testDir*/, bool p12) #endif } -#if !defined(_AIX) && !(defined(_WIN32) && defined(ICE_USE_OPENSSL)) - // +#if !defined(_AIX) // On AIX 6.1, the default root certificates don't validate demo.zeroc.com. - // On Windows with OpenSSL there aren't any system CAs. - // cout << "testing system CAs... " << flush; { // diff --git a/cpp/test/IceSSL/configuration/TestI.cpp b/cpp/test/IceSSL/configuration/TestI.cpp index f9dfb1fdc6f..9befaf7cdd5 100644 --- a/cpp/test/IceSSL/configuration/TestI.cpp +++ b/cpp/test/IceSSL/configuration/TestI.cpp @@ -17,7 +17,7 @@ ServerI::noCert(const Ice::Current& c) try { Ice::SSL::ConnectionInfoPtr info = dynamic_pointer_cast(c.con->getInfo()); - test(info->certs.size() == 0); + test(!info->peerCertificate); } catch (const Ice::LocalException& ex) { diff --git a/matlab/lib/+Ice/+SSL/ConnectionInfo.m b/matlab/lib/+Ice/+SSL/ConnectionInfo.m index f0b6e8f15b0..c4e161ed359 100644 --- a/matlab/lib/+Ice/+SSL/ConnectionInfo.m +++ b/matlab/lib/+Ice/+SSL/ConnectionInfo.m @@ -4,38 +4,26 @@ % Provides access to the connection detaisl of an SSL connection. % % ConnectionInfo Properties: - % cipher - The negotiated cipher suite. - % certs - The certificate chain. - % verified - The certificate chain verification status. + % peerCertificate - The peer certificate. % Copyright (c) ZeroC, Inc. All rights reserved. methods - function obj = ConnectionInfo(underlying, incoming, adapterName, connectionId, cipher, ... - certs, verified) + function obj = ConnectionInfo(underlying, incoming, adapterName, connectionId, ... + peerCertificate) if nargin == 0 underlying = []; incoming = false; adapterName = ''; connectionId = ''; - cipher = ''; - certs = []; - verified = false; + peerCertificate = ''; end obj = obj@Ice.ConnectionInfo(underlying, incoming, adapterName, connectionId); - obj.cipher = cipher; - obj.certs = certs; - obj.verified = verified; + obj.peerCertificate = peerCertificate; end end properties(SetAccess=private) - % cipher - The negotiated cipher suite. - cipher char - - % certs - The certificate chain. - certs - - % verified - The certificate chain verification status. - verified logical + % peerCertificate - The peer certificate. + peerCertificate end end diff --git a/matlab/lib/+Ice/Connection.m b/matlab/lib/+Ice/Connection.m index 5bfb00e6e5d..a8383029172 100644 --- a/matlab/lib/+Ice/Connection.m +++ b/matlab/lib/+Ice/Connection.m @@ -217,7 +217,7 @@ function throwException(obj) case 'ssl' r = Ice.SSL.ConnectionInfo(underlying, info.incoming, info.adapterName, info.connectionId, ... - info.cipher, info.certs, info.verified); + info.peerCertificate); case 'udp' r = Ice.UDPConnectionInfo(underlying, info.incoming, info.adapterName, info.connectionId, ... diff --git a/matlab/src/Connection.cpp b/matlab/src/Connection.cpp index 2f3d1b5a000..4b001656e9b 100644 --- a/matlab/src/Connection.cpp +++ b/matlab/src/Connection.cpp @@ -2,6 +2,7 @@ // Copyright (c) ZeroC, Inc. All rights reserved. // +#include "../../cpp/src/Ice/SSL/SSLUtil.h" #include "Future.h" #include "Ice/Ice.h" #include "Util.h" @@ -28,9 +29,7 @@ namespace McastAddress, McastPort, Headers, - Cipher, - Certs, - Verified, + PeerCertificate, NumFields // Number of fields in structure, must be last }; @@ -49,7 +48,7 @@ namespace "mcastAddress", "mcastPort", "headers", - "certs"}; + "peerCertificate"}; mxArray* createInfo(const shared_ptr& info) { @@ -111,7 +110,12 @@ namespace if (sslInfo) { type = "ssl"; - mxSetFieldByNumber(r, 0, Field::Certs, createCertificateList(sslInfo->certs)); + string encoded; + if (sslInfo->peerCertificate) + { + encoded = Ice::SSL::encodeCertificate(sslInfo->peerCertificate); + } + mxSetFieldByNumber(r, 0, Field::PeerCertificate, createStringFromUTF8(encoded)); } mxSetFieldByNumber(r, 0, Field::Type, createStringFromUTF8(type)); diff --git a/matlab/src/Util.cpp b/matlab/src/Util.cpp index 5fb2e0db70f..bc1726528fd 100644 --- a/matlab/src/Util.cpp +++ b/matlab/src/Util.cpp @@ -613,18 +613,6 @@ IceMatlab::createByteList(const vector& bytes) return r; } -mxArray* -IceMatlab::createCertificateList(const vector& certs) -{ - auto r = mxCreateCellMatrix(1, static_cast(certs.size())); - mwIndex i = 0; - for (auto cert : certs) - { - mxSetCell(r, i++, createStringFromUTF8(cert->encode())); - } - return r; -} - namespace { string lookupKwd(const string& name) diff --git a/matlab/src/Util.h b/matlab/src/Util.h index 255ab027685..c992589b04b 100644 --- a/matlab/src/Util.h +++ b/matlab/src/Util.h @@ -41,7 +41,6 @@ namespace IceMatlab void getStringList(mxArray*, std::vector&); mxArray* createByteArray(const std::byte*, const std::byte*); mxArray* createByteList(const std::vector&); - mxArray* createCertificateList(const std::vector&); std::string idToClass(const std::string&); diff --git a/matlab/test/Ice/properties/Client.m b/matlab/test/Ice/properties/Client.m index 8e30ca73f08..119622e4da2 100644 --- a/matlab/test/Ice/properties/Client.m +++ b/matlab/test/Ice/properties/Client.m @@ -7,7 +7,7 @@ function client(args) loadlibrary('ice', @iceproto) end - props = Ice.createProperties() + props = Ice.createProperties(); fprintf('testing ice properties with set default values...'); toStringMode = props.getIceProperty('Ice.ToStringMode'); diff --git a/php/src/Connection.cpp b/php/src/Connection.cpp index e9b6cccd039..3cad33f4458 100644 --- a/php/src/Connection.cpp +++ b/php/src/Connection.cpp @@ -3,6 +3,7 @@ // #include "Connection.h" +#include "../../cpp/src/Ice/SSL/SSLUtil.h" #include "Endpoint.h" #include "Ice/Ice.h" #include "Types.h" @@ -468,7 +469,12 @@ IcePHP::connectionInit(void) INIT_NS_CLASS_ENTRY(ce, "Ice", "SSLConnectionInfo", nullptr); ce.create_object = handleConnectionInfoAlloc; sslConnectionInfoClassEntry = zend_register_internal_class_ex(&ce, connectionInfoClassEntry); - zend_declare_property_string(sslConnectionInfoClassEntry, "certs", sizeof("certs") - 1, "", ZEND_ACC_PUBLIC); + zend_declare_property_string( + sslConnectionInfoClassEntry, + "peerCertificate", + sizeof("peerCertificate") - 1, + "", + ZEND_ACC_PUBLIC); return true; } @@ -584,24 +590,12 @@ IcePHP::createConnectionInfo(zval* zv, const Ice::ConnectionInfoPtr& p) { auto info = dynamic_pointer_cast(p); - zval zarr; - AutoDestroy listDestroyer(&zarr); - - Ice::StringSeq encoded; - transform( - info->certs.cbegin(), - info->certs.cend(), - back_inserter(encoded), - [](const auto& cert) { return cert->encode(); }); - - if (createStringArray(&zarr, encoded)) - { - add_property_zval(zv, "certs", &zarr); - } - else + string encoded; + if (info->peerCertificate) { - return false; + encoded = Ice::SSL::encodeCertificate(info->peerCertificate); } + add_property_string(zv, "peerCertificate", const_cast(encoded.c_str())); } if (dynamic_pointer_cast(p)) diff --git a/python/modules/IcePy/ConnectionInfo.cpp b/python/modules/IcePy/ConnectionInfo.cpp index 2771114925a..03b8daf9fc7 100644 --- a/python/modules/IcePy/ConnectionInfo.cpp +++ b/python/modules/IcePy/ConnectionInfo.cpp @@ -3,6 +3,7 @@ // #include "ConnectionInfo.h" +#include "../../cpp/src/Ice/SSL/SSLUtil.h" #include "EndpointInfo.h" #include "Ice/Ice.h" #include "Util.h" @@ -205,18 +206,16 @@ extern "C" extern "C" #endif static PyObject* - sslConnectionInfoGetCerts(ConnectionInfoObject* self, PyObject* /*args*/) + sslConnectionInfoGetPeerCertificiate(ConnectionInfoObject* self, PyObject* /*args*/) { auto info = dynamic_pointer_cast(*self->connectionInfo); assert(info); - PyObject* certs = PyList_New(0); - Ice::StringSeq encoded; - for (vector::const_iterator i = info->certs.begin(); i != info->certs.end(); ++i) + string encoded; + if (info->peerCertificate) { - encoded.push_back((*i)->encode()); + encoded = Ice::SSL::encodeCertificate(info->peerCertificate); } - stringSeqToList(encoded, certs); - return certs; + return createString(encoded); } static PyGetSetDef ConnectionInfoGetters[] = { @@ -310,10 +309,10 @@ static PyGetSetDef WSConnectionInfoGetters[] = { }; static PyGetSetDef SSLConnectionInfoGetters[] = { - {STRCAST("certs"), - reinterpret_cast(sslConnectionInfoGetCerts), + {STRCAST("peerCertificate"), + reinterpret_cast(sslConnectionInfoGetPeerCertificiate), 0, - PyDoc_STR(STRCAST("certificate chain")), + PyDoc_STR(STRCAST("peer certificate")), 0}, {0, 0} /* sentinel */ }; diff --git a/ruby/src/IceRuby/Connection.cpp b/ruby/src/IceRuby/Connection.cpp index abf04158299..df331591d66 100644 --- a/ruby/src/IceRuby/Connection.cpp +++ b/ruby/src/IceRuby/Connection.cpp @@ -3,6 +3,7 @@ // #include "Connection.h" +#include "../../cpp/src/Ice/SSL/SSLUtil.h" #include "Endpoint.h" #include "Ice/Ice.h" #include "Types.h" @@ -279,13 +280,12 @@ IceRuby::createConnectionInfo(const Ice::ConnectionInfoPtr& p) info = Data_Wrap_Struct(_sslConnectionInfoClass, 0, IceRuby_ConnectionInfo_free, new Ice::ConnectionInfoPtr(p)); Ice::SSL::ConnectionInfoPtr ssl = dynamic_pointer_cast(p); - Ice::StringSeq encoded; - for (vector::const_iterator i = ssl->certs.begin(); i != ssl->certs.end(); ++i) + string encoded; + if (ssl->peerCertificate) { - encoded.push_back((*i)->encode()); + encoded = Ice::SSL::encodeCertificate(ssl->peerCertificate); } - - rb_ivar_set(info, rb_intern("@certs"), stringSeqToArray(encoded)); + rb_ivar_set(info, rb_intern("@peerCertificate"), createString(encoded)); } else if (dynamic_pointer_cast(p)) { @@ -404,7 +404,7 @@ IceRuby::initConnection(VALUE iceModule) // // Instance members. // - rb_define_attr(_sslConnectionInfoClass, "certs", 1, 0); + rb_define_attr(_sslConnectionInfoClass, "peerCertificate", 1, 0); } Ice::ConnectionPtr diff --git a/swift/src/Ice/ConnectionInfoFactory.swift b/swift/src/Ice/ConnectionInfoFactory.swift index ed46164718f..2e5c43643c8 100644 --- a/swift/src/Ice/ConnectionInfoFactory.swift +++ b/swift/src/Ice/ConnectionInfoFactory.swift @@ -96,29 +96,28 @@ private class WSConnectionInfoI: ConnectionInfoI, WSConnectionInfo { } private class SSLConnectionInfoI: ConnectionInfoI, SSLConnectionInfo { - var certs: [SecCertificate] + var peerCertificate: SecCertificate? init( underlying: ConnectionInfo?, incoming: Bool, adapterName: String, connectionId: String, - certs: StringSeq + peerCertificate: String ) { - self.certs = [] let beginPrefix = "-----BEGIN CERTIFICATE-----\n" let endPrefix = "\n-----END CERTIFICATE-----\n" - for cert in certs { - var raw = cert - if raw.hasPrefix(beginPrefix) { - raw = String(raw.dropFirst(beginPrefix.count)) - raw = String(raw.dropLast(endPrefix.count)) - } + var raw = peerCertificate + if raw.hasPrefix(beginPrefix) { + raw = String(raw.dropFirst(beginPrefix.count)) + raw = String(raw.dropLast(endPrefix.count)) + } - if let data = NSData(base64Encoded: raw, options: .ignoreUnknownCharacters) { - if let c = SecCertificateCreateWithData(kCFAllocatorDefault, data) { - self.certs.append(c) - } + self.peerCertificate = nil + if let data = NSData(base64Encoded: raw, options: .ignoreUnknownCharacters) { + if let c = SecCertificateCreateWithData(kCFAllocatorDefault, data) { + self.peerCertificate = c } } + super.init( underlying: underlying, incoming: incoming, adapterName: adapterName, connectionId: connectionId) @@ -245,14 +244,14 @@ class ConnectionInfoFactory: ICEConnectionInfoFactory { incoming: Bool, adapterName: String, connectionId: String, - certs: [String] + peerCertificate: String ) -> Any { return SSLConnectionInfoI( underlying: getUnderlying(underlying), incoming: incoming, adapterName: adapterName, connectionId: connectionId, - certs: certs) + peerCertificate: peerCertificate) } #if os(iOS) || os(watchOS) || os(tvOS) diff --git a/swift/src/Ice/SSLConnectionInfo.swift b/swift/src/Ice/SSLConnectionInfo.swift index f98a4a5f12b..30ba370973c 100644 --- a/swift/src/Ice/SSLConnectionInfo.swift +++ b/swift/src/Ice/SSLConnectionInfo.swift @@ -7,5 +7,5 @@ import Foundation /// Provides access to the connection details of an SSL connection public protocol SSLConnectionInfo: ConnectionInfo { /// The certificate chain. - var certs: [SecCertificate] { get set } + var peerCertificate: SecCertificate? { get set } } diff --git a/swift/src/IceImpl/Connection.h b/swift/src/IceImpl/Connection.h index 87b20d12923..8dccdfb5e60 100644 --- a/swift/src/IceImpl/Connection.h +++ b/swift/src/IceImpl/Connection.h @@ -82,7 +82,7 @@ ICEIMPL_API @protocol ICEConnectionInfoFactory incoming:(BOOL)incoming adapterName:(NSString*)adapterName connectionId:(NSString*)connectionId - certs:(NSArray*)certs; + peerCertificate:(NSString*)peerCertificate; #if TARGET_OS_IPHONE diff --git a/swift/src/IceImpl/Connection.mm b/swift/src/IceImpl/Connection.mm index bd7ed57d159..455ec310729 100644 --- a/swift/src/IceImpl/Connection.mm +++ b/swift/src/IceImpl/Connection.mm @@ -3,6 +3,7 @@ // #import "Connection.h" +#import "../../cpp/src/Ice/SSL/SSLUtil.h" #import "Convert.h" #import "Endpoint.h" #import "IceUtil.h" @@ -325,11 +326,16 @@ - (BOOL)throwException:(NSError**)error auto sslInfo = std::dynamic_pointer_cast(infoPtr); if (sslInfo) { + std::string encoded; + if (sslInfo->peerCertificate) + { + encoded = Ice::SSL::encodeCertificate(sslInfo->peerCertificate); + } return [factory createSSLConnectionInfo:underlying incoming:sslInfo->incoming adapterName:toNSString(sslInfo->adapterName) connectionId:toNSString(sslInfo->connectionId) - certs:toNSArray(sslInfo->certs)]; + peerCertificate:toNSString(encoded)]; } #if TARGET_OS_IPHONE diff --git a/swift/src/IceImpl/Convert.h b/swift/src/IceImpl/Convert.h index 26921d8aca7..145a2f54eee 100644 --- a/swift/src/IceImpl/Convert.h +++ b/swift/src/IceImpl/Convert.h @@ -11,11 +11,6 @@ @class ICERuntimeException; -namespace Ice::SSL -{ - class Certificate; -} - NSError* convertException(std::exception_ptr); std::exception_ptr convertException(ICERuntimeException*); @@ -46,8 +41,6 @@ fromObjC(id object, std::string& s) NSObject* toObjC(const std::shared_ptr& endpoint); void fromObjC(id object, std::shared_ptr& endpoint); -NSObject* toObjC(const std::shared_ptr& cert); - template NSMutableArray* toNSArray(const std::vector& seq) diff --git a/swift/src/IceImpl/Convert.mm b/swift/src/IceImpl/Convert.mm index a5e86740193..e18639e343d 100644 --- a/swift/src/IceImpl/Convert.mm +++ b/swift/src/IceImpl/Convert.mm @@ -416,9 +416,3 @@ ICEEndpoint* endpt = object; endpoint = object == [NSNull null] ? nullptr : [endpt endpoint]; } - -NSObject* -toObjC(const std::shared_ptr& cert) -{ - return toNSString(cert->encode()); -} diff --git a/swift/test/IceSSL/configuration/TestI.swift b/swift/test/IceSSL/configuration/TestI.swift index e2b01a18677..19906fa51e6 100644 --- a/swift/test/IceSSL/configuration/TestI.swift +++ b/swift/test/IceSSL/configuration/TestI.swift @@ -17,7 +17,7 @@ class ServerI: SSLServer { func noCert(current: Ice.Current) throws { do { let info = try current.con!.getInfo() as! SSLConnectionInfo - try _helper.test(info.certs.count == 0) + try _helper.test(info.peerCertificate == nil) } catch is Ice.LocalException { try _helper.test(false) } @@ -26,7 +26,7 @@ class ServerI: SSLServer { func checkCert(subjectDN _: String, issuerDN _: String, current: Ice.Current) throws { do { let info = try current.con!.getInfo() as! SSLConnectionInfo - try _helper.test(info.certs.count == 2) + try _helper.test(info.peerCertificate != nil) } catch is Ice.LocalException { try _helper.test(false) }