NO

Author Topic: OpenSSL 3.2.0 - w/ EVP string encrypt/decrypt (secret key)  (Read 4602 times)

Offline WiiLF23

  • Member
  • *
  • Posts: 89
OpenSSL 3.2.0 - w/ EVP string encrypt/decrypt (secret key)
« on: December 18, 2023, 01:41:21 AM »
I needed OpenSSL for my Winsock client/server to implement symmetrical encryption/decryption for UDP messages and file data during socket session, for which I send an initial decryption key via socket from client side to server, and server returns a key acknowledgement awaiting further action from client (sendto/recvfrom).

OpenSSL was exactly what I was looking for. It works just fine, uses EVP* functions and utilizes aes256-cbc algorithm using a secret key. Includes 2 functions:

Code: [Select]
void encrypt(const char *inputString, const unsigned char *key, char *encryptedOutput)
void decrypt(const char *hexString, const unsigned char *key, char **decryptedOutput)

Now, the point was to find a non-MSVCRT version of the libcrypto DLL, and find the static libraries which was available from 3 different online repos and are code signed until June 2024. You just locate a newer copy by that point, and you will also be on a newer OpenSSL version.

The first page I found was at
https://wiki.openssl.org/index.php/Binaries

Which gave me a clear direction. I chose OpenSSL 3.2.0
https://kb.firedaemon.com/support/solutions/articles/4000121705

Now in the ZIP contains x86 and x64 versions of the necessary files.

