Skip to content

Using Cryptographic Keys

Cryptography is useful in algorithm development for a range of purposes, from straightforward encryption of value to shuffling collections and permuting data in a manner that is consistent across masking jobs. The extensible algorithm framework automatically provides each algorithm with a cryptographic key. This key is wrapped by a service provider object that implements the CryptoService interface, providing a number of useful operations based on the algorithm's key. It is also possible to retrieve the raw key assigned to the algorithm as an array of bytes.

Similar to working with files, there is a KeyReference type that represents a reference to the key. This is present to support access to keys stored in alternative locations (ex. a key vault) in the future. Currently, the only supported value for these references is "", which indicates that the per-algorithm key stored on the Delphix Masking Engine should be used.

Note

When working with the Masking SDK maskApp and maskScript utilities, each algorithm's key is a stable hash of its algorithm name, but maybe temporarily set to a random value using the -K flag.

Using the CryptoService Provider

The first step any algorithm that wishes to use its algorithm key must take is to retrieve a handle to a cryptographic service provider during initialization. This is done by calling the ComponentService object's getCryptoService method. The returned provider wraps the key. The operations supported by the CryptoService interface are as follows:

  • getRawKey - retrieve the raw key associated with this provider as a byte array.
  • wrap - wraps an array of bytes to create a CryptoService object. This is useful for accessing CryptoService methods when the algorithm's key is stored in an alternative location or hard-coded in the algorithm source.
  • deriveNewKey - derive a new key by permuting this provider's key using SHA-256. A new CryptoService object is returned wrapping the new key. The zero-argument version of this method returns the same key each time it is called on the same provider - in order to create multiple, different keys, a different salt must be provided to each method call. It is advisable that whenever an algorithm wishes to use cryptography for multiple purposes, new and distinct keys be derived for each purpose.
  • computeHashedLookupIndex - compute an integer value from 0 to (modulus - 1) by hashing the input value + key. This method is designed to allow randomized, but consistent, lookups into a replacement table based on the input value.
  • shuffleList and shuffleListNoCollisions - these methods shuffle their argument List in-place using the key to seed the randomization. The "noCollisions" variant ensures that no object in the list remains in its original position.

Example Algorithm

public class StringHashedLookup implements MaskingAlgorithm<String> {
   private List<String> replacements;
   private CryptoService crypto;

   public KeyReference key = new KeyReference();

   @JsonProperty("replacementFile")
   public FileReference replacementFile;

   @Override
   public void validate() throws ComponentConfigurationException {
       GenericReference.checkRequiredReference(replacementFile, "replacementFile");
   }

   @Override
   public void setup(@Nonnull ComponentService serviceProvider) {
       replacements = new ArrayList<>();

       String line;
       try (InputStream is = serviceProvider.openInputFile(replacementFile);
            BufferedReader reader =
                new BufferedReader(new InputStreamReader(is, "UTF_8"))) {
            while ((line = reader.readLine()) != null) {
                replacements.add(line);
           }
       } catch (IOException e) {
               throw new RuntimeException(e);
       }
       crypto = serviceProvider.getCryptoService(key);
   }

   @Override
   public String mask(@Nullable String input) {
       if (input == null || input.length() == 0) {
           return input;
       }
       return replacements.get((int) crypto.computeHashedLookupIndex(input, replacements.size()));
   }
}

Some methods have been omitted for brevity.

This example algorithm functions very similarly to the existing Secure Lookup algorithm, except it employs a different hash method from the new CryptoService provider.

  • The algorithm is configured with an input file by supplying a public, annotated FileReference field replacementFile.
  • In the setup method, the replacement file is ingested and saved as a list of values.
  • Additionally in setup, the cryptographic service provider is initialized using the default key reference, accessing the algorithm's key.
  • The mask method uses the computeHashedLookupIndex method to compute the index of the replacement to use from the replacements list.