Azure IOT Hub Rest API Unauthorized - rest

I am trying to use Azure Iot hub REST API to create device by following links
Create a new device identity
Control access to IoT Hub
And my http data is like
{
"status":"connected",
"authentication":{ "symmetricKey":{
"primaryKey":"key in shared access policies",
"secondaryKey":"key in shared access policies"}
},
"statusReason":"reason",
"deviceId":"test123"
}
My header is like
["Content-Type": "application/json", "Authorization": "SharedAccessSignature sig=(key in shared access policies public key)=&se=1481687791&skn=iothubowner&sr=(my iot hub name).azure-devices.net%2fdevices%2ftest123"]
But i get error 401
{"Message":"ErrorCode:IotHubUnauthorizedAccess;Unauthorized","ExceptionMessage":"Tracking ID:(tracking id )-TimeStamp:12/14/2016 03:15:17"}
Anyone know how to fixed it , or to track the exceptionMessage ?

The problem of 401 is, probably, in the way you are calculating the SAS.
The full process to calculate a SAS for the IoT Hub (in C#) is:
private static readonly DateTime epochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
public static string SharedAccessSignature(string hostUrl, string policyName, string policyAccessKey, TimeSpan timeToLive)
{
if (string.IsNullOrWhiteSpace(hostUrl))
{
throw new ArgumentNullException(nameof(hostUrl));
}
var expires = Convert.ToInt64(DateTime.UtcNow.Add(timeToLive).Subtract(epochTime).TotalSeconds).ToString(CultureInfo.InvariantCulture);
var resourceUri = WebUtility.UrlEncode(hostUrl.ToLowerInvariant());
var toSign = string.Concat(resourceUri, "\n", expires);
var signed = Sign(toSign, policyAccessKey);
var sb = new StringBuilder();
sb.Append("sr=").Append(resourceUri)
.Append("&sig=").Append(WebUtility.UrlEncode(signed))
.Append("&se=").Append(expires);
if (!string.IsNullOrEmpty(policyName))
{
sb.Append("&skn=").Append(WebUtility.UrlEncode(policyName));
}
return sb.ToString();
}
private static string Sign(string requestString, string key)
{
using (var hmacshA256 = new HMACSHA256(Convert.FromBase64String(key)))
{
var hash = hmacshA256.ComputeHash(Encoding.UTF8.GetBytes(requestString));
return Convert.ToBase64String(hash);
}
}
If you want to create the device in the IoTHub you have to have a policy with full permissions that mean:
Registry read and write, Service connect and Device connect.
If you need a full functional example, in C#, about how use the IoT Hub REST API to create a device, check if a device exists and send messages to the IoT Hub I have wrote this post about it (the post is in spanish but I can imagine that what you need is just the code).

it looks like your SAS is wrong. It shouldn't include the devices part in the end. If you open the Iot Hub Device Explorer you can Generate SAS token to access the Iot Hub API. You should create the SAS for the IoT hub level and for a device level (which include the devive id in the SAS like you have).
So your SAS should look like this -
SharedAccessSignature sr={iot hub name}.azure-devices.net&sig={sig}&se={se}&skn=iothubowner

There are two edits you need to do:
In your http data, only deviceId is required, other are optional, you can do it like this:
{
deviceId: "test123"
}
Note that there are no double quotes around deviceId.
Like #shachar said, you need remove "%2fdevices%2ftest123" in SAS token of the header. About generating SAS token you can utilize Device Explorer.
This is my test result:

The format of SAS token you use is wrong. To create a device, you need to use SAS Token for IoT Hub. You could easily use Azure IoT Toolkit extension for Visual Studio Code to generate SAS Token for IoT Hub as below screenshot.
BTW, the format of SAS token for device is /^SharedAccessSignature sr=iot-hub-test.azure-devices.net%2Fdevices%2Fdevice1&sig=.+&se=.+$/, while the format of SAS token for IoT Hub is /^SharedAccessSignature sr=iot-hub-test.azure-devices.net&sig=.+&skn=iothubowner&se=.+$/

//Following code is to generate the SAS token programatically.
string sasToken = new SharedAccessSignatureBuilder()
{ KeyName = name,
Key = key,
Target = target,
TimeToLive = TimeSpan.FromDays(days)
}.ToSignature();
//use this sas token as authorization header before calling the iot restapi

Related

List Storage Accounts only listing a few classic storage accounts

List Storage Accounts https://management.core.windows.net//services/storageservices
says that it lists the storage accounts that are available in the specified subscription and the get storage account keys work only for these storage accounts that are returned as part of this call.
But the response is giving me only few storage accounts which are classic, how do i get the other storage accounts?
But the response is giving me only few storage accounts which are
classic, how do i get the other storage accounts?
By "other" storage accounts, I guess you're meaning "Azure Resource Manager (ARM)" storage accounts. There's a different API to get ARM storage accounts that make use of Azure AD based authentication.
To learn more about ARM API to list storage accounts, please see this link: https://learn.microsoft.com/en-us/rest/api/storagerp/storageaccounts#StorageAccounts_List.
To learn more about how to authenticate/authorize ARM API calls, please see this link: https://learn.microsoft.com/en-us/rest/api/
I agree with Gaurav Mantri, if you’d like to list ARM storage accounts under a specified subscription, please use this API:
GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Storage/storageAccounts?api-version=2016-12-01
And the following code sample works fine on my side, please refer to it.
string tenantId = "{tenantId}";
string clientId = "{clientId}";
string clientSecret = "{secret}";
string subscriptionid = "{subscriptionid}";
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId, clientSecret);
var result = await authenticationContext.AcquireTokenAsync(resource: "https://management.azure.com/", clientCredential: credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(string.Format("https://management.azure.com/subscriptions/{0}/providers/Microsoft.Storage/storageAccounts?api-version=2016-12-01", subscriptionid));
request.Method = "GET";
request.Headers["Authorization"] = "Bearer " + token;
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
//extract data from response
}
catch (WebException ex)
{
//ex.Message;
}
Besides, this article explained how to create AD application and service principal that can access resources, please refer to it.
Thanks for your response,by other storage accounts I meant the storage accounts under the classic storage accounts itself which were not getting listed.
Instead of using
https://management.core.windows.net//services/storageservices
I used the REST API's
for the new storage accounts
/management.azure.com/subscriptions/id/providers/Microsoft.Storage/storageAccounts?api-version=2016-12-01
for classic:
/management.azure.com/subscriptions//providers/Microsoft.ClassicStorage/storageAccounts?api-version=
and to get keys
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/listKeys?api-version=2016-12-01
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ClassicStorage/storageAccounts/{accountName}/listKeys?api-version=2016-11-01

