在 C# 中允许 CX509PrivateKeyClass 的 KeyProtection
Posted
技术标签:
【中文标题】在 C# 中允许 CX509PrivateKeyClass 的 KeyProtection【英文标题】:Allowing KeyProtection for CX509PrivateKeyClass in C# 【发布时间】:2020-06-02 10:46:47 【问题描述】:我们目前有一个使用 Microsoft Active Directory 证书服务生成 CA 证书的解决方案。
我们有一个您可以登录的应用程序,您可以发送 CA 证书请求。
在幕后,这会加载一个 iframe(指向 MS CA 证书服务上的表单)使用 jQuery 复制数据负载并提交表单。
表单提交使用 ActiveX 和 CertEnroll 创建一个 CSR,并将其发布到另一个 ASP 页面。 (整个MS AD证书服务都是ASP页面)
问题是这只适用于 IE(显然)
我的建议是移动 CSR 生成服务器端,这样它就不会依赖 IE 并且可以在任何浏览器中使用。
我有一个可行的解决方案,它将数据发送到 api,并在 api 中生成 CSR 并将其转发到 MS CA 证书服务器,从而生成有效证书。
问题
问题是,一旦我安装了证书,“您有一个与此证书对应的私钥”的信息就丢失了。。在现有系统上,此消息显示如下:
我的假设是这是因为尚未为私钥设置 KeyProtection。不幸的是,它在现有 VBScript 实现中使用的标志需要用户输入:XCN_NCRYPT_UI_PROTECT_KEY_FLAG。每次请求证书时,都会提示用户保护密钥
对于我的 .Net Framework 实现,我尝试了两种不同的 CSR 生成器,一种使用 Bouncy Castle,另一种使用 CERTENROLLLib 的互操作。
我将在下面显示的代码将是 CERTENROLLLib 实现,但如果有人对这两种解决方案有任何想法,我会很高兴听到他们的意见
CERTENROLLLib CSR 一代
var values = new Dictionary<string, string>
"E", emailAddress ,
"CN", commonName ,
"OU", organizationalUnit ,
"L", locality
;
var privateKey = new CX509PrivateKeyClass
Length = 1024,
MachineContext = false,
CspInformations = GetCryptographicProvider(),
// This can't be set as it requires UI feedback
//KeyProtection = X509PrivateKeyProtection.XCN_NCRYPT_UI_PROTECT_KEY_FLAG,
ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_NONE,
;
// Create the actual key pair
privateKey.Create();
// Initialize the PKCS#10 certificate request object based on the private key.
// Using the context, indicate that this is a user certificate request and don't
// provide a template name
var certificateRequest = new CX509CertificateRequestPkcs10Class();
certificateRequest.InitializeFromPrivateKey(
X509CertificateEnrollmentContext.ContextUser,
privateKey,
string.Empty
);
var extension = new CX509ExtensionsClass
GetExtensionKeyUsage(),
GetExtensionEnhancedKeyUsage()
;
certificateRequest.X509Extensions.AddRange(extension);
// Encode the name in using the Distinguished Name object
var valueCollection = values.Select(v => $"v.Key=v.Value").Reverse();
var subject = new CX500DistinguishedNameClass();
subject.Encode(
string.Join(";", valueCollection), X500NameFlags.XCN_CERT_NAME_STR_ENABLE_PUNYCODE_FLAG
);
// Assigning the subject name by using the Distinguished Name object initialized above
certificateRequest.Subject = subject;
// Create enrollment request
var enrollmentRequest = new CX509EnrollmentClass();
enrollmentRequest.InitializeFromRequest(certificateRequest);
var csr = enrollmentRequest.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64REQUESTHEADER);
所以我的问题是如何为私钥添加密钥保护,如屏幕截图所示?
由于这是服务器端,我不能指望任何用户反馈,所以还有另一种方法。
任何指导都将受到欢迎,这可能很清楚,我对 CSR 生成还很陌生。
最终这将被重写,我们可能会生成我们自己的证书,但我只是想改善目前的情况。
非常感谢
【问题讨论】:
【参考方案1】:首先,这不是一个答案,而是分享一些可以提供帮助的想法和经验。
我之前在 CERTENROLLLib 库的 1.0.0.0 版本中使用了类似的代码(这是要走的路,因为它有大多数用于生成证书的设置,而不是 Bouncy Castle)。
我认为您的请求是正确的,但不知何故,MS CA 正在颁发没有私钥的证书,因此您导入它并最终丢失“您有一个与此证书对应的私钥”。
我这样说是因为我的代码没有设置 KeyProtection
并保留默认值 X509PrivateKeyProtection.XCN_NCRYPT_UI_NO_PROTECTION_FLAG
,但我能够生成包含私钥的证书(我实际上再次使用 CERTENROLLLib 来生成证书MS CA,所以这是一个主要区别)。
这就像以 .cer 格式(不带 PK)和 .pfx 格式(带 PK)导出证书。
因此,MS CA 的注册请求必须有一些特殊设置。
这是我的参考代码,它创建一个证书并将其添加到本地计算机上的个人证书中。
// Create enrollment request
var enrollmentRequest = new CX509Enrollment();
enrollmentRequest.InitializeFromRequest(certificateRequest);
string csr = enrollmentRequest.CreateRequest();
// and install it back as the response
enrollmentRequest.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password
// output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
var base64encoded = enrollmentRequest.CreatePFX("", // no password, this is for internal consumption
PFXExportOptions.PFXExportChainWithRoot);
// instantiate the target class with the PKCS#12 data (and the empty password)
var certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(
System.Convert.FromBase64String(base64encoded), "",
// mark the private key as exportable (this is usually what you want to do)
System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable
);
我也决定发这个,因为你说
我们可能会生成自己的证书
这实际上是我的代码所做的,它包括 PK,所以也许这篇文章可能会有所帮助。
【讨论】:
我开始认为这可能需要尽早完成,这看起来是一个非常好的起点,非常感谢!以上是关于在 C# 中允许 CX509PrivateKeyClass 的 KeyProtection的主要内容,如果未能解决你的问题,请参考以下文章
请解释为啥在 Cpp、Java 和 C# 中允许或不允许 List<A> 强制转换为 List<A.super> [关闭]