I am trying to use nShield from Thales to generate pair of asymmetric keys on it.
I have found the following example on msdn:
CspParameters csp = new CspParameters(1, "eToken Base Cryptographic Provider");
csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
key = rsa.ToXmlString(true);
}
catch(Exception ex )
{
string s = ex.Message;
}
I can use KeySafe to succesfully connect and generate key-pairs on the HSM.
The code above throws the following exception:
System.Security.Cryptography.CryptographicException
"Invalid Signature." System.Security.Cryptography.CryptographicException
I have the feeling that I am not setting the correct second parameter in the CspParameters constructor. This is what it says in the example:
// The 1st parameter comes from HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Defaults\Provider Types.
// The 2nd parameter comes from HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Defaults\Provider.
I don't see any nCipher or nShield or Thales or anything like that there.
Edit:
Working test:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
byte[] data = Encoding.ASCII.GetBytes("string");
byte[] enc = rsa.Encrypt(data, false);
String dec = Encoding.ASCII.GetString(rsa.Decrypt(enc, false));
key = rsa.ToXmlString(true);
You need to run nCipher CSP install wizard which is located under Start > All Programs > nCipher in order to register nCipher CSP in your operating system. After that mentioned registry entries will be available and you will be able to read exact CSP name from them.
Related
My code
csp = new RSACryptoServiceProvider(cspParms);
causes the error: "Keyset does not exist".
string keyContainerName = "abcd";
CspParameters cspParms = new CspParameters(24, "Microsoft Enhanced RSA and AES Cryptographic Provider", keyContainerName);
cspParms.Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider csp = null;
csp = new RSACryptoServiceProvider(cspParms);
csp = new RSACryptoServiceProvider(cspParms);
causes error: "Object already exists".
Then I added the three lines below before calling
csp = new RSACryptoServiceProvider(cspParms);
CryptoKeyAccessRule rule = new CryptoKeyAccessRule("everyone", CryptoKeyRights.FullControl, AccessControlType.Allow);
cspParms.CryptoKeySecurity = new CryptoKeySecurity();
cspParms.CryptoKeySecurity.SetAccessRule(rule);
Then I got the error: "Keyset does not exist".
Even I removed the added three lines, I still got the error: Keyset does not exist.
I found the reason.
If I run as an admin, then there is no these error.
I was using windows 10, and I made everyone could read/write my users folder. But recently my windows was auto upgraded to win 11. The security setting for the users changed, this caused the trouble.
I've written an ASP.NET Core 2.0 website in C# and have Facebook authentication enabled, so it requires HTTPS. I'm using the native Kestrel web server to host the site and have a listener set to take the PFX certificate per MS' documentation. I can't seem to find a way for Kestrel to recognize the private key after recall from Key Vault. I know it's present, as I wrote two debug statements that indicate it is, in fact present.
This is the function that I'm using to retrieve the secret, which is working.
public static async Task<X509Certificate2> GetKeyVaultCert()
{
X509Certificate2 pfx;
try
{
var kvClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
var secret = await kvClient
.GetSecretAsync("https://macscampvault.vault.azure.net/secrets/letsencrypt").ConfigureAwait(false);
byte[] bytes;
if(secret.ContentType == "application/x-pkcs12")
bytes = Convert.FromBase64String(secret.Value);
else
{
bytes = new byte[0];
Console.WriteLine("secret is not PFX!!");
throw new ArgumentException("This is not a PFX string!!");
}
var password = new SecureString();
var coll = new X509Certificate2Collection();
coll.Import(bytes, null, X509KeyStorageFlags.Exportable);
pfx = coll[0];
// File output added in case I end up needing to write cert to container
// File.WriteAllBytes(Directory.GetCurrentDirectory().ToString() + "/Macs.pfx", bytes);
Console.WriteLine(pfx.HasPrivateKey);
Console.WriteLine(pfx.GetRSAPrivateKey());
}
catch (Exception ex)
{
Console.WriteLine($"There was a problem during the key vault operation\n{ex.Message}");
throw;
}
return pfx;
}
The debug statements after the assignment call pfx = coll[0]; tell me that this private key exists, but when I try to connect to the website using lynx https://localhost I receive the following exception:
System.NotSupportedException: The server mode SSL must use a certificate with the associated private key.
So, how do I use the private key? Here's a gist to the file in question.
I already was helped by How to serialize and deserialize a PFX certificate in Azure Key Vault? but after following it, I got to this state.
In your gist you have the following code:
var keyVaultCert = GetKeyVaultCert().Result ??
throw new ArgumentNullException("GetKeyVaultCert().Result");
pfx = new X509Certificate2(keyVaultCert.RawData);
The second line there removes the private key, because the RawData property just returns the DER encoded X.509 object.
keyVaultCert is already an X509Certificate2 with a private key, you probably want to just use it.
pfx = GetKeyVaultCert().Result ?? throw etc;
I have an x509 certificate with a public and private key that is stored on a safenet usb token.
I have some data I want to sign. I need to use the public key of the certificate to verify the signature.
Ultimate code doing the signing with my own self signed certificate:
RSACryptoServiceProvider rsa1 = (RSACryptoServiceProvider)useCertificate.PrivateKey;
byte[] digitalSignature = rsa1.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
And the code to verify using the public key of the certificate:
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)useCertificate.PublicKey.Key;
Verified = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), digitalSignature);
With the self signed certificate this works fine. The signature I get back is 256
Bytes.
With the token using this code to obtain the signature and then verify it, I get only 128 Byte signature and the verify fails:
CspParameters csp = new CspParameters(1, "SafeNet RSA CSP");
csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
csp.KeyNumber = (int)KeyNumber.Signature;
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(csp);
Verify code same as above.
I note that the certificate I want to use is the default in the token. Why am I only getting a 128 Byte signature back instead of 256? I suspect that is why it won't verify.
Do I need some other parameters and settings in my csp?
Thanks
* Update based on comments *
It's clear that I am using 1024 bits when I specify the csp.keyNumber = (int)KeyNumber.Signature - but this is the only way the token actually returns anything. Even though the token key size is 2048 bits and the key specification is AT_KEYEXCHANGE. When I use the exchange keynumber which I think is actually correct, then when I try to compute a signature I am prompted to login, but then I get an exception "The parameter is invalid". So I need one of 2 things as far as I can see:
1 - how to use the public key to verify the signature using 1024 bits (without the token - we need to verify on a machine without the token).
or
2 - how to set whatever is incorrect so that we can get passed the exception -- which I think is the better idea.
Does anyone have any advice on what I can do about this exception or what might be causing it?
Full exception details below:
HResult = -2147024809
Message = The parameter is incorrect.
Stack Trace
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash)
at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash)
at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, String str)
at TE.Program.Main(String[] args) in z:\Work\compusolve\enctest\TE\TE\Program.cs:line 77
The answer to this is two fold. If you are using one of these devices, I found that in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider
There are 3 different providers. Each with identical settings for type and even image - the dll used. But selecting a different one, in my case Datakey RSP CSP, provided the 256 byte signature based on the 2048 bit key. You also have to ensure that the certificate you are using is the default certificate in the token. In my case there were two different certificates. I was verifying using one, but signing using another.
Complete source code for a test client is below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
namespace TE
{
class Program
{
static void Main(string[] args)
{
try
{
// these variables should be changed to math your installation
// find CSP's in this windows registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider
string TokenCSPName = "Datakey RSA CSP";
string TokenCertificateName = "ACME Inc";
string NonTokenCertificateName = "SelfSigned";
string certLocation = "Token"; // change to something else to use self signed "Token" for token
// the certificate on the token should be installed into the local users certificate store
// tokens will not store or export the private key, only the public key
// find the certificate we want to use - there's no recovery if the certificate is not found
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly);
X509Certificate2Collection certificates = store.Certificates;
X509Certificate2 certificate = new X509Certificate2();
X509Certificate2 useCertificate = new X509Certificate2();
if (certLocation == "Token")
{
for (int i = 0; i < certificates.Count; i++)
{
certificate = certificates[i];
string subj = certificate.Subject;
List<X509KeyUsageExtension> extensions = certificate.Extensions.OfType<X509KeyUsageExtension>().ToList();
if (certificate.GetNameInfo(X509NameType.SimpleName, false).ToString() == TokenCertificateName)
{
for (int j = 0; j < extensions.Count; j++)
{
if ((extensions[j].KeyUsages & X509KeyUsageFlags.DigitalSignature) == X509KeyUsageFlags.DigitalSignature)
{
useCertificate = certificate;
j = extensions.Count + 1;
}
}
}
}
} else
{
for (int i = 0; i < certificates.Count; i++)
{
certificate = certificates[i];
string subj = certificate.Subject;
List<X509KeyUsageExtension> extensions = certificate.Extensions.OfType<X509KeyUsageExtension>().ToList();
if (certificate.GetNameInfo(X509NameType.SimpleName, false).ToString() == NonTokenCertificateName)
useCertificate = certificate;
}
}
CspParameters csp = new CspParameters(1, TokenCSPName);
csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
csp.KeyNumber = (int)KeyNumber.Exchange;
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(csp);
string SignatureString = "Data that is to be signed";
byte[] plainTextBytes = Encoding.ASCII.GetBytes(SignatureString);
bool Verified = false;
using (SHA1CryptoServiceProvider shaM = new SHA1CryptoServiceProvider())
{
// hash the data to be signed - you can use signData and avoid the hashing if you like
byte[] hash = shaM.ComputeHash(plainTextBytes);
// sign the hash
byte[] digitalSignature = rsa1.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
// check your signature size here - if not 256 bytes then you may not be using the proper
// crypto provider
// Verify the signature with the hash
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)useCertificate.PublicKey.Key;
Verified = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), digitalSignature);
if (Verified)
{
Console.WriteLine("Signature Verified");
}
else
{
Console.WriteLine("Signature Failed Verification");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
I have to challenge your assertion that it's actually the default key container key (you may have caused that one to be created the first time your code ran, since you didn't assert the UseExistingKey flag).
Assuming the certificate is in your cert store, run certutil -user -silent store my and find the certificate entry and check the Key Container value:
================ Certificate 11 ================
Serial Number: 0123456789abcdeffedcba9876543210
Issuer: CN=Intermediate Certificate Authority
NotBefore: 10/21/2016 7:26 AM
NotAfter: 10/21/2017 7:26 AM
Subject: CN=bartonjs
Non-root Certificate
Template:
Cert Hash(sha1): 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14
Key Container = le-Smartcard-987abcdf-6332-43-16531
Provider = Microsoft Base Smart Card Crypto Provider
If you copy/paste whatever value that is and use it as the key container name your signatures will probably start being the correct size.
(If your certificate is in the machine store instead of the user store, omit the -user option)
I am developing an encryption decryption software. I used RSA encryption to encypt my symmetric key.
I followed the code provided in Walkthrough: Creating a Cryptographic Application
My encryption and decryption done successfully in same machine. But when I tried to decrypt from other computer, an error: bad data is occurring.(It can be decrypted from same machine.)
I think the problem is on getting private key from keycontainer. How to get the private key generated in first machine in the second machine.
I googled a lot but everything in same machine.
Please help me, give me an idea to get private key in other machine.
public void GetPrivateKey()
{
string c;
cspp.KeyContainerName = keyName;
rsa = new RSACryptoServiceProvider(cspp);
rsa.PersistKeyInCsp = true;
if (rsa.PublicOnly == true)
c= "Key: " + cspp.KeyContainerName + " - Public Only";
else
c = "Key: " + cspp.KeyContainerName + " - Full Key Pair";
}
public string decryptkey(string at)
{
byte[] KeyEncrypted;
KeyEncrypted = File.ReadAllBytes(at);
//System.IO.File.ReadAllBytes(at);//for good
objr.GetPrivateKey();
byte[] KeyDecrypted = objr.rsa.Decrypt(KeyEncrypted, false);
string skey = GetString(KeyDecrypted);
return skey;
}
Bad data Error happens in this line,
byte[] KeyDecrypted = objr.rsa.Decrypt(KeyEncrypted, false);.
Please..
Use the RSACryptoServiceProvider.ToXmlString method to export the private key. You need to pass true to this method to export the private key. This will generate for you an XML document that contains the key parameters including the private parameters.
On the second machine, use RSACryptoServiceProvider.FromXmlString to import the private key into a RSACryptoServiceProvider instance.
However, for security reasons, I recommend that instead of doing this, generate the private key on one machine (the machine that will do the decryption part), and then use the RSACryptoServiceProvider.ToXmlString and pass false to it to just export the public key. On the other machine (that will do the encryption part), import the public key using the RSACryptoServiceProvider.FromXmlString method.
Using the public key alone, you can do the encryption part of the process.
It is only for decryption that you are required to have the private key.
Here is some sample code:
//Do this on one machine
RSACryptoServiceProvider rsa_machine1 = new RSACryptoServiceProvider(); //You might initialize this in a different way
var xml = rsa_machine1.ToXmlString(true); //or pass false to just export the public key
Now take the value of the xml variable to the other machine (maybe by saving it to a file and then manually copying that file to the second machine)
//This is done on the second machine
RSACryptoServiceProvider rsa_machine2 = new RSACryptoServiceProvider();
rsa_machine2.FromXmlString(xml);
I need to create a self-signed certificate (for local encryption - it's not used to secure communications), using C#.
I've seen some implementations that use P/Invoke with Crypt32.dll, but they are complicated and it's hard to update the parameters - and I would also like to avoid P/Invoke if at all possible.
I don't need something that is cross platform - running only on Windows is good enough for me.
Ideally, the result would be an X509Certificate2 object that I can use to insert into the Windows certificate store or export to a PFX file.
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-----");
}
}
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).
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.
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.
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
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.