how to connect to azure (management) rest api via C# in IIS - azure

I am trying to setup a website (local testing atm), to connect to azure rest api to see our settings. I created a cert locally (W7 machine):
makecert -sky exchange -r -n "CN=azureConnectionNew" -pe -a sha1 -len 2048 -ss My "azureConnectionNew.cer"
I can see the cert in the certs MMC snap in. (do not have a right click edit permissions option when I view the cert in here).
I have a class library that setups up the connection, the cert is passed in by getting the cert (via the thumb string), this works great for the console app, but when I try and do this in a web app it all goes wrong. I get 403 errors.
I first thought that this was due to the fact that the website is running as the ApplicationPoolIdentity so doesn't have access to the cert. So I tried passing in the cert (to the same code as the console app), by loading the actual file:
var path = #"C:\temp\azureconnection\azureConnectionNew.cer";
var cert = new X509Certificate2();
cert.Import(path);
I still get 403 errors.
I tried exporting the cer file from MMC certificates snap in as a pfx file, (with private keys included). I set the local IIS set to use this cert and navigated to the https version of my local site but still got 403.
I am not sure how to include / setup / reference the cert so that IIS can send a HttpWebRequest from the server side to Azure and get a valid response.

It is always better to use Thumbprint of the certificate to get the certificate. Please make sure you have created the certificate correctly. Also please check you have placed the certificate in Personal certificate section in Local Machine. You can check this using MMC snap in. please try below code..
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var certificate = store.Certificates
.Cast<X509Certificate2>()
.SingleOrDefault(c => string.Equals(c.Thumbprint, “CertificateThumbprint”, StringComparison.OrdinalIgnoreCase)); // please replace CertificateThumbprint with original Thumbprint

This isn't the right way to use the certificate - it needs to be stored in the personal/certificates store of the user running the code (you should update the App Pool identity to be a user who can login and into whose certificates you import the cert. Here's sample code showing you how to use the service API: http://code.msdn.microsoft.com/windowsazure/CSAzureManagementAPI-609fc31a/

Related

Azure App Service Fails to Find Newly Created Certificate

