#443 Remove custom certificate validators.

This commit is contained in:
Chris R 2015-09-10 08:41:08 -07:00
parent ebcad24307
commit 2982d743d8
20 changed files with 6 additions and 895 deletions

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.Internal;
@ -63,7 +62,7 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
Options.MetadataAddress += ".well-known/openid-configuration";
}
var httpClient = new HttpClient(ResolveHttpMessageHandler(Options));
var httpClient = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler());
httpClient.Timeout = Options.BackchannelTimeout;
httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
@ -80,28 +79,5 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
{
return new JwtBearerAuthenticationHandler();
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
private static HttpMessageHandler ResolveHttpMessageHandler(JwtBearerAuthenticationOptions options)
{
HttpMessageHandler handler = options.BackchannelHttpHandler ??
#if DNX451
new WebRequestHandler();
// If they provided a validator, apply it or fail.
if (options.BackchannelCertificateValidator != null)
{
// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (webRequestHandler == null)
{
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
}
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
}
#else
new HttpClientHandler();
#endif
return handler;
}
}
}

View File

@ -66,18 +66,6 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
/// </summary>
public TimeSpan BackchannelTimeout { get; set; } = TimeSpan.FromMinutes(1);
#if DNX451
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// when retrieving metadata.
/// </summary>
/// <value>
/// The pinned certificate validator.
/// </value>
/// <remarks>If this property is null then the default certificate checks are performed,
/// validating the subject name and if the signing chain is a trusted party.</remarks>
public ICertificateValidator BackchannelCertificateValidator { get; set; }
#endif
/// <summary>
/// Configuration provided directly by the developer. If provided, then MetadataAddress and the Backchannel properties
/// will not be used. This information should not be updated during request processing.

View File

@ -13,7 +13,6 @@
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.Net.Http.WebRequest": "",
"System.Net.Http": ""
}
},

View File

@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
Options.StateDataFormat = new PropertiesDataFormat(dataProtector);
}
Backchannel = new HttpClient(ResolveHttpMessageHandler(Options));
Backchannel = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler());
Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET OAuth middleware");
Backchannel.Timeout = Options.BackchannelTimeout;
Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
@ -96,28 +96,5 @@ namespace Microsoft.AspNet.Authentication.OAuth
{
return new OAuthAuthenticationHandler<TOptions>(Backchannel);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
private static HttpMessageHandler ResolveHttpMessageHandler(OAuthAuthenticationOptions options)
{
HttpMessageHandler handler = options.BackchannelHttpHandler ??
#if DNX451
new WebRequestHandler();
// If they provided a validator, apply it or fail.
if (options.BackchannelCertificateValidator != null)
{
// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (webRequestHandler == null)
{
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
}
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
}
#else
new HttpClientHandler();
#endif
return handler;
}
}
}

View File

@ -42,19 +42,6 @@ namespace Microsoft.AspNet.Authentication.OAuth
/// </summary>
public string UserInformationEndpoint { get; set; }
#if DNX451
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// in back channel communications belong to the auth provider.
/// </summary>
/// <value>
/// The pinned certificate validator.
/// </value>
/// <remarks>If this property is null then the default certificate checks are performed,
/// validating the subject name and if the signing chain is a trusted party.</remarks>
public ICertificateValidator BackchannelCertificateValidator { get; set; }
#endif
/// <summary>
/// Get or sets the text that the user can display on a sign in user interface.
/// </summary>

View File

@ -13,7 +13,6 @@
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.Net.Http.WebRequest": "",
"System.Net.Http": ""
}
},

View File

