9 #include <openssl/ssl.h>
10 #include <openssl/err.h>
11 #include <openssl/bio.h>
13 #include <openssl/ssl.h>
14 #include <openssl/err.h>
15 #include <openssl/bio.h>
25 void SslStream::InitializeOpenSSL() {
29 SSL_load_error_strings();
30 OpenSSL_add_all_algorithms();
35 : m_spInnerStream(innerStream),
36 m_bLeaveInnerStreamOpen(false),
43 if (innerStream.
IsNull()) {
44 throw ArgumentNullException(
"innerStream cannot be null.");
50 : m_spInnerStream(innerStream),
51 m_bLeaveInnerStreamOpen(leaveInnerStreamOpen),
58 if (innerStream.
IsNull()) {
59 throw ArgumentNullException(
"innerStream cannot be null.");
69 void* SslStream::CreateSslContext(
bool isServer) {
71 SSL_CTX* ctx = SSL_CTX_new(isServer ? TLS_server_method() : TLS_client_method());
76 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
82 SSL_CTX* ctx =
static_cast<SSL_CTX*
>(rawCtx);
83 X509* cert =
static_cast<X509*
>(certificate->GetInternalCert());
84 EVP_PKEY* pkey =
static_cast<EVP_PKEY*
>(certificate->GetInternalKey());
86 if (SSL_CTX_use_certificate(ctx, cert) <= 0) {
92 if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) {
101 SSL* ssl = SSL_new(ctx);
106 BIO* bioIn = BIO_new(BIO_s_mem());
107 BIO* bioOut = BIO_new(BIO_s_mem());
110 SSL_set_bio(ssl, bioIn, bioOut);
120 SSL_CTX* ctx =
static_cast<SSL_CTX*
>(CreateSslContext(
false));
124 SSL_set_tlsext_host_name(ssl, targetHost.
GetRawString());
125 SSL_set_connect_state(ssl);
136 SSL_CTX* ctx =
static_cast<SSL_CTX*
>(CreateSslContext(
true));
138 ConfigureServerCert(ctx, certificate);
141 SSL_set_accept_state(ssl);
147 int read = pStream->
Read(buffer, 0,
sizeof(buffer));
149 BIO_write(
static_cast<BIO*
>(pBioIn), buffer, read);
153 if (!pBioOut)
return;
156 int read = BIO_read(
static_cast<BIO*
>(pBioOut), buffer,
sizeof(buffer));
157 if (read <= 0)
break;
158 spInnerStream->Write(buffer, 0, read);
164 if (err == SSL_ERROR_WANT_READ) {
166 }
else if (err != SSL_ERROR_WANT_WRITE) {
168 ERR_error_string_n(ERR_get_error(), errBuf,
sizeof(errBuf));
173 void SslStream::ProcessHandshake() {
175 SSL* ssl =
static_cast<SSL*
>(m_pSsl);
176 while (!SSL_is_init_finished(ssl)) {
177 int ret = SSL_do_handshake(ssl);
184 void SslStream::FlushOutboundBio() {
199 m_spInnerStream->Flush();
206 if (err == SSL_ERROR_WANT_READ) {
209 int read = spStream->Read(rawBuf, 0,
sizeof(rawBuf));
210 if (read <= 0)
return 0;
211 BIO_write(
static_cast<BIO*
>(pBioIn), rawBuf, read);
214 if (err == SSL_ERROR_ZERO_RETURN)
return 0;
215 if (err == SSL_ERROR_WANT_WRITE) {
220 ERR_error_string_n(ERR_get_error(), errBuf,
sizeof(errBuf));
228 SSL* ssl =
static_cast<SSL*
>(m_pSsl);
231 int ret = SSL_read(ssl, buffer + offset, count);
232 if (ret > 0)
return ret;
233 int r =
HandleReadError(SSL_get_error(ssl, ret), m_spInnerStream, m_pBioIn, m_pBioOut);
234 if (r >= 0)
return r;
239 if (err == SSL_ERROR_WANT_WRITE) {
241 }
else if (err == SSL_ERROR_WANT_READ) {
246 ERR_error_string_n(ERR_get_error(), errBuf,
sizeof(errBuf));
255 SSL* ssl =
static_cast<SSL*
>(m_pSsl);
258 while (written < count) {
259 int ret = SSL_write(ssl, buffer + offset + written, count - written);
261 HandleWriteError(SSL_get_error(ssl, ret), m_spInnerStream, m_pBioIn, m_pBioOut);
276 SSL_free(
static_cast<SSL*
>(m_pSsl));
282 SSL_CTX_free(
static_cast<SSL_CTX*
>(m_pSslCtx));
290 if (!m_bLeaveInnerStreamOpen && !m_spInnerStream.IsNull()) {
291 m_spInnerStream->Dispose();
293 m_spInnerStream =
nullptr;
Defines the exception thrown when an invalid argument is provided to a method.
Defines the exception thrown when a null reference is passed to a method that does not accept it.
The exception that is thrown when an I/O error occurs.
Provides a stream that uses the Transport Layer Security (TLS) protocol to secure network communicati...
ArgumentException(const String &sMessage)
Initializes a new instance of the ArgumentException class with a specified error message.
ArgumentNullException(const String &sMessage)
Initializes a new instance of the ArgumentNullException class with a specified error message.
The exception that is thrown when an I/O error occurs.
Provides a generic view of a sequence of bytes.
virtual int Read(char *buffer, int offset, int count)=0
Reads a sequence of bytes from the current stream and advances the position within the stream by the ...
bool CanWrite() const override
Gets a value indicating whether the current stream supports writing.
void Flush() override
Flushes data written to the stream to the underlying transport.
void AuthenticateAsServer(const SmartPointer<::DotNetDupe::System::Security::Cryptography::X509Certificates::X509Certificate2 > &certificate)
Called by servers to authenticate the server and optionally the client in a client-server connection.
long GetLength() const override
Gets the length of the data in the stream (unsupported for TLS stream).
long GetPosition() const override
Gets the position within the current stream (unsupported for TLS stream).
void SetPosition(long value) override
Sets the position within the current stream (unsupported for TLS stream).
void AuthenticateAsClient(const String &targetHost)
Called by clients to authenticate the server and optionally the client in a client-server connection.
void SetLength(long value) override
Sets the length of this stream (unsupported for TLS stream).
int Read(char *buffer, int offset, int count) override
Reads data from this stream into the specified byte buffer.
SslStream(const SmartPointer< IO::Stream > &innerStream)
Initializes a new instance of the SslStream class using the specified inner stream.
~SslStream() override
Releases all unmanaged resources and closes OpenSSL structures.
bool CanSeek() const override
Gets a value indicating whether the current stream supports seeking (always false for TLS).
void Write(const char *buffer, int offset, int count) override
Encrypts and writes the specified number of bytes to the underlying stream.
long Seek(long offset, int origin) override
Sets the current position of this stream to the given value (unsupported).
bool CanRead() const override
Gets a value indicating whether the current stream supports reading.
void Dispose() override
Disposes TLS context, BIO buffers, and optionally inner transport.
A unified smart pointer that supports both unique and shared ownership semantics.
bool IsNull() const noexcept
Checks if the SmartPointer is null.
T * Get() const noexcept
Gets the raw pointer.
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
String()
Initializes a new instance of the String class to an empty string.
const char * GetRawString() const
static int HandleReadError(int err, const SmartPointer< IO::Stream > &spStream, void *pBioIn, void *pBioOut)
static void FlushBioOutbound(void *pBioOut, const SmartPointer< IO::Stream > &spInnerStream)
static std::once_flag s_sslInitOnce
static void PumpNetworkToBio(IO::Stream *pStream, void *pBioIn, const char *pErrorContext)
static void HandleWriteError(int err, const SmartPointer< IO::Stream > &spStream, void *pBioIn, void *pBioOut)
static void HandleHandshakeError(int err, void *pBioIn, void *pBioOut, const SmartPointer< IO::Stream > &spStream)
static SSL * CreateAndBindSsl(SSL_CTX *ctx, void *&pBioIn, void *&pBioOut)