Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

403 forbidden When call Azure rest api from a web role instance

Tags:

azure

I have a very strange problem. I published a webrole to azure cloud service. In this project, it requires webrole call a Azure Rest API, I can get the response in local emulator but, if I publish it to Azure I get the 403 forbidden Error. I am sure that I installed the certificate to Azure.

This error can be reproduced with following steps:

  1. First create a certificate with link below: http://msdn.microsoft.com/en-us/library/windowsazure/gg651127.aspx
  2. Create a cloud service with a webrole, and the certificate in Azure portal, cloud service Certificate and webrole->property->certificate.
  3. Publish the project.
  4. Remote log in to the web role instance.
  5. Create a console application at local then copy the debug folder to remote instance and run the exe in remote application. you can find the application can run perfect in local, but in Azure instance, it seems like it can find certificate, but still get 403 forbidden error.

Console app code:

static void Main(string[] args)
    {
        try
        {
            // X.509 certificate variables.
            X509Store certStore = null;
            X509Certificate2Collection certCollection = null;
            X509Certificate2 certificate = null;

            // Request and response variables.
            HttpWebRequest httpWebRequest = null;
            HttpWebResponse httpWebResponse = null;

            // Stream variables.
            Stream responseStream = null;
            StreamReader reader = null;

            // URI variable.
            Uri requestUri = null;

            // Specify operation to use for the service management call.
            // This sample will use the operation for listing the hosted services.
            string operation = "hostedservices";

            // The ID for the Windows Azure subscription.
            string subscriptionId = "";

            // The thumbprint for the certificate. This certificate would have been
            // previously added as a management certificate within the Windows Azure management portal.
            string thumbPrint = "";

            // Open the certificate store for the current user.
            certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            certStore.Open(OpenFlags.ReadOnly);

            // Find the certificate with the specified thumbprint.
            certCollection = certStore.Certificates.Find(
                                 X509FindType.FindByThumbprint,
                                 thumbPrint,
                                 false);

            // Close the certificate store.
            certStore.Close();

            // Check to see if a matching certificate was found.
            if (0 == certCollection.Count)
            {
                throw new Exception("No certificate found containing thumbprint " + thumbPrint);
            }

            // A matching certificate was found.
            certificate = certCollection[0];
            Console.WriteLine("Using certificate with thumbprint: " + thumbPrint);

            // Create the request.
            requestUri = new Uri("https://management.core.windows.net/"
                                 + subscriptionId 
                                 + "/services/" 
                                 + operation);

            httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(requestUri);

            // Add the certificate to the request.
            httpWebRequest.ClientCertificates.Add(certificate);

            // Specify the version information in the header.
            httpWebRequest.Headers.Add("x-ms-version", "2011-10-01");

            // Make the call using the web request.
            httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();

            // Display the web response status code.
            Console.WriteLine("Response status code: " + httpWebResponse.StatusCode);

            // Display the request ID returned by Windows Azure.
             if (null != httpWebResponse.Headers)
             {
                 Console.WriteLine("x-ms-request-id: "
                 + httpWebResponse.Headers["x-ms-request-id"]);
             }

            // Parse the web response.
            responseStream = httpWebResponse.GetResponseStream();
            reader = new StreamReader(responseStream);
            // Display the raw response.
            Console.WriteLine("Response output:");
            Console.WriteLine(reader.ReadToEnd());

            // Close the resources no longer needed.
            httpWebResponse.Close(); 
            responseStream.Close(); 
            reader.Close();
        }
        catch (Exception e)
        {

            Console.WriteLine("Error encountered: " + e.Message);

            // Exit the application with exit code 1.
            Console.ReadLine();
            System.Environment.Exit(1);

        }
        finally
        {
            // Exit the application.
            Console.ReadLine();
            System.Environment.Exit(0);
        }
    }
like image 952
EthenHY Avatar asked Feb 15 '23 18:02

EthenHY


2 Answers

I was running into the same issue using the azure create cert link you provided. I found out that when creating the certificate using that method, the private key was not being uploaded to the cloud service. Even though the service was able to find the certificate, it was still unauthorized when submitting requests.

Using the following method to create a private and public key certificate worked. In the Visual Studio Command Prompt, create a .cer and .pfx file:

makecert -r -pe -n "CN=AzureManage" -sky exchange "AzureManage.cer" -sv "AzureManage.pvk"
pvk2pfx -pvk "AzureManage.pvk" -spc "AzureManage.cer" -pfx "AzureManage.pfx" -pi password

The first command creates a private and public key file. You will be prompted for a password a few times. The second command combines the two into a pfx file. If you leave -pi password off, then you will be prompted for the password instead of entering it in the terminal.

You'll then want to import the files appropriately:

  • Import the pfx into your Local Machine / Personal certificate store using mmc.
  • Upload the pfx to the Azure Cloud Service.
  • Upload the cer to the Azure Management Certificates store.
  • Add the thumbprint of the pfx to your Azure Role Certificates property.

You can then use the Azure management REST API as follows:

X509Certificate2 GetCertificate(string thumbprint)
{
  var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
  store.Open(OpenFlags.ReadOnly);
  var certs = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);

  if (certs.Count == 0) return null;
  var cert = certs[0];
  store.Close();
  return cert;
}

HttpWebRequest request = WebRequest.CreateHttp(apiUrl);
request.ClientCertificates.Add(cert);
request.Headers.Add("x-ms-version", "2012-03-01");
like image 122
jeffaudio Avatar answered May 19 '23 00:05

jeffaudio


I believe your problem lies with this line of code:

certStore = new X509Store(StoreName.My, **StoreLocation.CurrentUser**);

I would expect that a properly uploaded certificate (assuming it was a .pfx that uploaded properly thru the management portal) is stored in the LocalMachine store, not CurrentUser.

Also, in order to read the certificate from the certificate store, your Role needs to run in Full Trust (this can be specified/validated in the Role's project properties in visual studio)

like image 24
Igorek Avatar answered May 18 '23 23:05

Igorek