@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
Options.TokenValidationParameters.ValidAudience = Options.ClientId;
}
Backchannel = new HttpClient(ResolveHttpMessageHandler(Options));
Backchannel = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler());
Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET OpenIdConnect middleware");
Backchannel.Timeout = Options.BackchannelTimeout;
Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
@ -149,29 +149,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
return new OpenIdConnectAuthenticationHandler(Backchannel);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
private static HttpMessageHandler ResolveHttpMessageHandler(OpenIdConnectAuthenticationOptions options)
{
var handler = options.BackchannelHttpHandler ??
#if DNX451
new WebRequestHandler();
// If they provided a validator, apply it or fail.
if (options.BackchannelCertificateValidator != null)
{
// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (webRequestHandler == null)
{
throw new InvalidOperationException(Resources.OIDCH_0102_Exception_ValidatorHandlerMismatch);
}
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
}
#else
new HttpClientHandler();
#endif
return handler;
}
private class StringSerializer : IDataSerializer<string>
{
public string Deserialize(byte[] data)

View File

@ -7,7 +7,6 @@ using System.Diagnostics.CodeAnalysis;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Security.Claims;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.Framework.Caching.Distributed;
@ -66,18 +65,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
/// </summary>
public string Authority { get; set; }
#if DNX451
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// when retrieving metadata.
/// </summary>
/// <value>
/// The pinned certificate validator.
/// </value>
/// <remarks>If this property is null then the default certificate checks are performed,
/// validating the subject name and if the signing chain is a trusted party.</remarks>
public ICertificateValidator BackchannelCertificateValidator { get; set; }
#endif
/// <summary>
/// The HttpMessageHandler used to retrieve metadata.
/// This cannot be set at the same time as BackchannelCertificateValidator unless the value

View File

@ -14,8 +14,7 @@
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.Net.Http": "",
"System.Net.Http.WebRequest": ""
"System.Net.Http": ""
}
},
"dnxcore50": {

View File

@ -74,7 +74,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "SignInScheme"));
}
_httpClient = new HttpClient(ResolveHttpMessageHandler(Options));
_httpClient = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler());
_httpClient.Timeout = Options.BackchannelTimeout;
_httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
_httpClient.DefaultRequestHeaders.Accept.ParseAdd("*/*");
@ -90,28 +90,5 @@ namespace Microsoft.AspNet.Authentication.Twitter
{
return new TwitterAuthenticationHandler(_httpClient);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
private static HttpMessageHandler ResolveHttpMessageHandler(TwitterAuthenticationOptions options)
{
var handler = options.BackchannelHttpHandler ??
#if DNX451
new WebRequestHandler();
// If they provided a validator, apply it or fail.
if (options.BackchannelCertificateValidator != null)
{
// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (webRequestHandler == null)
{
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
}
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
}
#else
new HttpClientHandler();
#endif
return handler;
}
}
}

View File

@ -3,7 +3,6 @@
using System;
using System.Net.Http;
using System.Security.Claims;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Authentication.Twitter
@ -22,16 +21,6 @@ namespace Microsoft.AspNet.Authentication.Twitter
Caption = AuthenticationScheme;
CallbackPath = new PathString("/signin-twitter");
BackchannelTimeout = TimeSpan.FromSeconds(60);
#if DNX451
// Twitter lists its valid Subject Key Identifiers at https://dev.twitter.com/docs/security/using-ssl
BackchannelCertificateValidator = new CertificateSubjectKeyIdentifierValidator(
new[]
{
"A5EF0B11CEC04103A34A659048B21CE0572D7D47", // VeriSign Class 3 Secure Server CA - G2
"0D445C165344C1827E1D20AB25F40163D8BE79A5", // VeriSign Class 3 Secure Server CA - G3
"5F60CF619055DF8443148A602AB2F57AF44318EF", // Symantec Class 3 Secure Server CA - G4
});
#endif
}
/// <summary>
@ -53,18 +42,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
/// The back channel timeout.
/// </value>
public TimeSpan BackchannelTimeout { get; set; }
#if DNX451
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// in back channel communications belong to Twitter.
/// </summary>
/// <value>
/// The pinned certificate validator.
/// </value>
/// <remarks>If this property is null then the default certificate checks are performed,
/// validating the subject name and if the signing chain is a trusted party.</remarks>
public ICertificateValidator BackchannelCertificateValidator { get; set; }
#endif
/// <summary>
/// The HttpMessageHandler used to communicate with Twitter.
/// This cannot be set at the same time as BackchannelCertificateValidator unless the value

View File

@ -12,7 +12,6 @@
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.Net.Http.WebRequest": "",
"System.Net.Http": ""
}
},

