-
Notifications
You must be signed in to change notification settings - Fork 982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exposes SSL Stream and adds more TLS settings #132
Merged
+159
−51
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
namespace DotNetty.Handlers.Tls | ||
{ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Security.Authentication; | ||
using System.Security.Cryptography.X509Certificates; | ||
|
||
public sealed class ClientTlsSettings : TlsSettings | ||
{ | ||
IReadOnlyCollection<X509Certificate2> certificates; | ||
|
||
public ClientTlsSettings(string targetHost) | ||
: this(targetHost, new List<X509Certificate>()) | ||
{ | ||
} | ||
|
||
public ClientTlsSettings(string targetHost, List<X509Certificate> certificates) | ||
: this(false, certificates, targetHost) | ||
{ | ||
} | ||
|
||
public ClientTlsSettings(bool checkCertificateRevocation, List<X509Certificate> certificates, string targetHost) | ||
: this(SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, checkCertificateRevocation, certificates, targetHost) | ||
{ | ||
} | ||
|
||
public ClientTlsSettings(SslProtocols enabledProtocols, bool checkCertificateRevocation, List<X509Certificate> certificates, string targetHost) | ||
:base(enabledProtocols, checkCertificateRevocation) | ||
{ | ||
this.X509CertificateCollection = new X509CertificateCollection(certificates.ToArray()); | ||
this.TargetHost = targetHost; | ||
this.Certificates = certificates.AsReadOnly(); | ||
} | ||
|
||
internal X509CertificateCollection X509CertificateCollection { get; set; } | ||
|
||
public IReadOnlyCollection<X509Certificate> Certificates { get; } | ||
|
||
public string TargetHost { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
namespace DotNetty.Handlers.Tls | ||
{ | ||
using System.Security.Authentication; | ||
using System.Security.Cryptography.X509Certificates; | ||
|
||
public sealed class ServerTlsSettings : TlsSettings | ||
{ | ||
public ServerTlsSettings(X509Certificate certificate) | ||
: this(false, certificate) | ||
{ | ||
} | ||
|
||
public ServerTlsSettings(bool checkCertificateRevocation, X509Certificate certificate) | ||
: this(SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, checkCertificateRevocation, certificate) | ||
{ | ||
} | ||
|
||
public ServerTlsSettings(SslProtocols enabledProtocols, bool checkCertificateRevocation, X509Certificate certificate) | ||
: base(enabledProtocols, checkCertificateRevocation) | ||
{ | ||
this.Certificate = certificate; | ||
} | ||
|
||
public X509Certificate Certificate { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,6 @@ namespace DotNetty.Handlers.Tls | |
using System.IO; | ||
using System.Net.Security; | ||
using System.Runtime.ExceptionServices; | ||
using System.Security.Authentication; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
@@ -21,48 +20,47 @@ namespace DotNetty.Handlers.Tls | |
|
||
public sealed class TlsHandler : ByteToMessageDecoder | ||
{ | ||
readonly TlsSettings settings; | ||
const int FallbackReadBufferSize = 256; | ||
const int UnencryptedWriteBatchSize = 14 * 1024; | ||
|
||
static readonly Exception ChannelClosedException = new IOException("Channel is closed"); | ||
static readonly Action<Task, object> HandshakeCompletionCallback = new Action<Task, object>(HandleHandshakeCompleted); | ||
|
||
readonly SslStream sslStream; | ||
readonly MediationStream mediationStream; | ||
readonly TaskCompletionSource closeFuture; | ||
|
||
TlsHandlerState state; | ||
int packetLength; | ||
readonly MediationStream mediationStream; | ||
volatile IChannelHandlerContext capturedContext; | ||
BatchingPendingWriteQueue pendingUnencryptedWrites; | ||
Task lastContextWriteTask; | ||
readonly TaskCompletionSource closeFuture; | ||
readonly bool isServer; | ||
readonly X509Certificate2 certificate; | ||
readonly string targetHost; | ||
bool firedChannelRead; | ||
IByteBuffer pendingSslStreamReadBuffer; | ||
Task<int> pendingSslStreamReadFuture; | ||
|
||
TlsHandler(bool isServer, X509Certificate2 certificate, string targetHost, RemoteCertificateValidationCallback certificateValidationCallback) | ||
public TlsHandler(TlsSettings settings) | ||
: this(stream => new SslStream(stream, true), settings) | ||
{ | ||
Contract.Requires(!isServer || certificate != null); | ||
Contract.Requires(isServer || !string.IsNullOrEmpty(targetHost)); | ||
} | ||
|
||
this.closeFuture = new TaskCompletionSource(); | ||
public TlsHandler(Func<Stream, SslStream> sslStreamFactory, TlsSettings settings) | ||
{ | ||
Contract.Requires(sslStreamFactory != null); | ||
Contract.Requires(settings != null); | ||
|
||
this.isServer = isServer; | ||
this.certificate = certificate; | ||
this.targetHost = targetHost; | ||
this.settings = settings; | ||
this.closeFuture = new TaskCompletionSource(); | ||
this.mediationStream = new MediationStream(this); | ||
this.sslStream = new SslStream(this.mediationStream, true, certificateValidationCallback); | ||
this.sslStream = sslStreamFactory(this.mediationStream); | ||
} | ||
|
||
public static TlsHandler Client(string targetHost) => new TlsHandler(false, null, targetHost, null); | ||
|
||
public static TlsHandler Client(string targetHost, X509Certificate2 certificate) => new TlsHandler(false, certificate, targetHost, null); | ||
public static TlsHandler Client(string targetHost) => new TlsHandler(new ClientTlsSettings(targetHost)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we leave these for common cases and internalize sslStreamFactory in such a case? |
||
|
||
public static TlsHandler Client(string targetHost, X509Certificate2 certificate, RemoteCertificateValidationCallback certificateValidationCallback) => new TlsHandler(false, certificate, targetHost, certificateValidationCallback); | ||
|
||
public static TlsHandler Server(X509Certificate2 certificate) => new TlsHandler(true, certificate, null, null); | ||
public static TlsHandler Client(string targetHost, X509Certificate clientCertificate) => new TlsHandler(new ClientTlsSettings(targetHost, new List<X509Certificate>{ clientCertificate })); | ||
public static TlsHandler Server(X509Certificate certificate) => new TlsHandler(new ServerTlsSettings(certificate)); | ||
|
||
public X509Certificate LocalCertificate => this.sslStream.LocalCertificate; | ||
|
||
|
@@ -74,7 +72,7 @@ public override void ChannelActive(IChannelHandlerContext context) | |
{ | ||
base.ChannelActive(context); | ||
|
||
if (!this.isServer) | ||
if (this.settings is ServerTlsSettings) | ||
{ | ||
this.EnsureAuthenticated(); | ||
} | ||
|
@@ -161,7 +159,7 @@ public override void HandlerAdded(IChannelHandlerContext context) | |
base.HandlerAdded(context); | ||
this.capturedContext = context; | ||
this.pendingUnencryptedWrites = new BatchingPendingWriteQueue(context, UnencryptedWriteBatchSize); | ||
if (context.Channel.Active && !this.isServer) | ||
if (context.Channel.Active && this.settings is ClientTlsSettings) | ||
{ | ||
// todo: support delayed initialization on an existing/active channel if in client mode | ||
this.EnsureAuthenticated(); | ||
|
@@ -217,23 +215,23 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input | |
break; | ||
} | ||
|
||
int packetLength = TlsUtils.GetEncryptedPacketLength(input, offset); | ||
if (packetLength == -1) | ||
int encryptedPacketLength = TlsUtils.GetEncryptedPacketLength(input, offset); | ||
if (encryptedPacketLength == -1) | ||
{ | ||
nonSslRecord = true; | ||
break; | ||
} | ||
|
||
Contract.Assert(packetLength > 0); | ||
Contract.Assert(encryptedPacketLength > 0); | ||
|
||
if (packetLength > readableBytes) | ||
if (encryptedPacketLength > readableBytes) | ||
{ | ||
// wait until the whole packet can be read | ||
this.packetLength = packetLength; | ||
this.packetLength = encryptedPacketLength; | ||
break; | ||
} | ||
|
||
int newTotalLength = totalLength + packetLength; | ||
int newTotalLength = totalLength + encryptedPacketLength; | ||
if (newTotalLength > TlsUtils.MAX_ENCRYPTED_PACKET_LENGTH) | ||
{ | ||
// Don't read too much. | ||
|
@@ -245,8 +243,8 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input | |
|
||
// We have a whole packet. | ||
// Increment the offset to handle the next packet. | ||
packetLengths.Add(packetLength); | ||
offset += packetLength; | ||
packetLengths.Add(encryptedPacketLength); | ||
offset += encryptedPacketLength; | ||
totalLength = newTotalLength; | ||
} | ||
|
||
|
@@ -482,19 +480,16 @@ bool EnsureAuthenticated() | |
if (!oldState.HasAny(TlsHandlerState.AuthenticationStarted)) | ||
{ | ||
this.state = oldState | TlsHandlerState.Authenticating; | ||
if (this.isServer) | ||
var serverSettings = settings as ServerTlsSettings; | ||
if (serverSettings != null) | ||
{ | ||
this.sslStream.AuthenticateAsServerAsync(this.certificate, false, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, false) // todo: change to begin/end | ||
this.sslStream.AuthenticateAsServerAsync(serverSettings.Certificate, false, serverSettings.EnabledProtocols, serverSettings.CheckCertificateRevocation) | ||
.ContinueWith(HandshakeCompletionCallback, this, TaskContinuationOptions.ExecuteSynchronously); | ||
} | ||
else | ||
{ | ||
var certificateCollection = new X509Certificate2Collection(); | ||
if (this.certificate != null) | ||
{ | ||
certificateCollection.Add(this.certificate); | ||
} | ||
this.sslStream.AuthenticateAsClientAsync(this.targetHost, certificateCollection, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, false) // todo: change to begin/end | ||
var clientSettings = (ClientTlsSettings)settings; | ||
this.sslStream.AuthenticateAsClientAsync(clientSettings.TargetHost, clientSettings.X509CertificateCollection, clientSettings.EnabledProtocols, clientSettings.CheckCertificateRevocation) | ||
.ContinueWith(HandshakeCompletionCallback, this, TaskContinuationOptions.ExecuteSynchronously); | ||
} | ||
return false; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
namespace DotNetty.Handlers.Tls | ||
{ | ||
using System.Security.Authentication; | ||
|
||
public abstract class TlsSettings | ||
{ | ||
protected TlsSettings(SslProtocols enabledProtocols, bool checkCertificateRevocation) | ||
{ | ||
this.EnabledProtocols = enabledProtocols; | ||
this.CheckCertificateRevocation = checkCertificateRevocation; | ||
} | ||
|
||
public SslProtocols EnabledProtocols { get; } | ||
|
||
public bool CheckCertificateRevocation { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shall we remove TLS 1.0 from here by default as well already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to do a breaking change. Let's do that in version 0.4.0