Firebase: Authenticate an existing user using REST API and Firebases hidden Auth URL

For the past 3 years we have used HTML/Js only with Firebase but now we are using Unity as well.
The current Unity/Firebase only works on Android/iOS when deployed and 99% of our work is on the windows store.
I've actually got a pretty decent Unity/Firebase codebase going but it requires me to use a full App Secret.
All the other libraries expose a method to login with Email/Password but the REST API only allows the use of a token or your app secret that it then states is ill advised to put into your client; I guess the thinking is if you're using a different library that you'll have your own auth/user method which we don't...
Now, I've pulled apart the web version and got this:
https://auth.firebase.com/v2/<myfirebase>/auth/password?&email=dennis%40<mysite>&password=<mypassword>v=js-2.2.9&transport=json&suppress_status_codes=true
So there IS an endpoint that I can send stuff to and I've tested it inside unity with good results.
Obviously the URL isn't guaranteed to stay working but I'm wondering if there is any reason NOT to use this?
Also, Why not just expose this endpoint in the official REST API?
As I understand it, that URL will continue to work for your Legacy Firebase project. You will have to do the same sort of reverse engineering if you want to update to the new Firebase 3.0 API. However, if you are still using a legacy Firebase project -- I encourage you to take a look at this. It has not been updated to work with Firebase 3.0 -- so I needed to do something similar to what you did to allow login to the new API.
I was able to do this with the new API using C# as follows (where FirebaseManager is a Singleton I wrote for Global variables and functions to write and read from/to the DB :
Hashtable loginData = new Hashtable();
loginData.Add ("email", <EMAIL-GOES-HERE>);
loginData.Add ("password", <PASSWORD-GOES-HERE>);
loginData.Add ("returnSecureToken", true);
UnityHTTP.Request loginRequest = new UnityHTTP.Request ("post",
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key="
+ <YOUR-PROJECT-API-KEY-GOES-HERE>, loginData);
loginRequest.Send ((request) => {
Hashtable jsonResponse = (Hashtable)JSON.JsonDecode(request.response.Text);
if (jsonResponse == null) {
DisplayErrorMessage("Error logging in. Server returned null or malformed response");
}
FirebaseManager.Instance.idToken = (string)jsonResponse["idToken"]; // This is your auth token
FirebaseManager.Instance.uid = (string)jsonResponse["localId"]; // this is your "uid"
});
// I have a list of users in my db keyed by the "uid" -- I access them like this
UnityHTTP.Request fullnameRequest = new UnityHTTP.Request ("get",
<YOUR-DATABASE-ROOT-URL-HERE>
+ "/users/" + FirebaseManager.Instance.uid + ".json?auth=" + FirebaseManager.Instance.idToken);
fullnameRequest.Send ((request) => {
Debug.Log(request.response.Text);
Hashtable jsonResponse = (Hashtable)JSON.JsonDecode(request.response.Text);
if (jsonResponse == null) {
DisplayErrorMessage("Error getting user info. Server returned null or malformed response");
}
FirebaseManager.Instance.fullname = (string)jsonResponse["fullname"];
FirebaseManager.Instance.groupId = (string)jsonResponse["group"]; // just storing this in memory
});
So I don't think there is any harm in using the URL, just make sure you budget time for more work when things change.