I have an Azure app service I did not create but now maintain. The app service finds a certificate in a Key Vault by thumbprint and in turn uses that to get a token for doing some SQL work via nightly jobs.
It appears the certificate was set to auto renew after 80% of its valid date (12 months). The day the cert renewed my nightly jobs started to fail. I'm reasonably certain the new certificate is at the root of the problem.
As best I can tell it designed to work like this:
Job fires via Azure Logic App
annomyous POST to a reports processing API (end result should be .PDF report creation for email atachment)
API has Appsetting.json that contains the current certificates thumbprint
Thumbprint is used in the line of code below to find the certificate in the cert store
Cert is used to aquire access token and perform work
When I install both the old certificate and the new certificate on my local machine and run the entire process it works find with the old certificate and fails on this line with the new auto-generated certificate. It also fails with any new certificates I try to make in Azure and export from Azure and import to my dev machine. I've double/triple checked the appsettings to make sure the Thumbprint in question is correct and updated.
var signingCert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == _appSettings.AzureAD.CertificateThumbprint);
When this was configured a year ago the process was to go into App Service, TLS/SSL setting blade, select Private Key Certificates (.pfx) and finally + Import Key Vault Certificate.
From there you selected the Key Vault and Certificate, then changed the Appsettings.Json to have the new Thumbprint.
Why will it work with the old (soon to expire) certificate and corresponding Thumbprint entry into appsettings but fails to work with any newly created certificates and corresponding correct Thumbprint entry?
I've looked at the Configuration for the App Service in question and it has the following setting when I understand is supposed to let the app service see all certificates registered to it, right?
WEBSITE_LOAD_CERTIFICATES = *
EDIT:
After some more testing I find that any cert I export form Azure and import on my computer will successfully iterate the cert store and find any Certificate I provide a valid Thumbprint. What it won't do is use that cert to obtain a access token. The complete code is below.
The certificate that is due to expire soon will get a proper access token and run the rest of the process by getting the proper data from the DB.
The exception I get with all the other certificates suggest something about base64 encoding but I can't quite figure that out. Any ideas?
Exception for all but the original certificate:
Client assertion contains an invalid signature
Successful Access token with this code only with original certificate:
private async Task<string> GetDatabaseTokenFromCert()
{
X509Certificate2 cert;
var store = new X509Store(StoreLocation.CurrentUser);
var authContext = new AuthenticationContext(_appSettings.AzureAD.AADInstance + _appSettings.AzureAD.TenantId);
try
{
store.Open(OpenFlags.ReadOnly);
var signingCert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == _appSettings.AzureAD.CertificateThumbprint);
if (signingCert == null)
{
throw new FileNotFoundException("Cannot locate certificate for DB access!", _appSettings.AzureAD.CertificateThumbprint);
}
cert = signingCert;
}
finally
{
store.Close();
}
var certCred = new ClientAssertionCertificate(_appSettings.AzureAD.ClientId, cert);
var result = await Retry(() => authContext.AcquireTokenAsync(_appSettings.SqlConfig.ResourceId, certCred));
return result?.AccessToken;
}
Turns out you need to also add the new certificate to the app registration. As a .cer file without the private key, obviously.
So if you get the error message:
AADSTS700027: Client assertion contains an invalid signature. [Reason - The key was not found., Thumbprint of key used by client: 'YourNewCert'
Go to Key Vault, export new cert as .cer file and import it into the App Service that is trying to obtain the Access token from AcquireTokenAsync
In my case the order of operation is:
Logic app fires off anonymous call as a POST to web API
Web API uses Thumbprint of Cert in question via appsetting.json
Finds cert with thumbprint that is in App Registration of the web API
AcquireTokenAsync takes Azure info and Cert and returns Access Token
This will work or fail on this line of original post
var result = await Retry(() => authContext.AcquireTokenAsync(_appSettings.SqlConfig.ResourceId, certCred));

Read Azure default wildcard certificate from ASP.NET Core

I'm putting up a staging environment in an Azure App Service. That sits under the Free Tier, where you cannot upload custom SSL certificates. In order to test my ASP.NET Core application setup, and specifically if it can correctly load a certificate from store, I'd like to try that without having to satisfy all requirements for a trusted certificate (change tier, get domain, get certificate, ...).
I'm aware about Let's Encrypt, but that would still force me to switch tier in order to add a certificate to Azure app service. Plus, I'm not sure you can create a certificate for a domain some-site.azurewebsites.net (I've read something somewhere against using anything related to azurewebsites.net for a custom certificate), so maybe that would also need a custom domain anyway.
I was wondering if there's a way to access, from application code, the default wildcard certificate that MS owns for *.azurewebsites.net. So something like grabbing MS Azure certificate thumbprint, storing it in WEBSITE_LOAD_CERTIFICATES app setting, then load it from store as done here in chapter 3. Access from app".
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
"insert-here-thumbprint-of-azure-default-cert",
false);
// Get the first cert with the thumbprint
if (certCollection.Count > 0)
{
X509Certificate2 cert = certCollection[0];
// Use certificate
Console.WriteLine(cert.FriendlyName);
}
certStore.Close();
Is this doable? TA.
Apparently, another SO thread has the answer. The gist of it is:
You will not be able to access this certificate programmatically in your WebApp as this certificate is not really installed on the Azure WebApp.
Not sure if this should be closed as duplicate.

Configuring Postman to talk to Azure Service Management API