View File

@ -1,81 +0,0 @@
// 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.
#if DNX451
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Authentication
{
/// <summary>
/// Provides pinned certificate validation based on the subject key identifier of the certificate.
/// </summary>
public class CertificateSubjectKeyIdentifierValidator : ICertificateValidator
{
private readonly HashSet<string> _validSubjectKeyIdentifiers;
/// <summary>
/// Initializes a new instance of the <see cref="CertificateSubjectKeyIdentifierValidator"/> class.
/// </summary>
/// <param name="validSubjectKeyIdentifiers">A set of subject key identifiers which are valid for an HTTPS request.</param>
public CertificateSubjectKeyIdentifierValidator([NotNull] IEnumerable<string> validSubjectKeyIdentifiers)
{
_validSubjectKeyIdentifiers = new HashSet<string>(validSubjectKeyIdentifiers, StringComparer.OrdinalIgnoreCase);
if (_validSubjectKeyIdentifiers.Count == 0)
{
throw new ArgumentOutOfRangeException(nameof(validSubjectKeyIdentifiers));
}
}
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication.
/// </summary>
/// <param name="sender">An object that contains state information for this validation.</param>
/// <param name="certificate">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote certificate.</param>
/// <returns>A Boolean value that determines whether the specified certificate is accepted for authentication.</returns>
public bool Validate(object sender, X509Certificate certificate, [NotNull] X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors != SslPolicyErrors.None)
{
return false;
}
if (chain.ChainElements.Count < 2)
{
// Self signed.
return false;
}
foreach (var chainElement in chain.ChainElements)
{
string subjectKeyIdentifier = GetSubjectKeyIdentifier(chainElement.Certificate);
if (string.IsNullOrEmpty(subjectKeyIdentifier))
{
continue;
}
if (_validSubjectKeyIdentifiers.Contains(subjectKeyIdentifier))
{
return true;
}
}
return false;
}
private static string GetSubjectKeyIdentifier(X509Certificate2 certificate)
{
const string SubjectKeyIdentifierOid = "2.5.29.14";
var extension = certificate.Extensions[SubjectKeyIdentifierOid] as X509SubjectKeyIdentifierExtension;
return extension == null ? null : extension.SubjectKeyIdentifier;
}
}
}
#endif

View File

