如何使用 C # 创建自签名证书?

我需要使用 C # 创建一个自签名证书(用于本地加密——它不用于保护通信)。

我见过一些使用 请求Crypt32.dll的实现,但它们很复杂,很难更新参数——如果可能的话,我还想避免 P/Invoke。

我不需要跨平台的东西-只在 Windows 上运行对我来说已经足够好了。

理想情况下,结果将是一个 X509證 icate2对象,我可以使用该对象将其插入 Windows 证书存储或导出到 PFX文件。

119764 次浏览

This implementation uses the CX509CertificateRequestCertificate COM object (and friends - MSDN doc) from certenroll.dll to create a self signed certificate request and sign it.

The example below is pretty straight forward (if you ignore the bits of COM stuff that goes on here) and there are a few parts of the code that are really optional (such as EKU) which are none-the-less useful and easy to adapt to your use.

public static X509Certificate2 CreateSelfSignedCertificate(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 = true;
privateKey.Length = 2048;
privateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE; // use is not limited
privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
privateKey.Create();


// Use the stronger SHA512 hashing algorithm
var hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
AlgorithmFlags.AlgorithmFlagsNone, "SHA512");


// 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.ContextMachine, privateKey, "");
cert.Subject = dn;
cert.Issuer = dn; // 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;
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
);
}

The result can be added to a certificate store using X509Store or exported using the X509Certificate2 methods.

For a fully managed and not tied to Microsoft's platform, and if you're OK with Mono's licensing, then you can look at X509CertificateBuilder from Mono.Security. Mono.Security is standalone from Mono, in that it doesn't need the rest of Mono to run and can be used in any compliant .Net environment (e.g. Microsoft's implementation).

You can use the free PluralSight.Crypto library to simplify programmatic creation of self-signed X.509 certificates:

    using (CryptContext ctx = new CryptContext())
{
ctx.Open();


X509Certificate2 cert = ctx.CreateSelfSignedCertificate(
new SelfSignedCertProperties
{
IsPrivateKeyExportable = true,
KeyBitLength = 4096,
Name = new X500DistinguishedName("cn=localhost"),
ValidFrom = DateTime.Today.AddDays(-1),
ValidTo = DateTime.Today.AddYears(1),
});


X509Certificate2UI.DisplayCertificate(cert);
}

PluralSight.Crypto requires .NET 3.5 or later.

Another option is to use the CLR Security extensions library from CodePlex, which implements a helper function to generate self-signed X.509 certificates:

X509Certificate2 cert = CngKey.CreateSelfSignedCertificate(subjectName);

You can also look at the implementation of that function (in CngKeyExtensionMethods.cs) to see how to create the self-signed certificate explicitly in managed code.

This is the Powershell version on how to create a certificate. You can use it by executing the command. Check https://technet.microsoft.com/itpro/powershell/windows/pkiclient/new-selfsignedcertificate

Edit: forgot to say that after you create the certificate, you can use the Windows "manage computer certificates" program, to export the certificate to .CER or other type.

Since .NET 4.7.2 you can create self-signed certs using System.Security.Cryptography.X509Certificates.CertificateRequest.

For example:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;


public class CertificateUtil
{
static void MakeCert()
{
var ecdsa = ECDsa.Create(); // generate asymmetric key pair
var req = new CertificateRequest("cn=foobar", ecdsa, HashAlgorithmName.SHA256);
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));


// Create PFX (PKCS #12) with private key
File.WriteAllBytes("c:\\temp\\mycert.pfx", cert.Export(X509ContentType.Pfx, "P@55w0rd"));


// Create Base 64 encoded CER (public key only)
File.WriteAllText("c:\\temp\\mycert.cer",
"-----BEGIN CERTIFICATE-----\r\n"
+ Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)
+ "\r\n-----END CERTIFICATE-----");
}
}

If it helps anyone else, I needed to generate a test certificate in PEM format (so needed crt and key files), using the answer from Duncan Smart, I produced the following...

public static void MakeCert(string certFilename, string keyFilename)
{
const string CRT_HEADER = "-----BEGIN CERTIFICATE-----\n";
const string CRT_FOOTER = "\n-----END CERTIFICATE-----";


const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----\n";
const string KEY_FOOTER = "\n-----END RSA PRIVATE KEY-----";


using var rsa = RSA.Create();
var certRequest = new CertificateRequest("cn=test", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);


// We're just going to create a temporary certificate, that won't be valid for long
var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(1));


// export the private key
var privateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey(), Base64FormattingOptions.InsertLineBreaks);


File.WriteAllText(keyFilename, KEY_HEADER + privateKey + KEY_FOOTER);


// Export the certificate
var exportData = certificate.Export(X509ContentType.Cert);


var crt = Convert.ToBase64String(exportData, Base64FormattingOptions.InsertLineBreaks);
File.WriteAllText(certFilename, CRT_HEADER + crt + CRT_FOOTER);
}

Extending 0909EMs answer with SubjectAlternativeNames based on code found here: Understanding self-signed certificates in c#

        public static void MakeCert(string certFilename, string keyFilename)
{
const string CRT_HEADER = "-----BEGIN CERTIFICATE-----\n";
const string CRT_FOOTER = "\n-----END CERTIFICATE-----";


const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----\n";
const string KEY_FOOTER = "\n-----END RSA PRIVATE KEY-----";


using var rsa = RSA.Create();
var certRequest = new CertificateRequest("cn=test", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);


// Adding SubjectAlternativeNames (SAN)
var subjectAlternativeNames = new SubjectAlternativeNameBuilder();
subjectAlternativeNames .AddDnsName("test");
certRequest.CertificateExtensions.Add(subjectAlternativeNames.Build());


// We're just going to create a temporary certificate, that won't be valid for long
var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(1));


// export the private key
var privateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey(), Base64FormattingOptions.InsertLineBreaks);


File.WriteAllText(keyFilename, KEY_HEADER + privateKey + KEY_FOOTER);


// Export the certificate
var exportData = certificate.Export(X509ContentType.Cert);


var crt = Convert.ToBase64String(exportData, Base64FormattingOptions.InsertLineBreaks);
File.WriteAllText(certFilename, CRT_HEADER + crt + CRT_FOOTER);
}

And for definition of the usage of a key using X509KeyUsageExtension look here https://stackoverflow.com/a/48210587/226278