Posting data to the event hub - azure

Working with the event hub and found something I find quite odd.
How can I send data to the eventhub acting as a device which i'm not.
private static Task<HttpResponseMessage> PostTelemetryAsync(string test)
{
var serviceNamespace = "dev-hub";
var hubName = "eventhub";
var url = string.Format("/{0}/publishers/testdevice/messages/", hubName);
// Create client.
var httpClient = new HttpClient
{
BaseAddress = new Uri(string.Format("https://{0}.servicebus.windows.net/", serviceNamespace))
};
var payload = JsonConvert.SerializeObject(test);
var sas = createToken("dev-hub", "anotherDevice", "IdmUSeHmcrLfjSfc2ssJVvLcsMIHM/uqG1xSLUIh5t4=");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", sas);
var content = new StringContent(payload, Encoding.UTF8, "application/json");
content.Headers.Add("ContentType", "application/json");
return httpClient.PostAsync(url, content);
}
private static string createToken(string resourceUri, string keyName, string key)
{
TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
var week = 60 * 60 * 24 * 7;
var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + week);
string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry, keyName);
return sasToken;
}
In the code above I´m generating a SAS token for the device anotherDevice but posting to the url ...publishers/testdevice/messages/. That is a different device.
The eventprocessor I´m using thinks that the data is sent from the testdevice but the SAS token is generated for anotherDevice.
Is it supposed to work like this? How can I use a SAS token for a different device to send data to the hub or am I missing something here?

The sas token it is to authorize your application to send data. You can have hundreads of different publishers all of them using the same sas token. Think as the authorization being this shared key that you distribute to your devices, you don't need to register the publishers before you send. Use the token just as a key to send metrics not as a way to register which device sent the data.

Related

Problems with generating SAS manually for blob file

I'm trying to make a downloader attached to the service bus that is going to download the files from blob storage. But I'm having some problems with generating the SAS token manually, please se the error message below.
I'm getting error <AuthenticationErrorDetail>Signature fields not well formed.</AuthenticationErrorDetail>
private static string createToken(string resourceUri, string keyName, string key)
{
var now = DateTime.UtcNow;
TimeSpan sinceEpoch = now - new DateTime(1970, 1, 1);
var time = 60 * 2;
var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + time);
var expiryDateString = now.AddSeconds(time).ToUniversalTime().ToString("u");
string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
var sasToken = String.Format(
CultureInfo.InvariantCulture,
"{0}?sp={1}&st={2}&se={3}&spr={4}&sv={5}&sr={6}&sig={7}",
resourceUri,
"r",
DateTime.UtcNow.ToUniversalTime().ToString("u"),
expiryDateString,
"https",
"2019-02-02",
"b",
HttpUtility.UrlEncode(signature));
return sasToken;
}
Is it the stringToSign? Or signature as a whole? I'm a but unusre, to maybe I need to use the HttpUtility.UrlEncoder on all fields?
Please try the code below, it generates a working sas token for me:
static void Main(string[] args)
{
string resourceUri = "https://xx.blob.core.windows.net/test1/1.txt";
string account_name= "storage account name";
string key = "storage account key";
string s1 = createToken(resourceUri,account_name,key);
Console.WriteLine(s1);
Console.ReadLine();
}
private static string createToken(string resourceUri, string account_name, string key)
{
var accountName = account_name;
var accountKey = key;
var start = DateTime.UtcNow.AddHours(-2).ToString("yyyy-MM-ddTHH:mm:ssZ");
var end = DateTime.UtcNow.AddHours(2).ToString("yyyy-MM-ddTHH:mm:ssZ");
var permission = "rwdlac";
var serviceType = "b";
var resourceTypes = "sco";
var protocol = "https";
var serviceVersion = "2019-02-02";
//here is the difference from your code.
var stringToSign = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n",accountName, permission,serviceType,resourceTypes,start,end,"",protocol,serviceVersion);
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(accountKey));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
var sasToken = string.Format("?sv={0}&ss={1}&srt={2}&sp={3}&se={4}&st={5}&spr={6}&sig={7}", serviceVersion,
serviceType, resourceTypes, permission, end, start, protocol, HttpUtility.UrlEncode(signature));
var urlToListTables = resourceUri + sasToken;
return urlToListTables;
}
Please let me know if you still have any issues.

