CNGKey.Create won't persist - c#

I'm playing with CNGKey and the storage. I would like to store the key, and later retrieve it for encryption.
I am usingCngKey.Create and I see that it is persisted in the file system. To test access to it, immediately after the Create command I get false for CngKey.Existsm using visual studio's 'watch' window.
This happens for both RSA, using Microsoft's built in enum, and AES, using "AES" string.
My code for AES:
CngKeyCreationParameters keyParams = new CngKeyCreationParameters
{
ExportPolicy = CngExportPolicies.AllowExport,
KeyCreationOptions = CngKeyCreationOptions.MachineKey | CngKeyCreationOptions.OverwriteExistingKey,
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
//KeyUsage = CngKeyUsages.Decryption
};
CngAlgorithm aesAlgorithm = new CngAlgorithm("AES");
CngKey.Create(aesAlgorithm, "mykeyAES", keyParams);
My code for RSA:
CngKeyCreationParameters keyParams = new CngKeyCreationParameters
{
ExportPolicy = CngExportPolicies.AllowExport,
KeyCreationOptions = CngKeyCreationOptions.MachineKey | CngKeyCreationOptions.OverwriteExistingKey,
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
//KeyUsage = CngKeyUsages.Decryption
};
if (!CngKey.Exists(keyName, CngProvider.MicrosoftSoftwareKeyStorageProvider))
{
CngKey key = CngKey.Create(CngAlgorithm.Rsa, keyName, keyParams);
}
The only relevant information I get from searching the web, is getting to the same questions on SO which don't help me much with my specific case.
Appreciate any help!
Edit:
According to #Martheen's reply, Open has changed to:
CngKey key = CngKey.Open(keyName, CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey);
I'm getting true on CngKey.Exists but get an exception 'keypair does not exist'

If you create the key as machine-wide, you'd have to specify it too on accessing them
CngKey.Exists("mykeyAES", CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey));
and
CngKey.Open("mykeyRSA", CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey));

Related