CoTURN: How to use TURN REST API?

I have build coturn and run it successfully. ip:192.168.1.111. Now the question I faced is to get the Turn credential through REST API.
https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00 According to the passage the request format should be
GET /?service=turn&username=mbzrxpgjys
and response should be JSON. Now my question is:
a) How to configure and command TURN SERVER to make it run in REST API mode?
b) How to write a http request in the right format so TURN SERVER can reply correctly? could you give me an example?
Few things to be clarified here are:
GET /?service=turn&username=mbzrxpgjys which returns a JSON, is just a suggested uri for retrieving time-limited TURN credentials from the server, you do not have to follow that, your uri can be just /?giveMeCredentials. In fact, I use my socket connection to retrieve this data, not direct http call with json response. End of day, it does not matter how you( the client that uses said TURN) get those credentials as long as they are valid.
You do not make any requests to the TURN server directly, no rest api call to TURN server is under your control.
you allocate a secret key when you are starting the TURN server, this can be taken from a db(thus dynamically changable), but lazy that I am, just hard-coded, and gave it in the turn config file, also remember to enable REST API. As part of turn command, turnserver ... --use-auth-secret --static-auth-secret=MySecretKey
Now, in your application server, you would use the same secret key to generate credentials, for username, it is UNIX timestamp and some string( can be random or user id or something) seperated by : and the password would be HMAC of the username with your secret key.
about the UNIX timestamp, this has be the time in TURN server till which your credentials has to be valid, so which calculating this make sure you take into account of the clock time difference between your application server and your turn server.
Now some sample code taken from my answer to another question
command for stating TURN server:
turnserver -v --syslog -a -L xx.xxx.xx.xx -X yy.yyy.yyy.yy -E zz.zzz.zz.zzz --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --static-auth-secret=my_secret --realm=north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -q 100 -Q 300 --cipher-list=ALL
node.js code for creating TURN credentials in application server:
var crypto = require('crypto');
function getTURNCredentials(name, secret){
var unixTimeStamp = parseInt(Date.now()/1000) + 24*3600, // this credential would be valid for the next 24 hours
username = [unixTimeStamp, name].join(':'),
password,
hmac = crypto.createHmac('sha1', secret);
hmac.setEncoding('base64');
hmac.write(username);
hmac.end();
password = hmac.read();
return {
username: username,
password: password
};
}
Browser code for using this:
...
iceServers:[
{
urls: "turn:turn_server_ip",
username: username,
credential:password
}
...
After (many) hours of frustration, #Mido's excellent answer here was the only thing that actually got CoTurn's REST API working for me.
My credential server is PHP and I use CoTurn's config file 'turnserver.conf' so here's a tested and working translation of Mido's work for that situation:
Assuming a 'shared secret' of '3575819665154b268af59efedee8826e', here are the relevant turnserver.conf entries:
lt-cred-mech
use-auth-secret
static-auth-secret=3575819665154b268af59efedee8826e
...and the PHP (which misled me for ages):
$ttl = 24 * 3600; // Time to live
$time = time() + $ttl;
$username = $time . ':' . $user;
$password = base64_encode(hash_hmac('sha1', $username, '3575819665154b268af59efedee8826e', true));
Building upon #Mido and #HeyHeyJC answers, here is the Python implementation to build credentials for coturn.
import hashlib
import hmac
import base64
from time import time
user = 'your-arbitrary-username'
secret = 'this-is-the-secret-configured-for-coturn-server'
ttl = 24 * 3600 # Time to live
timestamp = int(time()) + ttl
username = str(timestamp) + ':' + user
dig = hmac.new(secret.encode(), username.encode(), hashlib.sha1).digest()
password = base64.b64encode(dig).decode()
print('username: %s' % username)
print('password: %s' % password)
Here is a web application to test the login to your coturn server. Use turn:host.example.com as the server name.
I came across similar issue (getting REST API working with TURN server) recently and learned that TURN server doesn't support REST API calls at all and just provides support for an authentication format with shared secret when we enable REST API support in TURN config. The draft only provides info on things that we need to consider while implementing such REST API and WE need to create the API on our own or use something like turnhttp to generate the temporary username password combo.
As #mido detailed, you can implement the username/password generation part in the application itself. But if you have reasons to separate this from the application and want to implement it as an entirely different API service, instead of implementing a complete API as per the draft, I came across another post in which the OP provided a PHP script to generate temp username & password and this one works pretty well once you modify the hash_hmac() function to the following,
$turn_password = hash_hmac('sha1', $turn_user, $secret_key, true);
We need to base64 encode the RAW output of hash_hmac to get it working and I believe this is why it was not working for the OP in that link.
You should be able to test authentication using turnutils_uclient command to verify that the temp username/password combo is working as expected.
turnutils_uclient -y -u GENERATED_USERNAME -w GENERATED_PASSWORD yourturnserver.com
Once you have verified authentication and confirmed that it's working, you can setup webserver for the PHP script to make it available to your application and fetch the temporary username/password combo. Also, you would need to implement other security setup (authentication) to protect the API from unauthorized access.
I know this is an old post, just sharing my findings here hoping that it will be useful for someone someday.
Here is my c# implementation with TTL
public string[] GenerateTurnPassword(string username)
{
long ttl = 3600 * 6;
var time = DateTimeOffset.Now.ToUnixTimeSeconds() + ttl;
var newuser = time + ":" + username;
byte[] key = Encoding.UTF8.GetBytes("YOURSECRET");
HMACSHA1 hmacsha1 = new HMACSHA1(key);
byte[] buffer = Encoding.UTF8.GetBytes(newuser);
MemoryStream stream = new MemoryStream(buffer);
var hashValue = hmacsha1.ComputeHash(stream);
string[] arr = new string[2];
arr[0] = Convert.ToBase64String(hashValue);
arr[1] = newuser;
return arr;
}
Well #Augusto Destrero provided implementation will cause TypeError: key: expected bytes or bytearray, but got 'str' on Python 3.7.6, for anyone looking for another Python implementation, here is an example:
import time
import hmac
import hashlib
import base64
secret = b'abcdefghijkmln'
def generateTurnUsernamePwd():
username = "arbitry username here"
password = hmac.new(secret, bytes(username, 'UTF-8'), hashlib.sha1).digest()
passwordStr = base64.b64encode(password).decode("utf-8")
return username,passwordStr
print(generateTurnUsernamePwd())
The main difference is key and message keyword arguments in hmac lib has to be bytes in newer version , while in older versions, it requires str.
I thought it worthwhile to add to the answer the actual text of the documentation of coturn regardingg this topic and a link to it for those interested:
--auth-secret TURN REST API flag. Flag that sets a special WebRTC authorization option that is based upon authentication secret. The
feature purpose is to support "TURN Server REST API" as described
in the TURN REST API section below. This option uses timestamp
as part of combined username: usercombo -> "timestamp:username",
turn user -> usercombo, turn password ->
base64(hmac(input_buffer = usercombo, key = shared-secret)). This
allows TURN credentials to be accounted for a specific user id. If
you don't have a suitable id, the timestamp alone can be used. This
option is just turns on secret-based authentication. The actual
value of the secret is defined either by option static-auth-secret,
or can be found in the turn_secret table in the database.
Here is an example for go with ttl:
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"fmt"
"time"
)
const turnTokenTtl = time.Hour * 24
const turnSecret = "your secret"
func getTurnCredentials(name string) (string, string) {
timestamp := time.Now().Add(turnTokenTtl).Unix()
username := fmt.Sprintf("%d:%s", timestamp, name)
h := hmac.New(sha1.New, []byte(turnSecret))
h.Write([]byte(username))
credential := base64.StdEncoding.EncodeToString(h.Sum(nil))
return username, credential
}

Receiving data from Arduino by using Azure Mobile Services

I am working on a project where I need to read and write data from a particular column and a row from Azure Mobile Services to an Arduino. Simply, I need to read and write data to an azure mobile service by an Arduino. The writing part is pretty much done but I am facing a bit of problem in reading the data.
Here is my scenario
1: Upload sensor data from Arduino to Azure mobile service (completed almost)
2: Develop an windows phone app to access the data from azure mobile service (competed)
3: Develop an windows phone app to write data to the azure table(completed)
4: Use the data written in the above step to control my Arduino (STUCK)
Any help will be appreciated.
Assuming you have an HTTP client to do REST on your Arduino, you could do something like the following:
String read(String table) {
HttpClient http;
http_header_t headers[] = {
{ "X-ZUMO-APPLICATION", "applicationKeyHere" },
{ "Content-Type", "application/json" },
{ NULL, NULL } // NOTE: Always terminate headers with NULL
};
http_request_t request;
http_response_t response;
request.hostname = "mobileservicename.azure-mobile.net";
request.port = 80;
request.path = "/tables/" + table; //update with your table name
http.get(request, response, headers);
return response.body;
}

Where do I store my Azure role settings that are not to be stored as plain text?

Looks like the standard way of storing settings of Azure roles is under <ConfigurationSettings> tag in the .cscfg file. Looks convenient, but the file is not encrypted in any way - it is an XML that is uploaded to the Azure portal as plain text and is stored as plain text and can be edited at any time.
In my application I'll need settings that should not be stored as plain text - like for example a password to my SQL Azure database. I'd rather not have a plaintext XML file with that password. How do I store such role settings?
The typical way to do this on-premises is to use DPAPI on a single machine. Of course, this has problems on a web farm. To work around this, you can share a single key on each machine and encrypt. The easiest way to do this is to use certificate based encryption.
Nothing against the SQL Azure posts referenced by Michael, but that had to be the longest series ever to tell you to use the PKCS12 configuration provider. The only reason to use that provider is that it works in conjuction with the built-in tooling from ASP.NET that can read from appSettings automatically. It doesn't help with ServiceConfiguration that needs to change.
If all you want to do is securely protect a setting (typically in ServiceConfig) and you don't mind writing a utility class to do it, then you can use these two functions with any certificate (with private key) uploaded to Windows Azure. This is exactly how the password for remote access is encrypted in the Service Configuration.
Encrypt:
var passwordBytes = UTF8Encoding.UTF8.GetBytes("p#ssw0rd");
var contentInfo = new ContentInfo(passwordBytes);
var thumb = "F49E41878B6D63A8DD6B3650030C1A06DEBB5E77";
var env = new EnvelopedCms(contentInfo);
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Cast<X509Certificate2>().Where (xc => xc.Thumbprint == thumb).Single();
env.Encrypt(new CmsRecipient(cert));
Convert.ToBase64String(env.Encode()).Dump();
}
finally
{
if (store != null)
store.Close();
}
Decrypt:
var thumb = "F49E41878B6D63A8DD6B3650030C1A06DEBB5E77";
var cipherText = "MIIBrwYJKoZIhvcNAQcDoIIBoDCCAZwCAQAxggFgMIIBXAIBADBEMDAxLjAsBgNVBAMTJWR1bm5yeTd0YWIucmVkbW9uZC5jb3JwLm1pY3Jvc29mdC5jb20CECNRAOTmySOQTA2HuEpAcD4wDQYJKoZIhvcNAQEBBQAEggEAkIxJNnCb1nkZe3Gk2zQO8JQn2hOYM9+O9yx1eprTn7dCwjIlYulUMIYwFCMDI7TiYCXG7cET2IP/ooNBPYwxzAvEL5dUVIMK9EDE0jyRP3sGPGiSvG0MW8+xZuQx4wMGNSwm2lVW1ReVRGEpTeTcUFSBCPvXsULpbqCqXtSTgjsHngxgOKjmrWBIdrxCDxtfzvNPgSQ2AVqLTRKgFTN9RHUwJJ2zhGW+F+dBfxai3nlr7HN7JKiIdlNA0UjCd/kSIZqNfPlvd2V58RBMpkW+PEp3vpBa/8D/fhU3Qg/XBNXhroES7aVDB5E16QYO6KgPdXMCpLcQ4e9t1UhokEwUizAzBgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECEImLeoQJeVkgBCQ94ZxmHnVkBWrID+S4PEd";
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Cast<X509Certificate2>().Where (xc => xc.Thumbprint == thumb).Single();
var bytes = Convert.FromBase64String(cipherText);
var env = new EnvelopedCms();
env.Decode(bytes);
env.Decrypt();
Encoding.UTF8.GetString(env.ContentInfo.Content).Dump();
}
finally
{
if (store != null)
store.Close();
}
Check the SQL Azure blog, they blogged exactly about this!
Securing Your Connection String in Windows Azure: Part 1 (original)
Securing Your Connection String in Windows Azure: Part 2 (original)
Securing Your Connection String in Windows Azure: Part 3 (original)
also, previous posts at
http://blogs.msdn.com/b/sqlazure/archive/tags/security/