Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Implement client certificate authentication #351

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a2c4e3a
Task.Run -> ThreadPool.QueueUserWorkItem
benaadams Nov 1, 2015
c0f8276
Move logging to new style
benaadams Nov 3, 2015
fb74658
Implement client certificate authentication
tmds Nov 10, 2015
344c821
Remove sync block->task
benaadams Nov 10, 2015
48b7b79
Pass around ITlsConnectionFeature instead of X509Certificate2, code c…
tmds Nov 11, 2015
26ff03d
SslProtocols.Defaults not available on all platforms, use Tls (like d…
tmds Nov 11, 2015
449b303
Add HttpsConnectionFilterOptions
tmds Nov 11, 2015
0500043
.Result -> .GetAwaiter().GetResult()
benaadams Nov 11, 2015
ade0dba
Slow date wait for CI
benaadams Nov 11, 2015
e5ff33e
Remove UvAsyncHandle.DangerousClose
halter73 Nov 10, 2015
86e1924
Remove System beta tag in project.json for coreclr packages.
Nov 12, 2015
358e94f
Rewrite certificate validation callback, add tests where client does …
tmds Nov 12, 2015
9e62d65
Add ClientCertificateValidationCallback option, add test to see the c…
tmds Nov 12, 2015
cee178f
Code cleanup
tmds Nov 12, 2015
fdfd7dc
Merge branch 'benaadams/Task.Run-QueueUserWorkItem' into dev
halter73 Nov 12, 2015
0ead246
Merge branch 'benaadams/reduce-log-boxing' into dev
halter73 Nov 13, 2015
9958427
Merge branch 'benaadams/result-getawaiter' into dev
halter73 Nov 13, 2015
983997a
Merge branch 'nickcraver/cancellation' into dev
halter73 Nov 13, 2015
8ad7b71
Merge branch 'benaadams/slower-ci-date' into dev
halter73 Nov 13, 2015
6b99536
Merge branch 'benaadams/odd-sync' into dev
halter73 Nov 13, 2015
59cdd60
Don't pre-complete too many writes
halter73 Nov 10, 2015
fc346f7
Avoid some closure allocations in SocketOutput
halter73 Nov 10, 2015
52fe469
Update project.json
muratg Nov 13, 2015
dd1ffa5
Skip some tests on Mono to prevent CI hangs.
Nov 11, 2015
2ac5e4c
Remove unneeded dependency System.Net.NameResolution.
Nov 13, 2015
a3a49d2
Reset Frame.Scheme on each request (#366).
Nov 13, 2015
5c52b8a
Add IConnectionFilter.PrepareRequest and use it to set the ITlsConnec…
tmds Nov 14, 2015
bed31a3
Implement client certificate authentication
tmds Nov 10, 2015
8e56ef9
Pass around ITlsConnectionFeature instead of X509Certificate2, code c…
tmds Nov 11, 2015
f6ca379
SslProtocols.Defaults not available on all platforms, use Tls (like d…
tmds Nov 11, 2015
6e5ea33
Add HttpsConnectionFilterOptions
tmds Nov 11, 2015
35ef29f
Rewrite certificate validation callback, add tests where client does …
tmds Nov 12, 2015
ea039ac
Add ClientCertificateValidationCallback option, add test to see the c…
tmds Nov 12, 2015
d489e11
Code cleanup
tmds Nov 12, 2015
1940c3a
Add IConnectionFilter.PrepareRequest and use it to set the ITlsConnec…
tmds Nov 14, 2015
4a940bd
Merge with dev, check client certificate on CoreCLR
tmds Nov 14, 2015
060c1e8
Merge
tmds Nov 14, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
language: csharp
sudo: false
sudo: required
dist: trusty
addons:
apt:
packages:
- libunwind8
install:
- curl -sSL https://github.com/libuv/libuv/archive/v1.4.2.tar.gz | tar zxfv - -C /tmp && cd /tmp/libuv-1.4.2/
- sh autogen.sh
Expand All @@ -9,4 +14,5 @@ install:
- export LD_LIBRARY_PATH="$HOME/libuvinstall/lib"
- cd $OLDPWD
script:
- ./build.sh --quiet verify
- export KOREBUILD_TEST_DNXCORE=1
- ./build.sh --quiet verify
2 changes: 1 addition & 1 deletion samples/SampleApp/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"dnxcore50": {
"dependencies": {
"System.Console": "4.0.0-beta-*"
"System.Console": "4.0.0-*"
}
}
},
Expand Down
12 changes: 12 additions & 0 deletions src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNet.Server.Kestrel.Https
{
public enum ClientCertificateMode
{
NoCertificate,
AllowCertificate,
RequireCertificate
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace Microsoft.AspNet.Server.Kestrel.Https
{
public delegate bool ClientCertificateValidationCallback(
X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Https
public static class HttpsApplicationBuilderExtensions
{
public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert)
{
return app.UseKestrelHttps(new HttpsConnectionFilterOptions { ServerCertificate = cert});
}

public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, HttpsConnectionFilterOptions options)
{
var serverInfo = app.ServerFeatures.Get<IKestrelServerInformation>();

Expand All @@ -21,7 +26,7 @@ public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app,

var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter();

serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, prevFilter);
serverInfo.ConnectionFilter = new HttpsConnectionFilter(options, prevFilter);

return app;
}
Expand Down
80 changes: 73 additions & 7 deletions src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,37 @@

using System;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Internal;
using Microsoft.AspNet.Server.Kestrel.Filter;

namespace Microsoft.AspNet.Server.Kestrel.Https
{
public class HttpsConnectionFilter : IConnectionFilter
{
private readonly X509Certificate2 _cert;
private readonly X509Certificate2 _serverCert;
private readonly ClientCertificateMode _clientCertMode;
private readonly ClientCertificateValidationCallback _clientValidationCallback;
private readonly IConnectionFilter _previous;
private X509Certificate2 _clientCert;

public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous)
public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFilter previous)
{
if (cert == null)
if (options.ServerCertificate == null)
{
throw new ArgumentNullException(nameof(cert));
throw new ArgumentNullException(nameof(options.ServerCertificate));
}
if (previous == null)
{
throw new ArgumentNullException(nameof(previous));
}

_cert = cert;
_serverCert = options.ServerCertificate;
_clientCertMode = options.ClientCertificateMode;
_clientValidationCallback = options.ClientCertificateValidation;
_previous = previous;
}

Expand All @@ -35,10 +43,68 @@ public async Task OnConnection(ConnectionFilterContext context)

if (string.Equals(context.Address.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
var sslStream = new SslStream(context.Connection);
await sslStream.AuthenticateAsServerAsync(_cert);
SslStream sslStream;
if (_clientCertMode == ClientCertificateMode.NoCertificate)
{
sslStream = new SslStream(context.Connection);
await sslStream.AuthenticateAsServerAsync(_serverCert);
}
else
{
sslStream = new SslStream(context.Connection, leaveInnerStreamOpen: false,
userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
{
if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable))
{
return _clientCertMode != ClientCertificateMode.RequireCertificate;
}


if (_clientValidationCallback == null)
{
if (sslPolicyErrors != SslPolicyErrors.None)
{
return false;
}
}
#if DOTNET5_4
// conversion X509Certificate to X509Certificate2 not supported
// https://github.com/dotnet/corefx/issues/4510
X509Certificate2 certificate2 = null;
return false;
#else
X509Certificate2 certificate2 = certificate as X509Certificate2 ??
new X509Certificate2(certificate);

#endif
if (_clientValidationCallback != null)
{
if (!_clientValidationCallback(certificate2, chain, sslPolicyErrors))
{
return false;
}
}

_clientCert = certificate2;
return true;
});
await sslStream.AuthenticateAsServerAsync(_serverCert, clientCertificateRequired: true,
enabledSslProtocols: SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls,
checkCertificateRevocation: false);
}
context.Connection = sslStream;
}
}

public void PrepareRequest(IFeatureCollection features)
{
_previous.PrepareRequest(features);

if (_clientCert != null)
{
features.Set<ITlsConnectionFeature>(
new TlsConnectionFeature { ClientCertificate = _clientCert });
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Security.Cryptography.X509Certificates;

namespace Microsoft.AspNet.Server.Kestrel.Https
{
public class HttpsConnectionFilterOptions
{
public HttpsConnectionFilterOptions()
{
ClientCertificateMode = ClientCertificateMode.NoCertificate;
}

public X509Certificate2 ServerCertificate { get; set; }
public ClientCertificateMode ClientCertificateMode { get; set; }
public ClientCertificateValidationCallback ClientCertificateValidation { get; set; }
}
}
4 changes: 2 additions & 2 deletions src/Microsoft.AspNet.Server.Kestrel.Https/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"net451": { },
"dotnet5.4": {
"dependencies": {
"System.Net.Security": "4.0.0-beta-*"
"System.Net.Security": "4.0.0-*"
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.IO;
using Microsoft.AspNet.Http.Features;

namespace Microsoft.AspNet.Server.Kestrel.Filter
{
public class ConnectionFilterContext
{
public ServerAddress Address { get; set; }
public Stream Connection { get; set; }
public Stream Connection { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;

namespace Microsoft.AspNet.Server.Kestrel.Filter
{
public interface IConnectionFilter
{
Task OnConnection(ConnectionFilterContext context);
void PrepareRequest(IFeatureCollection features);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Server.Kestrel.Infrastructure;

namespace Microsoft.AspNet.Server.Kestrel.Filter
Expand All @@ -12,5 +13,8 @@ public Task OnConnection(ConnectionFilterContext context)
{
return TaskUtilities.CompletedTask;
}

public void PrepareRequest(IFeatureCollection features)
{}
}
}
2 changes: 1 addition & 1 deletion src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ private void OnRead(UvStreamHandle handle, int status)

private Frame CreateFrame()
{
return new Frame(this, _remoteEndPoint, _localEndPoint);
return new Frame(this, _remoteEndPoint, _localEndPoint, ConnectionFilter);
}

void IConnectionControl.Pause()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public partial class Frame : IFeatureCollection,
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
// See also: tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/FrameFeatureCollection.cs

private string _scheme;
private string _pathBase;
private int _featureRevision;

Expand Down Expand Up @@ -90,12 +89,12 @@ string IHttpRequestFeature.Scheme
{
get
{
return _scheme ?? "http";
return Scheme ?? "http";
}

set
{
_scheme = value;
Scheme = value;
}
}

Expand Down Expand Up @@ -277,7 +276,7 @@ void IHttpResponseFeature.OnCompleted(Func<object, Task> callback, object state)
OnCompleted(callback, state);
}

Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
async Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
{
StatusCode = 101;
ReasonPhrase = "Switching Protocols";
Expand All @@ -290,8 +289,10 @@ Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
ResponseHeaders["Upgrade"] = values;
}
}
ProduceStartAndFireOnStarting(immediate: true).GetAwaiter().GetResult();
return Task.FromResult(DuplexStream);

await ProduceStartAndFireOnStarting(immediate: true);

return DuplexStream;
}

IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
Expand Down
12 changes: 10 additions & 2 deletions src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Server.Kestrel.Filter;
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
Expand Down Expand Up @@ -49,24 +50,28 @@ public partial class Frame : FrameContext, IFrameControl

private readonly IPEndPoint _localEndPoint;
private readonly IPEndPoint _remoteEndPoint;
private readonly IConnectionFilter _connectionFilter;

public Frame(ConnectionContext context)
: this(context, remoteEndPoint: null, localEndPoint: null)
: this(context, remoteEndPoint: null, localEndPoint: null, connectionFilter: null)
{
}

public Frame(ConnectionContext context,
IPEndPoint remoteEndPoint,
IPEndPoint localEndPoint)
IPEndPoint localEndPoint,
IConnectionFilter connectionFilter)
: base(context)
{
_remoteEndPoint = remoteEndPoint;
_localEndPoint = localEndPoint;
_connectionFilter = connectionFilter;

FrameControl = this;
Reset();
}

public string Scheme { get; set; }
public string Method { get; set; }
public string RequestUri { get; set; }
public string Path { get; set; }
Expand Down Expand Up @@ -102,6 +107,7 @@ public void Reset()
ResetResponseHeaders();
ResetFeatureCollection();

Scheme = null;
Method = null;
RequestUri = null;
Path = null;
Expand Down Expand Up @@ -131,6 +137,8 @@ public void Reset()
{
httpConnectionFeature.IsLocal = false;
}

_connectionFilter?.PrepareRequest(this);
}

public void ResetResponseHeaders()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public override void SetLength(long value)

public override int Read(byte[] buffer, int offset, int count)
{
return ReadAsync(buffer, offset, count).Result;
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
}

#if NET451
Expand Down
Loading