How to create non-exportable private key with X509 certificate in windows key store programmatically (C#)

I am trying to create a private key and add a certificate(self-signed or signed from a CA) where I should be able to export the certificate only and make the private key non-exportable in C#. That is, if someone tries to export the certificate from certmgr, the export option will be disabled like this picture-
I want the same non-exportable option programmatically in C# while creating it. The private key usually becomes non-exportable when a .pfx/.p12 file is installed using Crypto Shell Extensions when Mark this Key is exportable is unchecked.
I can successfully create key pairs and add certificate entries in the windows key store. But Yes, export the private key option always becomes enabled that is I can't restrict the private key from export. I have tried this -
public void init(){
AsymmetricCipherKeyPair asymmetricCipherKeyPair = GetKeyPair();
X509Name issuer = this.GenerateRelativeDistinguishedName("test org");
X509Name subject = this.GenerateRelativeDistinguishedName("test user1");
Org.BouncyCastle.X509.X509Certificate cert = GenerateCertificate(issuer, subject, asymmetricCipherKeyPair.Private, asymmetricCipherKeyPair.Public);
importSelfSignedCert(asymmetricCipherKeyPair, cert);
}
private AsymmetricCipherKeyPair GetKeyPair()
{
return new Pkcs1xHandler().GenerateKeyPair(Constants.RsaKeyLength.Length2048Bits);
}
protected X509Name GenerateRelativeDistinguishedName(String commonName)
{
IDictionary attributes = new Hashtable();
IList ordering;
attributes.Add(X509Name.CN, commonName);
ordering = new ArrayList(attributes.Keys);
return new X509Name(ordering, attributes);
}
protected void importSelfSignedCert(AsymmetricCipherKeyPair asymmetricCipherKeyPair, Org.BouncyCastle.X509.X509Certificate cert)
{
try
{
int ID =1;
AsymmetricCipherKeyPair ackp = asymmetricCipherKeyPair;
var rsaPriv = Org.BouncyCastle.Security.DotNetUtilities.ToRSA(ackp.Private as RsaPrivateCrtKeyParameters);
// Setup RSACryptoServiceProvider with "KeyContainerName" set to "KeyContainer"+ enrollmentID
var csp = new CspParameters();
csp.KeyContainerName = "TestPrivKey" + ID;
csp.Flags |= CspProviderFlags.UseMachineKeyStore;
var rsaPrivate = new RSACryptoServiceProvider(csp);
// Import private key to windows keystrore, from already generated BouncyCastle rsa privatekey
rsaPrivate.ImportParameters(rsaPriv.ExportParameters(true));
//Console.Write("rsaprivate key:" + rsaPrivate.ToXmlString(true));
System.Security.Cryptography.X509Certificates.X509Certificate2 certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2();
var flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet;
certificate.Import(cert.GetEncoded(), String.Empty, flags);
certificate.PrivateKey = rsaPrivate;
// opening up the windows cert store because thats where I want to save it.
System.Security.Cryptography.X509Certificates.X509Store store = new System.Security.Cryptography.X509Certificates.X509Store(System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser);
store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.MaxAllowed);
store.Add(certificate);
store.Close();
rsaPrivate.PersistKeyInCsp = true; //persisting the key in container is important to retrieve the key later
///make non exporable
csp.Flags = CspProviderFlags.UseNonExportableKey;
var rsaPrivate2 = new RSACryptoServiceProvider(csp);
rsaPrivate2.ExportParameters(false); //restrict to export
rsaPrivate2.PersistKeyInCsp = true;
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("Error : " + e);
Console.WriteLine(e);
Log.Print(LogLevel.High, e.ToString());
}
}
And when I try to export the certificate, I get the option to Yes, export the private key too, like the below image -
Is there any way to make the private-key non-exportable like the first image while creating it programmatically? I would grateful for any hints, references or code samples. Thanks.
To make the private key non-exportable, the CspProviderFlags.UseNonExportableKey must additionally be set when importing the key:
...
var csp = new CspParameters();
csp.KeyContainerName = "TestPrivKey" + ID;
csp.Flags |= CspProviderFlags.UseMachineKeyStore | CspProviderFlags.UseNonExportableKey; // Fix
var rsaPrivate = new RSACryptoServiceProvider(csp);
rsaPrivate.ImportParameters(rsaPriv.ExportParameters(true));
...
If this is done, the Yes, export the private key option is disabled in the wizard.
Note that in the posted code that flag is also set, but it is set too late, namely after the certificate has been saved to the store.

Sign a CSR with Azure Key Vault

How can I achieve the very basic CSR Signing HSM functionality with Azure Key Vault?
I had found a very long and manual process to somehow achieve it:
Create a private key in Key Vault
Create a CSR, digest it with SHA256
Sign the digest with the previous private key using the Sign() method
Create a local x.509 cert and append the signature
Upload the new signed cert to Key Vault
Problem is, it is manual, long (also, quite a bit of latency) and error prone. Also I haven't found a single C# code example for this, and I'm looking for EC and not RSA.
The question is, is there a simple CertificateRequest.Sign() function in Key Vault? this seems to be so basic for an HSM-like service...
Thanks
This blog post by Vitaliy Slepakov describes a solution that he created which implements the steps you listed above using C#/.NET Core.
The code is available here:
https://github.com/vslepakov/keyvault-ca/
The heart of it is the following:
byte[] certificateRequest = /* ... */;
string issuerCertificateName = /* ... */;
KeyVaultServiceClient keyVaultServiceClient = /* ... */;
X509SignatureGenerator generator = /* see next section */;
var pkcs10CertificationRequest = new Pkcs10CertificationRequest(certificateRequest);
//TODO: Validate CSR via pkcs10CertificationRequest.Verify()
var info = pkcs10CertificationRequest.GetCertificationRequestInfo();
var notBefore = DateTime.UtcNow.AddDays(-1);
// Get the RSA public key from the CSR
var asymmetricKeyParameter = Org.BouncyCastle.Security.PublicKeyFactory.CreateKey(info.SubjectPublicKeyInfo);
var rsaKeyParameters = (Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)asymmetricKeyParameter;
var rsaKeyInfo = new RSAParameters
{
Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(),
Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned()
};
var publicKey = RSA.Create(rsaKeyInfo);
//TODO: Validate publicKey
var certBundle = await keyVaultServiceClient.GetCertificateAsync(issuerCertificateName).ConfigureAwait(false);
var signingCert = new X509Certificate2(certBundle.Cer);
// new serial number
var serialNumber = new byte[SerialNumberLength];
RandomNumberGenerator.Fill(serialNumber);
serialNumber[0] &= 0x7F;
var subjectDN = new X500DistinguishedName(subjectName);
var request = new CertificateRequest(subjectDN, publicKey, GetRSAHashAlgorithmName(hashSizeInBits), RSASignaturePadding.Pkcs1);
// Basic constraints
request.CertificateExtensions.Add(
new X509BasicConstraintsExtension(caCert, caCert, 0, true));
// Subject Key Identifier
var ski = new X509SubjectKeyIdentifierExtension(
request.PublicKey,
X509SubjectKeyIdentifierHashAlgorithm.Sha1,
false);
request.CertificateExtensions.Add(ski);
// Authority Key Identifier
if (issuerCAKeyCert != null)
request.CertificateExtensions.Add(BuildAuthorityKeyIdentifier(issuerCAKeyCert));
else
request.CertificateExtensions.Add(BuildAuthorityKeyIdentifier(subjectDN, serialNumber.Reverse().ToArray(), ski));
if (caCert)
request.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature |
X509KeyUsageFlags.KeyCertSign |
X509KeyUsageFlags.CrlSign,
true));
else
{
// Key Usage
var defaultFlags =
X509KeyUsageFlags.DigitalSignature |
X509KeyUsageFlags.DataEncipherment |
X509KeyUsageFlags.NonRepudiation |
X509KeyUsageFlags.KeyEncipherment;
request.CertificateExtensions.Add(new X509KeyUsageExtension(defaultFlags, true));
// Enhanced key usage
request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection {
new Oid("1.3.6.1.5.5.7.3.1"),
new Oid("1.3.6.1.5.5.7.3.2") }, true));
}
if (issuerCAKeyCert != null)
{
if (notAfter > issuerCAKeyCert.NotAfter)
{
notAfter = issuerCAKeyCert.NotAfter;
}
if (notBefore < issuerCAKeyCert.NotBefore)
{
notBefore = issuerCAKeyCert.NotBefore;
}
}
var issuerSubjectName = issuerCAKeyCert != null ? issuerCAKeyCert.SubjectName : subjectDN;
X509Certificate2 signedCert = request.Create(
issuerSubjectName,
generator,
notBefore,
notAfter,
serialNumber);
The custom X509SignatureGenerator implementation is here and used the following Key Vault SDK method:
HashAlgorithm hash = /* see GitHub */;
var digest = hash.ComputeHash(data);
var resultKeyVaultPkcs = await keyVaultClient.SignAsync(signingKey, algorithm, digest, RSASignaturePadding.Pkcs1);
Hopefully you can adapt this code to meet your needs. I'll be doing that as well. 😀
KV has a sign operation, but it's not specific to any data type (such as a cert request).
The KV .NET SDK offers this https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.keyvault.keyvaultclient.signwithhttpmessagesasync?view=azure-dotnet-legacy which is just a wrapper over REST APIs https://learn.microsoft.com/en-us/rest/api/keyvault/
This isn't exactly what you want, but I wrote some code to demonstrate C# KV encrypt/decrypt which should help you get started https://github.com/x509cert/AzureKeyVault

Create certificate in azure key vault using key vault stored self signed certificate