I am trying to use Postman Chrome app to make REST calls against Microsoft Azure Service Management API. However, I get the following error
<Message>The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.</Message>
Here is what I did.
I created a self signed certificate. I uploaded that certificate to Azure Certificate store in the management portal and added the same to the trusted root certification authorities in my windows certificate store. However, I can't still make a valid API call. Can someone guide me down the right path here.
Finally I solved it myself. Here are the steps
1) Create a certificate using the following command in your Visual Studio Command Prompt
makecert -sky exchange -r -n "CN=<certname>" -pe -a sha1 -len 2048 -ss My <certname>.cer"
2) Upload the cer file in the settings portion of the azure management portal
3) Export a pfx file containing the private key to a location on your machine.
4) Add that pfx file to Chrome in settings certificates in trusted root certificates list
5) Make the REST call using Postman.
Enhancement of #csprabala's answer as my edit of his answer was rejected. Credits go to #csprabala.
Create a certificate using the program makecert. You find it either in your Visual Studio command window or in another location. Run this command:
makecert -sky exchange -r -n "CN=<certname>" -pe -a sha512 -len 2048 - sv "<certname>.pvk" "<certname>.cer"
Upload the <certname>.cer file in the settings portion of the Azure management portal .
Create a <certname>.pfx file containing the private key with this command (program is in the same location as in 1.):
pvk2pfx.exe -pvk "<certname>.pvk" -spc "<certname>.cer" -pfx "<certname>. pfx"
Import the file <certname>.pfx to the Windows user certificates store. You can do this in Chrome in the settings under "HTTPS/SSL" > "Manage Certificates ...". The "Personal" certificate store is appropriate.
Make the REST call using Postman.

Will a Windows Store app always disallow a self-signed certificate even if explicitly trusted?

I've seen both this and this — same problem, different question.
I'm trying to connect my Windows 8.1 Store app to an ASP.NET Web API web service, secured over HTTPS using a self-signed certificate. It's a proof-of-concept application that will end up on < 5 different machines and seen only internally, so I was planning to just install the certificate as trusted on each of the target machines.
When I try this on my development setup, both HttpClient APIs fail to establish the trust relationship when calling the service.
Windows.Web.Http.HttpClient exception: "The certificate authority is invalid or incorrect"
System.Net.Http.HttpClient exception: "The remote certificate is invalid according to the validation procedure."
My self-signed certificate (public-key-only .cer version) is installed in both the "User" and "Local Machine" Trusted Root Certification Authorities on the client. I'm really surprised that this isn't enough to get WinRT to trust it. Is there something I'm missing, or is there just no way to set up the trust relationship for a self-signed SSL certificate that will make HttpClient happy?
Details on my setup:
ASP.NET Web API
Azure web role running in Azure emulator
Cert issuer: 127.0.0.1
Cert subject: 127.0.0.1
Cert key: 2048-bit
Windows 8.1 Store application
Certificate (.cer file with public key only) installed in User\Trusted Root Certification Authorities
Certificate (.cer file with public key only) installed in Local Machine\Trusted Root Certification Authorities
Certificate (.cer file with public key only) added to Windows Store app manifest under "CA"
I am not asking for a workaround to configure HttpClient to accept self-signed or invalid certificates in general — I just want to configure a trust relationship with THIS one. Is this possible?
You should be able to find out what is the problem with the certificate by doing a request like this:
// using Windows.Web.Http;
private async void Foo()
{
HttpRequestMessage request = null;
try
{
request = new HttpRequestMessage(
HttpMethod.Get,
new Uri("https://localhost"));
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.SendRequestAsync(request);
}
catch (Exception ex)
{
// Something like: 'Untrusted, InvalidName, RevocationFailure'
Debug.WriteLine(String.Join(
", ",
request.TransportInformation.ServerCertificateErrors));
}
}
Using a HttpBaseProtocolFilter you can ignore certificate errors:
// using Windows.Web.Http;
// using Windows.Web.Http.Filters;
// using Windows.Security.Cryptography.Certificates;;
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure);
HttpClient client = new HttpClient(filter);
HttpResponseMessage response = await client.SendRequestAsync(request);
The piece I was missing turned out to be that the certificate wasn't in the list of of IIS Server Certificates on my local machine!
Opening IIS Manager and checking out the Server Certificates section, I did find a 127.0.0.1 SSL certificate already set up by the Azure emulator:
CN = 127.0.0.1
O = TESTING ONLY
OU = Windows Azure DevFabric
However, my own self-signed certificate that I made outside of IIS, also with CN=127.0.0.1, was not in the list. I imported it, and now my Windows Store app's HttpClient connects happily (certificate warnings went away in Chrome and IE as well!)
If anyone can firm up the technical details on this, please comment — this fix feels a bit magical and I'm not sure I can pinpoint precisely why this worked. Possibly some confusion on my part between the two certs for 127.0.0.1, even though the thumbprint I had configured in my Azure project was always the one I was intending to use?