@ -1,128 +0,0 @@
// 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.
#if DNX451
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Framework.Internal;
using Microsoft.Win32;
namespace Microsoft.AspNet.Authentication
{
/// <summary>
/// Implements a cert pinning validator passed on
/// http://datatracker.ietf.org/doc/draft-ietf-websec-key-pinning/?include_text=1
/// </summary>
public class CertificateSubjectPublicKeyInfoValidator : ICertificateValidator
{
private readonly HashSet<string> _validBase64EncodedSubjectPublicKeyInfoHashes;
private readonly SubjectPublicKeyInfoAlgorithm _algorithm;
/// <summary>
/// Initializes a new instance of the <see cref="CertificateSubjectPublicKeyInfoValidator"/> class.
/// </summary>
/// <param name="validBase64EncodedSubjectPublicKeyInfoHashes">A collection of valid base64 encoded hashes of the certificate public key information blob.</param>
/// <param name="algorithm">The algorithm used to generate the hashes.</param>
public CertificateSubjectPublicKeyInfoValidator([NotNull] IEnumerable<string> validBase64EncodedSubjectPublicKeyInfoHashes, SubjectPublicKeyInfoAlgorithm algorithm)
{
_validBase64EncodedSubjectPublicKeyInfoHashes = new HashSet<string>(validBase64EncodedSubjectPublicKeyInfoHashes);
if (_validBase64EncodedSubjectPublicKeyInfoHashes.Count == 0)
{
throw new ArgumentOutOfRangeException(nameof(validBase64EncodedSubjectPublicKeyInfoHashes));
}
if (_algorithm != SubjectPublicKeyInfoAlgorithm.Sha1 && _algorithm != SubjectPublicKeyInfoAlgorithm.Sha256)
{
throw new ArgumentOutOfRangeException(nameof(algorithm));
}
_algorithm = algorithm;
}
/// <summary>
/// Validates at least one SPKI hash is known.
/// </summary>
/// <param name="sender">An object that contains state information for this validation.</param>
/// <param name="certificate">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote certificate.</param>
/// <returns>A Boolean value that determines whether the specified certificate is accepted for authentication.</returns>
public bool Validate(object sender, X509Certificate certificate, [NotNull] X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors != SslPolicyErrors.None)
{
return false;
}
if (chain.ChainElements.Count < 2)
{
return false;
}
using (HashAlgorithm algorithm = CreateHashAlgorithm())
{
foreach (var chainElement in chain.ChainElements)
{
X509Certificate2 chainedCertificate = chainElement.Certificate;
string base64Spki = Convert.ToBase64String(algorithm.ComputeHash(ExtractSpkiBlob(chainedCertificate)));
if (_validBase64EncodedSubjectPublicKeyInfoHashes.Contains(base64Spki))
{
return true;
}
}
}
return false;
}
private static byte[] ExtractSpkiBlob(X509Certificate2 certificate)
{
// Get a native cert_context from the managed X590Certificate2 instance.
var certContext = (NativeMethods.CERT_CONTEXT)Marshal.PtrToStructure(certificate.Handle, typeof(NativeMethods.CERT_CONTEXT));
// Pull the CERT_INFO structure from the context.
var certInfo = (NativeMethods.CERT_INFO)Marshal.PtrToStructure(certContext.pCertInfo, typeof(NativeMethods.CERT_INFO));
// And finally grab the key information, public key, algorithm and parameters from it.
NativeMethods.CERT_PUBLIC_KEY_INFO publicKeyInfo = certInfo.SubjectPublicKeyInfo;
// Now start encoding to ASN1.
// First see how large the ASN1 representation is going to be.
UInt32 blobSize = 0;
var structType = new IntPtr(NativeMethods.X509_PUBLIC_KEY_INFO);
if (!NativeMethods.CryptEncodeObject(NativeMethods.X509_ASN_ENCODING, structType, ref publicKeyInfo, null, ref blobSize))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
// Allocate enough space.
var blob = new byte[blobSize];
// Finally get the ASN1 representation.
if (!NativeMethods.CryptEncodeObject(NativeMethods.X509_ASN_ENCODING, structType, ref publicKeyInfo, blob, ref blobSize))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
return blob;
}
[SuppressMessage("Microsoft.Security.Cryptography", "CA5354:SHA1CannotBeUsed", Justification = "Only used to verify cert hashes.")]
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposal.")]
private HashAlgorithm CreateHashAlgorithm()
{
return _algorithm == SubjectPublicKeyInfoAlgorithm.Sha1 ? (HashAlgorithm)new SHA1CryptoServiceProvider() : new SHA256CryptoServiceProvider();
}
}
}
#endif

View File

@ -1,74 +0,0 @@
// 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.
#if DNX451
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Authentication
{
/// <summary>
/// Provides pinned certificate validation based on the certificate thumbprint.
/// </summary>
public class CertificateThumbprintValidator : ICertificateValidator
{
private readonly HashSet<string> _validCertificateThumbprints;
/// <summary>
/// Initializes a new instance of the <see cref="CertificateThumbprintValidator"/> class.
/// </summary>
/// <param name="validThumbprints">A set of thumbprints which are valid for an HTTPS request.</param>
public CertificateThumbprintValidator([NotNull] IEnumerable<string> validThumbprints)
{
_validCertificateThumbprints = new HashSet<string>(validThumbprints, StringComparer.OrdinalIgnoreCase);
if (_validCertificateThumbprints.Count == 0)
{
throw new ArgumentOutOfRangeException(nameof(validThumbprints));
}
}
/// <summary>
/// Validates that the certificate thumbprints in the signing chain match at least one whitelisted thumbprint.
/// </summary>
/// <param name="sender">An object that contains state information for this validation.</param>
/// <param name="certificate">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote certificate.</param>
/// <returns>A Boolean value that determines whether the specified certificate is accepted for authentication.</returns>
public bool Validate(object sender, X509Certificate certificate, [NotNull] X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors != SslPolicyErrors.None)
{
return false;
}
if (chain.ChainElements.Count < 2)
{
// Self signed.
return false;
}
foreach (var chainElement in chain.ChainElements)
{
string thumbprintToCheck = chainElement.Certificate.Thumbprint;
if (thumbprintToCheck == null)
{
continue;
}
if (_validCertificateThumbprints.Contains(thumbprintToCheck))
{
return true;
}
}
return false;
}
}
}
#endif

