Show / Hide Table of Contents

Smartcrypt Unstructured Data

The Smartcrypt Unstructured Data component is particularly useful when encrypting data stored in files and other unstructured containers. ZIP archives are used for compression and encryption. Accordingly, large data sets benefit from this component.

Prerequisites

You must have the PKArchive.NET DLL available already. If you installed the PKArchive Toolkit, the DLL will already be available to your project. Alternatively, copy it to your project repository and reference it that way.

Setup

To get started, you'll need a new instance of IUnstructuredData. See the MetaClient docs for details on how to obtain that instance.


public static IUnstructuredData SetUp(IMetaClient metaClient) =>
  new UnstructuredData.Builder()
    .MetaClient(metaClient)
    .License("<Your license here>")
    .Build();

Instances of IUnstructuredData are thread safe and should be reused throughout your application.

Create a ZIP

The basic steps of creating a ZIP are the same regardless of which encryption parameters are used.


public static void CreateZip(
  IUnstructuredData unstructured,
  IDictionary<string, Stream> filesToArchive
)
{
  using (var factory = unstructured.CreateArchiveFactory())

    // The factory provides a stream variant as well
  using (var archive = factory.CreateArchiveFile("path/to/output.zip"))
  {
    foreach (var entry in filesToArchive)
    {
      var entryName = entry.Key;
      var entryContent = entry.Value;
      using (var itemOptions = factory.CreateItemOptions())
      using (var status = Factory.CreateStatInfo())
      {
        status.Name = entryName;
        archive.AddItem(factory.Context, status, archive, entryContent, itemOptions).Dispose();
      }
    }

    // When finished with the archive, be sure to save
    archive.Save(factory.Context)?.Dispose();
  }
}

Configuring encryption

By default, the SDK chooses an encryption method that provides strong security and is well suited for most applications. It may be desirable to change the encryption method (also called the encryption algorithm) used to create new ZIP entries. The SDK provides a simple of way setting the most common and useful encryption methods. Configure the IPKItemOptions, and then use them as you typically would, following the instructions for creating a ZIP.


public static IPKItemOptions ConfigureEncryptionMethod(IArchiveFactory factory)
{
  var itemOptions = factory.CreateItemOptions();

  // Configure encryption to use AE2 (for compatibility with other ZIP programs)
  itemOptions.SetEncryptionMethod(EncryptionMethod.Ae2);

  // Configure encryption to use AES-256
  itemOptions.SetEncryptionMethod(EncryptionMethod.Aes256);

  // Now use the configured options to add items to the ZIP as you normally would
  return itemOptions;
}

Advanced usage

For very advanced applications, it may be necessary to be more detailed when configuring the encryption method. This snippet demonstrates how to configure the encryption more thoroughly. Review the PK_ENCRYPT_METHOD and PK_CRYPT_FLAGS enums for an exhaustive list of options.


public static IPKItemOptions EncryptionMethodAdvanced(IArchiveFactory factory)
{
  var itemOptions = factory.CreateItemOptions();
  var flags = CreateFlagsBitmask();

  // Configure encryption to use AE2 (for compatibility with other ZIP programs)
  itemOptions.SetEncryptMethod(PK_ENCRYPT_METHOD.PK_ENCRYPT_METHOD_AE2, 256, flags);

  // Configure encryption to use AES-256
  itemOptions.SetEncryptMethod(PK_ENCRYPT_METHOD.PK_ENCRYPT_METHOD_AES_256, 256, flags);

  // Configure encryption to use AES-192
  itemOptions.SetEncryptMethod(PK_ENCRYPT_METHOD.PK_ENCRYPT_METHOD_AES_192, 192, flags);

  // Configure encryption to use AES-128
  itemOptions.SetEncryptMethod(PK_ENCRYPT_METHOD.PK_ENCRYPT_METHOD_AES_128, 128, flags);

  // Configure encryption to use 3DES
  itemOptions.SetEncryptMethod(PK_ENCRYPT_METHOD.PK_ENCRYPT_METHOD_3DES, 168, flags);

  // Disable encryption
  itemOptions.SetEncryptMethod(PK_ENCRYPT_METHOD.PK_ENCRYPT_METHOD_NONE, 0, flags);

  // Now use the configured options to add items to the ZIP as you normally would
  return itemOptions;
}

public static PK_CRYPT_FLAGS CreateFlagsBitmask()
{
  // Always include the default flag
  var flags = PK_CRYPT_FLAGS.PK_CRYPT_DEFAULT;

  // If using a Smartkey and/or password, add the password key flag
  flags |= PK_CRYPT_FLAGS.PK_CRYPT_PASSWORD_KEY;

  // If using a Contingency Key, add the certificate key flag
  flags |= PK_CRYPT_FLAGS.PK_CRYPT_CERTIFICATE_KEY;

  return flags;
}

Configuring compression

By default, the SDK chooses compression setting that balances speed with compressed size. However, it is possible to change the compression method. One might use a different compression algorithm , such as deflate, store, and LZMA, or specify a different compression level (higher for smaller ZIPs, lower for faster processing) as the application requires.


public static IPKItemOptions CompressionMethod(IArchiveFactory factory)
{
  var itemOptions = factory.CreateItemOptions();

  // Configure compression to use the deflate algorithm at level 6
  itemOptions.SetCompressMethodID(PK_COMPRESS_METHOD.PK_COMPRESS_METHOD_DEFLATE, 6, 0);

  // Configure compression to use the store algorithm (no compression)
  itemOptions.SetCompressMethodID(PK_COMPRESS_METHOD.PK_COMPRESS_METHOD_STORE, 0, 0);

  // Now use the configured options to add items to the ZIP as you normally would
  return itemOptions;
}

