Here is the best I could come up with. Suggestions for improvement are welcome!
static void encrypt(const QByteArray &data, const QByteArray &key, QByteArray *encrypted) { // Create the crypto provider context. HCRYPTPROV hProvider = NULL; if (!CryptAcquireContext(&hProvider, NULL, // pszContainer = no named container NULL, // pszProvider = default provider PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { throw std::runtime_error("Unable to create crypto provider context."); } // Construct the blob necessary for the key generation. AesBlob128 aes_blob; aes_blob.header.bType = PLAINTEXTKEYBLOB; aes_blob.header.bVersion = CUR_BLOB_VERSION; aes_blob.header.reserved = 0; aes_blob.header.aiKeyAlg = CALG_AES_128; aes_blob.key_length = kAesBytes128; memcpy(aes_blob.key_bytes, key.constData(), kAesBytes128); // Create the crypto key struct that Windows needs. HCRYPTKEY hKey = NULL; if (!CryptImportKey(hProvider, reinterpret_cast<BYTE*>(&aes_blob), sizeof(AesBlob128), NULL, // hPubKey = not encrypted 0, // dwFlags &hKey)) { throw std::runtime_error("Unable to create crypto key."); } // The CryptEncrypt method uses the *same* buffer for both the input and // output (!), so we copy the data to be encrypted into the output array. // Also, for some reason, the AES-128 block cipher on Windows requires twice // the block size in the output buffer. So we resize it to that length and // then chop off the excess after we are done. encrypted->clear(); encrypted->append(data); encrypted->resize(kAesBytes128 * 2); // This acts as both the length of bytes to be encoded (on input) and the // number of bytes used in the resulting encrypted data (on output). DWORD length = kAesBytes128; if (!CryptEncrypt(hKey, NULL, // hHash = no hash true, // Final 0, // dwFlags reinterpret_cast<BYTE*>(encrypted->data()), &length, encrypted->length())) { throw std::runtime_error("Encryption failed"); } // See comment above. encrypted->chop(length - kAesBytes128); CryptDestroyKey(hKey); CryptReleaseContext(hProvider, 0); }
source share