View File

@ -1,30 +0,0 @@
// 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.
#if DNX451
using System;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace Microsoft.AspNet.Authentication
{
/// <summary>
/// Interface for providing pinned certificate validation, which checks HTTPS
/// communication against a known good list of certificates to protect against
/// compromised or rogue CAs issuing certificates for hosts without the
/// knowledge of the host owner.
/// </summary>
public interface ICertificateValidator
{
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication.
/// </summary>
/// <param name="sender">An object that contains state information for this validation.</param>
/// <param name="certificate">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote certificate.</param>
/// <returns>A Boolean value that determines whether the specified certificate is accepted for authentication.</returns>
bool Validate(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors);
}
}
#endif

View File

@ -1,123 +0,0 @@
// 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;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Shouldly;
using Xunit;
namespace Microsoft.AspNet.Authentication
{
public class CertificateSubjectKeyIdentifierValidatorTests
{
private static readonly X509Certificate2 SelfSigned = new X509Certificate2("selfSigned.cer");
private static readonly X509Certificate2 Chained = new X509Certificate2("katanatest.redmond.corp.microsoft.com.cer");
// The Katana test cert has a valid full chain
// katanatest.redmond.corp.microsoft.com -> MSIT Machine Auth CA2 -> Microsoft Internet Authority -> Baltimore CyberTrustRoot
private const string KatanaTestKeyIdentifier = "d964b2941aaf3e62761041b1f3db098edfa3270a";
private const string MicrosoftInternetAuthorityKeyIdentifier = "2a4d97955d347e9db6e633be9c27c1707e67dbc1";
[Fact]
public void ConstructorShouldNotThrowWithValidValues()
{
var instance = new CertificateSubjectKeyIdentifierValidator(new[] { string.Empty });
instance.ShouldNotBe(null);
}
[Fact]
public void ConstructorShouldThrownWhenTheValidHashEnumerableIsNull()
{
Should.Throw<ArgumentNullException>(() =>
new CertificateSubjectKeyIdentifierValidator(null));
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateChainErrors()
{
var instance = new CertificateSubjectKeyIdentifierValidator(new[] { string.Empty });
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateChainErrors);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateNameMismatch()
{
var instance = new CertificateSubjectKeyIdentifierValidator(new[] { string.Empty });
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateNameMismatch);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateNotAvailable()
{
var instance = new CertificateSubjectKeyIdentifierValidator(new[] { string.Empty });
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateNotAvailable);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenPassedASelfSignedCertificate()
{
var instance = new CertificateSubjectKeyIdentifierValidator(new[] { string.Empty });
var certificateChain = new X509Chain();
certificateChain.Build(SelfSigned);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, SelfSigned, certificateChain, SslPolicyErrors.None);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenPassedATrustedCertificateWhichDoesNotHaveAWhitelistedSubjectKeyIdentifier()
{
var instance = new CertificateSubjectKeyIdentifierValidator(new[] { string.Empty });
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnTrueWhenPassedATrustedCertificateWhichHasItsSubjectKeyIdentifierWhiteListed()
{
var instance = new CertificateSubjectKeyIdentifierValidator(
new[]
{
KatanaTestKeyIdentifier
});
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(true);
}
[Fact]
public void ValidatorShouldReturnTrueWhenPassedATrustedCertificateWhichHasAChainElementSubjectKeyIdentifierWhiteListed()
{
var instance = new CertificateSubjectKeyIdentifierValidator(
new[]
{
MicrosoftInternetAuthorityKeyIdentifier
});
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(true);
}
}
}

View File

@ -1,173 +0,0 @@
// 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;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Shouldly;
using Xunit;
namespace Microsoft.AspNet.Authentication
{
public class CertificateSubjectPublicKeyInfoValidatorTests
{
private static readonly X509Certificate2 SelfSigned = new X509Certificate2("selfSigned.cer");
private static readonly X509Certificate2 Chained = new X509Certificate2("katanatest.redmond.corp.microsoft.com.cer");
// The Katana test cert has a valid full chain
// katanatest.redmond.corp.microsoft.com -> MSIT Machine Auth CA2 -> Microsoft Internet Authority -> Baltimore CyberTrustRoot
// The following fingerprints were generated using the go program in appendix A of the Public Key Pinning Extension for HTTP
// draft-ietf-websec-key-pinning-05
private const string KatanaTestSha1Hash = "xvNsCWwxvL3qsCYChZLiwNm1D6o=";
private const string KatanaTestSha256Hash = "AhR1Y/xhxK2uD7YJ0xKUPq8tYrWm4+F7DgO2wUOqB+4=";
private const string MicrosoftInternetAuthoritySha1Hash = "Z3HnseSVDEPu5hZoj05/bBSnT/s=";
private const string MicrosoftInternetAuthoritySha256Hash = "UQTPeq/Tlg/vLt2ijtl7qlMFBFkbGG9aAWJbQMOMWFg=";
[Fact]
public void ConstructorShouldNotThrowWithValidValues()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new string[1], SubjectPublicKeyInfoAlgorithm.Sha1);
instance.ShouldNotBe(null);
}
[Fact]
public void ConstructorShouldThrownWhenTheValidHashEnumerableIsNull()
{
Should.Throw<ArgumentNullException>(() =>
new CertificateSubjectPublicKeyInfoValidator(null, SubjectPublicKeyInfoAlgorithm.Sha1));
}
[Fact]
public void ConstructorShouldThrowWhenTheHashEnumerableContainsNoHashes()
{
Should.Throw<ArgumentOutOfRangeException>(() =>
new CertificateSubjectPublicKeyInfoValidator(new string[0], SubjectPublicKeyInfoAlgorithm.Sha1));
}
[Fact]
public void ConstructorShouldThrowIfAnInvalidAlgorithmIsPassed()
{
Should.Throw<ArgumentOutOfRangeException>(() =>
new CertificateSubjectPublicKeyInfoValidator(new string[0], (SubjectPublicKeyInfoAlgorithm)2));
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateChainErrors()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new string[1], SubjectPublicKeyInfoAlgorithm.Sha1);
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateChainErrors);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateNameMismatch()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new string[1], SubjectPublicKeyInfoAlgorithm.Sha1);
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateNameMismatch);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateNotAvailable()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new string[1], SubjectPublicKeyInfoAlgorithm.Sha1);
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateNotAvailable);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenPassedASelfSignedCertificate()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new string[1], SubjectPublicKeyInfoAlgorithm.Sha1);
var certificateChain = new X509Chain();
certificateChain.Build(SelfSigned);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, SelfSigned, certificateChain, SslPolicyErrors.None);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenPassedATrustedCertificateWhichDoesNotHaveAWhitelistedSha1Spki()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new string[1], SubjectPublicKeyInfoAlgorithm.Sha1);
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnTrueWhenPassedATrustedCertificateWhichHasItsSha1SpkiWhiteListed()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new[] { KatanaTestSha1Hash }, SubjectPublicKeyInfoAlgorithm.Sha1);
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(true);
}
[Fact]
public void ValidatorShouldReturnTrueWhenPassedATrustedCertificateWhichHasAChainElementSha1SpkiWhiteListed()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new[] { MicrosoftInternetAuthoritySha1Hash }, SubjectPublicKeyInfoAlgorithm.Sha1);
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(true);
}
[Fact]
public void ValidatorShouldReturnFalseWhenPassedATrustedCertificateWhichDoesNotHaveAWhitelistedSha256Spki()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new string[1], SubjectPublicKeyInfoAlgorithm.Sha256);
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnTrueWhenPassedATrustedCertificateWhichHasItsSha256SpkiWhiteListed()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new[] { KatanaTestSha256Hash }, SubjectPublicKeyInfoAlgorithm.Sha256);
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(true);
}
[Fact]
public void ValidatorShouldReturnTrueWhenPassedATrustedCertificateWhichHasAChainElementSha256SpkiWhiteListed()
{
var instance = new CertificateSubjectPublicKeyInfoValidator(new[] { MicrosoftInternetAuthoritySha256Hash }, SubjectPublicKeyInfoAlgorithm.Sha256);
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(true);
}
}
}

