C# 使用证书身份验证调用Azure资源速率API时获取403

C# 使用证书身份验证调用Azure资源速率API时获取403,c#,.net,powershell,azure,C#,.net,Powershell,Azure,我正在尝试创建一个控制台应用程序,可以使用证书身份验证调用。为此,我使用了以下分支 我得到一个403错误。我已将一个Web应用程序添加到我的Azure广告中。在清单中,我已使用以下PowerShell命令从已签名的证书中复制了密钥凭据 $cert=New-SelfSignedCertificate -Subject "CN=RateCardCert" -CertStoreLocation "Cert:\CurrentUser\My" -KeyExportPolicy Exportable -K

我正在尝试创建一个控制台应用程序,可以使用证书身份验证调用。为此,我使用了以下分支

我得到一个403错误。我已将一个Web应用程序添加到我的Azure广告中。在清单中,我已使用以下PowerShell命令从已签名的证书中复制了密钥凭据

$cert=New-SelfSignedCertificate -Subject "CN=RateCardCert"
-CertStoreLocation "Cert:\CurrentUser\My"  -KeyExportPolicy Exportable -KeySpec Signature  
$bin = $cert.RawData $base64Value = [System.Convert]::ToBase64String($bin)
$bin = $cert.GetCertHash() 
$base64Thumbprint = [System.Convert]::ToBase64String($bin) 
$keyid = [System.Guid]::NewGuid().ToString() 
$jsonObj = @ customKeyIdentifier=$base64Thumbprint;keyId=$keyid;type="AsymmetricX509Cert";usage="Verify";value=$base64Value} 
$keyCredentials=ConvertTo-Json @($jsonObj) | Out-File "keyCredentials.txt"
在de控制台应用程序中,我使用以下函数获取令牌

public static string GetOAuthTokenFromAAD_ByCertificate(string TenanatID, string ClientID, string CertificateName)
    {
        //Creating the Authentication Context
        var authContext = new AuthenticationContext(string.Format("https://login.windows.net/{0}", TenanatID));
        //Console.WriteLine("new authContext made");
        //Creating the certificate object. This will be used to authenticate
        X509Certificate2 cert = null;
        //Console.WriteLine("empty 'cert' made, null");
        //The Certificate should be already installed in personal store of the current user under 
        //the context of which the application is running.
        X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);


        try
        {
            //Trying to open and fetch the certificate
            store.Open(OpenFlags.ReadOnly);
            var certCollection = store.Certificates;
            var certs = certCollection.Find(X509FindType.FindBySubjectName, CertificateName, false);
            //Checking if certificate found
            if (certs == null || certs.Count <= 0)
            {
                //Throwing error if certificate not found
                throw new Exception("Certificate " + CertificateName + " not found.");
            }
            cert = certs[0];
        }
        finally
        {
            //Closing the certificate store
            store.Close();
        }

        //Creating Client Assertion Certificate object
        var certCred = new ClientAssertionCertificate(ClientID, cert);

        //Fetching the actual token for authentication of every request from Azure using the certificate
        var token = authContext.AcquireToken("https://management.core.windows.net/", certCred);

        //Optional steps if you need more than just a token from Azure AD
        //var creds = new TokenCloudCredentials(subscriptionId, token.AccessToken);
        //var client = new ResourceManagementClient(creds); 

        //Returning the token
        return token.AccessToken;
    }
公共静态字符串GetOAuthTokenFromAAD_ByCertificate(字符串租户ID、字符串客户端ID、字符串证书名称)
{
//创建身份验证上下文
var authContext=新的AuthenticationContext(string.Format(“https://login.windows.net/{0},租户ID));
//Console.WriteLine(“新建authContext”);
//正在创建证书对象。这将用于身份验证
X509Certificate2Cert=null;
//Console.WriteLine(“生成空“证书”,空”);
//证书应已安装在当前用户的个人存储中
//应用程序正在运行的上下文。
X509Store=新的X509Store(StoreName.My,StoreLocation.CurrentUser);
尝试
{
//正在尝试打开并获取证书
打开(OpenFlags.ReadOnly);
var certCollection=store.Certificates;
var certs=certCollection.Find(X509FindType.FindBySubjectName,CertificateName,false);
//检查是否找到证书

如果(certs==null | | certs.Count在评论中聊天后发现问题:

您正在调用Azure资源管理API,但您仅授予了Azure服务管理API的权限。您需要将应用的服务主体添加到订阅中的角色。请先查找您的订阅,然后查找访问控制(IAM)刀片,然后将您的应用添加到其中的角色。您应该能够使用其名称找到它


如果您想限制其功能,还可以将服务主体添加到资源组上的角色或特定资源中。

您是否在Azure AD中授予了应用权限?403表示授权失败,而不是身份验证,因此证书可能工作正常。我已通过转到AAD->app Reg向应用程序添加了权限IStritions-->appname-->所需权限。我已添加“Windows Azure服务管理API”和“Windows Azure Active Directory”。对于后者,我已添加所有权限(请确保)但403仍然存在..您是否按了授予权限按钮?添加权限只表明它需要这些权限,但授予它们是一个单独的操作。我已经这样做了,但不幸的是没有用。仍然是相同的控制台输出,我迷路了:PAhh,您正在调用ARM API,但您只在ASM API上授予了权限,您支持吗bably需要将你的应用程序的服务主体添加到订阅中的角色。找到你的订阅,然后找到用户刀片(我认为),然后将你的应用程序添加到那里的角色。你应该能够找到它的名称。
//Get the AAD User token to get authorized to make the call to the Usage API
        string token = GetOAuthTokenFromAAD_ByCertificate("<MyTenantName.onmicrosoft.com", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "RateCardCert");


            // Build up the HttpWebRequest
        string requestURL = String.Format("{0}/{1}/{2}/{3}",
                   ConfigurationManager.AppSettings["ARMBillingServiceURL"],
                   "subscriptions",
                   ConfigurationManager.AppSettings["SubscriptionID"],
                   "providers/Microsoft.Commerce/RateCard?api-version=2015-06-01-preview&$filter=OfferDurableId eq 'MS-AZR-0044P' and Currency eq 'EUR' and Locale eq 'nl-NL' and RegionInfo eq 'NL'");
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestURL);

        // Add the OAuth Authorization header, and Content Type header
        request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token);
        request.ContentType = "application/json";

        // Call the RateCard API, dump the output to the console window
        try
        {
            // Call the REST endpoint
            Console.WriteLine("Calling RateCard service...");
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Console.WriteLine(String.Format("RateCard service response status: {0}", response.StatusDescription));
            Stream receiveStream = response.GetResponseStream();

            // Pipes the stream to a higher level stream reader with the required encoding format. 
            StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
            var rateCardResponse = readStream.ReadToEnd();
            Console.WriteLine("RateCard stream received.  Press ENTER to continue with raw output.");
            Console.ReadLine();
            Console.WriteLine(rateCardResponse);
            Console.WriteLine("Raw output complete.  Press ENTER to continue with JSON output.");
            Console.ReadLine();

            // Convert the Stream to a strongly typed RateCardPayload object.  
            // You can also walk through this object to manipulate the individuals member objects. 
            RateCardPayload payload = JsonConvert.DeserializeObject<RateCardPayload>(rateCardResponse);

            Console.WriteLine(rateCardResponse.ToString());
            response.Close();
            readStream.Close();
            Console.WriteLine("JSON output complete.  Press ENTER to close.");
            Console.ReadLine();
        }
        catch(Exception e)
        {
            Console.WriteLine(String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : ""));
            Console.ReadLine();
        }