Smartcrypt Key Management
The Key Management component is the starting point for all interactions with the Smartcrypt SDK. The Key Management component makes it easy to login as a Smartcrypt user and to create, retrieve, share, and use Smartkeys. Policies configured on the Smartcrypt manager are automatically enforced, and data stored locally are cryptographically tamper resistant.
Setup
To get started, you'll need a new instance of IKeyManagement
. See the MetaClient
docs for details on how to obtain that instance.
private sealed class InMemoryPersistenceCallback : IPersistenceCallback
{
private readonly IDictionary<string, string> stored
= new Dictionary<string, string>();
public bool OnSaveData(IDictionary<string, string> toSave, ISet<string> toDelete)
{
foreach (var (key, value) in toSave) stored[key] = value;
foreach (var key in toDelete) stored.Remove(key);
return true;
}
public IDictionary<string, string> OnLoadData() =>
new Dictionary<string, string>(stored);
}
public static ISmartcryptKeyManagement SetUp(
[NotNull] IMetaClient metaClient
) => new SmartcryptKeyManagement.Builder()
.MetaClient(metaClient)
// In real code, you should use something more durable than an in-memory store
.PersistenceCallback(new InMemoryPersistenceCallback())
.Build();
Logging In
var metaClient = new MetaClient.Builder
{
AppName = "Logging In",
AppVersion = "1.0.0",
Server = "https://oh1.smartcrypt.com/mds"
}.Build();
var smartcryptKeyManagement = new SmartcryptKeyManagement.Builder()
.MetaClient(metaClient)
.PersistenceCallback(new InMemoryPersistenceCallback())
.Build();
var accountManagement = smartcryptKeyManagement.AccountManagement();
/*
* There are 3 types of login methods: managed, unmanaged, and implicit.
*
* Managed login is used for accounts that are managed by Active Directory. The credentials used are the
* same as those of the domain account.
*
* This login variant requires the developer to provide credentials, but does not require any system
* configuration.
*/
accountManagement.LoginManagedAccount("example@smartcrypt.com", "password");
/*
* Implicit login uses the domain account of the computer user who owns the process running MetaClient.
* Typically, this is the user logged-in to Windows, Mac, etc, but may be a different user. On Windows,
* Integrated Windows Authentication is used for a seamless experience. On *nix systems, Kerberos is used,
* and the kinit program, or a similar utility, must be used to configure the account prior to login.
*
* This login variant does not require the developer to provide any credentials, but does require system
* configuration by the system administrator.
*
* Note that to use implicit login, the developer must supply a server URL to the MetaClient builder.
*/
accountManagement.LoginImplicitAccount();
/*
* Unmanaged login is used for accounts that are created in Smartcrypt Enterprise Manager. These do not live
* in Active Directory. The credentials are owned by Smartcrypt Enterprise Manager.
*
* This login variant requires the developer to provide credentials, but does not require any system
* configuration.
*/
accountManagement.LoginUnManagedAccount("unmanaged@smartcrypt.com", "password");
Creating and Updating Smartkeys
var metaClient = new MetaClient.Builder()
{
AppName = "CreatingAndUpdatingSmartkeys",
AppVersion = "1.0.0"
}.Build();
var smartcryptKeyManagement = new SmartcryptKeyManagement.Builder()
.MetaClient(metaClient)
.PersistenceCallback(new InMemoryPersistenceCallback())
.Build();
smartcryptKeyManagement.AccountManagement().LoginManagedAccount("<your username>", "<your password>");
var smartkeys = smartcryptKeyManagement.Smartkeys();
// To create a new Smartkey, prepare a SmartkeySpec with the information for the new key.
var createSmartkeySpec = new SmartkeySpec
{
Name = "Sample",
Kind = "acme_sample",
};
createSmartkeySpec.AddAccess.Add("john.doe@example.com");
// Key creation is synchronous, but you can be notified of updates to the key by listening to the Observable
var keyObservable = smartkeys.Create(createSmartkeySpec);
var smartkey = keyObservable
.Do(key => Console.WriteLine($"Got revision {key.Revision} for created Smartkey {key.Name}"))
.Take(1)
.Wait();
// To update a key, once again prepare a SmartkeySpec. Most types of keys can be updated in some fashion
var updateSmartkeySpec = new SmartkeySpec {Name = "Updated Sample"};
updateSmartkeySpec.RemoveAccess.Add("john.doe@example.com");
// Key updates are synchronous, but once again, you can be notified of updates to the key by listening to
// the observable
var updatedKeyObservable = smartkeys.Update(smartkey, updateSmartkeySpec);
updatedKeyObservable
.Subscribe(key => Console.WriteLine($"Got updated Smartkey {key.Name}"));
Background Processing
Smartcrypt Key Management performs operations in the background on your behalf. In some cases, you may need more control over how these operations occur. This sample demonstrates monitoring and controlling these operations.
var metaClient = new MetaClient.Builder
{
AppName = "BackgroundProcessing",
AppVersion = "1.0.0"
}.Build();
var smartcryptKeyManagement = new SmartcryptKeyManagement.Builder()
.MetaClient(metaClient)
.PersistenceCallback(new InMemoryPersistenceCallback())
.Build();
// Background errors come out as Notifications and can easily be logged or monitored
smartcryptKeyManagement.DataStorage().Notifications()
.Select(notifications => notifications.Where(notification => notification is SyncProblemNotification))
.SelectMany(problems => problems.ToObservable())
.Subscribe(problem => Console.WriteLine(problem.Message));
// Background processing can be paused. This may be necessary if your app goes into the a state where the
// process is alive, but the user does not expect it to be running, such as a backgrounded mobile app
smartcryptKeyManagement.PauseBackgroundProcessing();
// Background processing can also be resumed
smartcryptKeyManagement.ResumeBackgroundProcessing();
Persisting Smartkeys
It is common that encrypted data need to have information about which Smartkey to use stored alongside it. The Smartcrypt Key Management SDK provides a mechanism for serializing and deserializing Smartkeys in a way that doesn't leak information and makes key retrieval on a separate system bulletproof.
var metaClient = new MetaClient.Builder
{
AppName = "PersistingSmartkeys",
AppVersion = "1.0.0"
}.Build();
var smartcryptKeyManagement = new SmartcryptKeyManagement.Builder()
.MetaClient(metaClient)
.PersistenceCallback(new InMemoryPersistenceCallback())
.Build();
smartcryptKeyManagement.AccountManagement().LoginManagedAccount("<your username>", "<your password>");
var smartkeys = smartcryptKeyManagement.Smartkeys();
// For this example, any Smartkey will do. We'll just take the first one we get
var smartkey = smartkeys.ListAll()
.ToTask().Result
.First();
// Smartkey serialization is straightforward. When serializing, the information of the provided Smartkey is
// used.
var serializedRepresentation = smartkeys.Serialize(smartkey);
Console.WriteLine($"Serialized Smartkey {smartkey.Name}: {serializedRepresentation}");
// Retrieving a Smartkey from its serialized form is also straightforward.
var deserializedSmartkey = smartkeys.Deserialize(serializedRepresentation).Take(1).Wait();
Console.WriteLine($"Smartkey {deserializedSmartkey?.Name}");
Persisting Metadata
var metaClient = new MetaClient.Builder
{
AppName = "PersistingMetadata",
AppVersion = "1.0.0"
}.Build();
var builder = new SmartcryptKeyManagement.Builder()
.MetaClient(metaClient);
// Persistence is done as part of sync. Sync happens on a worker thread, so the persistence happens on the
// same worker thread. Persistence is synchronous, and the next sync will wait until persistence has
// completed, but SDK consumers don't need to know that.
var smartcryptKeyManagement = builder
.PersistenceCallback(new FilePersistenceCallback())
.Build();
// Data restore, both threading and timing, is controlled by the developer
var dataStorage = smartcryptKeyManagement.DataStorage();
await Task.Run(() =>
{
dataStorage.Restore();
});
// Be sure to define this class somewhere
private class FilePersistenceCallback : IPersistenceCallback
{
private const string Folder = "serializedMetadata/";
public bool OnSaveData(IDictionary<string, string> toSave, ISet<string> toDelete)
{
try
{
foreach (var (name, value) in toSave)
{
File.WriteAllText(Folder + name, value);
}
foreach (var name in toDelete)
{
File.Delete(Folder + name);
}
return true;
}
catch
{
return false;
}
}
public IDictionary<string, string> OnLoadData()
{
return Directory.EnumerateFiles(Folder)
.ToDictionary(
it => it.Split('/')[1],
File.ReadAllText
);
}
}
Working with Observables
The Smartcrypt Key Management SDK relies heavily on Observables. These may be unfamiliar, but are easy to get a hang of or convert to a more familiar paradigm. If unfamiliar with reactive programming, we recommend taking a look at the ReactiveX website for tutorials, guides, and more.
// Observables emit a stream of events. This is a push model rather than a pull model.
var observable = Observable.Range(0, 5);
// Observables can be transformed
var intsAsStrings = observable.Select(number => number.ToString());
// Most LINQ operations are available
var filteredInts = intsAsStrings.Where(number => number != "3");
// Observables only process when subscribed to. All of the previous "work" is only setup - none of it has
// run yet. When we subscribe now, the work will happen.
var subscription = filteredInts.Subscribe(Console.WriteLine);
// Subscriptions must be disposed when we're done with them, otherwise the stream will continue processing
// when we no longer need it
subscription.Dispose();
// Observable can easily be integrated into an Async/Await workflow
var lastNumber = await Observable.Range(0, 5).ToTask();
Console.WriteLine($"With a task: {lastNumber}");
// Observables can also be made blocking, effectively making them a single, synchronous call
var lastNumberBlocking = Observable.Range(0, 4).LastAsync().Wait();
Console.WriteLine($"With a blocking call: {lastNumberBlocking}");