I think I have read every single thing on the internet about this (bold statement I know) but I can't work it out...
I have a very simple webpage that gets the status VMs on Azure, which works fine on my machine. I created a Cert on my local machine with makecert and debug runs fine.
After deploying it to another server on IIS all I get is 403 errors.
Things I tried:
Exporting Cert from my dev machine with private key and importing onto the test server
Creating new Cert with makecert (edit: recreated the cert on the server I want to deploy to) (according to this link from MSN), upload to Azure, update code to search for new thumbprint, redeploy and admire the same error msg..
Both times I changed the app pool identity to a user account that is log-on-able (and reverted)
Tried with cert as both localmachine and current user, with user updated in the app pool
I changed my get cert code to more resemble an answer from a similar question, but finding the cert doesn't appear to be the issue.. if I remove the cert created on the server, I get a different error.
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var certificate = store.Certificates.Cast<X509Certificate2>().SingleOrDefault(c => string.Equals(c.Thumbprint, thumbprint, StringComparison.OrdinalIgnoreCase)); // please replace CertificateThumbprint with original Thumbprint
return certificate;
Ref: how to connect to azure (management) rest api via C# in IIS
Code to create HttpClient:
WebRequestHandler handler = new WebRequestHandler();
String CertThumbprint = _certthumbprint;
X509Certificate2 managementCert = FindX509Certificate(CertThumbprint);
if (managementCert != null)
{
handler.ClientCertificates.Add(managementCert);
HttpClient httpClient = new HttpClient(handler);
httpClient.DefaultRequestHeaders.Add("x-ms-version", "2014-05-01");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
return httpClient;
}
Retrieve VMs Code:
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deploymentslots/{2}", _subscriptionid, ServiceName, "Production");
XDocument vms = new XDocument();
vms.Add(new XElement("VirtualMachines"));
ApplyNamespace(vms.Root, ns);
try
{
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
XDocument xml = XDocument.Load(responseStream);
var roles = xml.Root.Descendants(ns + "RoleInstance");
foreach (XElement r in roles)
{
XElement svcNamee1 = new XElement("ServiceName", ServiceName);
ApplyNamespace(svcNamee1, ns);
r.Add(svcNamee1);
vms.Root.Add(r);
}
}
}
This code is currently about 95% copy and paste from here
The resolution for me in this case was to create a new Publishsettings file via powershell and import that on the server via powershell. Then use the thumbprint from that in code. Making a cert on the server and uploading to Azure still doesn't work for whatever reason...
Related
I have an SSL certificate installed on my domain and I wanted to use it for signing with IdentityServer 4. Now I found that there is a method which would let me do that:
services.AddIdentityServer().AddSigningCredentials(certificate);
However, I cannot figure out how to actually get my certificate and pass it to the identity server.
I have tried the following:
var cert = X509Certificate.CreateFromCertFile(fileName);
services.AddIdentityServer().AddSigningCredentials(certificate);
The error that I get is it cannot convert from
'System.Security.Cryptography.X509Certificates.X509Certificate' to 'Microsoft.IdentityModel.Tokens.SIgningCredential'
Now I don't understand why it is complaining about signing credentials when one of the overrides for the method is the certificate.
I ended up resolving it like this. I'm using a shared server where I am hosting this and I could not find the file name for the certificate or the path to get it. So I ended up just opening the store and finding it that way. Not very efficient, but it will do the trick until I move it to a dedicated server and have more control.
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2 cert = null;
foreach (X509Certificate2 certificate in store.Certificates)
{
if (!string.IsNullOrWhiteSpace(certificate?.SubjectName?.Name) && certificate.SubjectName.Name.StartsWith("CN=*.mysite.com"))
{
cert = certificate;
break;
}
}
Maybe it can't convert due to issue in permissions or loading the certificate as a stream.
In my case using IdentityServer3 the below code works:
/// <summary>
/// Load the certificate that sign the Id or Jw token
/// </summary>
/// <returns></returns>
private static X509Certificate2 LoadCertificate()
{
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
return new X509Certificate2(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigMngr.GetAppSettingsValue<string>("IdSrv:SigningCertificatePath")), ConfigMngr.GetAppSettingsValue<string>("IdSrv:SigningCertificatePassword"));
}
Then in owin's startup file, I am passing it as below:
SigningCertificate = LoadCertificate(),
I know in Idsrv4 it's a different implementation than the code I posted but it should be the same abstraction, for example, you loading X509Certificate but it's deprecated so make sure to use the correct overloading to load the certificate as stream and make sure to return the correct type.
Also, This code is testable with IdSrv4:
var fileName = Path.Combine(env.WebRootPath, "FileName" );
if (!File.Exists(fileName))
{
throw new FileNotFoundException("No Signing Certificate!");
}
var cert = new X509Certificate2(fileName, "Pass" );
services.AddIdentityServer().AddSigningCredential(cert)
So instead of using
X509Certificate.CreateFromCertFile(fileName);
You can construct a new X509Certificate2 certificate like this:
var cert = new X509Certificate2(fileName, "Pass" );
And pass it to the owin middleware:
services.AddIdentityServer().AddSigningCredential(cert)
I'm trying to implement server authentication in C# (using .NET appln). I would like to achieve the following :
Connect to HTTPS URL using
String sslServerHost = "https://mail.google.com";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sslServerHost);
If HTTPS request fails, I would be adding the server certificate to the local windows certification store.
X509Certificate cert = request.ServicePoint.Certificate;
//convert the X509Certificate to an X509Certificate2 object by passing it into the constructor
X509Certificate2 cert2 = new X509Certificate2(cert);
X509Store userCaStore = new X509Store(storeName: StoreName.Root, storeLocation: StoreLocation.CurrentUser);
// Code to import server certifictes to windows store.
userCaStore.Open(OpenFlags.ReadOnly);
userCaStore.Add(cert2);
The below is the C# code that I have used for performing server authentication.
As evident from the below code snippet, I have NEITHER ignored the certificate validation NOR added the X509Certificate to the local trust store, but still I was able to establish connection to the HTTPS URL ( WebRequest.create(url) and request.GetResponse() doesn't throw any exceptions )
String sslServerHost = "https://mail.google.com";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sslServerHost);
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
try
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Debug.WriteLine(responseFromServer);
Console.WriteLine(responseFromServer);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Why is it that I'm not getting an exception when trying to access the HTTPS URL given that I have NOT added the server certificates (X509Certificate) to the local windows store.
In short, how to achieve or implement server authentication in C# where if server certificate were not added then the C# code should throw an exception.
For Java, there is a good link https://github.com/escline/InstallCert/blob/master/InstallCert.java which best describes the server authentication mechanism where if client is trying to access an HTTPS Server and if server's certificate is not present in the Java trust store, then JVM throws an exception.
This mechanism doesn't seem to hold good for .NET applications. Any help or insight would be appreciated !!
The reason is probably that mail.google.com is already trusted, since the certificate chain leads up to some root certificate which IS already in your certificate store (in "Trusted Root Certification Authorities").
If you want to test failure, follow a tutorial to create your own CA and certificates using something like OpenSSL. Then set up a web site in IIS with this untrusted certificate.
According with the web request official documentation of the web request The certificate could be installed in My certificate store of the current user. That might explain why it nevers throws an error. You can try to connect to a server without adding the certificate to the cert store and see if that is throwing an error.
The solutions I have found to solve this problem involve setting a callback on ServicePointManager.ServerCertificateValidationCallback
ServicePointManager.ServerCertificateValidationCallback =
MyRemoteCertificateValidationCallback;
public bool MyRemoteCertificateValidationCallback(System.Object sender,
X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
bool isOk = true;
// If there are errors in the certificate chain, look at each error to determine
the cause.
if (sslPolicyErrors != SslPolicyErrors.None) {
for(int i=0; i<chain.ChainStatus.Length; i++) {
if(chain.ChainStatus[i].Status !=
X509ChainStatusFlags.RevocationStatusUnknown) {
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
chain.ChainPolicy.VerificationFlags =
X509VerificationFlags.AllFlags;
bool chainIsValid = chain.Build((X509Certificate2)certificate);
if(!chainIsValid) {
isOk = false;
}
}
}
} else {
X509Certificate2 cert3 = new X509Certificate2(certificate);
bool verify = cert3.Verify();
var cert1 = new X509Certificate2(certificate);
if (cert1.NotAfter <= DateTime.Now)
{
return false;
}
}
return isOk;
}
I am using the below method to get certificate from the x509 store
private X509Certificate GetCert(string certThumbPrint)
{
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, certThumbPrint.Trim(), false);
certStore.Close();
// Check to see if our certificate was added to the collection. If no, throw an error, if yes, create a certificate using it.
if (0 == certCollection.Count || certCollection.Count == null)
{
throw new ApiException(string.Format("Error: Admin Service Client : No certificate found containing thumbprint {0}",certThumbPrint), ApiLogCategory.Configuration, ErrorCodes.AdminServiceClient);
}
var cert = new X509Certificate(certCollection[0].RawData, string.Empty, X509KeyStorageFlags.MachineKeySet);
return cert;
}
I was getting error so I tried debugging and found that the application is unable to open the X509 store. What can I do in this case.
It looks like a permission related issue.
Is your certificate installed in personal store?
if so, Right click the certificate => All tasks => Manage private key => Add IIS AppPool\AppPoolName and grant it Full control
if you're running app pool under a different account than application pool identity then that that user.
I have added a mastercard sdk to my asp.net core api. The call to api works fine on my local machine. But when deployed on azure it throws the above error.
I have tried everything.
I have attached a debugger to the live site. It throws error when I call
SetAuthentication
public MatchType RequestCall(string consumerKey, string keyAlias, string keyPassword)
{
byte[] certificateBytes = GetCertificateStream();
ApiConfig.SetAuthentication(new OAuthAuthentication(consumerKey, certificateBytes, keyAlias, keyPassword));
ApiConfig.SetSandbox(true);
RequestMap map = GenerateRequestMap();
TerminationInquiryRequest response = TerminationInquiryRequest.Create(map);
return GetMatchType(response);
}
public byte[] GetCertificateStream()
{
var resourceName = typeof(TerminationMatchInquiry).GetTypeInfo().Assembly.GetManifestResourceNames()[0];//Get certificate resource name
using (Stream CertStream = typeof(TerminationMatchInquiry).GetTypeInfo().Assembly.GetManifestResourceStream(resourceName))
{
byte[] RawBytes = new byte[CertStream.Length];
for (int Index = 0; Index < CertStream.Length; Index++)
{
RawBytes[Index] = (byte)CertStream.ReadByte();
}
return RawBytes.ToArray();
}
}
After review the source of MasterCard SDK for C#, I found that, it will create an instance of X509Certificate2 when we invoke OAuthAuthentication. The error is caused in this line.
cert = new X509Certificate2(rawCertificateData, password, keyStoreFlags | X509KeyStorageFlags.Exportable);
In the Azure WebApp, if we try to use the certificate, we need to upload the certificate from the Azure portal. Add the WEBSITE_LOAD_CERTIFICATES with thumbprint value in the Azure WebApp application. More detail info please refer to blog.
I need to access a certificate from my Azure Function.
I followed the steps outlined in Runtime error loading certificate in Azure Functions but it didn't work out.
private static X509Certificate2 GetCertificate(string thumbprint, TraceWriter log)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
log.Info("Enumerating certificates");
foreach (var cert in store.Certificates) {
log.Info(cert.Subject);
}
var col = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (col == null || col.Count == 0)
{
return null;
}
return col[0];
}
finally
{
store.Close();
}
}
Two certificates where uploaded to the Azure Function and the setting WEBSITE_LOAD_CERTIFICATES was added as well and set to either * or to the thumpbrint of the required certificate, but to no avail.
The GetCertificate method should print a list of all certificates in the store, but the store is empty.
Any clues on how to solve this?
UPDATE:
Client certificates are now supported in the Consumption plan.
Client certificates are not yet supported in our Consumption plan, only in App Service plan. This is tracked by an issue in our repo here. We're working on it - please follow that issue for status. Thanks.