在 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 下载到客户端浏览器,以某种方式签名然后上传回服务器。
我目前有一个需要用户 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 下载到客户端浏览器,以某种方式签名然后上传回服务器。