Renew SAS token used by Azure Iot Hub DeviceClient - azure

How to renew SASToken which is generated and valid for one hour given the code below?
I cannot see any method to renew the SAS token.
public class IotHubService
{
Microsoft.Azure.Devices.Client.DeviceClient _deviceClient;
public void InitDeviceClient()
{
var sasToken = CreateOrRenewIotSasToken();
_deviceClient = DeviceClient.Create(_iotHubEndpoint,
new DeviceAuthenticationWithToken(deviceId, sasToken),
TransportType.Mqtt_WebSocket_Only);
_deviceClient.SetRetryPolicy(retryPolicy);
// Set handlers and callbacks
_deviceClient.SetConnectionStatusChangesHandler(ConnectionStatusChanged);
_iotHubCancellationTokenSource = new CancellationTokenSource();
await _deviceClient.OpenAsync(_iotHubCancellationTokenSource.Token);
}
public async Task<string> CreateOrRenewIotSasToken()
{
var iotHubEndpoint = AppConfig.IotHubEndpoint;
var deviceId = await GetDeviceIdAsync();
var sharedKey = _appConfigProvider.AppConfig.SharedKey;
var symmetricKey = _symmetricKeyProvider.GenerateSymmetricKey(deviceId, sharedKey);
_iotSasToken = GenerateSasToken(iotHubEndpoint, symmetricKey, null,3600);
return _iotSasToken;
}
public string GenerateSasToken(string resourceUri, string key, string policyName, int expiryInSeconds)
{
var fromEpochStart = _dateTimeProvider.GetUtcDateTime() - new DateTime(1970, 1, 1);
var expiry = $"{fromEpochStart.TotalSeconds + expiryInSeconds}";
var stringToSign = WebUtility.UrlEncode(resourceUri) + "\n" + expiry;
var hmac = new HMACSHA256(Convert.FromBase64String(key));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
var token = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}", WebUtility.UrlEncode(resourceUri), WebUtility.UrlEncode(signature), expiry);
if (!String.IsNullOrEmpty(policyName))
{
token += "&skn=" + policyName;
}
return token;
}
}
Microsoft.Azure.Devices.Client: 1.21.0

Related

Cannot validate AAD access token - IDX10511: Signature validation failed

I am trying to build a method which validates my tokens. I am retrieving my tokens from Azure Active Directory with Open Id Connect Authorization Code Flow. The tokens that I get are the access_token and the id_token. I am using .NET Core.
My validation code is as follows:
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
var handler = new JwtSecurityTokenHandler();
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
try
{
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidIssuers = new [] { "https://login.microsoftonline.com/tenantid/v2.0" },
ValidAudiences = new [] { "client-Id" },
ValidateAudience = true,
ValidateIssuer = true,
IssuerSigningKeys = config.SigningKeys,
ValidateLifetime = true
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken validatedToken = null;
tokenHandler.ValidateToken(token.AccessToken, validationParameters, out validatedToken);
return validatedToken != null;
}
catch (SecurityTokenInvalidSignatureException ex)
{
return false;
}
catch(SecurityTokenValidationException)
{
return false;
}
The code below works for the id_token BUT
does not work for the access_token
The error message which I am getting when this method is executed for the access_token is:
IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: CtAAALb-8NsDe333734859crfOc
'.
kid: 'CtAAALb-8NsDe333734859crfOc'.
Exceptions caught:
' '
the nonce header has to be SHA2 hashed before signature verification
Here is an example of code where you can see
jsonToken.Header.Add("nonce", hashedNonce);
private static bool _hashNonceBeforeValidateToken = true;
private const string MicrosoftGraphApplicationId = "00000003-0000-0000-c000-000000000000";
private const string MicrosoftIssuer = "https://sts.windows.net";
public static bool ValidateTokenSignature(string accessToken, ApplicationConfiguration applicationConfiguration) {
var tokenHandler = new JwtSecurityTokenHandler();
var jsonToken = tokenHandler.ReadJwtToken(accessToken);
string[] parts = accessToken.Split('.');
string header = parts[0];
string payload = parts[1];
string signature = parts[2];
//hash nonce and update header with the hash before validating
if (_hashNonceBeforeValidateToken &&
jsonToken.Header.TryGetValue("nonce", out object nonceAsObject))
{
string plainNonce = nonceAsObject.ToString();
using (SHA256 sha256 = SHA256.Create())
{
byte[] hashedNonceAsBytes = sha256.ComputeHash(
System.Text.Encoding.UTF8.GetBytes(plainNonce));
string hashedNonce = Base64Url.Encode(hashedNonceAsBytes);
jsonToken.Header.Remove("nonce");
jsonToken.Header.Add("nonce", hashedNonce);
header = tokenHandler.WriteToken(jsonToken).Split('.')[0];
accessToken = $"{header}.{payload}.{signature}";
}
}
//get the Microsoft JWT signature public key
string stsDiscoveryEndpoint = $"https://login.microsoftonline.com/{applicationConfiguration.TenantId}/v2.0/.well-known/openid-configuration";
if (jsonToken.Header.TryGetValue("ver", out object version) && version.ToString() == "1.0")
{
stsDiscoveryEndpoint = $"https://login.microsoftonline.com/{applicationConfiguration.TenantId}/.well-known/openid-configuration";
}
var openidConfigManaged = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint,
new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever());
var configTask = openidConfigManaged.GetConfigurationAsync();
configTask.Wait();
var config = configTask.Result;
var parameteres = new TokenValidationParameters()
{
RequireAudience = true,
ValidateAudience = true,
ValidAudiences = new[] { applicationConfiguration.ApplicationId, MicrosoftGraphApplicationId },
ValidateIssuer = true,
ValidIssuers = new string[] { $"{MicrosoftIssuer}/{applicationConfiguration.TenantId}/", config.Issuer },
IssuerSigningKeys = config.SigningKeys,
ValidateIssuerSigningKey = true,
RequireExpirationTime = true,
ValidateLifetime = true,
};
var claimPrincipal = tokenHandler.ValidateToken(
accessToken, parameteres, out SecurityToken validatedToken);
return claimPrincipal.Identity.IsAuthenticated;
}
Is the access_token audience your API or Microsoft Graph/other 3rd party service? It only makes sense to validate the tokens that you (your service) consumes, other audiences will take care of this on their own. On top of that, the signature of that JWT may be opaque to you.
See this for more - https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/812#issuecomment-456700813

