Include the developer certificate by default, make it noop when environment != development

This commit is contained in:
Javier Calvarro Nelson 2017-05-19 15:25:49 -07:00
parent 45f9780d32
commit 63f7f9a62c
4 changed files with 100 additions and 5 deletions

View File

@ -44,15 +44,20 @@ namespace Microsoft.AspNetCore.Diagnostics.Identity.Service
public async Task InvokeAsync(HttpContext context)
{
var credentialsProvider = context.RequestServices.GetRequiredService<ISigningCredentialsPolicyProvider>();
var openIdOptionsCache = context.RequestServices.GetRequiredService<IOptionsCache<OpenIdConnectOptions>>();
var credentialsProvider = context.RequestServices.GetService<DeveloperCertificateSigningCredentialsSource>();
if (credentialsProvider == null)
{
await _next(context);
return;
}
var openIdOptionsCache = context.RequestServices.GetRequiredService<IOptionsCache<OpenIdConnectOptions>>();
if (_environment.IsDevelopment() &&
context.Request.Path.Equals(_options.Value.ListeningEndpoint))
{
if (context.Request.Method.Equals(HttpMethods.Get))
{
var credentials = await credentialsProvider.GetAllCredentialsAsync();
var credentials = await credentialsProvider.GetCredentials();
bool hasDevelopmentCertificate = await IsDevelopmentCertificateConfiguredAndValid();
var foundDeveloperCertificate = FoundDeveloperCertificate();
if (!foundDeveloperCertificate || !hasDevelopmentCertificate)
@ -102,7 +107,6 @@ namespace Microsoft.AspNetCore.Diagnostics.Identity.Service
store.Open(OpenFlags.ReadWrite);
store.Add(imported);
store.Close();
_identityServiceOptionsCache.TryRemove(Options.DefaultName);
openIdOptionsCache.TryRemove(OpenIdConnectDefaults.AuthenticationScheme);
context.Response.StatusCode = StatusCodes.Status204NoContent;
@ -131,7 +135,7 @@ namespace Microsoft.AspNetCore.Diagnostics.Identity.Service
async Task<bool> IsDevelopmentCertificateConfiguredAndValid()
{
var certificates = await credentialsProvider.GetAllCredentialsAsync();
var certificates = await credentialsProvider.GetCredentials();
return certificates.Any(
c => _timeStampManager.IsValidPeriod(c.NotBefore, c.Expires) &&
c.Credentials.Key is X509SecurityKey key &&

View File

@ -0,0 +1,73 @@
// 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.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service
{
public class DeveloperCertificateSigningCredentialsSource : ISigningCredentialsSource
{
private readonly IHostingEnvironment _environment;
private readonly ITimeStampManager _timeStampManager;
public DeveloperCertificateSigningCredentialsSource(
IHostingEnvironment environment,
ITimeStampManager timeStampManager)
{
_environment = environment;
_timeStampManager = timeStampManager;
}
public Task<IEnumerable<SigningCredentialsDescriptor>> GetCredentials()
{
if (!_environment.IsDevelopment())
{
return Task.FromResult(Enumerable.Empty<SigningCredentialsDescriptor>());
}
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, "CN=IdentityService.Development", validOnly: false);
var valid = cert.OfType<X509Certificate2>().FirstOrDefault(c => _timeStampManager.IsValidPeriod(c.NotBefore, c.NotAfter));
store.Close();
if (valid != null)
{
return Task.FromResult<IEnumerable<SigningCredentialsDescriptor>>(new[] { CreateDescriptor(valid) });
}
else
{
return Task.FromResult(Enumerable.Empty<SigningCredentialsDescriptor>());
}
}
}
private SigningCredentialsDescriptor CreateDescriptor(X509Certificate2 certificate)
{
CryptographyHelpers.ValidateRsaKeyLength(certificate);
var credentials = new SigningCredentials(new X509SecurityKey(certificate), CryptographyHelpers.FindAlgorithm(certificate));
return new SigningCredentialsDescriptor(
credentials,
CryptographyHelpers.GetAlgorithm(credentials),
certificate.NotBefore,
certificate.NotAfter,
GetMetadata());
IDictionary<string, string> GetMetadata()
{
var rsaParameters = CryptographyHelpers.GetRSAParameters(credentials);
return new Dictionary<string, string>
{
[JsonWebKeyParameterNames.E] = Base64UrlEncoder.Encode(rsaParameters.Exponent),
[JsonWebKeyParameterNames.N] = Base64UrlEncoder.Encode(rsaParameters.Modulus),
};
}
}
}
}

View File

@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Identity.Service.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
@ -56,6 +58,20 @@ namespace Microsoft.AspNetCore.Identity.Service
return builder;
}
public static IIdentityServiceBuilder DisableDeveloperCertificate(this IIdentityServiceBuilder builder)
{
var services = builder.Services;
foreach (var service in services.ToList())
{
if (service.ImplementationType == typeof(DeveloperCertificateSigningCredentialsSource))
{
services.Remove(service);
}
}
return builder;
}
public static IIdentityServiceBuilder AddSigningCertificate(this IIdentityServiceBuilder builder, Func<X509Certificate2> func)
{
var cert = func();

View File

@ -83,6 +83,8 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddSingleton<ITokenClaimsProvider, TimestampsTokenClaimsProvider>();
services.AddSingleton<ITokenClaimsProvider, TokenHashTokenClaimsProvider>();
services.AddSingleton<ProtocolErrorProvider>();
services.AddSingleton<ISigningCredentialsSource, DeveloperCertificateSigningCredentialsSource>();
services.AddSingleton<DeveloperCertificateSigningCredentialsSource>();
services.AddSingleton<IPasswordHasher<TApplication>, PasswordHasher<TApplication>>();
services.AddScoped<ISigningCredentialsPolicyProvider, DefaultSigningCredentialsPolicyProvider>();