Created
October 17, 2019 14:07
-
-
Save MariusVolkhart/508b8df80d0a6b0e947e32f7466c0bb3 to your computer and use it in GitHub Desktop.
Encrypting a ZIP with a Contingency Groups
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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