How to change issuer Name while creating X509Certificate2 in C# - c#

I am working with creating X509Certificate2 certificate in my C#.net application. While creating certificate, how to set the issuer name? currently issuer name shows same as subject name.
Please help.

Hmm.
Finally I have used Bouncy Castle dll to create certificates.Using this, there is a method to set issuer name.
This is the fully code to generate x509Certificate and to store into Trusted People store:
private X509Certificate2 GeneratePFXFile(string certificate,string company,string email,string state,string locality,string username,string country)
{
X509Certificate2 cert = null;
try
{
var kpgen = new RsaKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 2048));
var kp = kpgen.GenerateKeyPair();
var gen = new X509V3CertificateGenerator();
var certName = new X509Name("CN=" + certificate);
var issuer = new X509Name("C="+country+",O="+company+",OU=LBC Mundial Corp.USA,E="+email+",L="+locality+",ST="+state);
var serialNo = BigInteger.ProbablePrime(120, new Random());
gen.SetSerialNumber(serialNo);
gen.SetSubjectDN(certName);
gen.SetIssuerDN(issuer);
gen.SetNotAfter(DateTime.Now.AddYears(50));
gen.SetNotBefore(DateTime.Now);
gen.SetSignatureAlgorithm("MD5WithRSA");
gen.SetPublicKey(kp.Public);
gen.AddExtension(
X509Extensions.AuthorityKeyIdentifier.Id,
false,
new AuthorityKeyIdentifier(
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public),
new GeneralNames(new GeneralName(certName)),
serialNo));
gen.AddExtension(
X509Extensions.ExtendedKeyUsage.Id,
false,
new ExtendedKeyUsage(new ArrayList() { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") }));
var newCert = gen.Generate(kp.Private);
byte[] pfx = DotNetUtilities.ToX509Certificate(newCert).Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pfx, (string)null);
X509Store store = new X509Store((StoreName)StoreName.TrustedPeople, (StoreLocation)StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
cert = new X509Certificate2(pfx,(string)null, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
store.Add(cert);
store.Close();
}
catch (Exception ex)
{
ShowError(ex.Message);
return null;
}
return cert;
}

Related

Trying to access Azure Keyvault from .Net Framework project is hanging

I'm currently working on a 256 bit AES encryption API project for my job. One of the aspects of these encryption APIs is they need to access our Azure Keyvault to retrieve a key (we have different keys for different projects).
For some reason the .Net Framework project hangs when trying to access the key vault after the first successful execution. It will hang on this line: var key = client.GetKeyAsync($"https://automationkeys.vault.azure.net/keys/{product}").GetAwaiter().GetResult();
I have the same encryption API made using .Net Core and I'm able to execute calls multiple times in a row without issue.
After doing some reading I have a feeling it has to do with async / await but I don't know enough about all that to see where the problem is.
Here is my full KeyVaultAccessor class:
public static class KeyVaultAccessor
{
public static string GetKey(string product)
{
var keyValue = string.Empty;
try
{
var client = GetKeyVaultClient(<my_app_id>, <keyvault_cert_thumbprint>);
var key = client.GetKeyAsync($"https://automationkeys.vault.azure.net/keys/{product}").GetAwaiter().GetResult();
keyValue = key?.KeyIdentifier.Version;
if (string.IsNullOrEmpty(keyValue))
{
Assert.Fail($"Key was null or empty for product: {product}");
}
}
catch (Exception e)
{
Assert.Fail($"Error occurred while attempting to retrieve key for product: {product}. {e.Message}");
}
return keyValue;
}
private static KeyVaultClient GetKeyVaultClient(string appId, string thumbprint)
{
var keyVault = new KeyVaultClient(async (authority, resource, scope) =>
{
var authenticationContext = new AuthenticationContext(authority, null);
X509Certificate2 certificate;
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
var certificateCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (certificateCollection.Count == 0)
{
throw new Exception("<certificate name> not installed in the store");
}
certificate = certificateCollection[0];
}
finally
{
store.Close();
}
var clientAssertionCertificate = new ClientAssertionCertificate(appId, certificate);
var result = await authenticationContext.AcquireTokenAsync(resource, clientAssertionCertificate);
return result.AccessToken;
});
return keyVault;
}
}
Not quite sure your root reason, but if you want to get a key in Azure keyVault by ClientCertificateCredential and local cert, try the code below which works perfectly for me:
using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Azure.Identity;
using Azure.Security.KeyVault.Keys;
namespace key_vault_console_app
{
class Program
{
static void Main(string[] args)
{
var keyVaultName = "";
var tenantID = "";
var appID = "";
var certThumbprint = "";
var kvUri = $"https://{keyVaultName}.vault.azure.net";
var certCred = new ClientCertificateCredential(tenantID, appID, GetLocalCert(certThumbprint));
var client = new KeyClient(new Uri(kvUri), certCred);
Console.Write(client.GetKey("<your key name>").Value.Key.Id);
}
public static X509Certificate2 GetLocalCert(string thumbprint)
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
var certificateCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (certificateCollection.Count == 0)
{
throw new Exception("cert not installed in the store");
}
return certificateCollection[0];
}
finally
{
store.Close();
}
}
}
}
Result:

c# .net core certificate chain on linux fails, windows works

I create a self sign certificate in debug with .net core and store it into the certificate store (my/root). When the app runs under windows the certificate cain is okay but under linux (ubuntu) the certificate chain check fails.
Certificate creation code is:
public static X509Certificate2 BuildSelfSignedServerCertificate()
{
using RSA rsa = RSA.Create(2048);
var request = new CertificateRequest("CN=My_Certificate",
rsa,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment |
X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
request.CertificateExtensions
.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
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") }, false));
var certificate = request.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)),
new DateTimeOffset(DateTime.UtcNow.AddDays(3650)));
LogFileLevelInformation("Created new self sign certificate.");
return certificate;
}
Then I add the certificate at my and root stores:
public static void CreateCert()
{
var certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadWrite);
bool result = certificateStore.Certificates.OfType<X509Certificate2>()
.Any(cert => cert.Subject.Contains("My_Certificate"));
if (result)
return;
var cert = BuildSelfSignedServerCertificate();
certificateStore.Add(cert);
certificateStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser, OpenFlags.MaxAllowed);
certificateStore.Open(OpenFlags.MaxAllowed);
certificateStore.Add(cert);
LogFileLevelInformation("New certificate added to store.");
}
then later I verify the chain:
private X509Certificate2 VerifyCertificateChain(X509Certificate2 certificate)
{
var chain = new X509Chain();
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
var chainBuilt = chain.Build(certificate);
if (chainBuilt)
{
LogDebugLevelInformation("The certificate chain is validated.")
return certificate;
}
return null;
}
the certificate check under windows with
var chainBuilt = chain.Build(certificate);
returns with result true, but when the same code runs under linux it returns false.
the certificate is available on linux under:
/root/.dotnet/corefx/cryptography/x509stores/root
and
/root/.dotnet/corefx/cryptography/x509stores/my
chain.StatusCode = "unable to get local issuer certificate"
Any idea why it is failing under linux?

Adding an SSL Certificate programmatically to get working https

I have some code that is supposed to add an ssl certifacate to a port. It does say it has succesfully added a certificate, but when i host the website and then type in the localhost url (localhost:{port}) the page keeps loading. I don't understand why it doesn't work.
I already tried to host the website without ssl and that worked fine so that's not the problem.
public static class Certificate
{
public static void Standard(int port)
{
var certSubjectName = "HelloMyNameIsHank";
var expiresIn = TimeSpan.FromHours(1);
var cert = GenerateCert(certSubjectName, expiresIn);
Console.WriteLine("Generated certificate, {0}Thumbprint: {1}{0}", Environment.NewLine, cert.Thumbprint);
RegisterSslOnPort(port, cert.Thumbprint);
Console.WriteLine($"Registerd SSL on port: {port}");
}
private static void RegisterSslOnPort(int port, string certThumbprint)
{
var appId = Guid.NewGuid();
string arguments = $"http add sslcert ipport=0.0.0.0:{port} certhash={certThumbprint} appid={{{appId}}}";
ProcessStartInfo procStartInfo = new ProcessStartInfo("netsh", arguments);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
var process = Process.Start(procStartInfo);
while (!process.StandardOutput.EndOfStream)
{
string line = process.StandardOutput.ReadLine();
Console.WriteLine(line);
}
process.WaitForExit();
}
public static X509Certificate2 GenerateCert(string certName, TimeSpan expiresIn)
{
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
var existingCert = store.Certificates.Find(X509FindType.FindBySubjectName, certName, false);
if (existingCert.Count > 0)
{
store.Close();
return existingCert[0];
}
else
{
var cert = CreateSelfSignedCertificate(certName, expiresIn);
store.Add(cert);
store.Close();
return cert;
}
}
public static X509Certificate2 CreateSelfSignedCertificate(string subjectName, TimeSpan expiresIn)
{
// 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.Add(expiresIn);
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
);
}
}
I want to be able to host my website with https. If anyone knows why this doesn't work please tell me.
I'm using httplistener for hosting. I don't want to use ASP.NET and/or IIS.
I'm not to familiar with ssl certificates so I would also really appreciate some code snippets even if it's just pseudo-code.

The system cannot find the file specified using X509Certificate2 to connect Google OAuth2.0 in an Azure Server

I'm using this code to connect to Google OAuth2.0 and I'm using the C# libraries.
using (var tImpersonationContext = new ImpersonationContext(ConfigurationManager.AppSettings["ImpersonationDomain"], ConfigurationManager.AppSettings["ImpersonationUser"], ConfigurationManager.AppSettings["ImpersonationPassword"]))
{
string[] tScopes = new string[] { AnalyticsService.Scope.Analytics };
var tKeyFilePath = Path.Combine(System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]), String.Format("GoogleCertificates\\{0}", this.OAuthKeyName));
var tServiceAccountEmail = this.OAuthServiceAccountEmail;
//loading the Key file
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.Exportable);
var tCredential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(tServiceAccountEmail)
{
Scopes = tScopes
}.FromCertificate(tCertificate));
if (tCredential.RequestAccessTokenAsync(CancellationToken.None).Result)
{
this.SessionToken = tCredential.Token.AccessToken;
}
}
In my dev enviroment everything works just perfect but when I move the app to my Azure Server I got this error:
The system cannot find the file specified.
When I try to execute this line:
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.Exportable);
I'm sure that the path is correct, in fact if I copy and paste the path in the File Explorer the wizard to import the certificate starts.
I'm sure that the impersonation is working because the line is excecuted.
I have tried to change it to:
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.MachineKeySet);
Or this:
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.UserProtected | X509KeyStorageFlags.DefaultKeySet);
And nothing is working, in fact everything I have tried from other questions of this web site are not working.
If it help is a Windows App that I pretend to use in a task scheduled, to get data from Google Analytics.
Does someone have any idea why is not working in the Azure Server??
Thanks in advance.
UPDATE
I have tried to install the certificate and I have change the code to this:
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.MachineKeySet);
But now I get this error:
Key not valid for use in specified state.
I have change all the code an the aproach and now is working.
First you have to instal your certificate in the MMC manager inside trusted root certificates authorities under certificates and it have to be set as a local machine.
And then you just have to use this code:
X509Store tCertStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
tCertStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection tCertCollection = tCertStore.Certificates.Find(X509FindType.FindByThumbprint, this.OAuthKeyFingerprint, false);
// Get the first cert with the thumbprint
if (tCertCollection.Count > 0)
{
// header
var tHeader = new { typ = "JWT", alg = "RS256" };
// claimset
var tTimes = GetExpiryAndIssueDate();
var tClaimset = new
{
iss = this.OAuthServiceAccountEmail,
scope = "https://www.googleapis.com/auth/analytics",
aud = "https://www.googleapis.com/oauth2/v3/token",
iat = tTimes[0],
exp = tTimes[1],
};
JavaScriptSerializer ser = new JavaScriptSerializer();
// encoded header
var tHeaderSerialized = ser.Serialize(tHeader);
var tHeaderBytes = Encoding.UTF8.GetBytes(tHeaderSerialized);
var tHeaderEncoded = Convert.ToBase64String(tHeaderBytes);
// encoded claimset
var tClaimsetSerialized = ser.Serialize(tClaimset);
var tClaimsetBytes = Encoding.UTF8.GetBytes(tClaimsetSerialized);
var tClaimsetEncoded = Convert.ToBase64String(tClaimsetBytes);
// input
var tInput = tHeaderEncoded + "." + tClaimsetEncoded;
var tInputBytes = Encoding.UTF8.GetBytes(tInput);
X509Certificate2 tCertificate = tCertCollection[0];
// signiture
var tRSA = tCertificate.PrivateKey as RSACryptoServiceProvider;
var tCspParam = new CspParameters
{
KeyContainerName = tRSA.CspKeyContainerInfo.KeyContainerName,
KeyNumber = tRSA.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2,
Flags = CspProviderFlags.UseMachineKeyStore
};
var tAesCsp = new RSACryptoServiceProvider(1024, tCspParam) { PersistKeyInCsp = true };
var tSignatureBytes = tAesCsp.SignData(tInputBytes, "SHA256");
var tSignatureEncoded = Convert.ToBase64String(tSignatureBytes);
// jwt
var tJWT = tHeaderEncoded + "." + tClaimsetEncoded + "." + tSignatureEncoded;
HttpClient tClient = new HttpClient();
Dictionary<string, string> tPost = new Dictionary<string, string>
{
{"assertion", tJWT},
{"grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"}
};
FormUrlEncodedContent tContent = new FormUrlEncodedContent(tPost);
var tURI = "https://www.googleapis.com/oauth2/v3/token";
HttpResponseMessage tResult = tClient.PostAsync(tURI, tContent).Result;
TokenResponse tTokenObject = JsonConvert.DeserializeObject<TokenResponse>(tResult.Content.ReadAsStringAsync().Result);
this.SessionToken = tTokenObject.access_token;
}
tCertStore.Close();
I hope this help someone.
Happy coding.

Find certificate by thumbprint

System can't find certificate by string thumbprint
var thumbprint = "‎2E7F6E8A0124E6745C3999EE15770C0A4931F342";
X509Certificate2 certificate = new X509Certificate2();
X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly);
var c = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false).OfType<X509Certificate>().FirstOrDefault();
this core returns null. But I try this too
foreach (X509Certificate2 mCert in store.Certificates)
{
var c= store.Certificates.Find(X509FindType.FindByThumbprint, mCert.Thumbprint, false).Count;
}
c is always 1 , so some problem is in characters. I copied this thumbprint value.
The following works for me:
public async Task<X509Certificate2> GetCertificate(string certificateThumbprint)
{
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.OfType<X509Certificate2>()
.FirstOrDefault(x => x.Thumbprint == certificateThumbprint);
store.Close();
return cert;
}

Categories

Resources