![](/img/trans.png)
[英]makecert.exe missing in windows 10, how to get it and use it
[英]Upgrading from makecert.exe to CertEnroll - issues with certificate trust
我有一个迄今为止使用 makecert.exe 生成自证书的应用程序。 但是,由于 makecert 无法添加 SubjectAltName 字段,因此我需要将代码迁移到 certenroll.dll
这是原始的 makecert 代码:
public static X509Certificate2 MakeCert(string subjectName)
{
X509Certificate2 cert;
string certFile = Path.Combine(Path.GetTempPath(), subjectName + ".cer");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "makecert.exe",
Arguments = " -pe -ss my -n \"CN=" + subjectName + ", O=myCert, OU=Created by me\" -sky exchange -in MyCustomRoot -is my -eku 1.3.6.1.5.5.7.3.1 -cy end -a sha1 -m 132 -b 10/08/2018 " + certFile,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
process.Start();
string str = "";
while (!process.StandardOutput.EndOfStream)
{
var line = process.StandardOutput.ReadLine();
str += line;
//Console.WriteLine(line);
}
process.WaitForExit();
cert = new X509Certificate2(certFile);
// Install Cert
try
{
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
try
{
var contentType = X509Certificate2.GetCertContentType(certFile);
var pfx = cert.Export(contentType);
cert = new X509Certificate2(pfx, (string)null, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
store.Add(cert);
}
finally
{
store.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(String.Format("Could not create the certificate from file from {0}", certFile), ex);
}
return cert;
}
这是 certenroll.dll 代码:
public static X509Certificate2 CertOpen(string subjectName)
{
try
{
X509Store store = new X509Store("My", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
try
{
var cer = store.Certificates.Find(
X509FindType.FindBySubjectName,
subjectName,
false);
if (cer.Count > 0)
{
return cer[0];
}
else
{
return null;
}
}
finally
{
store.Close();
}
}
catch
{
return null;
}
}
public static X509Certificate2 CertCreateNew(string subjectName)
{
// create DN for subject and issuer
var dn = new CX500DistinguishedName();
dn.Encode("CN=" + subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
// create a new private key for the certificate
CX509PrivateKey privateKey = new CX509PrivateKey();
privateKey.ProviderName = "Microsoft Base Cryptographic Provider v1.0";
privateKey.MachineContext = false;
privateKey.Length = 2048;
privateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE; // use is not limited
privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
privateKey.Create();
var hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
AlgorithmFlags.AlgorithmFlagsNone, "SHA256");
// add extended key usage if you want - look at MSDN for a list of possible OIDs
var oid = new CObjectId();
oid.InitializeFromValue("1.3.6.1.5.5.7.3.1"); // SSL server
var oidlist = new CObjectIds();
oidlist.Add(oid);
var eku = new CX509ExtensionEnhancedKeyUsage();
eku.InitializeEncode(oidlist);
// Create the self signing request
var cert = new CX509CertificateRequestCertificate();
cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, privateKey, "");
X509Certificate2 signer = CertOpen("MyCustomRoot");
if (signer == null)
{
throw new CryptographicException("Signer not found");
}
String base64str = Convert.ToBase64String(signer.RawData);
ISignerCertificate signerCertificate = new CSignerCertificate();
signerCertificate.Initialize(false, X509PrivateKeyVerify.VerifySilent, EncodingType.XCN_CRYPT_STRING_BASE64, base64str);
// this line MUST be called AFTER IX509CertificateRequestCertificate.InitializeFromPrivateKey call,
// otherwise you will get OLE_E_BLANK uninitialized object error.
cert.SignerCertificate = (CSignerCertificate)signerCertificate;
cert.Subject = dn;
cert.Issuer.Encode(signer.Subject, X500NameFlags.XCN_CERT_NAME_STR_NONE); ; // the issuer and the subject are the same
cert.NotBefore = DateTime.Now;
// this cert expires immediately. Change to whatever makes sense for you
cert.NotAfter = DateTime.Now.AddYears(10);
cert.X509Extensions.Add((CX509Extension)eku); // add the EKU
cert.HashAlgorithm = hashobj; // Specify the hashing algorithm
cert.Encode(); // encode the certificate
// Do the final enrollment process
var enroll = new CX509Enrollment();
enroll.InitializeFromRequest(cert); // load the certificate
enroll.CertificateFriendlyName = subjectName; // Optional: add a friendly name
string csr = enroll.CreateRequest(); // Output the request in base64
// and install it back as the response
enroll.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 = enroll.CreatePFX("", // no password, this is for internal consumption
PFXExportOptions.PFXExportChainWithRoot);
// instantiate the target class with the PKCS#12 data (and the empty password)
return 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
);
}
在 Crypt32 的帮助下,我现在遇到了 signerCertificate.Initialize 行的问题。 我似乎无法让它使用我的自我证书。 根证书。 我假设我正在尝试以错误的格式提供它,因为我收到以下错误:
该证书不具有引用私钥的属性。 0x8009200a (CRYPT_E_UNEXPECTED_MSG_TYPE)
您必须在IX509CertificateRequestCertificate
对象(代码中的cert
变量)的SignerCertificate
属性中指定签名者证书。 签名者证书必须以ISignerCertificate
实例的形式提供。 更多信息: ISignerCertificate 接口
更新 1 (13.12.2019)
很抱歉,但几乎ISignerCertificate
调用中的每一部分都是不正确的。
如果您指定X509PrivateKeyVerify.VerifyNone
,则不会检查私钥是否存在。 您需要使用X509PrivateKeyVerify.VerifySilent
标志。
您正在使用带有 PEM 页眉和页脚的 Base64 格式将证书格式化为字符串。 您正在使用EncodingType.XCN_CRYPT_STRING_BASE64
,它需要没有 PEM 包络的原始 Base64 字符串。 PEM 格式的证书使用EncodingType.XCN_CRYPT_STRING_BASE64HEADER
编码类型。 在你的情况下,我会这样做:
X509Certificate signer = CertOpen("MyCustomRoot");
if (signer == null) {
throw new CryptographicException("Signer not found");
}
String base64str = Convert.ToBase64String(signer.RawData);
signerCertificate.Initialize(false, X509PrivateKeyVerify.VerifySilent, EncodingType.XCN_CRYPT_STRING_BASE64, base64str);
<...>
// put issuer directly from issuer cert:
issuer.Encode(signer.Subject, X500NameFlags.XCN_CERT_NAME_STR_NONE);
<...>
cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, privateKey, "");
// this line MUST be called AFTER IX509CertificateRequestCertificate.InitializeFromPrivateKey call,
// otherwise you will get OLE_E_BLANK uninitialized object error.
cert.SignerCertificate = signerCertificate;
此外,还有一些小的改进:
CertOpen
方法中,您不会关闭商店。if (cer != null && cer.Count >0)
-- IIRC, X509Certificate2Collection.Find
从不返回 null,所以只需检查返回的集合是否为非空。SHA512
。 使用 TLS 1.2 时,Windows 中禁用 SHA512更新 2 (14.12.2019)
我用昨天提供的修改重新编写了代码,代码有效。 CRYPT_E_UNEXPECTED_MSG_TYPE
错误表明签名者证书在证书存储中没有私钥。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.