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");
}
}