Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save MariusVolkhart/508b8df80d0a6b0e947e32f7466c0bb3 to your computer and use it in GitHub Desktop.
Save MariusVolkhart/508b8df80d0a6b0e947e32f7466c0bb3 to your computer and use it in GitHub Desktop.
Encrypting a ZIP with a Contingency Groups
using System;
using System.Text;
using JetBrains.Annotations;
using PKWARE.ArchiveAPI;
using PKWARE.Smartcrypt.MetaClient;
using PKWARE.Smartcrypt.Protocol;
using PKWARE.Smartcrypt.Unstructured.ArchiveSupport;
namespace Your.Namespace
{
/// <summary>A password listener that applies a Smartcrypt Contingency Group during encryption when
/// only a password is used (no Smartkey). It is recommended to use a Smartkey instead. For
/// Contingency Groups to work, the application must indicated that it is called Smartcrypt.</summary>
public sealed class ContingencyGroupApplyingPasswordListener : UnifiedPasswordListener
{
[NotNull]
private readonly IMetaClient metaClient;
[NotNull]
private readonly MetaClientPasswordListener underlying;
[CanBeNull]
public string Password { private get; set; }
public ContingencyGroupApplyingPasswordListener([NotNull] IMetaClient metaClient) : base(metaClient.Logger)
{
this.metaClient = metaClient ?? throw new ArgumentNullException(nameof(metaClient));
underlying = new MetaClientPasswordListener(metaClient);
}
public override DecryptionParameters GetDecryptionParameters(IPKArchiveRootFolder archive, IPKStat status,
int retryCount, KmsInfo kms)
{
return underlying.GetDecryptionParameters(archive, status, retryCount, kms);
}
public override EncryptionParameters GetEncryptionParameters(IPKArchiveRootFolder archive, IPKStat status,
EncryptionChoices choices)
{
var parameters = new EncryptionParameters();
// Grab a local reference because the instance reference may change while we're in this method
var password = Password;
if (choices.RequestedUsePassword && !string.IsNullOrEmpty(password))
{
var info = metaClient.ApplyContingencyGroup(password);
parameters.Password = Encoding.GetEncoding("ISO-8859-1").GetBytes(info.Password);
parameters.KmsInfo = new KmsInfo(info.Json);
}
return parameters;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using PKWARE.ArchiveAPI;
using PKWARE.Smartcrypt.MetaClient;
using PKWARE.Smartcrypt.Unstructured;
public sealed class Sample {
public void Run(MetaClient metaClient, IUnstructuredData unstructured, List<string> files, Smartkey smartkey, string archiveFile) {
using (IArchiveFactory factory = unstructured.CreateArchiveFactory())
{
// An archive context is necessary for some features such as
// progress notification or password encryption that require callback
// messages.
var context = factory.Context;
// Create a password listener that will be used to pass the password
// and/or Smartkey to the archive.
// If not using a Smartkey (only a password, see ContingencyGroupApplyingPasswordListener.cs)
var listener = new MetaClientPasswordListener(metaClient);
// Tell the listener which Smartkey or password should be used
listener.EncryptionSpec = new EncryptionSpec {Smartkey = smartkey};
// Tell the library about the password listener
context.PasswordListenerEx = listener;
// There are two classifications of options available in the API. Those
// that apply to the entire archive, and those which can be changed on
// an item-by-item basis. These are represented by the IPKArchiveOptions
// and IPKItemOptions interfaces, respectively.
using (IPKItemOptions itemOpts = factory.CreateItemOptions())
{
// AE-2 encryption, for compatibility with 3rd-party ZIP applications - 256 bits
PK_ENCRYPT_METHOD cryptMethod = PK_ENCRYPT_METHOD.PK_ENCRYPT_METHOD_AE2;
UInt32 cryptLen = 256;
// The third parameter of SetEncryptMethod() is a flag saying
// (in this case) that we'll encrypt using a password and a certificate.
PK_CRYPT_FLAGS cryptFlags = PK_CRYPT_FLAGS.PK_CRYPT_PASSWORD_KEY;
// Ensure that we turn on the default compatibility flags
cryptFlags |= PK_CRYPT_FLAGS.PK_CRYPT_DEFAULT;
itemOpts.SetEncryptMethod(cryptMethod, cryptLen, cryptFlags);
// The PKZIP Toolkit supports several ways to create archives.
// Considerations are whether you want to create a brand new archive or
// modify an existing one, and whether you want to write an archive to
// a file or a data stream.
//
// IArchiveFactory.CreateArchiveFile() used in this sample creates a new
// archive using the IPKCreateArchiveFile interface. This interface
// is easy to use but is limited to creating new archives. To modify
// an existing archive use the IPKDelayArchive interface instead.
using (IPKCreateArchiveFile archive = factory.CreateArchiveFile(archiveFile))
{
foreach (var fileName in files)
{
// Add files or folders to the archive
// Items can be added to an archive in several ways. If the items are
// files, the easiest way is to call the AddFile() method, which
// accepts either a file or a folder. If the method is passed a folder
// name, then the entire contents of the folder are added to the
// archive. Files are added recursively, by default.
//
// itemOpts contains the item options for the archive item.
// Add the a file or folder
using (IPKArchiveItem item = archive.AddFile(context, fileName, null, null, itemOpts,
null, PK_ADD_FILE.PK_ADD_FILE_DEFAULT | PK_ADD_FILE.PK_ADD_FILE_OPTIONS_PATH))
{ }
}
itemCount = archive.ItemCount(PK_ITEM_FLAG.PK_ITEM_TYPE_NON_FOLDER, true);
folderCount = archive.ItemCount(PK_ITEM_FLAG.PK_ITEM_TYPE_FOLDER, true);
// When all of the items have been added to the archive, the
// application must tell the API that it should close the archive file.
// This is done with the Save() method.
//
// The Save() method returns a IPKExtractArchive object that can be used
// to extract the contents of the archive. Often, as in this sample
// application, this object is simply discarded, but it gives the
// application a quick way to extract the contents of an archive that
// has just been created without having to re-read the archive file.
if (itemCount > 0 || folderCount > 0)
{
using (IPKExtractArchive newArchive = archive.Save(context))
{
Console.WriteLine("Archive {0} successfully created with {1} items, {2} folders",
archiveFile, itemCount, folderCount);
}
}
else
Console.WriteLine("Archive {0} not created: no files or folders were added", archiveFile);
}
}
}
}
}
import com.pkware.archive.ArchiveEntry;
import com.pkware.archive.zip.ZipArchiveOutput;
import java.io.File;
import java.security.cert.X509Certificate;
public final class Sample {
public void run() {
// This is the archive being created. We want this archive encrypted using a Contingency Key.
ZipArchiveOutput zipArchive = getZipArchive();
configureForEncryption(zipArchive);
// Finally, add files to the archive
for (File file : getFilesToAdd()) {
archive.addFile(file);
}
// Close the archive
archive.finish();
}
private ZipArchiveOutput getZipArchive() {
// TODO return a reference to the archive. We recommend either a call to
// com.pkware.smartcrypt.unstructured.UnstructuredData.newZipOutputFile
// or to
// com.pkware.smartcrypt.unstructured.UnstructuredData.newZipOutputStream
}
private void configureForEncryption(ZipArchiveOutput archive) {
// TODO Configure the archive for encryption. Specify the algorithm to use, Smartkeys to use, etc.
// We recommend using com.pkware.smartcrypt.unstructured.UnstructuredData.encrypt() if using a Smartkey.
// The following is just a sample of what is possible. We only recommend this if not using a Smartkey.
String password = "your password";
EncryptionInfo info = metaClient.getArchiveEncryptionInfo(null, password);
KMSInfo kms = new KMSInfo();
kms.data = info.json.getBytes(StandardCharsets.UTF_8);
archive.setPassword(info.password.getBytes(StandardCharsets.ISO_8859_1), kms);
archive.setCryptAlgorithm(ArchiveEntry.CRYPT_AE2);
}
private List<File> getFilesToAdd() {
// TODO return the files that should be part of the ZIP
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment