根据验证程序,远程证书无效-无法解决此问题

时间:2019-03-26 13:06:03

标签: asp.net-core certificate signalr openid identityserver4

我在这个问题上徘徊了好几天,而且没有一个答案可以解决这个问题。

我正在使用Windows 10系统并使用VisualStudio 2017实施。 我使用AspNetCore实现了以下项目:

1。)Web.AuthServer:用于身份验证的IdentityServer4。

2。)Web.ApiServer:第一个SignalR服务器。

3。)Web.ApiSwitch:第二个SignalR服务器。 它具有2个SignalR-Clients作为HostedService 两个SignalR-Server之间的“桥梁”。>

Web.ApiSwitch启动其HostedService,该HostedService连接到自身和Web.ApiServer,包括Web.AuthServer上的身份验证。只要它们带有一些“ localhost:PORT” URL,它就可以很好地工作。

现在我已经尝试使用“ MyIP:PORT”运行所有项目。 Web.AuthServer正在使用HTTPS和自签名证书(由OpenSSL生成)。 证书本身已使用以下命令行构建:

生成私钥:

openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout IdentityServer4Auth.key -out IdentityServer4Auth.crt -subj "/CN=example.com" -days 3650

生成证书:

openssl pkcs12 -export -out IdentityServer4Auth.pfx -inkey IdentityServer4Auth.key -in IdentityServer4Auth.crt -certfile IdentityServer4Auth.crt

该文件已添加到mmc:

1。)文件->添加或删除管理单元->证书->添加->计算机帐户->确定 2.)将证书(.cer)导入到个人->受信任的根证书颁发机构) 3.)将具有可导出私钥支持的pfx导入个人->证书。

Web.AuthServer的代码:

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
    .UseKestrel(options =>
    {
        options.Listen(IPAddress.Any, 5000, listenOptions =>
        {
            listenOptions.UseHttps();
        });
    })
    .UseStartup<Startup>()
    .ConfigureLogging(builder =>
    {
        builder.ClearProviders();
        builder.AddSerilog();
    })
    .Build();

Web.AuthSever-ConfigureServices:

public void ConfigureServices(IServiceCollection services)
 {
 // Gets connection strings from "appsettings.json".
 string csApplicationContext = Configuration.GetConnectionString("ApplicationContext");
 string csConfigurationStore = Configuration.GetConnectionString("ConfigurationStore");
 var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

 var settings = JsonFileManager<ServerSettings>.Load(AppDomain.CurrentDomain.BaseDirectory + "Config\\svConf.json");

 // Add cross origin resource sharing.
 services.AddCors(options =>
 {
     options.AddPolicy("default", policy =>
     {
         policy.WithOrigins(settings.CorsOrigins)
               .AllowAnyHeader()
               .AllowAnyMethod()
               .AllowCredentials();
     });
 });

 // Add bearer token authentication.
 services.AddAuthentication()
     .AddJwtBearer(jwt =>
     {
         jwt.Authority = settings.JWTBearerSettings.Authority;
         jwt.Audience = settings.JWTBearerSettings.Audience;
         jwt.RequireHttpsMetadata = settings.JWTBearerSettings.RequireHttpsMetadata;
         jwt.Validate();
     });

 services.AddPolicyServerClient(Configuration.GetSection("Policy"))
     .AddAuthorizationPermissionPolicies();

 // DB und User registieren für DI
 services.AddDbContext<ApplicationDbContext>(builder =>
     builder.UseSqlite(csApplicationContext, sqlOptions =>
         sqlOptions.MigrationsAssembly(migrationsAssembly)));

 services.AddIdentity<ApplicationUser, IdentityRole>()
     .AddEntityFrameworkStores<ApplicationDbContext>();

 services.AddTransient<IClientStore, ClientService>();

 // Add IS4 as authentication server.
 var is4Builder = services.AddIdentityServer(options =>
     {
         options.Events.RaiseErrorEvents = true;
         options.Events.RaiseFailureEvents = true;
         options.Events.RaiseSuccessEvents = true;
         options.Events.RaiseInformationEvents = true;
     })
     // Add config data (clients, resources, CORS).
     .AddConfigurationStore(options =>
         options.ConfigureDbContext = builder =>
             builder.UseSqlite(csConfigurationStore, sqlOptions =>
                 sqlOptions.MigrationsAssembly(migrationsAssembly)))
     .AddClientStore<ClientService>()
     .AddAspNetIdentity<ApplicationUser>();

 SigninCredentialExtension.AddSigninCredentialFromConfig(is4Builder, Configuration.GetSection("SigninKeyCredentials"), Logger);

 services.AddMvc(options =>
     {
     // this sets up a default authorization policy for the application
     // in this case, authenticated users are required (besides controllers/actions that have [AllowAnonymous]
     var policy = new AuthorizationPolicyBuilder()
                     .RequireAuthenticatedUser()
                     .Build();
     options.Filters.Add(new AuthorizeFilter(policy));

     options.SslPort = 5000;
     options.Filters.Add(new RequireHttpsAttribute());
 });
 }

Web.AuthSever-配置:

public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
    app.UseDeveloperExceptionPage();
else
    app.UseExceptionHandler("/Home/Error");

// Use specific cross origin resource sharing configuration.
app.UseCors("default");

app.UseDefaultFiles();

app.UsePolicyServerClaims();

app.UseStaticFiles();

app.UseHttpsRedirection();

app.UseIdentityServer();

// Adding test data to database.
await InitializeDbTestData.GenerateTestData(app);

app.UseMvcWithDefaultRoute();
}

Web.AuthSever-SigninCredentialExtension:

public static class SigninCredentialExtension
{
private const string KeyType = "KeyType";
private const string KeyTypeKeyFile = "KeyFile";
private const string KeyTypeKeyStore = "KeyStore";
private const string KeyTypeTemporary = "Temporary";
private const string KeyFilePath = "KeyFilePath";
private const string KeyFilePassword = "KeyFilePassword";
private const string KeyStoreIssuer = "KeyStoreIssuer";

public static IIdentityServerBuilder AddSigninCredentialFromConfig(
    this IIdentityServerBuilder builder, IConfigurationSection options, ILogger logger)
{
    string keyType = options.GetValue<string>(KeyType);
    logger.LogDebug($"SigninCredentialExtension keyType is {keyType}");

    switch (keyType)
    {
        case KeyTypeTemporary:
            logger.LogDebug($"SigninCredentialExtension adding Developer Signing Credential");
            builder.AddDeveloperSigningCredential();
            break;

        case KeyTypeKeyFile:
            AddCertificateFromFile(builder, options, logger);
            break;

        case KeyTypeKeyStore:
            AddCertificateFromStore(builder, options, logger);
            break;
    }

    return builder;
}

    public static X509Certificate2 GetCertificateByThumbprint(string thumbprint)
{
    using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
    {
        certStore.Open(OpenFlags.ReadOnly);
        X509Certificate2Collection certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
        if (certCollection.Count > 0) return certCollection[0];
    }
    return null;
}

private static void AddCertificateFromStore(IIdentityServerBuilder builder,
    IConfigurationSection options, ILogger logger)
{
    var keyIssuer = options.GetValue<string>(KeyStoreIssuer);
    logger.LogDebug($"SigninCredentialExtension adding key from store by {keyIssuer}");

    X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.ReadOnly);

    var certificates = store.Certificates.Find(X509FindType.FindByIssuerName, keyIssuer, true);

    if (certificates.Count > 0)
    {
        builder.AddSigningCredential(certificates[0]);
        builder.AddValidationKey(certificates[0]);
    }
    else
        logger.LogError("A matching key couldn't be found in the store");
}

private static void AddCertificateFromFile(IIdentityServerBuilder builder,
    IConfigurationSection options, ILogger logger)
{
    var keyFilePath = options.GetValue<string>(KeyFilePath);
    var keyFilePassword = options.GetValue<string>(KeyFilePassword);

    if (File.Exists(keyFilePath))
    {
        logger.LogDebug($"SigninCredentialExtension adding key from file {keyFilePath}");
        builder.AddSigningCredential(new X509Certificate2(keyFilePath, keyFilePassword));
    }
    else
    {
        logger.LogError($"SigninCredentialExtension cannot find key file {keyFilePath}");
    }
}
}

Web.ApiServer的代码:

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
    .UseKestrel(options =>
    {
        options.Listen(IPAddress.Any, 5004, listenOptions =>
        {
            listenOptions.UseHttps();
        });
    })
    .UseStartup<Startup>()
    .ConfigureLogging(builder =>
    {
        builder.ClearProviders();
        builder.AddSerilog();
    })
    .Build();

Web.ApiServer-ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
// Add cross origin resource sharing.
services.AddCors(options =>
{
    options.AddPolicy("default", policy =>
    {
        policy.WithOrigins(_settings.CorsOrigins)
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowCredentials();
    });
});

// Add bearer token authentication and our IS4 as authentication server.
services.AddAuthentication(_settings.ISAuthenticationSettings.DefaultScheme)
.AddIdentityServerAuthentication(options =>
{
    options.Authority = _settings.ISAuthenticationSettings.Authority;
    options.RequireHttpsMetadata = _settings.ISAuthenticationSettings.RequireHttpsMetadata;
    options.ApiName = _settings.ISAuthenticationSettings.ApiName;

    // Handling the token from query string in due to the reason
    // that signalR clients are handling them over it.
    options.TokenRetriever = new Func<HttpRequest, string>(req =>
    {
        var fromHeader = TokenRetrieval.FromAuthorizationHeader();
        var fromQuery = TokenRetrieval.FromQueryString();
        return fromHeader(req) ?? fromQuery(req);
    });

    options.Validate();
});

// Add singalR as event bus.
services.AddSignalR(options => options.EnableDetailedErrors = true);

services.AddMvcCore(options =>
        {
            options.SslPort = 5003;
            options.Filters.Add(new RequireHttpsAttribute());
        })
        .AddAuthorization()
        .AddJsonFormatters();

// Register ConnectionHost as hosted service with its wrapper class.
services.AddSingleton<Microsoft.Extensions.Hosting.IHostedService, ConnectionHost>();
}

Web.ApiServer-配置:

   public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
    app.UseDeveloperExceptionPage();

app.UseHttpsRedirection();

// Has to be called before UseSignalR and UseMvc!
app.UseAuthentication();

// Use specific cross origin resource sharing configuration.
app.UseCors("default");

app.UseSignalR(routes => routes.MapHub<EventHub>("/live"));

app.UseMvc();
   }

令牌请求或SignalR客户端:

public static async Task<TokenResponse> RequestTokenAsync(string authority, string clientID, string scope)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(authority);
if (disco.IsError) throw new Exception(disco.Error);

var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
    Address = disco.TokenEndpoint,

    ClientId = clientID,
    ClientSecret = "SomeTestSecret",
    Scope = scope
});

if (response.IsError)
{
    throw new Exception(response.Error);
}

return response;
}

来自Web.ApiServer的ConfigureServices的TokenRetriever仅用于获取对SignalR客户端的身份验证,因为它们正在通过查询字符串传递令牌。它完成了工作。

现在是问题:

Web.ApiServer的HostedService的客户端正在尝试从Web.AuthServer获取身份验证令牌(jwt承载),但是每次我得到 以下异常:

System.Security.Authentication.AuthenticationException: 'The remote certificate is invalid according to the validation procedure.'

如果我打开浏览器并输入Web.AuthServer“ MyIP:5000”的地址,那么在接受自签名证书之后,一切都可以正常工作。 但是Web.ApiServer的HostedService的客户端无法执行此操作。 如何排除此异常并获取一些有效的证书?我在客户端实施过程中缺少什么吗?希望有人可以帮助我-自4天多以来一直陷入困境。

1 个答案:

答案 0 :(得分:0)

为使客户端信任服务器,客户端会检查服务器针对TLS公开的证书上的许多属性,例如“是预期域的证书”,“证书已过期”。客户端将检查的一件事是证书链,这是一个信任链。

https://knowledge.digicert.com/solution/SO16297.html

当您从证书颁发机构购买证书时,部分就是您要购买的-例如,让我们看看Facebook使用的证书。

Facebook Chain

它们具有可用于facebook的所有子域的通配符证书,并且受信任的根证书颁发机构为DigiCert(https://www.digicert.com/welcome/compatibility.htm)。通过使用广受信任的Digicert CA,客户端可以知道Facebook证书是由Digicert颁发的,因此可以信任Facebook证书。

这是您缺少的部分。您使用的是自签名证书,您的客户不了解根CA,也无法建立信任链。通过手动接受证书,您可以解决根本原因,但是显然,这对于您无法完全控制的客户端不起作用。

https://letsencrypt.org/提供了免费的CA服务,该服务现在可在大量客户中使用-在许多方面,它是一个不错的解决方案,支持自动证书续订。因此,与其使用自签名证书,不如使用letencrypt为您的服务器生成证书(有关如何执行此操作的文章很多)

使用由客户端信任的CA颁发的证书是解决此问题的正确方法。