Application specific isolated CngKey - c#

I am creating a CngKey. The key is being generated successfully. The problem I am facing is that, I want to use concept of Sandboxing. Like in Android or iOS, one application cannot access the data of another application. I want the similar behavior in windows application. Here is my code to create a CngKey:
private CngKey generateKeyPair(string keyId)
{
CngKey key = null;
if (CngKey.Exists(keyId, CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey))
{
key = CngKey.Open(keyId, CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey);
}
else
{
key = CngKey.Create(CngAlgorithm.ECDsaP256, keyId, new CngKeyCreationParameters
{
ExportPolicy = CngExportPolicies.None,
KeyCreationOptions = CngKeyCreationOptions.MachineKey,
KeyUsage = CngKeyUsages.AllUsages,
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
UIPolicy = new CngUIPolicy(CngUIProtectionLevels.None)
});
}
return key
}
Let's say this key is created by Application_A. But if Application_B knows the keyId, it can access the same key. I don't want this. I cannot use GenericRead access rule to restrict it:
new CryptoKeyAccessRule(
new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null),
CryptoKeyRights.GenericRead,
AccessControlType.Allow)
Since I have to access it at various points in Application_A.
How can I achieve my goal? Am I using the right thing for my purpose?

Related

C# Add two HTTPS bindings with two different certificate with the ServerManager not working

I am trying to create two different bindings on two different site on IIS with C# from my website.
Those 2 binding needs to be set with a certificate (one specific for each bindings). The problem is, the bindings are correctly created but the two bindings are created with the same certificate.
Here my code :
public async Task AddBindings(string code)
{
await AddBinding("Website1", "website1.com");
await AddBinding("Website2", "website2.com");
}
private async Task AddBinding(string siteName, string urlDomain)
{
using (ServerManager serverMgr = new ServerManager())
{
var site = serverMgr.Sites[siteName];
var certif = GetCertificate("*." + urlDomain);
site.Bindings.Add("*:443:" + urlDomain, certif.GetCertHash(), "My");
serverMgr.CommitChanges();
serverMgr.Dispose();
}
}
private X509Certificate2 GetCertificate(string nameStartWith)
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly);
X509Certificate2 certif = null;
foreach (var certificate in store.Certificates)
{
var certifDate = DateTime.Parse(certificate.GetEffectiveDateString());
if (certificate.FriendlyName.StartsWith(nameStartWith))
{
certif = certificate;
}
}
store.Close();
return certif;
}
The "funny things" in the function AddBindings, in this order the two bindings are going to be created with the Website2 certificate, and if I change the order of the two rows it's going to use Website1.
Thank you very much for your helps !
Your code definitely leads to that, because nowhere you specify that SNI mappings should be used.
The correct function call you should make is this overloading version,
Add (string bindingInformation, byte[] certificateHash, string certificateStoreName, Microsoft.Web.Administration.SslFlags sslFlags)
You can read more about SNI mappings in Windows HTTP API from here.

C# asp.net CERTENROLLLib PKCS10 creation

I'm trying to create a keypair running the above code sample. I'm running this inside a activex. I have no problem to run this local, but when I install it on my server, it's not working properly, it only works if I run my IE as administrator.
System.UnauthorizedAccessException:
CertEnroll::CX509PrivateKey::Create: Access Denied. 0x80070005 (WIN32:
5)
em CERTENROLLLib.IX509PrivateKey.Create()
Any tips on how to run this without adm permission? Or there's any other way to create a key pair, send to CA and write the cert to a smartcard?
I'm following this code:
https://blogs.msdn.microsoft.com/alejacma/2008/09/05/how-to-create-a-certificate-request-with-certenroll-and-net-c/
public String CreateBase64KeyPair(string CN)
{
string msg = string.Empty;
try
{
CX509CertificateRequestPkcs10 objPkcs10 = (CX509CertificateRequestPkcs10)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509CertificateRequestPkcs10"));
IX509PrivateKey objPrivateKey = (IX509PrivateKey)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey"));
CCspInformation objCSP = (CCspInformation)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CCspInformation"));
CCspInformations objCSPs = (CCspInformations)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CCspInformations"));
CX500DistinguishedName objDN = (CX500DistinguishedName)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName"));
CX509Enrollment objEnroll = (CX509Enrollment)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509Enrollment"));
CObjectIds objObjectIds = (CObjectIds)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CObjectIds"));
CObjectId objObjectId = (CObjectId)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CObjectId"));
CX509ExtensionKeyUsage objExtensionKeyUsage = (CX509ExtensionKeyUsage)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionKeyUsage"));
CX509ExtensionEnhancedKeyUsage objX509ExtensionEnhancedKeyUsage = (CX509ExtensionEnhancedKeyUsage)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionEnhancedKeyUsage"));
// Initialize the csp object using the desired Cryptograhic Service Provider (CSP)
objCSP.InitializeFromName(YPSIDCSP_NAME);
objCSP.GetDefaultSecurityDescriptor(true);
// Add this CSP object to the CSP collection object
objCSPs.Add(objCSP);
//Provide key container name, key length and key spec to the private key object
objPrivateKey.Length = 1024; //KEY_LEN_MY_DEFAULT
objPrivateKey.ProviderType = X509ProviderType.XCN_PROV_RSA_FULL; //XEnroll.ProviderType=1
objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE; //XEnroll.KeySpec=AT_KEYEXCHANGE
objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
objPrivateKey.MachineContext = false;
// Provide the CSP collection object (in this case containing only 1 CSP object) to the private key object
objPrivateKey.CspInformations = objCSPs;
// Create the actual key pair
objPrivateKey.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
objPkcs10.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, objPrivateKey, string.Empty);
// Key Usage Extension
objExtensionKeyUsage.InitializeEncode(
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE
);
objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
// Enhanced Key Usage Extension
objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2"); // OID for Client Authentication usage
objObjectIds.Add(objObjectId);
objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
// Encode the name in using the Distinguished Name object
objDN.Encode("CN=" + CN.Trim(), X500NameFlags.XCN_CERT_NAME_STR_NONE);
// Assing the subject name by using the Distinguished Name object initialized above
objPkcs10.Subject = objDN;
// Create enrollment request
objEnroll.InitializeFromRequest(objPkcs10);
return objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
}
catch (Exception ex)
{
return ex.ToString();
}
}

adding extensions to a certificate request ( password-challenge ) with C# and CertENrollLib