Azure AD Adal4j Token recieved after refresh token is not signed JWT

I am developing an authentication service for my web based java application using Azure AD OpenID connect framework. I am referring to adal4j-1.2.0.jar
The authentication is happening as per the behavior. I am getting the JWT claims and able to validate it.
But when 60 mins of session timeout occurs and I am trying to get new token claims using refresh token, the new tokens are not Signed JWT. They are Plain JWT.
I am using below call to acquire token using my initial refresh token which I am caching.
acquireTokenByRrefreshToken(refreshtoken, credential,null,null)
For validation of token, I am using the code as below
IDtokenValidator validator = new IDTokenValidator(issuer,clientID, JWSAlgo,URL)
validator.validate(idToken, exoectedNoounce); //this line throws badjwtexception signed ID token expected
Can anyone help me to understand how can I redeem the refresh token to get new Signed tokens. Or after redeeming the token, the new tokens are always Plain JWT.
I believe ,you are using implicit grant flow to get token.You are getting token from authorization end point.In this flow ,you will not get refresh token.Either you need to get new token after session expire or create a hidden frame which can get token before session expire.
You could refer to the official doc to acquire access token and refresh token by code grant flow.
Actually,methods in adal4j are implemented via HTTP REST API so that you could refer to the code below to request AuthorizationCode.
public static void getAuthorizationCode() throws IOException {
String encoding = "UTF-8";
String params = "client_id=" + clientId
+ "&response_type=" + reponseType
+ "&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F"
+ "&response_mode=query"
+ "&resource=https%3A%2F%2Fgraph.windows.net"
+ "&state=12345";
String path = "https://login.microsoftonline.com/" + tenantId + "/oauth2/authorize";
byte[] data = params.getBytes(encoding);
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(data.length));
conn.setConnectTimeout(5 * 1000);
OutputStream outStream = conn.getOutputStream();
outStream.write(data);
outStream.flush();
outStream.close();
System.out.println(conn.getResponseCode());
System.out.println(conn.getResponseMessage());
BufferedReader br = null;
if (conn.getResponseCode() != 200) {
br = new BufferedReader(new InputStreamReader((conn.getErrorStream())));
} else {
br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
}
System.out.println("Response body : " + br.readLine());
}
Then you could get access token using the AuthorizationCode you got and get refresh code using the code below.
public static void getToken(String refreshToken) throws IOException {
String encoding = "UTF-8";
String params = "client_id=" + clientId + "&refresh_token=" + refreshToken
+ "&grant_type=refresh_token&resource=https%3A%2F%2Fgraph.windows.net";
String path = "https://login.microsoftonline.com/" + tenantId + "/oauth2/token";
byte[] data = params.getBytes(encoding);
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(data.length));
conn.setConnectTimeout(5 * 1000);
OutputStream outStream = conn.getOutputStream();
outStream.write(data);
outStream.flush();
outStream.close();
System.out.println(conn.getResponseCode());
System.out.println(conn.getResponseMessage());
BufferedReader br = null;
if (conn.getResponseCode() != 200) {
br = new BufferedReader(new InputStreamReader((conn.getErrorStream())));
} else {
br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
}
System.out.println("Response body : " + br.readLine());
}
Hope it helps you.

Can't delete Azure Storage Table using Azure REST API

I am getting an error "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature."
I followed the authorization tutorial provided by Microsoft, Delete Table, Authentication for the Azure Storage Services.
Am I missing anything?
It seems that you’d like to delete table via rest api.
DELETE https://myaccount.table.core.windows.net/Tables('mytable')
the following sample works fine on my side, please refer to the code to generate the signature.
string StorageAccount = "account name here";
string StorageKey = "account key here";
string tablename = "table name";
string requestMethod = "DELETE";
string mxdate = "";
string storageServiceVersion = "2015-12-11";
protected void Button1_Click(object sender, EventArgs e)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture,
"https://{0}.table.core.windows.net/Tables('{1}')",
StorageAccount, tablename));
req.Method = requestMethod;
//specify request header
string AuthorizationHeader = generateAuthorizationHeader();
req.Headers.Add("Authorization", AuthorizationHeader);
req.Headers.Add("x-ms-date", mxdate);
req.Headers.Add("x-ms-version", storageServiceVersion);
req.ContentType = "application/json";
req.Accept = "application/json;odata=minimalmetadata";
using (HttpWebResponse response = (HttpWebResponse)req.GetResponse())
{
}
}
public string generateAuthorizationHeader()
{
mxdate = DateTime.UtcNow.ToString("R");
string canonicalizedResource = $"/{StorageAccount}/Tables('{tablename}')";
string contentType = "application/json";
string stringToSign = $"{requestMethod}\n\n{contentType}\n{mxdate}\n{canonicalizedResource}";
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(StorageKey));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
String authorization = String.Format("{0} {1}:{2}",
"SharedKey",
StorageAccount,
signature
);
return authorization;
}

Azure File Share REST API for Xamarin

In Xamarin client app, I want to access Azure Files using SAS token with Portable Class Library. It seems I can not do it using latest WindowAzure.Storage nuget package as it may only works with Blob, Table,... and it requires lots of dependencies.
Is there anyway to accomplish this?
I ended up with using Azure File Storage REST API.
Basically we request SAS token generated from Azure Share first then using that SAS token in URL to send http request to Azure Files Storage:
https://[yourshare].file.core.windows.net/[yourdirectory]/[yourfile]?[your_sas_token]
I have created a class to help client doing some basic operations as below (it is portable class so can use anywhere in client side):
public class AzureFileREST
{
private AzureSASToken _azureShareToken;
public AzureFileREST(AzureSASToken azureShareToken)
{
_azureShareToken = azureShareToken;
}
public async Task CreateIfNotExist(string directoryName)
{
var existed = await CheckDirectoryExists(directoryName);
if (!existed)
{
await CreateDirectory(directoryName);
}
}
public async Task<bool> CheckDirectoryExists(string directoryName)
{
using (var client = new HttpClient())
{
//Get directory (https://msdn.microsoft.com/en-us/library/azure/dn194272.aspx)
var azureCreateDirUrl = _azureShareToken.Url + directoryName + _azureShareToken.SASToken + "&restype=directory";
var response = await client.GetAsync(azureCreateDirUrl).ConfigureAwait(false);
return (response.StatusCode != System.Net.HttpStatusCode.NotFound);
}
}
public async Task CreateDirectory(string directoryName)
{
using (var client = new HttpClient())
{
//Create directory (https://msdn.microsoft.com/en-us/library/azure/dn166993.aspx)
var azureCreateDirUrl = _azureShareToken.Url + directoryName + _azureShareToken.SASToken + "&restype=directory";
var response = await client.PutAsync(azureCreateDirUrl, null).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
}
public async Task UploadFile(string fileName, byte[] fileBytes)
{
using (var client = new HttpClient())
{
//Create empty file first (https://msdn.microsoft.com/en-us/library/azure/dn194271.aspx)
var azureCreateFileUrl = _azureShareToken.Url + fileName + _azureShareToken.SASToken;
client.DefaultRequestHeaders.Add("x-ms-type", "file");
client.DefaultRequestHeaders.Add("x-ms-content-length", fileBytes.Length.ToString());
var response = await client.PutAsync(azureCreateFileUrl, null).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
//Then upload file (https://msdn.microsoft.com/en-us/library/azure/dn194276.aspx)
var azureUploadFileUrl = azureCreateFileUrl + "&comp=range";
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("x-ms-write", "update");
client.DefaultRequestHeaders.Add("x-ms-range", String.Format("bytes=0-{0}", (fileBytes.Length - 1).ToString()));
var byteArrayContent = new ByteArrayContent(fileBytes);
response = await client.PutAsync(azureUploadFileUrl, byteArrayContent).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
}
}
In server side, use the following function to generate SAS token from Share:
public AzureSASToken GetSASFromShare(string shareName)
{
var share = _fileclient.GetShareReference(shareName);
share.CreateIfNotExists();
string policyName = "UPARSharePolicy";
// Create a new shared access policy and define its constraints.
var sharedPolicy = new SharedAccessFilePolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddDays(15),
Permissions = SharedAccessFilePermissions.Read | SharedAccessFilePermissions.Write
};
// Get existing permissions for the share.
var permissions = share.GetPermissions();
// Add the shared access policy to the share's policies.
// Note that each policy must have a unique name.
// Maximum 5 policies for each share!
if (!permissions.SharedAccessPolicies.Keys.Contains(policyName))
{
if (permissions.SharedAccessPolicies.Count > 4)
{
var lastAddedPolicyName = permissions.SharedAccessPolicies.Keys.Last();
permissions.SharedAccessPolicies.Remove(lastAddedPolicyName);
}
permissions.SharedAccessPolicies.Add(policyName, sharedPolicy);
share.SetPermissions(permissions);
}
var sasToken = share.GetSharedAccessSignature(sharedPolicy);
//fileSasUri = new Uri(share.StorageUri.PrimaryUri.ToString() + sasToken);
return new AzureSASToken ()
{
Name = shareName,
Url = share.StorageUri.PrimaryUri.ToString() + "/",
SASToken = sasToken
};
}
Finally using class like this:
var azureFileRest = new AzureFileREST(sasToken);
await azureFileRest.CreateIfNotExist(directoryName);
await azureFileRest.UploadFile(directoryName + "/" + fileName, bytes);

getting 403 forbidden webexception for azure blob put request

I am using this code to upload some text directly to azure blob using rest api. I am getting an webexception 403 forbidden. Can someone plaese tell me where am i going wrong in my code
private String CreateAuthorizationHeader(String canonicalizedString, CloudBlob blob)
{
String signature = string.Empty;
using (HMACSHA256 hmacSha256 = new HMACSHA256())
{
Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);
signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}
String authorizationHeader = String.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", "SharedKeyLite", myaccount, signature);
return authorizationHeader;
}
private void PutBlob(String containerName, String blobName , CloudBlob blob)
{
String requestMethod = "PUT";
String urlPath = String.Format("{0}", blobName);
String storageServiceVersion = "2009-10-01";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
// if (uploadPST.HasFile)
// {
string content = "Sample file";
// Stream content = uploadPST.FileBytes;
UTF8Encoding utf8Encoding = new UTF8Encoding();
Byte[] blobContent = utf8Encoding.GetBytes(content);
Int32 blobLength = blobContent.Length;
const String blobType = "BlockBlob";
/* String canonicalizedHeaders = String.Format(
"x-ms-blob-type:{0}\nx-ms-date:{1}\nx-ms-version:{2}",
blobType,
dateInRfc1123Format,
storageServiceVersion);*/
String canonicalizedHeaders = String.Format(
"x-ms-date:{0}\nx-ms-meta-m1:{1}\nx-ms-meta-m1:{2}",
dateInRfc1123Format,
"v1",
"v2");
String canonicalizedResource = String.Format("/{0}/{1}", myaccount, urlPath);
String stringToSign = String.Format(
"{0}\n\n{1}\n\n{2}\n{3}",
requestMethod,
"text/plain; charset=UTF-8",
canonicalizedHeaders,
canonicalizedResource);
String authorizationHeader = CreateAuthorizationHeader(stringToSign, blob);
Uri uri = new Uri(CloudStorageAccount.FromConfigurationSetting("DataConnectionString").BlobEndpoint + "/" + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
// request.Headers.Add("x-ms-blob-type", blobType);
request.Headers.Add("x-ms-date", dateInRfc1123Format);
// request.Headers.Add("x-ms-version", storageServiceVersion);
request.Headers.Add("Authorization", authorizationHeader);
request.ContentLength = blobLength;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(blobContent ,0 ,blobLength);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
String ETag = response.Headers["ETag"];
}
// }
}
First, you construct an HMACSHA256 object using the default constructor -- this causes a random key to be generated and used for signing. What you want is the overload which accepts a string - and pass the azure account key.
Still, signing the request 'manually' can be tricky, as there are a lot of things to do and it's easy to mess up or forget something. Instead, I recommend you use the SignRequest method of StorageCredentialsAccountAndKey class (msdn doc) For instance;
// ...there exists a request object, and strings for the account name and key
var creds = new StorageCredentialsAccountAndKey(accountName, accountKey);
creds.SignRequest(request);
This will do everything needed to sign the request properly, including creating a canonicalized headers string, creating a date with the correct format, etc.

Resources