Skip to content

Instantly share code, notes, and snippets.

@chgeuer
Created June 23, 2014 16:54
Show Gist options
  • Save chgeuer/aa82a7c5bd41ef4cc507 to your computer and use it in GitHub Desktop.
Save chgeuer/aa82a7c5bd41ef4cc507 to your computer and use it in GitHub Desktop.
Shows how to configure access control service with symmetric keys for device identities
namespace IoTDemo
{
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Messaging;
using Common.ACS.Management;
/*
* This sample uses the Access Control Service management library from here:
* https://github.com/chgeuer/Windows-Azure-AD-Access-0dcde385/tree/master/C%23/Management/ManagementService/Common
*
*
* https://dsservicebus-sb.accesscontrol.windows.net/v2/mgmt/service/$metadata
*/
// <appSettings>
// <add key="ServiceBusNamespace" value="dsservicebus"/>
// <add key="ServiceBusManagementServiceIdentityName" value="owner"/>
// <add key="ServiceBusManagementServiceIdentityKey" value="hfkjdshfkjhdsjfhkdshfkshk==="/>
// <add key="AccessControlServiceManagementServiceIdentityName" value="SBManagementClient"/>
// <add key="AccessControlServiceManagementServiceIdentityKey" value="hfkjdshfkjhdsjfhkdshfkshk==="/>
//</appSettings>
internal class Program
{
private static readonly string deviceName = "46005d4e-3783-469e-834c-89af0980db9a";
private static readonly string deviceKey = "59bfi8Qy+DV/1qAD8VbS1Ew9HI3KDsr4QpJuAzb1iA0=";
private static void Main(string[] args)
{
var deviceId = new DeviceId(deviceName);
var client = SampleConfiguration.GetAdminQueueClientFromSampleCredentials(deviceId);
for (int i = 0; i < 5; i++)
{
client.Send(new BrokeredMessage(string.Format("Timebombed {0} ", DateTime.Now.ToString()))
{
TimeToLive = TimeSpan.FromSeconds(2)
});
Console.WriteLine("Sent message");
}
client.Send(new BrokeredMessage(string.Format("Not timebombed {0} ", DateTime.Now.ToString())) { });
Thread.Sleep(TimeSpan.FromSeconds(5));
var pingClient = deviceId.CreateDeviceQueueClient(SampleConfiguration.ServiceBusNamespace, Convert.FromBase64String(deviceKey),
ReceiveMode.PeekLock);
var tempMsg = pingClient.Receive(TimeSpan.FromSeconds(1));
if (tempMsg != null)
{
Console.WriteLine("Peeked into message, Abandon: " + tempMsg.GetBody<string>());
tempMsg.Abandon();
}
Console.WriteLine("Checking deadletter queue");
var deadClient = deviceId.CreateDeviceQueueClient(SampleConfiguration.ServiceBusNamespace, Convert.FromBase64String(deviceKey),
ReceiveMode.ReceiveAndDelete, QueueType.DeadLetter);
BrokeredMessage msg;
while ((msg = deadClient.Receive(TimeSpan.FromSeconds(1))) != null)
{
Console.WriteLine("Deadletter Received: " + msg.GetBody<string>());
}
Console.WriteLine("Start receiving");
var receiveClient = deviceId.CreateDeviceQueueClient(SampleConfiguration.ServiceBusNamespace, Convert.FromBase64String(deviceKey));
while ((msg = receiveClient.Receive(TimeSpan.FromSeconds(3))) != null)
{
Console.WriteLine("Received: " + msg.GetBody<string>());
}
Console.WriteLine("Finished receiving");
}
private static void MainSetup(string[] args)
{
ManagementService acsMgmt = SampleConfiguration.CreateAccessControlServiceManagement();
NamespaceManager queueMgmt = SampleConfiguration.CreateServiceBusManagement();
string serviceBusNamespace = SampleConfiguration.ServiceBusNamespace;
ServiceBusExtensions.DeleteAllAssets(acsMgmt, queueMgmt);
DeviceId deviceId = new DeviceId { Id = Guid.NewGuid() };
byte[] key = KeyGen.CreateNewKey();
acsMgmt.Provision(serviceBusNamespace, deviceId, key, queueMgmt);
var client = SampleConfiguration.GetAdminQueueClientFromSampleCredentials(deviceId);
for (int i = 0; i < 5; i++)
{
var body = string.Format("Test message {0}", i);
BrokeredMessage message = new BrokeredMessage(body)
{
ContentType = "text/plain",
TimeToLive = TimeSpan.FromSeconds(2),
To = "Dude"
};
message.Properties["TestProperty"] = "TestValue";
message.Properties["Message number"] = i;
client.Send(message);
}
Console.WriteLine("Sent all messages");
Thread.Sleep(TimeSpan.FromSeconds(3));
try
{
string connectionString = deviceId.GetServiceBusAmqpConnectionString(serviceBusNamespace, key);
var deadletterQueuePath = deviceId.GetDeadLetterQueuePath();
var receiveClient = QueueClient.CreateFromConnectionString(connectionString, deadletterQueuePath, ReceiveMode.ReceiveAndDelete);
while (true)
{
var brokeredMessage = receiveClient.Receive();
if (brokeredMessage != null)
{
Console.WriteLine(brokeredMessage.GetBody<string>());
// brokeredMessage.Complete();
}
}
}
finally
{
}
}
}
public class DeviceId
{
public Guid Id { get; set; }
public DeviceId() { }
public DeviceId(Guid id) { this.Id = id; }
public DeviceId(string id) { this.Id = Guid.Parse(id); }
public override string ToString()
{
return this.Id.ToString();
}
}
internal static class KeyGen
{
private static readonly RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
internal static byte[] CreateNewKey()
{
byte[] key = new byte[32];
rng.GetBytes(key);
return key;
}
}
public static class SampleConfiguration
{
public static string ServiceBusNamespace { get { return ConfigurationManager.AppSettings["ServiceBusNamespace"]; } }
public static string ServiceBusManagementServiceIdentityName { get { return ConfigurationManager.AppSettings["ServiceBusManagementServiceIdentityName"]; } }
public static string ServiceBusManagementServiceIdentityKey { get { return ConfigurationManager.AppSettings["ServiceBusManagementServiceIdentityKey"]; } }
private static string GetServiceBusManagementConnectionString()
{
return string.Format(
"Endpoint=https://{0}.servicebus.windows.net/;SharedSecretIssuer={1};SharedSecretValue={2}",
SampleConfiguration.ServiceBusNamespace,
SampleConfiguration.ServiceBusManagementServiceIdentityName,
SampleConfiguration.ServiceBusManagementServiceIdentityKey);
}
public static NamespaceManager CreateServiceBusManagement()
{
var connectionString = GetServiceBusManagementConnectionString();
return NamespaceManager.CreateFromConnectionString(connectionString);
}
public static string AccessControlServiceManagementNamespace { get { return SampleConfiguration.ServiceBusNamespace + "-sb"; } }
public static string AccessControlServiceManagementServiceIdentityName { get { return ConfigurationManager.AppSettings["AccessControlServiceManagementServiceIdentityName"]; } }
public static string AccessControlServiceManagementServiceIdentityKey { get { return ConfigurationManager.AppSettings["AccessControlServiceManagementServiceIdentityKey"]; } }
public static ManagementService CreateAccessControlServiceManagement()
{
return new ManagementService(
SampleConfiguration.AccessControlServiceManagementNamespace,
SampleConfiguration.AccessControlServiceManagementServiceIdentityName,
SampleConfiguration.AccessControlServiceManagementServiceIdentityKey);
}
public static QueueClient GetAdminQueueClientFromSampleCredentials(DeviceId deviceId,
ReceiveMode receiveMode = ReceiveMode.ReceiveAndDelete, QueueType queueType = QueueType.Regular)
{
return deviceId.CreateAdminQueueClient(
SampleConfiguration.ServiceBusNamespace,
SampleConfiguration.ServiceBusManagementServiceIdentityName,
Convert.FromBase64String(SampleConfiguration.ServiceBusManagementServiceIdentityKey),
receiveMode, queueType);
}
}
public static class ServiceBusExtensions
{
public static string GetQueuePath(this DeviceId deviceId)
{
return string.Format("devices/{0}", deviceId).ToLower();
}
public static string GetDeadLetterQueuePath(this DeviceId deviceId)
{
return QueueClient.FormatDeadLetterPath(deviceId.GetQueuePath());
}
public static string GetQueueUrl(this DeviceId deviceId, string serviceBusNamespace)
{
return string.Format("http://{0}.servicebus.windows.net/{1}", serviceBusNamespace, deviceId.GetQueuePath());
}
public static string GetRuleDescription(this string principal, string permission)
{
return string.Format("{0} can {1}", principal, permission);
}
public static string GetRuleGroupName(this DeviceId deviceId, string serviceBusNamespace)
{
return string.Format("Default Rule Group for {0}", deviceId.GetQueueUrl(serviceBusNamespace));
}
public static string GetServiceIdentityName(this DeviceId deviceId)
{
return string.Format("device-{0}", deviceId);
}
public static string GetServiceBusAmqpConnectionString(this DeviceId deviceId, string serviceBusNamespace, byte[] key)
{
return string.Format(
"Endpoint=sb://{0}.servicebus.windows.net/;SharedSecretIssuer={1};SharedSecretValue={2};TransportType=Amqp",
serviceBusNamespace, deviceId.GetServiceIdentityName(), Convert.ToBase64String(key));
}
internal static class Operation
{
internal const string Manage = "Manage";
internal const string Listen = "Listen";
internal const string Send = "Send";
}
public static byte[] Provision(this ManagementService acsMgmt, string serviceBusNamespace, DeviceId deviceId, byte[] keyBytes, NamespaceManager queueMgmt)
{
#region Identity and keys
var serviceIdentityName = deviceId.GetServiceIdentityName();
var serviceIdentity = new ServiceIdentity
{
Name = serviceIdentityName,
Description = serviceIdentityName
};
acsMgmt.AddToServiceIdentities(serviceIdentity);
string serviceIdentityKeyDisplayName = "key for " + serviceIdentityName;
DateTime keyValidityStartDate = DateTime.UtcNow;
DateTime keyValidityEndDate = DateTime.MaxValue;
byte[] keyBytesAsPasswordBytes = Encoding.UTF8.GetBytes(Convert.ToBase64String(keyBytes));
ServiceIdentityKey symmetricKey = new ServiceIdentityKey
{
DisplayName = serviceIdentityKeyDisplayName,
ServiceIdentityId = serviceIdentity.Id,
Usage = ServiceKeyUsage.Signing.ToString(),
StartDate = keyValidityStartDate,
EndDate = keyValidityEndDate,
Type = ServiceKeyType.Symmetric.ToString(),
Value = keyBytes
};
acsMgmt.AddRelatedObject(serviceIdentity, "ServiceIdentityKeys", symmetricKey);
ServiceIdentityKey passwordKey = new ServiceIdentityKey
{
DisplayName = serviceIdentityKeyDisplayName,
ServiceIdentityId = serviceIdentity.Id,
Usage = ServiceKeyUsage.Signing.ToString(),
StartDate = keyValidityStartDate,
EndDate = keyValidityEndDate,
Type = ServiceKeyType.Password.ToString(),
Value = keyBytesAsPasswordBytes
};
acsMgmt.AddRelatedObject(serviceIdentity, "ServiceIdentityKeys", passwordKey);
// acsMgmt.SaveChangesBatch();
#endregion
#region RuleGroup and Rules
string ruleGroupName = deviceId.GetRuleGroupName(serviceBusNamespace);
RuleGroup ruleGroup = acsMgmt.CreateRuleGroup(ruleGroupName);
Issuer localAuthority = acsMgmt.Issuers.Where(_ => _.Name == "LOCAL AUTHORITY").FirstOrDefault();
var perms = new List<Tuple<string, string>>
{
new Tuple<string, string>(deviceId.GetServiceIdentityName(), Operation.Listen),
new Tuple<string, string>("owner", Operation.Manage),
new Tuple<string, string>("owner", Operation.Send)
};
foreach (var perm in perms)
{
string principal = perm.Item1;
string permission = perm.Item2;
string ruleDescription = principal.GetRuleDescription(permission);
var rule = new Rule
{
Description = ruleDescription,
IssuerId = localAuthority.Id,
InputClaimType = ClaimTypes.NameIdentifier,
InputClaimValue = principal,
OutputClaimType = "net.windows.servicebus.action",
OutputClaimValue = permission
};
acsMgmt.AddRelatedObject(ruleGroup, "Rules", rule);
}
acsMgmt.SaveChangesBatch();
#endregion
#region RelyingParty
RelyingParty relyingParty = new RelyingParty
{
Name = string.Format("Queue for {0}", deviceId.GetServiceIdentityName()),
DisplayName = string.Format("Queue for {0}", deviceId.GetServiceIdentityName()),
TokenType = "SWT",
TokenLifetime = (int)TimeSpan.FromHours(1).TotalSeconds,
AsymmetricTokenEncryptionRequired = false
};
acsMgmt.AddToRelyingParties(relyingParty);
RelyingPartyAddress realmAddress = new RelyingPartyAddress()
{
Address = deviceId.GetQueueUrl(serviceBusNamespace),
EndpointType = RelyingPartyAddressType.Realm.ToString(),
};
acsMgmt.AddRelatedObject(relyingParty, "RelyingPartyAddresses", realmAddress);
RelyingPartyRuleGroup relyingPartyRuleGroup = new RelyingPartyRuleGroup
{
RuleGroupId = ruleGroup.Id,
RelyingPartyId = relyingParty.Id
};
acsMgmt.AddRelatedObject(relyingParty, "RelyingPartyRuleGroups", relyingPartyRuleGroup);
var identityProvider = acsMgmt.IdentityProviders.Where(_ => _.DisplayName == "Windows Live ID").FirstOrDefault();
RelyingPartyIdentityProvider relyingPartyIdentityProvider = new RelyingPartyIdentityProvider
{
RelyingPartyId = relyingParty.Id,
IdentityProviderId = identityProvider.Id
};
acsMgmt.AddRelatedObject(relyingParty, "RelyingPartyIdentityProviders", relyingPartyIdentityProvider);
acsMgmt.SaveChangesBatch();
#endregion
#region Queue creation
string queueName = deviceId.GetQueuePath();
queueMgmt.CreateQueue(new QueueDescription(queueName)
{
DefaultMessageTimeToLive = TimeSpan.FromMinutes(1),
EnableDeadLetteringOnMessageExpiration = true,
EnableBatchedOperations = true
});
#endregion
return keyBytes;
}
public static void DeleteAllAssets(this ManagementService acsMgmt, NamespaceManager queueMgmt)
{
queueMgmt.GetQueues().Where(_ => _.Path.Contains("devices/")).ToList().ForEach(_ => queueMgmt.DeleteQueue(_.Path));
acsMgmt
.ServiceIdentities
.Where(_ => _.Name.StartsWith("device-"))
.ToList()
.ForEach(_ => acsMgmt.DeleteServiceIdentityIfExists(_.Name));
acsMgmt.SaveChangesBatch();
var ruleGroupPrefix = string.Format("Default Rule Group for http://{0}.servicebus.windows.net/devices/", SampleConfiguration.ServiceBusNamespace);
acsMgmt
.RuleGroups
.Where(_ => _.Name.StartsWith(ruleGroupPrefix))
.ToList()
.ForEach(_ => acsMgmt.DeleteRuleGroupByNameIfExists(_.Name));
acsMgmt.SaveChangesBatch();
acsMgmt
.RelyingParties
.Where(_ => _.Name.StartsWith("Queue for device-"))
.ToList()
.ForEach(_ => acsMgmt.DeleteRelyingPartyByNameIfExists(_.Name));
acsMgmt.SaveChangesBatch();
}
public static QueueClient CreateDeviceQueueClient(this DeviceId deviceId, string serviceBusNamespace, byte[] keyBytes,
ReceiveMode receiveMode = ReceiveMode.ReceiveAndDelete,
QueueType queueType = QueueType.Regular)
{
string connectionString = deviceId.GetServiceBusAmqpConnectionString(serviceBusNamespace, keyBytes);
string queuePath = (queueType == QueueType.Regular) ? deviceId.GetQueuePath() : deviceId.GetDeadLetterQueuePath();
return QueueClient.CreateFromConnectionString(connectionString, queuePath, receiveMode);
}
public static QueueClient CreateAdminQueueClient(this DeviceId deviceId, string serviceBusNamespace, string ownerName, byte[] keyBytes,
ReceiveMode receiveMode = ReceiveMode.ReceiveAndDelete,
QueueType queueType = QueueType.Regular)
{
string connectionString = string.Format(
"Endpoint=sb://{0}.servicebus.windows.net/;SharedSecretIssuer={1};SharedSecretValue={2};TransportType=Amqp",
serviceBusNamespace, ownerName, Convert.ToBase64String(keyBytes));
string queuePath = (queueType == QueueType.Regular) ? deviceId.GetQueuePath() : deviceId.GetDeadLetterQueuePath();
return QueueClient.CreateFromConnectionString(connectionString, queuePath, receiveMode);
}
}
public enum QueueType
{
Regular = 0,
DeadLetter
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment