在 dotnet 核心中跟踪 X509 证书

Keeping Track of X509 Cert in dotnet core

我目前有一个需要用户 x509 证书的 DotNet Core 应用程序。我目前可以提取它并使用以下内容对其进行验证

在我的Program.cs

            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.ConfigureKestrel(o =>
                    {
                        o.ConfigureHttpsDefaults(o => 
                            o.ClientCertificateMode = ClientCertificateMode.RequireCertificate);
                    });
                });

在我的Startup.cs

            services.AddScoped<CertificateValidationService>();
            services.AddControllers();

            services.AddAuthentication(
                    CertificateAuthenticationDefaults.AuthenticationScheme)
                .AddCertificate(options =>
                {
                    options.AllowedCertificateTypes = CertificateTypes.All;
                    options.Events = new CertificateAuthenticationEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            context.NoResult();
                            context.Response.Headers.Add("Token-Expired", "true");
                            context.Response.ContentType = "text/plain";
                            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                            return Task.CompletedTask;
                        },
                        OnCertificateValidated = context =>
                        {
                            var validationService =
                                context.HttpContext.RequestServices
                                    .GetRequiredService<CertificateValidationService>();

                            if (validationService.ValidateCertificate(
                                context.ClientCertificate))
                            {

                                var claims = new[]
                                {
                                    new Claim(
                                        ClaimTypes.NameIdentifier,
                                        context.ClientCertificate.Subject,
                                        ClaimValueTypes.String,
                                        context.Options.ClaimsIssuer),
                                    new Claim(
                                        ClaimTypes.Name,
                                        context.ClientCertificate.Subject,
                                        ClaimValueTypes.String,
                                        context.Options.ClaimsIssuer)
                                };

                                context.Properties.SetParameter("x509", context.ClientCertificate);
                                context.Principal = new ClaimsPrincipal(
                                    new ClaimsIdentity(claims, context.Scheme.Name));
                                context.Success();
                            }
                            else
                            {
                                context.Fail($"Unrecognized client certificate: " +
                                             $"{context.ClientCertificate.GetNameInfo(X509NameType.SimpleName, false)}");
                            }
                            return Task.CompletedTask;



                        }
                    };
                });

            services.AddControllers();

我还有用于验证证书的实现,并且工作正常。但是,稍后在应用程序中我想执行以下操作

        [Route("/signature")]
        [HttpGet]
        public string Signature()
        {
            
            using (var signer = new PdfDocumentSigner(@"C:\test\Document.pdf"))
            {
                ITsaClient tsaClient = new TsaClient(new Uri(@"https://freetsa.org/tsr"),
                    DevExpress.Office.DigitalSignatures.HashAlgorithmType.SHA256);

                string signatureName = signer.GetSignatureFieldNames(false)[0];


                // Create a provider that retrieves certificates from a store:
                //    public CertificateStoreProvider(X509Certificate2Collection collection);

                using (var certificateStoreProvider =
                    new CertificateStoreProvider(new X509Store(StoreLocation.CurrentUser), true))
                {
                    // Add the signature to the security store
                    // and specify the CrlClient and OcspClient objects
                    // used to check the certificates' revocation status:
                    
                    signer.AddToDss(signatureName, new CrlClient(), new OcspClient(), certificateStoreProvider);
                }
                signer.SaveDocument(@"C:\test\signedLTV.pdf", new[] { new PdfSignatureBuilder(new PdfTimeStamp(tsaClient)) });
            }

            return "The file was signed";
        }

现在显然问题是这是我本地主机上的 X509 证书。但是我想做的是使用用户验证过的那个在 Program/Startup

中传递的那个

有没有一种方法可以让我以编程方式安全地获取或传递它?

你不能这样做。为了将客户端证书用于后续加密操作(如签名或解密),您需要客户端的私钥。但是,客户端从不发送其私钥。客户端的私钥绝不能离开客户端机器。你的整个方法是行不通的。这是相关主题,有更深入的解释:Open X509 Certificates Selection Using USB Token in C# Hosted on IIS

你可以做的是将签名过程移到客户端(在浏览器中执行),但这种方法有其自身的问题,比如你必须将整个 PDF 下载到客户端浏览器,以某种方式签名然后上传回服务器。