How to get CngKey in web application? - c#

I want to access a named key in an ASP.NET MVC application but i am not able to access it.
I create a key using powershell. please find below the code for that.
#Create Cng Key Parameter and set its properties
[System.Security.Cryptography.CngKeyCreationParameters] $cngKeyParameter = [System.Security.Cryptography.CngKeyCreationParameters]::new()
$cngKeyParameter.KeyUsage = [System.Security.Cryptography.CngKeyUsages]::AllUsages
$cngKeyParameter.ExportPolicy = [System.Security.Cryptography.CngExportPolicies]::AllowPlaintextExport
$cngKeyParameter.Provider = [System.Security.Cryptography.CngProvider]::MicrosoftSoftwareKeyStorageProvider
$cngKeyParameter.UIPolicy = [System.Security.Cryptography.CngUIPolicy]::new([System.Security.Cryptography.CngUIProtectionLevels]::None)
$cngKeyParameter.KeyCreationOptions = [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey
#Create Cng Property for Length, set its value and add it to Cng Key Parameter
[System.Security.Cryptography.CngProperty] $cngProperty = [System.Security.Cryptography.CngProperty]::new($cngPropertyName, [System.BitConverter]::GetBytes(2048), [System.Security.Cryptography.CngPropertyOptions]::None)
$cngKeyParameter.Parameters.Add($cngProperty)
#Create Cng Key for given $keyName using Rsa Algorithm
[System.Security.Cryptography.CngKey] $key = [System.Security.Cryptography.CngKey]::Create([System.Security.Cryptography.CngAlgorithm]::Rsa, "ExampleKeyName", $cngKeyParameter)
Write-Output "CNG Key : ExampleKeyName - Created"
The key gets created successfully but the same key is not getting accessed in web application using below code.
CngKey.Exists("ExampleKeyName")
Can anyone help?

CngKey.Exists("ExampleKeyName") tries to find user-level key, and your powershell script creates machine-level key. So you need to check with:
CngKey.Exists(
"ExampleKeyName",
CngProvider.MicrosoftSoftwareKeyStorageProvider,
CngKeyOpenOptions.MachineKey
);

Related

How to retrieve Secret from Azure Key Vault using DefaultAzureCredential

I have setup my keyVault in Azure, and add the secret there. I am now following instructions from Microsoft located here.
My current code looks like below:
var keyVaultName = Environment.GetEnvironmentVariable("KEY_VAULT_NAME");
var kvUri = "https://" + keyVaultName + "vault.azure.net";
var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
var secret = (await client.GetSecretAsync("my-secret-key")).Value.Value;
I already set up the environment variable (system setting) to hold the name of the key vault with the variable name KEY_VAULT_NAME.
With the code above I am getting a exceptions: The requested name is valid, but no data of the requested type was found
I have a feeling I am using DefaultAzureCredential wrongly and that there is something I am missing?
I ended up using this resource by Microsoft, which uses virtually identical code but also has details on setting up a managed identity for your web app and giving it access to the specific Key Vault.
The code in my question is also now working having set up that managed identity access.

Insert data into "Always encrypted" Azure SQL DB table using powershell

I have a Azure SQL DB with Always encrypted table and keys stored in Azure KeyVault. I wish to insert data into the "always encrypted" table using Powershell.
I understand the Invoke-sqlcmd does not support and I am using the SqlCommand.ExecuteNonQuery Method () to achieve the same
$connStr = "Server={0};Database={1};User ID={2};Password={3};Column Encryption Setting=enabled;" -f $SqlDBServerInstance, $SqlConnectionInfo.DatabaseName, $SqlConnectionInfo.UserName, $SqlConnectionInfo.Password
$sqlConn = New-Object System.Data.SqlClient.SqlConnection
$sqlConn.ConnectionString = $connStr
$sqlConn.Open()
$sqlcmd = New-Object System.Data.SqlClient.SqlCommand
$sqlcmd.Connection = $sqlConn
$sqlcmd.CommandText = "INSERT INTO dbo.SecureTable (Column1) VALUES (#Param1)"
$sqlcmd.Parameters.Add((New-Object Data.SqlClient.SqlParameter("#Param1", [Data.SQLDBType]::VarChar,50)))
$sqlcmd.Parameters["#Param1"].Value = "$Param1"
$sqlcmd.ExecuteNonQuery();
When I run this code I get below error even though I have right cryptographic access (Unwrap, Wrap, Verify, Sign) given to the service principal at key vault level in azure
Exception calling "ExecuteNonQuery" with "0" argument(s): "Failed to
decrypt a column encryption key. Invalid key store provider name:
'AZURE_KEY_VAULT'. A key store provider name must denote either a
system key store provider or a registered custom key store provider.
Valid system key store provider names are: 'MSSQL_CERTIFICATE_STORE',
'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'. Valid (currently registered)
custom key store provider names are: . Please verify key store
provider information in column master key definitions in the database,
and verify all custom key store providers used in your application are
registered properly."
The way described here does not work for me probably due to I am using Key Vault to store my keys.
To resolve this error, there is one step needed from C# to use InitializeAzureKeyVaultProvider but since I am trying to achieve this from Powershell and SqlServer PS module only, is there any way I can achieve this using Powershell only? Without going the C# way explained here

How to sign using new keyvault client from azure.security.keyvault

I recently started to replace the old Azure KeyVault client from the Microsoft.Azure.KeyVaultnamespace with the newer one in Azure.Security.KeyVault.
This works without any issues when getting secrets and certificates, but I am not sure how to sign anymore
How do I sign using the new keyvault client?
Did you check Azure SDK Azure.Security.KeyVault.Keys package on github?
Example code from that source:
SignResult rsaSignDataResult = rsaCryptoClient.SignData(SignatureAlgorithm.RS256, data);
Debug.WriteLine($"Signed data using the algorithm {rsaSignDataResult.Algorithm}, with key {rsaSignDataResult.KeyId}. The resulting signature is {Convert.ToBase64String(rsaSignDataResult.Signature)}");
For azure key vault, we need to create a signature from a digest using the specified key.
So, you could refer to the below code to sign some arbitrary data and verify the signatures using the CryptographyClient with both the EC and RSA keys we created.
byte[] digest = null;
using (HashAlgorithm hashAlgo = SHA256.Create())
{
digest = hashAlgo.ComputeHash(data);
}
SignResult rsaSignResult = rsaCryptoClient.Sign(SignatureAlgorithm.RS256, digest);
If you want to sign certificate you could refer to this article.

Trouble authenticating with SshUserKeyCredentials in libgit2sharp-SSH

Having some trouble authenticating with SshUserKeyCredentials using libgit2sharp-SSH:
var co = new CloneOptions();
co.CredentialsProvider = (_url, _user, _cred) => new SshUserKeyCredentials { PrivateKey="C:\\path\\to\\private_key" };
Repository.Clone("git#... .repository.git", path, co);
I found the SshUserKeyCredentials object browsing through the source code so my first question would be if it is possible to use this object to do deploy key based checkout from gitlab?
The object seems to want any combination of PrivateKey, Username, PublicKey and Passphrase. I'm currently using a PrivateKey.
The error I end up with:
{"Failed to start SSH session: Unable to exchange encryption keys"}
If this way isn't supposed to work is there an alternative way of using deploy keys to programmatically manage git from an C# environment?
I was able to figure out the following through trial-and-error and scouring the web.
You cannot have null for any field; use string.Empty.
Public and private key must be provided and in the proper format.
Private key had to be in PEM format (either use PuttyGen Conversion menu => Export OpenSSH or use openssl rsa -in id_rsa -out id_rsa.pem).
Public key had to be single line, starting with type, followed by base64 key, no comment at the end (this is the format shown in the public key text box on PuttyGen, except you have to remove the comment), e.g.
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAoblahblahblahblahblah
I added username git because that's what Bitbucket requires. Not sure you need that, but it can't be null, do string.Empty.
Example:
CredentialsHandler handler = (_url, _user, _cred) => new SshUserKeyCredentials
{
PrivateKey = #"C:\Users\blah\.ssh\keys\bitbucket.pem",
Username = "git",
Passphrase = string.Empty,
PublicKey = #"C:\Users\blah\.ssh\keys\bitbucket.pub"
}
See also:
PHP ssh2_auth_pubkey_file(): Authentication failed using public key: Invalid key data, not base64 encoded

Get Apple Keychain to recognize Bouncy Castle .NET created PKCS12 (.p12) store

Our organization manages a stable of iOS applications for multiple clients, which means dealing with a lot of different developer identity certificates and push notification certificates.
I have had success with the Bouncy Castle C# Crypto API in simplifying management of the certificates and private keys for push notifications, essentially eliminating the need for the Keychain for all our push notification certificates.
I would like to extend this to the developer identity certificates. The goal would be to store all the private key and certificate information in the database for each developer identity. Then when a new developer or build machine needs to be provisioned, server side code could wrap all of the certificates and private keys into one p12 archive with one password that could be imported into the target Mac's Keychain.
Unfortunately, the Mac Keychain doesn't like the p12 files I'm generating. This is annoying since I can successfully import these files into the Windows certificate manager just fine.
The code I'm using (the important parts) looks like this:
private byte[] GetP12Bytes(List<DevIdentity> identities, string password)
{
Pkcs12Store store = new Pkcs12Store();
foreach(DevIdentity ident in identities)
{
// Easiest to create a Bouncy Castle cert by converting from .NET
var dotNetCert = new X509Certificate2(ident.CertificateBytes);
// This method (not shown) parses the CN= attribute out of the cert's distinguished name
string friendlyName = GetFriendlyName(dotNetCert.Subject);
// Now reconstitute the private key from saved value strings
BigInteger modulus = new BigInteger(ident.PrivateKey.Modulus);
BigInteger publicExponent = new BigInteger(ident.PrivateKey.PublicExponent);
BigInteger privateExponent = new BigInteger(ident.PrivateKey.Exponent);
BigInteger p = new BigInteger(ident.PrivateKey.P);
BigInteger q = new BigInteger(ident.PrivateKey.Q);
BigInteger dP = new BigInteger(ident.PrivateKey.DP);
BigInteger dQ = new BigInteger(ident.PrivateKey.DQ);
BigInteger qInv = new BigInteger(ident.PrivateKey.QInv);
RsaKeyParameters kp = new RsaPrivateCrtKeyParameters(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv);
AsymmetricKeyEntry privateKey = new AsymmetricKeyEntry(kp);
// Now let's convert to a Bouncy Castle cert and wrap it for packaging
Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(dotNetCert);
X509CertificateEntry certEntry = new X509CertificateEntry(cert);
// Set the private key and certificate into the store
store.SetCertificateEntry(friendlyName, certEntry);
store.SetKeyEntry(ident.PrivateKeyName, privateKey, new X509CertificateEntry[] { certEntry });
}
using (MemoryStream ms = new MemoryStream())
{
store.Save(ms, password.ToCharArray(), new SecureRandom());
ms.Flush();
byte[] p12Bytes = ms.ToArray();
return p12Bytes;
}
}
Like I said, this works great for import on Windows, but fails with a very generic error when importing into the Mac Keychain.
There is one major difference I can see when loading a Keychain-generated p12 and my own generated p12 file, but I do not know if this is the cause.
If I load the Mac Keychain generated p12 into a Bouncy Castle PKCS12Store, and then examine the keys, on the Keychain p12, both the certificate and the private key have an attribute with the key "1.2.840.113549.1.9.21" with equivalent values (a DerOctetString with value #af8a1d6891efeb32756c12b7bdd96b5ec673e11e).
If I do the same to my generated p12 file, the private key contains the "1.2.840.113549.1.9.21" attribute, but the Certificate does not.
If I Google "1.2.840.113549.1.9.21", I find out that this OID means PKCS_12_LOCAL_KEY_ID . My only theory is that the Keychain relies on this to match up the certificate and private key, and that my generated file does not have this, so it fails.
However, I've tried adding these values to a Hashtable and then using the CertificateEntry constructor that takes the attribute hashtable. If I do that, and then save the bytes, and then reload the bytes, that attribute is again missing.
So I'm flummoxed. Maybe this attribute is a glitch in the Bouncy Castle API? Maybe there's something I'm doing wrong. Maybe the Keychain has ridiculous non-standard requirements for incoming p12 files. In any case, any help that could be provided would be greatly appreciated.
BouncyCastle's Pkcs12Store takes care of setting both the Friendly Name and Local Key ID attributes for you (or at least it does so in the 1.7 release, circa April 2011). My guess is that you must have used an older version where this didn't work.
Here's how I'm saving an iPhone Developer identity to a Pkcs12Store instance (extra stuff and security omitted):
var store = new Pkcs12Store();
// pairs is IEnumerable<Tuple<X509Certificate, AsymmetricKeyParameter>>
foreach (var pair in pairs)
{
var cn = pair.Item1.SubjectDN
.GetValueList(X509Name.CN).OfType<string>().Single();
var certEntry = new X509CertificateEntry(pair.Item1);
store.SetCertificateEntry(cn, certEntry);
var keyEntry = new AsymmetricKeyEntry(pair.Item2);
store.SetKeyEntry("Developer Name", keyEntry, new[] { certEntry });
}
store.Save(stream, string.Empty.ToArray(), new SecureRandom());
Importing the store in Keychain Access.app on OS X 10.7 correctly places the certificate and private key in the keychain and places the certificate within the private key in the UI, as with a certificate and key generated by Keychain Access itself.
On a side note, it seems that Pkcs12Store uses the public key of the certificate to generate the value of the LocalKeyId attribute shared by the certificate and key entries.
You can see the relevant section of the Pkcs12Store source here.

Categories