View File

@ -1,121 +0,0 @@
// 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;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Shouldly;
using Xunit;
namespace Microsoft.AspNet.Authentication
{
public class CertificateThumbprintValidatorTests
{
private static readonly X509Certificate2 SelfSigned = new X509Certificate2("selfSigned.cer");
private static readonly X509Certificate2 Chained = new X509Certificate2("katanatest.redmond.corp.microsoft.com.cer");
// The Katana test cert has a valid full chain
// katanatest.redmond.corp.microsoft.com -> MSIT Machine Auth CA2 -> Microsoft Internet Authority -> Baltimore CyberTrustRoot
private const string KatanaTestThumbprint = "a9894c464b260cac3f5b91cece33b3c55e82e61c";
private const string MicrosoftInternetAuthorityThumbprint = "992ad44d7dce298de17e6f2f56a7b9caa41db93f";
[Fact]
public void ConstructorShouldNotThrowWithValidValues()
{
var instance = new CertificateThumbprintValidator(new string[1]);
instance.ShouldNotBe(null);
}
[Fact]
public void ConstructorShouldThrownWhenTheValidHashEnumerableIsNull()
{
Should.Throw<ArgumentNullException>(() =>
new CertificateThumbprintValidator(null));
}
[Fact]
public void ConstructorShouldThrowWhenTheHashEnumerableContainsNoHashes()
{
Should.Throw<ArgumentOutOfRangeException>(() =>
new CertificateThumbprintValidator(new string[0]));
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateChainErrors()
{
var instance = new CertificateThumbprintValidator(new string[1]);
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateChainErrors);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateNameMismatch()
{
var instance = new CertificateThumbprintValidator(new string[1]);
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateNameMismatch);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenSslPolicyErrorsIsRemoteCertificateNotAvailable()
{
var instance = new CertificateThumbprintValidator(new string[1]);
bool result = instance.Validate(null, null, null, SslPolicyErrors.RemoteCertificateNotAvailable);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenPassedASelfSignedCertificate()
{
var instance = new CertificateThumbprintValidator(new string[1]);
var certificateChain = new X509Chain();
certificateChain.Build(SelfSigned);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, SelfSigned, certificateChain, SslPolicyErrors.None);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnFalseWhenPassedATrustedCertificateWhichDoesNotHaveAWhitelistedThumbprint()
{
var instance = new CertificateThumbprintValidator(new string[1]);
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(false);
}
[Fact]
public void ValidatorShouldReturnTrueWhenPassedATrustedCertificateWhichHasItsThumbprintWhiteListed()
{
var instance = new CertificateThumbprintValidator(new[] { KatanaTestThumbprint });
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(true);
}
[Fact]
public void ValidatorShouldReturnTrueWhenPassedATrustedCertificateWhichHasAChainElementThumbprintWhiteListed()
{
var instance = new CertificateThumbprintValidator(new[] { MicrosoftInternetAuthorityThumbprint });
var certificateChain = new X509Chain();
certificateChain.Build(Chained);
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool result = instance.Validate(null, Chained, certificateChain, SslPolicyErrors.None);
result.ShouldBe(true);
}
}
}

View File

@ -48,7 +48,6 @@ namespace Microsoft.AspNet.Authentication.Twitter
return null;
}
};
options.BackchannelCertificateValidator = null;
},
context =>
{
@ -123,7 +122,6 @@ namespace Microsoft.AspNet.Authentication.Twitter
return null;
}
};
options.BackchannelCertificateValidator = null;
},
context =>
{