(ZIP) openssl-3/x64/lib/libssl.lib
(ZIP) openssl-3/x64/lib/libcrypto.lib
(ZIP) openssl-3/x64/include/*
(ZIP) openssl-3/x64/bin/libssl-3-x64.dll

Rename "include" directory to "openssl" and place in application path.
Place both libs in same directory as application, and place libssl-3-x64.dll (or libssl-3.dll) in same path too.

Code: [Select]
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "libcrypto.lib")

#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/conf.h>

Generate the key:
Code: [Select]
// Init OpenSSL
OpenSSL_add_all_algorithms();
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);

unsigned char key[EVP_MAX_KEY_LENGTH];

// Generate random key
if (RAND_bytes(key, sizeof(key)) != 1) {
        log_error("Failed to generate random key");
        return 0;
}

// Convert the key to a hexadecimal string
char hexKey[(EVP_MAX_KEY_LENGTH * 2) + 1];
for (int i = 0; i < sizeof(key); ++i) {
    sprintf(&hexKey[i * 2], "%02x", key[i]);
}

hexKey[sizeof(key) * 2] = '\0';

Encrypting text with same key
Code: [Select]
char encryptedMessage[512];
encrypt("Some secret text here", (const unsigned char *)hexKey, encryptedMessage);

Decrypting text with same key
Code: [Select]
char *decryptedData;
decrypt(encryptedMessage, (const unsigned char *)hexKey, &decryptedData);

Encrypt/decrypt functions:
Code: [Select]
void encrypt(const char *inputString, const unsigned char *key, char *encryptedOutput) {
    EVP_CIPHER_CTX *ctx;
    int len;
    int ciphertextLen;
    unsigned char ciphertext[512];

    // Create and initialize the context
    ctx = EVP_CIPHER_CTX_new();
    EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, NULL);

    // Encrypt the input string
    EVP_EncryptUpdate(ctx, ciphertext, &len, (const unsigned char *)inputString, strlen(inputString));
    ciphertextLen = len;

    // Finalize the encryption
    EVP_EncryptFinal_ex(ctx, ciphertext + len, &len);
    ciphertextLen += len;

    // Clean up the context
    EVP_CIPHER_CTX_free(ctx);

    // Convert the encrypted data to a hexadecimal string
    for (int i = 0; i < ciphertextLen; ++i) {
        sprintf(&encryptedOutput[i * 2], "%02x", ciphertext[i]);
    }

    encryptedOutput[ciphertextLen * 2] = '\0';
}
Code: [Select]
void decrypt(const char *hexString, const unsigned char *key, char **decryptedOutput) {
    EVP_CIPHER_CTX *ctx;
    int len;
    int plaintextLen;

    // Create and initialize the context
    ctx = EVP_CIPHER_CTX_new();
    EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, NULL);

    // Convert the hexadecimal string to binary
    int hexLen = strlen(hexString);
    unsigned char *binaryData = (unsigned char *)malloc(hexLen / 2);

    for (int i = 0; i < hexLen / 2; ++i) {
        sscanf(&hexString[i * 2], "%2hhx", &binaryData[i]);
    }

    // Decrypt the binary data
    unsigned char *plaintext = (unsigned char *)malloc(hexLen / 2);
    EVP_DecryptUpdate(ctx, plaintext, &len, binaryData, hexLen / 2);
    plaintextLen = len;

    // Finalize the decryption
    EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
    plaintextLen += len;

    // Clean up the context
    EVP_CIPHER_CTX_free(ctx);
    free(binaryData);  // Free the dynamically allocated memory

    // Null-terminate the decrypted data
    plaintext[plaintextLen] = '\0';

    // Assign the decrypted data to the output buffer
    *decryptedOutput = (char *)plaintext;
}

Working perfectly for me. In case this helps anyone.
« Last Edit: December 18, 2023, 01:49:37 AM by WiiLF23 »

Offline John Z

  • Member
  • *
  • Posts: 860
Re: OpenSSL 3.2.0 - w/ EVP string encrypt/decrypt (secret key)
« Reply #1 on: December 18, 2023, 10:39:34 AM »
WiiLF23- Great contribution.

I had looked at OpenSSL in the past and got bogged down in the implementation.
Looks like you have nailed it down.

John Z

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Re: OpenSSL 3.2.0 - w/ EVP string encrypt/decrypt (secret key)
« Reply #2 on: December 18, 2023, 05:04:21 PM »
You can try with LibCurl OpenSSL that I ported to PellesC on my github account.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline WiiLF23

  • Member
  • *
  • Posts: 89
Re: OpenSSL 3.2.0 - w/ EVP string encrypt/decrypt (secret key)
« Reply #3 on: December 18, 2023, 10:40:41 PM »
Thanks guys! Yeah I added a array of supported algorithms that I load up into a combobox control. On WM_CLOSE it sets the index of the array in memory, so we can do this manually by selection instead of calling a direct function

Code: [Select]
const EVP_CIPHER *algorithm = EVP_get_cipherbyname("AES-256-CBC");
if (algorithm == NULL) {
        log_error("Unknown algorithm!");
        return;
    }

EVP_get_cipherbyname works with narrow chars. This became easy to allow selection!

Combo box setup:

Code: [Select]
static const char *algorithms[] = {
    "AES-128-ECB", "AES-128-CBC", "AES-128-CTR", "AES-128-GCM",
    "AES-192-ECB", "AES-192-CBC", "AES-192-CTR", "AES-192-GCM",
    "AES-256-ECB", "AES-256-CBC", "AES-256-CTR", "AES-256-GCM",
    "DES-ECB", "DES-CBC", "3DES-ECB", "3DES-CBC",
    "RC4",
    "Camellia-128-CBC", "Camellia-192-CBC", "Camellia-256-CBC",
    "IDEA-ECB", "IDEA-CBC",
    "SEED-ECB", "SEED-CBC",
    "ChaCha20-Poly1305"
};

WM_INITDIALOG:
Code: [Select]
HWND comboBox = GetDlgItem(hwndDlg, MY_COMBO_BOX_ID);
for (size_t i = 0; i < sizeof(algorithms) / sizeof(algorithms[0]); ++i) {
    int wideSize = MultiByteToWideChar(CP_UTF8, 0, algorithms[i], -1, NULL, 0);
    wchar_t *wideString = (wchar_t*)malloc(wideSize * sizeof(wchar_t));

    MultiByteToWideChar(CP_UTF8, 0, algorithms[i], -1, wideString, wideSize);
    SendMessage(comboBox, CB_ADDSTRING, 0, (LPARAM)wideString);

    free(wideString);
}

SendMessage(comboBox, CB_SETCURSEL, 10, 0); // Set index (optional)

Had to convert narrow to wide for the combobox for list, to allow the EVP_get_cipherbyname() function to take the string without issues.



You can read the OpenSSL version (see pic) by calling:

Code: [Select]
const char *openssl_version = OpenSSL_version(OPENSSL_VERSION);
« Last Edit: December 18, 2023, 11:17:02 PM by WiiLF23 »

Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2115
Re: OpenSSL 3.2.0 - w/ EVP string encrypt/decrypt (secret key)
« Reply #4 on: December 19, 2023, 09:20:56 AM »
An ANSI string in UNICODE program
Code: [Select]
SendMessageA(comboBox, CB_ADDSTRING, 0, (LPARAM)algorithms[i]);
May the source be with you