Azure Search using REST c#

I am trying to run the following code:
public class Item
{
[JsonProperty(PropertyName = "api-key")]
public string apikey { get; set; }
}
[[some method]]{
var url = "https://[search service name].search.windows.net/indexes/temp?api-version=2016-09-01";
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(HttpMethod.Put,url))
{
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var sItem = new Item { apikey = [AzureSearchAdminKey] };
var tststring = JsonConvert.SerializeObject(sItem);
var body=new StringContent(tststring, Encoding.UTF8,"application/json" );
request.Content = body;
request.Method = HttpMethod.Put;
using (HttpResponseMessage response = httpClient.SendAsync(request).Result)
{
var stringr = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(stringr);
Console.ReadLine();
}
}
}
}
I get the following error:
"Error reading JObject from JsonReader. Path '', line 0, position 0."
Could someone from search team tell me what I did wrong?
The api key should be in the HTTP header, and the index definition should be in the HTTP body.
Here's some sample code for creating a data source, index, and indexer, which reads from an Azure SQL DB and indexes its rows.
class Program
{
static void Main(string[] args)
{
var searchServiceName = "[search service name]";
var apiKey = "[api key]";
var dataSourceName = "[data source name]";
var indexName = "[index name]";
var indexerName = "[indexer name]";
var azureSqlConnectionString = "[Azure SQL connection string]";
var azureSqlTableName = "[table name]";
using (var httpClient = new HttpClient())
{
var dataSourceDefinition = AzureSqlDatasourceDefinition(azureSqlConnectionString, azureSqlTableName);
var putDataSourceRequest = PutDataSourceRequest(searchServiceName, apiKey, dataSourceName, dataSourceDefinition);
Console.WriteLine($"Put data source {putDataSourceRequest.RequestUri}");
Console.WriteLine();
var putDataSourceResponse = httpClient.SendAsync(putDataSourceRequest).Result;
var putDataSourceResponseContent = putDataSourceResponse.Content.ReadAsStringAsync().Result;
Console.WriteLine(putDataSourceResponseContent);
Console.WriteLine();
var indexDefinition = IndexDefinition();
var putIndexRequest = PutIndexRequest(searchServiceName, apiKey, indexName, indexDefinition);
Console.WriteLine($"Put index {putIndexRequest.RequestUri}");
Console.WriteLine();
var putIndexResponse = httpClient.SendAsync(putIndexRequest).Result;
var putIndexResponseContent = putIndexResponse.Content.ReadAsStringAsync().Result;
Console.WriteLine(putIndexResponseContent);
Console.WriteLine();
var indexerDefinition = IndexerDefinition(dataSourceName, indexName);
var putIndexerRequest = PutIndexerRequest(searchServiceName, apiKey, indexerName, indexerDefinition);
Console.WriteLine($"Put indexer {putIndexerRequest.RequestUri}");
Console.WriteLine();
var putIndexerResponse = httpClient.SendAsync(putIndexerRequest).Result;
var putIndexerResponseContent = putIndexerResponse.Content.ReadAsStringAsync().Result;
Console.WriteLine(putIndexerResponseContent);
Console.WriteLine();
var runIndexerRequest = RunIndexerRequest(searchServiceName, apiKey, indexerName);
Console.WriteLine($"Run indexer {runIndexerRequest.RequestUri}");
Console.WriteLine();
var runIndexerResponse = httpClient.SendAsync(runIndexerRequest).Result;
Console.WriteLine($"Success: {runIndexerResponse.IsSuccessStatusCode}");
Console.ReadLine();
}
}
static HttpRequestMessage PutDataSourceRequest(string searchServiceName, string apiKey, string dataSourceName,
string datasourceDefinition)
{
var request = new HttpRequestMessage(HttpMethod.Put,
$"https://{searchServiceName}.search.windows.net/datasources/{dataSourceName}?api-version=2016-09-01");
request.Headers.Add("api-key", apiKey);
var body = new StringContent(datasourceDefinition, Encoding.UTF8, "application/json");
request.Content = body;
return request;
}
static HttpRequestMessage PutIndexRequest(string searchServiceName, string apiKey, string indexName,
string indexDefinition)
{
var request = new HttpRequestMessage(HttpMethod.Put,
$"https://{searchServiceName}.search.windows.net/indexes/{indexName}?api-version=2016-09-01");
request.Headers.Add("api-key", apiKey);
var body = new StringContent(indexDefinition, Encoding.UTF8, "application/json");
request.Content = body;
return request;
}
static HttpRequestMessage PutIndexerRequest(string searchServiceName, string apiKey, string indexerName,
string indexerDefinition)
{
var request = new HttpRequestMessage(HttpMethod.Put,
$"https://{searchServiceName}.search.windows.net/indexers/{indexerName}?api-version=2016-09-01");
request.Headers.Add("api-key", apiKey);
var body = new StringContent(indexerDefinition, Encoding.UTF8, "application/json");
request.Content = body;
return request;
}
static HttpRequestMessage RunIndexerRequest(string searchServiceName, string apiKey, string indexerName)
{
var request = new HttpRequestMessage(HttpMethod.Post,
$"https://{searchServiceName}.search.windows.net/indexers/{indexerName}/run?api-version=2016-09-01");
request.Headers.Add("api-key", apiKey);
return request;
}
static string AzureSqlDatasourceDefinition(string connectionString, string tableName)
{
return #"
{
""description"": ""azure sql datasource"",
""type"": ""azuresql"",
""credentials"": { ""connectionString"": """ + connectionString + #""" },
""container"": { ""name"": """ + tableName + #""" },
""dataChangeDetectionPolicy"": {
""#odata.type"": ""#Microsoft.Azure.Search.HighWaterMarkChangeDetectionPolicy"",
""highWaterMarkColumnName"": ""highwatermark""
},
""dataDeletionDetectionPolicy"": {
""#odata.type"": ""#Microsoft.Azure.Search.SoftDeleteColumnDeletionDetectionPolicy"",
""softDeleteColumnName"": ""deleted"",
""softDeleteMarkerValue"": ""true""
}
}
";
}
static string IndexDefinition()
{
return #"
{
""fields"": [
{
""name"": ""id"",
""type"": ""Edm.String"",
""key"": true,
""searchable"": true,
""sortable"": true,
""retrievable"": true
},
{
""name"": ""field1"",
""type"": ""Edm.String"",
""searchable"": true,
""retrievable"": true
},
{
""name"": ""field3"",
""type"": ""Edm.Int32"",
""retrievable"": true
}
]
}
";
}
static string IndexerDefinition(string dataSourceName, string indexName)
{
return #"
{
""description"": ""indexer for azure sql datasource"",
""dataSourceName"": """ + dataSourceName + #""",
""targetIndexName"": """ + indexName + #""",
""schedule"": { ""interval"": ""P1D"" }
}
";
}
}
The indexer is scheduled to run once per day. You can set it to run more frequently if the data changes often, but it might affect your search throughput.
This is the table definition if you're interested
CREATE TABLE [dbo].[testtable](
[id] [int] IDENTITY(1,1) NOT NULL,
[field1] [nchar](10) NULL,
[field2] [nchar](10) NULL,
[field3] [int] NULL,
[highwatermark] [timestamp] NOT NULL,
[deleted] [bit] NOT NULL
) ON [PRIMARY]
INSERT INTO [dbo].[testtable] (field1, field2, field3, deleted) VALUES ('abc', 'def', 123, 0)
It looks like you're trying to modify your index definition, but the body of the request contains the api-key instead of the JSON for the index definition. The api-key needs to be in the request headers, not the body.
You might find it simpler to use the Azure Search .NET SDK instead of calling the REST API directly.

Azure Blob Get Storage Get all Blobs using http request facing 403 forbidden error

I want to get all blob containers from azure storage using http client request.
I did one sample but i am facing forbidden 403 error.
I attached my code,
private const string ListofContainersURL = "https://{0}.blob.core.windows.net/?comp=list&maxresults=3"; //https://myaccount.blob.core.windows.net/?comp=list&maxresults=3
public string ListofContainersinBlob()
{
string Requesturl = string.Format(ListofContainersURL, storageAccount );
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Requesturl);
string now = DateTime.UtcNow.ToString("R");
request.Method = "GET";
request.Headers.Add("x-ms-version", "2015-12-11");
request.Headers.Add("x-ms-date", now);
request.Headers.Add("Authorization", AuthorizationHeader1("GET", now, request, storageAccount, storageKey));
var response = request.GetResponseAsync();
using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse())
{
return resp.StatusCode.ToString();
}
}
private string AuthorizationHeader1(string method, string now, HttpWebRequest request, string storageAccount, string storageKey)
{
string headerResource = $"x-ms-blob-type:BlockBlob\nx-ms-date:{now}\nx-ms-version:2015-12-11";
string urlResource = $"/{storageAccount}";
String AuthorizationHeader = String.Format("{0} {1}:{2}", "SharedKey", storageAccount, storageKey);
return AuthorizationHeader;
}
There are some issues with your AuthorizationHeader1 method. Please refer to Authentication for the Azure Storage Services for instructions on how to construct authorization header. Please try this code:
private static string AuthorizationHeader1(string method, string now, HttpWebRequest request, string storageAccount, string storageKey)
{
string headerResource = $"x-ms-date:{now}\nx-ms-version:2015-12-11";
string canonicalizedResource = $"/{storageAccount}/\ncomp:list\nmaxresults:3";
var contentEncoding = "";
var contentLanguage = "";
var contentLength = "";
var contentMd5 = "";
var contentType = "";
var date = "";
var ifModifiedSince = "";
var ifMatch = "";
var ifNoneMatch = "";
var ifUnmodifiedSince = "";
var range = "";
var stringToSign = $"{method}\n{contentEncoding}\n{contentLanguage}\n{contentLength}\n{contentMd5}\n{contentType}\n{date}\n{ifModifiedSince}\n{ifMatch}\n{ifNoneMatch}\n{ifUnmodifiedSince}\n{range}\n{headerResource}\n{canonicalizedResource}";
var signature = "";
using (var hmacSha256 = new HMACSHA256(Convert.FromBase64String(storageKey)))
{
var dataToHmac = Encoding.UTF8.GetBytes(stringToSign);
signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}
String AuthorizationHeader = String.Format("{0} {1}:{2}", "SharedKey", storageAccount, signature);
return AuthorizationHeader;
}