Encryption

Encryption using both passwords and Smartkeys is straightforward. See the Key Management docs for details on how to obtain an ISmartkey.


public static void ContingencyGroupWithSmartkey(
  IUnstructuredData unstructured,
  IMetaClient metaClient,
  ISmartkey smartkey
)
{
  using (var factory = unstructured.CreateArchiveFactory())
  {
    var listener = new MetaClientPasswordListener(metaClient)
    {
      EncryptionSpec = new EncryptionSpec
      {
        Smartkey = smartkey
        // Optionally add a password here as well
      }
    };
    factory.Context.PasswordListenerEx = listener;

    // Proceed to create the ZIP as you normally would, using the factory created here.
  }
}

Note

In order for Smartcrypt Archive Contingency Groups to apply, you must run your application as Smartcrypt. See the MetaClient Policy docs for more details.

Contingency group without Smartkey

The recommended way to gain access to the Contingency Group functionality of Smartcrypt is to use Smartkeys when encrypting. There may be some scenarios in which using a Smartkey is not an option. For these scenarios, we also provide the option of skipping the Smartkey, but still gaining the benefits of Contingency Groups.


public static void ContingencyGroupWithoutSmartkey(
  IUnstructuredData unstructured,
  IMetaClient metaClient,
  string password
)
{
  using (var factory = unstructured.CreateArchiveFactory())
  {
    var listener = new MetaClientPasswordListener(metaClient)
    {
      EncryptionSpec = new EncryptionSpec {Password = password}
    };
    factory.Context.PasswordListenerEx = listener;

    // Proceed to create the ZIP as you normally would, using the factory created here.
  }
}

Contingency key

Applying a contingency key is a bit more involved. When possible, it is recommended to use Contingency Groups instead.


public sealed class ContingencyKeyPasswordListener : UnifiedPasswordListener
{
  [CanBeNull]
  public EncryptionSpec EncryptionSpec
  {
    set => metaClientPasswordListener.EncryptionSpec = value;
  }

  private readonly MetaClientPasswordListener metaClientPasswordListener;

  public ContingencyKeyPasswordListener(IMetaClient metaClient) : base(metaClient.Logger)
  {
    metaClientPasswordListener = new MetaClientPasswordListener(metaClient);
  }

  /// <summary>
  /// Delegates to the <see cref="metaClientPasswordListener"/>.
  /// </summary>
  public override DecryptionParameters GetDecryptionParameters(IPKArchiveRootFolder archive, IPKStat status,
    int retryCount, KmsInfo kms) =>
    metaClientPasswordListener.GetDecryptionParameters(archive, status, retryCount, kms);

  public override EncryptionParameters GetEncryptionParameters(IPKArchiveRootFolder archive, IPKStat status,
    EncryptionChoices choices)
  {
    // Delegate to MetaClient to apply Smartkey and password set using EncryptionSpec property
    var parameters = metaClientPasswordListener.GetEncryptionParameters(archive, status, choices);

    // Prepare the contingency key
    var session = Factory.CreateSession("Put license key here");
    var certificateBytes = LoadCertificate();
    var certificateStore = Factory.CreateCertificateStore(session, certificateBytes);

    // Apply the certificates to be used, including the Contingency Key
    parameters.Certificates = certificateStore;

    return parameters;
  }

  private byte[] LoadCertificate()
  {
    // Load the certificate as a byte array. This could be from disk, network, or hard coded.
    //   The certificate can be formatted as a .p7b, .cer, or .pgp.
    return new byte[0];
  }
}

public static void ContingencyKey(
  IUnstructuredData unstructured,
  IMetaClient metaClient,
  ISmartkey smartkey
)
{
  using (var factory = unstructured.CreateArchiveFactory())
  {
    var listener = new ContingencyKeyPasswordListener(metaClient)
    {
      EncryptionSpec = new EncryptionSpec
      {
        Smartkey = smartkey,
        // Optionally add a password here as well
      }
    };
    factory.Context.PasswordListenerEx = listener;

    // Tell the Archive to expect a contingency key. The key will be retrieved from the password listener via callback.
    using (var itemOptions = factory.CreateItemOptions())
    {
      var encryptionMethod =
        itemOptions.GetEncryptMethod(out var encryptionKeyLength, out var encryptionFlags);
      encryptionFlags |= PK_CRYPT_FLAGS.PK_CRYPT_COMBO_KEY;
      itemOptions.SetEncryptMethod(encryptionMethod, encryptionKeyLength, encryptionFlags);

      // Proceed to create the ZIP as you normally would, using the factory and itemOptions created here.
    }
  }
}

Decryption

Once you have an IUnstructuredData instance, decrypting ZIP archives encrypted using a Smartkey is easy! If you do not have access to the Smartkey, but are part of the Contingency Group, this snippet will also grant you access to the files.


public static void DecryptAZipFile(IUnstructuredData unstructured)
{
  using (var factory = unstructured.CreateArchiveFactory())
  using (var archive = factory.OpenArchiveFile("example.zip"))
  using (var item = archive.Item(0))
  {
    archive.ExtractFile(factory.Context, item, "path/to/extract/item/to");
  }
}

Back to top Copyright © 2018 PKWARE, Inc.