-1

I'm trying to create a protected ZIP in Windows file in C.

I've create the following approach based on these answers:

  1. Creating a ZIP file on Windows (XP/2003) in C/C++ #1
  2. Creating a ZIP file on Windows (XP/2003) in C/C++ #2

Here is the code:

#include <windows.h>
#include <shldisp.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <stdlib.h>
#include <objbase.h>

int main(int argc, TCHAR* argv[])
{
    DWORD strlen = 0;
    char szFrom[] = "C:\\Users\\dist",
        szTo[] = "C:\\Users\\harrison4\\Desktop\\example.zip";
    HRESULT hResult;
    IShellDispatch *pISD;
    Folder* pToFolder = NULL;
    VARIANT vDir, vFile, vOpt;
    BSTR strptr1, strptr2;

    FILE* f;
    fopen_s(&f, szTo, "wb");
    if (!f)
        return 0;

    fwrite("\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 22, 1, f);
    fclose(f);

    CoInitialize(NULL);

    hResult = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, &IID_IShellDispatch, (void**)&pISD);

    if (SUCCEEDED(hResult) && pISD != NULL)
    {
        strlen = MultiByteToWideChar(CP_ACP, 0, szTo, -1, 0, 0);
        strptr1 = SysAllocStringLen(0, strlen);
        MultiByteToWideChar(CP_ACP, 0, szTo, -1, strptr1, strlen);

        VariantInit(&vDir);
        vDir.vt = VT_BSTR;
        vDir.bstrVal = strptr1;
        hResult = pISD->NameSpace(vDir, &pToFolder);

        if (SUCCEEDED(hResult))
        {
            strlen = MultiByteToWideChar(CP_ACP, 0, szFrom, -1, 0, 0);
            strptr2 = SysAllocStringLen(0, strlen);
            MultiByteToWideChar(CP_ACP, 0, szFrom, -1, strptr2, strlen);

            VariantInit(&vFile);
            vFile.vt = VT_BSTR;
            vFile.bstrVal = strptr2;

            VariantInit(&vOpt);
            vOpt.vt = VT_I4;
            vOpt.lVal = 4;          // Do not display a progress dialog box

            printf("Copying %s to %s ...\n", szFrom, szTo);
            hResult = pToFolder->CopyHere(vFile, vOpt); //NOTE: this appears to always return S_OK even on error
            /*
             * 1) Enumerate current threads in the process using Thread32First/Thread32Next
             * 2) Start the operation
             * 3) Enumerate the threads again
             * 4) Wait for any new threads using WaitForMultipleObjects
             *
             * Of course, if the operation creates any new threads that don't exit, then you have a problem.
             */
            if (hResult == S_OK) {
                //NOTE: hard-coded for testing - be sure not to overflow the array if > 5 threads exist
                HANDLE hThrd[5];
                HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);  //TH32CS_SNAPMODULE, 0);
                DWORD NUM_THREADS = 0;
                if (h != INVALID_HANDLE_VALUE) {
                    THREADENTRY32 te;
                    te.dwSize = sizeof(te);
                    if (Thread32First(h, &te)) {
                        do {
                            if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))) {
                                //only enumerate threads that are called by this process and not the main thread
                                if ((te.th32OwnerProcessID == GetCurrentProcessId()) && (te.th32ThreadID != GetCurrentThreadId())) {
                                    //printf("Process 0x%04x Thread 0x%04x\n", te.th32OwnerProcessID, te.th32ThreadID);
                                    hThrd[NUM_THREADS] = OpenThread(SYNCHRONIZE, FALSE, te.th32ThreadID);
                                    NUM_THREADS++;
                                }
                            }
                            te.dwSize = sizeof(te);
                        } while (Thread32Next(h, &te));
                    }
                    CloseHandle(h);

                    printf("waiting for all threads to exit...\n");
                    //Wait for all threads to exit
                    WaitForMultipleObjects(NUM_THREADS, hThrd, TRUE, INFINITE);

                    //Close All handles
                    for (DWORD i = 0; i < NUM_THREADS; i++) {
                        CloseHandle(hThrd[i]);
                    }
                } //if invalid handle
            } //if CopyHere() hResult is S_OK

            SysFreeString(strptr2);
            pToFolder->Release();
        }

        SysFreeString(strptr1);
        pISD->Release();
    }

    CoUninitialize();

    printf("Press ENTER to exit\n");
    getchar();
    return 0;

}

It does work. However, I'm not able to find any documentation to set the password of the file.

I've also found the zip C library, which eases the ZIP creation, but unfortunately, it does not incorporates the password protection.

mllamazares
  • 7,876
  • 17
  • 61
  • 89
  • I am not sure Windows API includes encrypted zip functionality at all. You can get bits and pieces needed to do an AES encrypted zip, but probably a complete tool/library is easier. 7-zip has some options, but maybe is a bit large for your need and not sure how easy it is to build the DLL with only the wanted archive/compression/encryption included. – Fire Lancer Mar 10 '20 at 00:03
  • `fopen_s(..., "rb"); ... fwrite(...);` What's that supposed to accomplish? – IInspectable Mar 10 '20 at 06:29
  • @IInspectable it was a typo. Edited! thanks – mllamazares Mar 10 '20 at 07:17
  • 1
    Can you share the purpose of this? In the code you provided, you can create an empty zip file, but this cannot compress the existing file. For the protected zip file, I did not find such an API. You may need to use a third-party library. Please refer to [this link](https://stackoverflow.com/a/24756622/11128312) – Strive Sun Mar 10 '20 at 08:08
  • 1
    @StriveSun-MSFT I've found [this resource of minzip lib](https://chromium.googlesource.com/external/github.com/nmoinvaz/minizip/+/1.2/minizip.c) too with a clearer example. I'll keep you updated. Thanks! – mllamazares Mar 10 '20 at 10:01
  • 1
    Maybe you should write your own answer! Could be neat. – Prof. Falken Mar 12 '20 at 16:13
  • 4
    The bounty is a little pointless. The answer is to use a third party library, but recommendation questions are off topic here. – David Heffernan Mar 13 '20 at 10:24
  • The standard for encrypted zip is vague. PKZIP version 2 uses outdated encryption, this is the version which is currently supported by Windows. Modern encryption format rely on AES, but those formats are not supported by Windows. Most libraries support WinZip AES format. – Barmak Shemirani Mar 18 '20 at 04:57

2 Answers2

6

You can use minizip library on Windows. It can be compiled via CMake.

This is how you can use it to create a password-protected zip file:

#include "mz.h"
#include "mz_os.h"
#include "mz_strm.h"
#include "mz_strm_buf.h"
#include "mz_strm_split.h"
#include "mz_zip.h"
#include "mz_zip_rw.h"
#include <stdio.h>

int main() {
    void *writer = NULL;
    int32_t err = MZ_OK;

    mz_zip_writer_create(&writer);
    mz_zip_writer_set_password(writer, "mypassword");
    mz_zip_writer_set_compress_method(writer, MZ_COMPRESS_METHOD_DEFLATE);
    mz_zip_writer_set_compress_level(writer, MZ_COMPRESS_LEVEL_DEFAULT);
    err = mz_zip_writer_open_file(writer, "output.zip", 0, 0);
    if (err != MZ_OK) {
        printf("Could not open zip file for writing.");
        return 1;
    }
    err = mz_zip_writer_add_path(writer, "text1.txt", NULL, 0, 0);
    if (err != MZ_OK) {
        printf("Failed to add file to the archive.");
    }
    mz_zip_writer_close(writer);
    mz_zip_writer_delete(&writer);
    return 0;
}

This code will create a output.zip file with a password-protected file text1.txt.

For more information and uses refer to the minizip.c sample file.

I hope it helps.

Daniel Koch
  • 580
  • 2
  • 11
-1

IF you strictly do not want .zip format and can use external tools, here is an easy way: -

You can use external softwares like https://www.aescrypt.com/. Download it, place executable on the same folder as your c program, and then whenever you want to encrypt a file, make a system call using

system("aescrypt -e -p apples picture.jpg") 

(Code from the official documentation) : https://www.aescrypt.com/documentation/

EReload
  • 199
  • 2
  • 12