HttpRequest with Certificate fails in Azure Web-role

On my deployed azure web-role I try to send a request (GET) to a Web-Server that authorizes the request by the provided certificate of the requesting client.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
var filepath = Path.GetTempPath();
string certpath = Path.Combine(filepath, "somecert.cer");
Trc.Information(string.Format("Certificate at {0} will be used", certpath));
X509Certificate cert = X509Certificate.CreateFromCertFile(certpath);
WebRequest request = WebRequest.Create(endPoint);
((HttpWebRequest)request).ProtocolVersion = HttpVersion.Version10;
((HttpWebRequest)request).IfModifiedSince = DateTime.Now;
((HttpWebRequest)request).AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
((HttpWebRequest)request).ClientCertificates.Add(cert);
The above code works perfectly in the azure-emulator but not when it is deployed. Then the call to GetResponse fails always.
System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.
at System.Net.HttpWebRequest.GetResponse()
at XYZ.Import.DataImport.OpenResponseStream(String endPoint)
I read through many of the existing discussion threads where using SecurityProtocolType.Ssl3 solved the problem but it does not in my case. Are there further debugging options considering that it is running on azure?
Update1
I tried all debugging steps that were suggested by Alexey. They are really helpfull but quite hard to execute properly on azure.
Here is with what I came up with after at least two hours.
I used the System.Net settings supplied by this post [1].
At first the output was not present in the expected folder. The file system settings on the folder need to be tweaked. Therefore the NT AUTHORITY\NETWORK SERVICE account should be allowed on the target folder.
After that the file didn't show up as expected because there seems to be a problem when only a app.config is supplied. See this thread [2]. So I provided a app.config a [ProjectAssembly].dll.config and a web.config with the content from the post [1].
To test if the Problem is related to User rights I tested with elevated rights and without like shown in post [3].
In advance I changed the Test-Project to execute in two modes. The first mode tries to load the public part in the *.cer file like shown in the code above.
The other version uses the private certificate that is loaded with this command
X509Certificate cert = new X509Certificate2(certpath, "MYPASSWORD", X509KeyStorageFlags.MachineKeySet);
As a result I gained the following insights.
When using the public part (.cer) it only works when the rights are elevated and the private cert is imported into the machine store
When using the private (.pfx) it only works if the private cert is imported into the machine store
The second setup with (.pfx) runs even without elevated rights
While debugging the CAPI2 log only had informations that had no direct relevance. The System.Net diagnostics from point one above contained this.
System.Net Information: 0 : [1756] SecureChannel#50346327 - Cannot find the certificate in either the LocalMachine store or the CurrentUser store.
[snip]
System.Net Error: 0 : [1756] Exception in HttpWebRequest#36963566:: - The request was aborted: Could not create SSL/TLS secure channel..
System.Net Error: 0 : [1756] Exception in HttpWebRequest#36963566::GetResponse - The request was aborted: Could not create SSL/TLS secure channel..
From this output and the changing situation when the elevated rights are used I would deduce that I should look further into the rights of the running web-role in combination with the certificate store.
[1] http://msdn.microsoft.com/de-de/library/ty48b824(v=vs.110).aspx
[2] Combined Azure web role and worker role project not seeing app.config when deployed
[3] http://blogs.msdn.com/b/farida/archive/2012/05/01/run-the-azure-worker-role-in-elevated-mode-to-register-httplistener.aspx
Remove SecurityProtocolType.Ssl3
Turn on CAPI2 log and check it for errors (on your local machine).
If there isn't error, then check location of CA and intermediate certificates.
Turn on system.net diagnostics and check this log for errors.
In this article describes how to find and turn on CAPI2 eventlog.
Hope this help.

Resources