I created a self signed certificate in Azure Key Vault using the following method :
public void CreateRootCertificate()
{
var certPolicy = new CertificatePolicy();
certPolicy.Attributes = new CertificateAttributes();
certPolicy.Attributes.NotBefore = DateTime.Now;
certPolicy.Attributes.Expires = DateTime.Now.AddDays(1);
certPolicy.IssuerParameters = new IssuerParameters()
{
Name = "Self",
};
certPolicy.KeyProperties = new KeyProperties(true);
certPolicy.SecretProperties = new SecretProperties();
certPolicy.X509CertificateProperties = new X509CertificateProperties()
{
Subject = "CN=testyMcTesterson",
};
var operation = this.client.CreateCertificateAsync(keyVaultUrl, testRootName, certPolicy);
operation.Wait();
}
Now after I create the self signed certificate, I'd like to sign other certificates using this one. The only caveat is, I'd like to do it without having to pull the private key out of key vault. Is this even possible? I've tried several permutations of the following method.
public void CreateSignedCertificate()
{
var certPolicy = new CertificatePolicy();
certPolicy.Attributes = new CertificateAttributes();
certPolicy.Attributes.NotBefore = DateTime.Now;
certPolicy.Attributes.Expires = DateTime.Now.AddDays(1);
certPolicy.IssuerParameters = new IssuerParameters()
{
Name = "CN=testyMcTesterson"
};
certPolicy.KeyProperties = new KeyProperties(true);
certPolicy.SecretProperties = new SecretProperties();
certPolicy.X509CertificateProperties = new X509CertificateProperties()
{
Subject = "CN=testyJunior",
};
var operation = this.client.CreateCertificateAsync(keyVaultUrl, "testyJunior", certPolicy);
operation.Wait();
}
This includes setting the issuer to "testyMcTesterson" without CN=, setting it to the key vault certificate identifier and the key vault secret. I'd like to set it up so only the .cer file of the signing cert will ever leave key vault. All of these throw a 400 excpetion saying the IssuerParameters.Name property is invalid. I realize I'm more than likely missing some EKUs on both the root and the client, but the problem I'm trying to address right now is finding out if this scenario is even feasible. The documentation on the IssuerParameters class is lacking.

x509 certificate example in OpenSSL .NET Wrapper [duplicate]

Hi Iam using the OpenSSL .NET wrapper in my c# project. i want to generate an X509 certification but i don't really know the procedure. what should it contain (what parameters)...etc
this is my code, I did it after looking some tests:
OpenSSL.X509.X509Certificate x509 = new OpenSSL.X509.X509Certificate();
OpenSSL.Crypto.RSA rsa = new OpenSSL.Crypto.RSA();
rsa.GenerateKeys(1024, 0x10001, null, null);
OpenSSL.Crypto.CryptoKey key = new OpenSSL.Crypto.CryptoKey(rsa);
OpenSSL.Crypto.MessageDigestContext digest = new OpenSSL.Crypto.MessageDigestContext(
OpenSSL.Crypto.MessageDigest.SHA1);
I suppose that the certificate should take RSA private key and the digest as parameters and i have to configure it (date...and others parameters).
Can any one help me about that ? to finish my code ? thank you.
I use the following routine:
// Initialize the following with your information
var serial = 1234;
var issuer = new X509Name("issuer");
var subject = new X509Name("subject");
// Creates the key pair
var rsa = new RSA();
rsa.GenerateKeys(1024, 0x10001, null, null);
// Creates the certificate
var key = new CryptoKey(rsa);
var cert = new X509Certificate(serial, subject, issuer, key, DateTime.Now, DateTime.Now.AddYears(20));
// Dumps the certificate into a .cer file
var bio = BIO.File("C:/temp/cert.cer", "w");
cert.Write(bio);

Key Size in RSA C# is not changing !

I am generating key pair and store them in xml file using
ToXmlString(true);
I need to set the key size to 2048
according to the MSDN the only place to do this is from the constructor of the RSACryptoServiceProvider
private void AssignParameter(ProviderType providerType)
{
CspParameters cspParams;
cspParams = new CspParameters((int)providerType);
cspParams.KeyContainerName = RSAEncryption.containerName;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
cspParams.KeyNumber = (int)KeyNumber.Exchange;
this.rsa = new RSACryptoServiceProvider(2048, cspParams);
}
when I check the key size using
int x = this.rsa.KeySize;
I always get 1024
so whats the wrong here??
I've seen this before, try changing the container name or try
using (this.rsa = new RSACryptoServiceProvider(2048, cspParams))
{
}
or this.rsa.Clear(); after you are done with it.
If you already have a container with the same name it will re-use the container I believe.
You need first to clear the existing container like this:
rsa.PersistKeyInCsp = false;
rsa.Clear();
Then it should work with you.
Don't forget to set:
rsa.PersistKeyInCsp = true;

Categories