parent
d0de73684d
commit
f3b0fbe207
|
|
@ -26,6 +26,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
Negotiate = 8,
|
||||
Kerberos = 16,
|
||||
}
|
||||
public enum ClientCertificateMethod
|
||||
{
|
||||
NoCertificate = 0,
|
||||
AllowCertificate = 1,
|
||||
AllowRenegotation = 2,
|
||||
}
|
||||
public enum Http503VerbosityLevel : long
|
||||
{
|
||||
Basic = (long)0,
|
||||
|
|
@ -46,6 +52,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
public HttpSysOptions() { }
|
||||
public bool AllowSynchronousIO { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public Microsoft.AspNetCore.Server.HttpSys.AuthenticationManager Authentication { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||
public Microsoft.AspNetCore.Server.HttpSys.ClientCertificateMethod ClientCertificateMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public bool EnableResponseCaching { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public Microsoft.AspNetCore.Server.HttpSys.Http503VerbosityLevel Http503Verbosity { get { throw null; } set { } }
|
||||
public int MaxAccepts { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
// 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.AspNetCore.Server.HttpSys
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the client certificate negotiation method for HTTPS connections.
|
||||
/// </summary>
|
||||
public enum ClientCertificateMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// A client certificate will not be populated on the request.
|
||||
/// </summary>
|
||||
NoCertificate = 0,
|
||||
|
||||
/// <summary>
|
||||
/// A client certificate will be populated if already present at the start of a request.
|
||||
/// </summary>
|
||||
AllowCertificate,
|
||||
|
||||
/// <summary>
|
||||
/// The TLS session can be renegotiated to request a client certificate.
|
||||
/// </summary>
|
||||
AllowRenegotation
|
||||
}
|
||||
}
|
||||
|
|
@ -316,7 +316,17 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
{
|
||||
if (IsNotInitialized(Fields.ClientCertificate))
|
||||
{
|
||||
_clientCert = Request.GetClientCertificateAsync().Result; // TODO: Sync;
|
||||
var method = _requestContext.Server.Options.ClientCertificateMethod;
|
||||
if (method == ClientCertificateMethod.AllowCertificate)
|
||||
{
|
||||
_clientCert = Request.ClientCertificate;
|
||||
}
|
||||
else if (method == ClientCertificateMethod.AllowRenegotation)
|
||||
{
|
||||
_clientCert = Request.GetClientCertificateAsync().Result; // TODO: Sync over async;
|
||||
}
|
||||
// else if (method == ClientCertificateMethod.NoCertificate) // No-op
|
||||
|
||||
SetInitialized(Fields.ClientCertificate);
|
||||
}
|
||||
return _clientCert;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
/// </summary>
|
||||
public RequestQueueMode RequestQueueMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates how client certificates should be populated. The default is to allow renegotation.
|
||||
/// This does not change the netsh 'clientcertnegotiation' binding option which will need to be enabled for
|
||||
/// ClientCertificateMethod.AllowCertificate to resolve a certificate.
|
||||
/// </summary>
|
||||
public ClientCertificateMethod ClientCertificateMethod { get; set; } = ClientCertificateMethod.AllowRenegotation;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of concurrent accepts.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.HttpSys.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.HttpSys
|
||||
{
|
||||
|
|
@ -323,6 +326,30 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
KeyExchangeStrength = (int)handshake.KeyExchangeStrength;
|
||||
}
|
||||
|
||||
public X509Certificate2 ClientCertificate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_clientCert == null && SslStatus == SslStatus.ClientCert)
|
||||
{
|
||||
try
|
||||
{
|
||||
_clientCert = _nativeRequestContext.GetClientCertificate();
|
||||
}
|
||||
catch (CryptographicException ce)
|
||||
{
|
||||
RequestContext.Logger.LogDebug(ce, "An error occurred reading the client certificate.");
|
||||
}
|
||||
catch (SecurityException se)
|
||||
{
|
||||
RequestContext.Logger.LogDebug(se, "An error occurred reading the client certificate.");
|
||||
}
|
||||
}
|
||||
|
||||
return _clientCert;
|
||||
}
|
||||
}
|
||||
|
||||
// Populates the client certificate. The result may be null if there is no client cert.
|
||||
// TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
|
||||
// enable this, but it's unclear what Http.Sys would do.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Collections.ObjectModel;
|
|||
using System.Diagnostics;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
|
|
@ -518,5 +519,49 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
|
||||
return new ReadOnlyDictionary<int, ReadOnlyMemory<byte>>(info);
|
||||
}
|
||||
|
||||
internal X509Certificate2 GetClientCertificate()
|
||||
{
|
||||
if (_permanentlyPinned)
|
||||
{
|
||||
return GetClientCertificate((IntPtr)_nativeRequest, (HttpApiTypes.HTTP_REQUEST_V2*)_nativeRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (byte* pMemoryBlob = _backingBuffer)
|
||||
{
|
||||
var request = (HttpApiTypes.HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment);
|
||||
return GetClientCertificate(_originalBufferAddress, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Throws CryptographicException
|
||||
private X509Certificate2 GetClientCertificate(IntPtr baseAddress, HttpApiTypes.HTTP_REQUEST_V2* nativeRequest)
|
||||
{
|
||||
var request = nativeRequest->Request;
|
||||
long fixup = (byte*)nativeRequest - (byte*)baseAddress;
|
||||
if (request.pSslInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var sslInfo = (HttpApiTypes.HTTP_SSL_INFO*)((byte*)request.pSslInfo + fixup);
|
||||
if (sslInfo->SslClientCertNegotiated == 0 || sslInfo->pClientCertInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var clientCertInfo = (HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO*)((byte*)sslInfo->pClientCertInfo + fixup);
|
||||
if (clientCertInfo->pCertEncoded == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var clientCert = clientCertInfo->pCertEncoded + fixup;
|
||||
byte[] certEncoded = new byte[clientCertInfo->CertEncodedSize];
|
||||
Marshal.Copy((IntPtr)clientCert, certEncoded, 0, certEncoded.Length);
|
||||
return new X509Certificate2(certEncoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue