Skip navigation links

@ParametersAreNonnullByDefault

Package com.pkware.smartcrypt.keymanagement

Smartkey management and basic use; start here!

See: Description

Package com.pkware.smartcrypt.keymanagement Description

Smartkey management and basic use; start here!

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.

Logging In

MetaClient metaClient = new NativeMetaClient.Builder()
         .appName("LoggingIn")
         .appVersion("1.0.0")
         .server("https://oh1.smartcrypt.com/mds")
         .build();
 SmartcryptKeyManagement smartcryptKeyManagement = new SmartcryptKeyManagementImpl.Builder()
         .metaClient(metaClient)
         .persistenceCallback(new InMemoryPersistenceCallback())
         .build();

 AccountManagement accountManagement = smartcryptKeyManagement.getAccountManagement();

 // There are 3 types of login methods: managed, implicit, and unmanaged.
 //
 // 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

MetaClient metaClient = new NativeMetaClient.Builder()
         .appName("CreatingAndUpdatingSmartkeys")
         .appVersion("1.0.0")
         .deviceName(UUID.randomUUID().toString())
         .deviceUniqueId(UUID.randomUUID().toString())
         .platform(Platform.LINUX)
         .platformVersion("0")
         .build();
 SmartcryptKeyManagement smartcryptKeyManagement = new SmartcryptKeyManagementImpl.Builder()
         .metaClient(metaClient)
         .persistenceCallback(new InMemoryPersistenceCallback())
         .build();

 smartcryptKeyManagement.getAccountManagement().loginManagedAccount("<your username>", "<your password>");

 Smartkeys smartkeys = smartcryptKeyManagement.getSmartkeys();

 //
 // To create a new Smartkey, prepare a SmartkeySpec with the information for the new key.
 // Keys created using the SDK are always GenericSmartkeys.
 //
 SmartkeySpec createSmartkeySpec = new SmartkeySpec();
 createSmartkeySpec.name = "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
 Observable<Smartkey> keyObservable = smartkeys.create(createSmartkeySpec);
 Smartkey smartkey = keyObservable
         .doOnEach(genericSmartkeyNotification -> {
             Smartkey key = genericSmartkeyNotification.getValue();
             System.out.println("Got revision " + key.getRevision() + " for created Smartkey " + key.getName());
         })
         .blockingSingle();


 //
 // To update a key, once again prepare a SmartkeySpec. Most types of keys can be updated in some fashion
 //
 SmartkeySpec updateSmartkeySpec = new SmartkeySpec();
 updateSmartkeySpec.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
 Observable<Smartkey> updatedKeyObservable = smartkeys.update(smartkey, updateSmartkeySpec);
 updatedKeyObservable
         .subscribe(key -> System.out.println("Got updated Smartkey " + key.getName()));
 

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.
MetaClient metaClient = new NativeMetaClient.Builder()
         .appName("BackgroundProcessing")
         .appVersion("1.0.0")
         .deviceName(UUID.randomUUID().toString())
         .deviceUniqueId(UUID.randomUUID().toString())
         .platform(Platform.LINUX)
         .platformVersion("0")
         .build();
 SmartcryptKeyManagement smartcryptKeyManagement = new SmartcryptKeyManagementImpl.Builder()
         .metaClient(metaClient)
         .persistenceCallback(new InMemoryPersistenceCallback())
         .build();

 // Background errors come out as Notifications and can easily be logged or monitored
 smartcryptKeyManagement.getDataStorage().getNotifications()
         .map(unfiltered -> {
             Set<Notification> filtered = new LinkedHashSet<>(unfiltered.size());
             for (Notification notification : unfiltered) {
                 if (notification instanceof SyncProblemNotification) {
                     filtered.add(notification);
                 }
             }
             return filtered;
         })
         .flatMap(Observable::fromIterable)
         .subscribe(problem -> System.out.println(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.
MetaClient metaClient = new NativeMetaClient.Builder()
         .appName("PersistingSmartkeys")
         .appVersion("1.0.0")
         .deviceName(UUID.randomUUID().toString())
         .deviceUniqueId(UUID.randomUUID().toString())
         .platform(Platform.LINUX)
         .platformVersion("0")
         .build();
 SmartcryptKeyManagementImpl.Builder builder = new SmartcryptKeyManagementImpl.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.
 SmartcryptKeyManagement smartcryptKeyManagement = builder
         .persistenceCallback(new InMemoryPersistenceCallback())
         .build();

 smartcryptKeyManagement.getAccountManagement().loginManagedAccount("<your username>", "<your password>");

 Smartkeys smartkeys = smartcryptKeyManagement.getSmartkeys();

 // For this example, any Smartkey will do. We'll just take the first one we get
 Smartkey smartkey = smartkeys.listAll()
         .blockingFirst()
         .iterator().next();

 // Smartkey serialization is straightforward. When serializing, the information of the provided Smartkey is
 // used.
 String serializedRepresentation = smartkeys.serialize(smartkey);
 System.out.println("Serialized Smartkey " + smartkey.getName() + ": " + serializedRepresentation);

 // Retrieving a Smartkey from its serialized form is also straightforward.
 Smartkey deserializedSmartkey = smartkeys.deserialize(serializedRepresentation).blockingFirst();
 System.out.println("Smartkey " + deserializedSmartkey.getName());
 

Persisting Metadata

MetaClient metaClient = new NativeMetaClient.Builder()
         .appName("PersistingMetadata")
         .appVersion("1.0.0")
         .deviceName(UUID.randomUUID().toString())
         .deviceUniqueId(UUID.randomUUID().toString())
         .platform(Platform.LINUX)
         .platformVersion("0")
         .build();
 SmartcryptKeyManagementImpl.Builder builder = new SmartcryptKeyManagementImpl.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.
 SmartcryptKeyManagement smartcryptKeyManagement = builder
         .persistenceCallback(new FilePersistenceCallback())
         .build();

 // Data restore, both threading and timing, is controlled by the developer
 DataStorage dataStorage = smartcryptKeyManagement.getDataStorage();
 dataStorage.restore();

 // Be sure to define this class somewhere
 class FilePersistenceCallback implements PersistenceCallback {
     private final static String folder = "serializedMetadata";

     public boolean onSaveData(Map<String, String> toSave, Set<String> toDelete) {
         try {
             Files.createDirectory(Paths.get(folder));
             for (Map.Entry<String, String> datum : toSave.entrySet()) {
                 try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(folder, datum.getKey()))) {
                     writer.write(datum.getValue());
                 }
             }

             for (String name : toDelete) {
                 Files.deleteIfExists(Paths.get(folder, name));
             }
             return true;
         } catch (IOException e) {
             return false;
         }
     }

     public Map<String, String> onLoadData() {
         try {
             return Files.list(Paths.get(folder))
                     .collect(Collectors.toMap(
                             path -> path.getFileName().toString(),                      // Key
                             fileName -> {                                               // Value
                                 try {
                                     return new String(Files.readAllBytes(fileName));
                                 } catch (IOException e) {
                                     e.printStackTrace();
                                     return "";
                                 }
                             }
                     ));
         } catch (IOException e) {
             return Collections.emptyMap();
         }
     }
 }
 

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](http://reactivex.io/tutorials.html) for tutorials, guides, and more.
// Observables emit a stream of events. This is a push model rather than a pull model.
 Observable<Integer> observable = Observable.range(0, 5);

 // Observables can be transformed
 Observable<String> intsAsStrings = observable.map(Object::toString);

 // Use common stream/observable operators
 Observable<String> filteredInts = intsAsStrings.filter(number -> !"3".equals(number));

 // 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.
 Disposable subscription = filteredInts.subscribe(System.out::println);

 // Subscriptions must be disposed when we're done with them, otherwise the stream will continue processing
 // when we no longer need it
 subscription.dispose();

 //
 // Observables can also be made blocking, effectively making them a single, synchronous call
 //
 Integer lastNumberBlocking = Observable.range(0, 4).blockingLast();
 System.out.println("With a blocking call: " + lastNumberBlocking);
 
Skip navigation links
Copyright 2012-2019, PKWARE, Inc.