OWIN Hybrid with IdentityServer 3 Authorization code is too long error

Please, help!!!!
I am trying to follow a Hybrid implementation (Azure AD + Identity Server 3) from here
I am able to get to the AAD, I seem to get authenticated (get user info, etc) and receive a context.code:
When I pass that code into RequestAuthorizationCodeAsync I get an "invalid_grant" and if I look at the client, here is what I see (Authorization Code is too long):
Here is my code:
public class Startup
{
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
public void Configuration(IAppBuilder app)
{
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = "https://localhost:44300/",
PostLogoutRedirectUri = "https://localhost:44300/",
ResponseType = "code id_token",
Scope = "openid profile read write offline_access",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
},
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async context =>
{
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("oid").Value;
string tenantID = context.AuthenticationTicket.Identity.FindFirst("tid").Value;
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
context.ProtocolMessage.RedirectUri = appBaseUrl + "/";
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
Uri redirectUri = new Uri(context.Request.Uri.GetLeftPart(UriPartial.Path));
string authorizationCode = context.Code;
// use the code to get the access and refresh token
var tokenClient = new TokenClient(
Constants.TokenEndpoint,
clientId,
"secret", AuthenticationStyle.PostValues);
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(
authorizationCode, context.RedirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
// use the access token to retrieve claims from userinfo
var userInfoClient = new UserInfoClient(
new Uri(Constants.UserInfoEndpoint),
tokenResponse.AccessToken);
var userInfoResponse = await userInfoClient.GetAsync();
// create new identity
var id = new ClaimsIdentity(context.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.GetClaimsIdentity().Claims);
id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", context.ProtocolMessage.IdToken));
id.AddClaim(new Claim("sid", context.AuthenticationTicket.Identity.FindFirst("sid").Value));
context.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, context.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
context.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = context =>
{
// if signing out, add the id_token_hint
if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = context.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
context.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
}
return Task.FromResult(0);
}
}
});
}
}

How to use Azure storage emulator blob endpoint to get a blob?

The following code, when pointed to a real Azure storage account will successfully return the blob content:
var path = $"{container}/{blob}";
var rfcDate = DateTime.UtcNow.ToString("R");
var headers = "GET\n\n\n\n\n\n\n\n\n\n\n\n" +
"x-ms-blob-type:Block\n" +
$"x-ms-date:{rfcDate}\n" +
$"x-ms-version:{ServiceVersion}\n" +
$"/{AccountName}/{path}";
var uri = new Uri(BlobEndpoint + path);
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("x-ms-blob-type", "Block");
request.Headers.Add("x-ms-date", rfcDate);
request.Headers.Add("x-ms-version", ServiceVersion);
string signature = "";
using (var sha = new HMACSHA256(System.Convert.FromBase64String(AccountKey)))
{
var data = Encoding.UTF8.GetBytes(headers);
signature = System.Convert.ToBase64String(sha.ComputeHash(data));
}
var authHeader = $"SharedKey {AccountName}:{signature}";
request.Headers.Add("Authorization", authHeader);
using (var client = new HttpClient())
{
var response = await client.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
However, if I configure it to use the Azure emulator where:
AccountName = devstoreaccount1
AccountKey = Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==
BlobEndpoint = http://127.0.0.1:10000/
ServiceVersion = 2009-09-19
I always get 404. I'm using Azure Storage Emulator v4.6. Is the code or config incorrect or is this not supported with the emulator?
There are two issues with your code:
Blob Service in Storage Emulator listens at http://127.0.0.1:1000 however the base URI is http://127.0.0.1:1000/devstoreaccount1.
In computation of Signature String (header variable in your code), account name must appear twice. This is because the account name is part of your resource's URI path (URL for the blob would be http://127.0.0.1:1000/devstoreaccount1/container-name/blob-name).
Based on these, please try the following code:
static async Task<string> ReadBlobFromDevStorage()
{
var container = "temp";
var blob = "test.txt";
var ServiceVersion = "2009-09-19";
var AccountName = "devstoreaccount1";
var BlobEndpoint = "http://127.0.0.1:10000/devstoreaccount1";
var path = $"{container}/{blob}";
var AccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
var rfcDate = DateTime.UtcNow.ToString("R");
var headers = "GET\n\n\n\n\n\n\n\n\n\n\n\n" +
"x-ms-blob-type:Block\n" +
$"x-ms-date:{rfcDate}\n" +
$"x-ms-version:{ServiceVersion}\n" +
$"/{AccountName}/{AccountName}/{path}";
var uri = new Uri(BlobEndpoint + "/" + path);
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("x-ms-blob-type", "Block");
request.Headers.Add("x-ms-date", rfcDate);
request.Headers.Add("x-ms-version", ServiceVersion);
string signature = "";
using (var sha = new HMACSHA256(System.Convert.FromBase64String(AccountKey)))
{
var data = Encoding.UTF8.GetBytes(headers);
signature = System.Convert.ToBase64String(sha.ComputeHash(data));
}
var authHeader = $"SharedKey {AccountName}:{signature}";
request.Headers.Add("Authorization", authHeader);
using (var client = new HttpClient())
{
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
return content;
}
}

Resources