I have to add extensions to a certificate request ( CSR ) in such a way that I respect a given structure. Namely this one
On the left is the structure I must respect for the chalenge password, on the right the structure I get when I simply generate a OID object from the challenge-password OID value, then embedding all this directly into the extension list of the PKCS10 request:
CObjectId cp_oid = new CObjectId();
// OID 1.2.840.113549.1.9.7
// cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
cp_oid.InitializeFromValue("1.2.840.113549.1.9.7");
then I create a CX509Extension object add the OID to the PKCS10 request:
CX509Extension extension = new CX509Extension();
string b64__challengePassword=System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(this.challengePassword));
extension.Initialize(cp_oid, EncodingType.XCN_CRYPT_STRING_BASE64_ANY, b64__challengePassword);
_certificateRequest.X509Extensions.Add(extension);
since the structure is clearly different from what I must obtain ( see the right part of the previous picture ) , I am now using a more sophisticated approach:
_certificateRequest = new CX509CertificateRequestPkcs10();
_certificateRequest.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, (CX509PrivateKey)_privateKey, null);
_certificateRequest.Subject = (CX500DistinguishedName)_subjectName;
CObjectIds cp_oids = new CObjectIds();
CObjectId cp_oid = new CObjectId();
// OID 1.2.840.113549.1.9.7
// cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
cp_oid.InitializeFromValue("1.2.840.113549.1.9.7");
CX509Extension _extension = new CX509Extension();
cp_oids.Add(cp_oid);
//now how do I add that oid list to the 1.2.840.113549.1.9.14 OID ?
//I try with CX509ExtensionEnhancedKeyUsage instead of a simple CX509Extension
//which one of all these is the correct extensions?
/*
* IX509ExtensionAlternativeNames Specifies one or more alternative name forms for the subject of a certificate.
IX509ExtensionAuthorityKeyIdentifier Represents an AuthorityKeyIdentifier extension.
IX509ExtensionBasicConstraints Specifies whether the certificate subject is a certification authority and, if so, the depth of the subordinate certification authority chain.
IX509ExtensionCertificatePolicies Represents a collection of policy information terms.
IX509ExtensionMSApplicationPolicies Represents a collection of object identifiers that indicate how a certificate can be used by an application.
IX509ExtensionEnhancedKeyUsage Represents a collection of object identifiers that identify the intended uses of the public key contained in a certificate.
IX509ExtensionKeyUsage Represents restrictions on the operations that can be performed by the public key contained in the certificate.
IX509Extensions Manages a collection of IX509Extension objects.
IX509ExtensionSmimeCapabilities Represents a collection that reports the decryption capabilities of an email recipient to an email sender.
IX509ExtensionSubjectKeyIdentifier Represents a SubjectKeyIdentifier extension used to identify a signing certificate.
IX509ExtensionTemplate Represents a CertificateTemplate extension that contains a version 2 template.
IX509ExtensionTemplateName Represents a CertificateTemplateName extension that contains a version 1 template.
*/
CX509ExtensionEnhancedKeyUsage eku = new CX509ExtensionEnhancedKeyUsage();
eku.InitializeEncode(cp_oids);
eku.Critical = false;
CX509AttributeExtensions InitExt = new CX509AttributeExtensions();
// Add the extension objects into an IX509Extensions collection.
CX509Extensions ext1 = new CX509Extensions();
ext1.Add((CX509Extension)eku);
// Use the IX509Extensions collection//to initialize an IX509AttributeExtensions object.
CX509AttributeExtensions ext1att = new CX509AttributeExtensions();
ext1att.InitializeEncode(ext1);
//Add the IX509AttributeExtensions object to an IX509Attributes collection.
CX509Attributes att1 = new CX509Attributes();
att1.Add((CX509Attribute)ext1att);
//Use the IX509Attributes collection to initialize an ICryptAttribute object.
CCryptAttribute crypt1 = new CCryptAttribute();
crypt1.InitializeFromValues(att1);
//Initialize a CMC or PKCS #10 request object and retrieve the ICryptAttributes collection.
//Add the ICryptAttribute object to the ICryptAttributes collection for the request.
_certificateRequest.CryptAttributes.Add(crypt1);
//Console.WriteLine("-- encode");
this.status2 = this.status2 + "-- encode <BR>";
try
{
_certificateRequest.Encode();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
string rawData = _certificateRequest.get_RawData();
Console.WriteLine("data=" + rawData);
However I get the puzzling error "The file exists. (Exception from HRESULT: 0x80070050)" at the end of the process when encoding the request , I tried with different smartcards ad the key containers are OK, not full.
Is my approach toward adding this challenge-password extension correct and how can I interpret this error?
The answer to the error you are getting "The file exists. (Exception from HRESULT: 0x80070050)" is because trying to set a key on a template that already has a key. just comment this:
CX509ExtensionEnhancedKeyUsage eku = new CX509ExtensionEnhancedKeyUsage();
eku.InitializeEncode(cp_oids);
eku.Critical = false;
CX509AttributeExtensions InitExt = new CX509AttributeExtensions();
// Add the extension objects into an IX509Extensions collection.
CX509Extensions ext1= new CX509Extensions();
ext1.Add((CX509Extension)eku);
and it should work.
search for this in the in the article Working with Active Directory Certificate Service via C# for:
Seems that we finished, but if we just execute it will throw an
exception to us, said that the file exists when adding some
extensions.
it explains everything.
from the article:
The exception message could be a little bit confusing. In fact this is
because we defined something which had been defined in the certificate
template. If we dig into the source code we can see that the exception
occurred when we added the key usage extension.
And if we get back to the CA server and open the template we are
using, we can find that the key usage had been defined in the
template. This means in the code, or in the certificate request we
should not specify it again.
Hence we need to comment the code for adding the key usage, also we
need to comment the enhanced key usage part since it had been defined
in the template, too. Because we let the request supply the subject
name so here we can still specify the subject information in the
request. The method for generating request message would be like this.
Below is code to include Challenge Password into PKCS10 generated by CertEnroll:
private static byte[] getDerBytes(int tag, byte[] data)
{
if (data.Length > byte.MaxValue)
{
throw new NotSupportedException("Support for integers greater than 255 not yet implemented.");
}
var header = new byte[] { (byte)tag, (byte)data.Length };
return header.Concat(data).ToArray();
}
and
public static byte[] EncodePrintableString(string data)
{
var dataBytes = Encoding.ASCII.GetBytes(data);
return getDerBytes(0x13, dataBytes);
}
and finnally:
CObjectId cp_oid = new CObjectId();
cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
byte[] b64__challengePassword = EncodePrintableString("password");
ICryptAttribute ChallengeAttributes = new CCryptAttribute();
ChallengeAttributes.InitializeFromObjectId(cp_oid);
CX509Attribute ChallengeAttribute = new CX509Attribute();
ChallengeAttribute.Initialize(cp_oid, EncodingType.XCN_CRYPT_STRING_BASE64_ANY,
Convert.ToBase64String(b64__challengePassword));
ChallengeAttributes.Values.Add(ChallengeAttribute);
objPkcs10.CryptAttributes.Add((CCryptAttribute)ChallengeAttributes);

How to store a public key in a machine-level RSA key container

I'm having a problem using a machine level RSA key container when storing only the public key of a public/private key pair.
The following code creates a public/private pair and extracts the public key from that pair. The pair and the public key are stored in separate key containers. The keys are then obtained from those key containers at which point they should be the same as the keys going into the containers.
The code works when CspProviderFlags.UseDefaultKeyContainer is specified for CspParameters.Flags (i.e. the key read back out from the PublicKey container is the same), but when CspProviderFlags.UseMachineKeyStore is specified for CspParameters.Flags the key read back from PublicKey is different.
Why is the behaviour different, and what do I need to do differently to retrieve the public key from a machine-level RSA key container?
var publicPrivateRsa = new RSACryptoServiceProvider(new CspParameters()
{
KeyContainerName = "PublicPrivateKey",
Flags = CspProviderFlags.UseMachineKeyStore
//Flags = CspProviderFlags.UseDefaultKeyContainer
}
)
{
PersistKeyInCsp = true,
};
var publicRsa = new RSACryptoServiceProvider(new CspParameters()
{
KeyContainerName = "PublicKey",
Flags = CspProviderFlags.UseMachineKeyStore
//Flags = CspProviderFlags.UseDefaultKeyContainer
}
)
{
PersistKeyInCsp = true
};
//Export the key.
publicRsa.ImportParameters(publicPrivateRsa.ExportParameters(false));
Console.WriteLine(publicRsa.ToXmlString(false));
Console.WriteLine(publicPrivateRsa.ToXmlString(false));
//Dispose those two CSPs.
using (publicRsa)
{
publicRsa.Clear();
}
using (publicPrivateRsa)
{
publicRsa.Clear();
}
publicPrivateRsa = new RSACryptoServiceProvider(new CspParameters()
{
KeyContainerName = "PublicPrivateKey",
Flags = CspProviderFlags.UseMachineKeyStore
//Flags = CspProviderFlags.UseDefaultKeyContainer
}
);
publicRsa = new RSACryptoServiceProvider(new CspParameters()
{
KeyContainerName = "PublicKey",
Flags = CspProviderFlags.UseMachineKeyStore
//Flags = CspProviderFlags.UseDefaultKeyContainer
}
);
Console.WriteLine(publicRsa.ToXmlString(false));
Console.WriteLine(publicPrivateRsa.ToXmlString(false));
using (publicRsa)
{
publicRsa.Clear();
}
using (publicPrivateRsa)
{
publicRsa.Clear();
}
It seems that key containers are not intended for this purpose (this is implied by "How to: Store Asymmetric Keys in a Key Container" from the .NET Framework Developer's Guide, and confirmed by a disccusion on MSDN).
Other mechanisms, such as storing the key in an XML file, need to be used to achieve this goal.
This link may help you. http://msdn.microsoft.com/en-IN/library/tswxhw92(v=vs.80).aspx
var cp = new CspParameters();
cp.KeyContainerName = ContainerName;
// Create a new instance of RSACryptoServiceProvider that accesses
// the key container.
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);
// Delete the key entry in the container.
rsa.PersistKeyInCsp = false;
// Call Clear to release resources and delete the key from the container.
rsa.Clear();
This is what it says about key deletion.

Can someone explain C# CngKey.Create please?

The internet resources seem few and far between and the best MSDN page (as far as I could tell) throws an error!
Specifically, I'm not sure what to create as a CngKeyCreationParameters object...
CngKey : CngKey objects contain properties.
Some properties must be added to a key when it is created. Other properties
can be added after the key is created.
CngKeyCreationParameters:
The CngKeyCreationParameters class enables you to add properties to a key as it is being created.
your problem: I'm not sure what to create as a CngKeyCreationParameters object
here is how to do do this
// Create CngKeyCreationParameters
CngKeyCreationParameters keyParams = new CngKeyCreationParameters();
// set properties accordingly
keyParams.ExportPolicy = CngExportPolicies.AllowArchiving;
keyParams.KeyCreationOptions = CngKeyCreationOptions.MachineKey;
keyParams.Provider = new CngProvider("someprovider");
// here is how to use keyParams
CngKey mycngKey =
CngKey.Create(new CngAlgorithm(""), "keyName", keyParams);

Categories

Resources