问题
I need to decrypt the encrypted data by Crypto++ in libgcrypt due to C language restriction on target platform. So I've decided to use libgcrypt since it supporting the AES128 and GCM mode.
In Crypto++, the data is encrypted this way:
std::string encrypt_data(const std::string &data,
const std::vector<unsigned char> &iv,
const std::vector<unsigned char> &key)
{
CryptoPP::GCM<CryptoPP::AES>::Encryption encryptor;
encryptor.SetKeyWithIV(&key[0], key.size(), &iv[0]);
std::string ciphertext;
CryptoPP::StringSource ss( data, true,
new CryptoPP::AuthenticatedEncryptionFilter(
encryptor,
new CryptoPP::StringSink(ciphertext)
)
);
return ciphertext;
}
and successfully decrypted this way:
std::string decrypt_data(const std::string &data,
const std::vector<unsigned char> &iv,
const std::vector<unsigned char> &key)
{
CryptoPP::GCM<CryptoPP::AES>::Decryption decryptor;
decryptor.SetKeyWithIV(&key[0], key.size(), &iv[0]);
std::string recovered;
CryptoPP::StringSource ss( data, true,
new CryptoPP::AuthenticatedDecryptionFilter(
decryptor,
new CryptoPP::StringSink( recovered )
)
);
return recovered;
}
But the decoded data is wrong when I try to decode ciphertext
using libgcrypt by these steps:
gcry_cipher_open()
gcry_cipher_setkey()
gcry_cipher_setiv()
- Seperate the cipher text and authentication tag
gcry_cipher_decrypt(cipher text)
gcry_cipher_checktag(authentication tag)
Is there any steps I missed to replicate the Crypto++ decoding process?
Gcrypt decryption code (Expected output Decrypted cipher = password
):
#include <stdio.h>
#include <stdlib.h>
#include <gcrypt.h>
static unsigned char const aesSymKey[] = { 0x38, 0xb4, 0x8f, 0x1f, 0xcd, 0x63, 0xef, 0x32, 0xc5, 0xd1, 0x3f, 0x52, 0xbc, 0x4f, 0x5b, 0x24 };
static unsigned char const aesIV[] = { 0xE4, 0xEF, 0xC8, 0x08, 0xEB, 0xB8, 0x69, 0x95, 0xF3, 0x44, 0x6C, 0xE9, 0x15, 0xE4, 0x99, 0x7E };
static unsigned char const aesPass[] = { 0xda, 0x84, 0x3f, 0x01, 0xa0, 0x14, 0xfd, 0x85 };
static unsigned char const aesTag[] = { 0xdf, 0x5f, 0x9f, 0xe2, 0x9d, 0x7e, 0xc3, 0xdf, 0x7a, 0x1e, 0x59, 0xd8, 0xe6, 0x61, 0xf7, 0x7e };
#define GCRY_CIPHER GCRY_CIPHER_AES128
#define GCRY_MODE GCRY_CIPHER_MODE_GCM
int main(){
gcry_error_t gcryError;
gcry_cipher_hd_t gcryCipherHd;
if (!gcry_check_version(GCRYPT_VERSION))
{
fputs("libgcrypt version mismatch\n", stderr);
exit(2);
}
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
if(!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
{
fputs("libgcrypt has not been initialized\n", stderr);
abort();
}
size_t keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER);
size_t blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER);
char * outBuffer = malloc(blkLength);
gcryError = gcry_cipher_open(
&gcryCipherHd, // gcry_cipher_hd_t *
GCRY_CIPHER, // int
GCRY_MODE, // int
0); // unsigned int
if (gcryError)
{
printf("gcry_cipher_open failed: %s/%s\n",
gcry_strsource(gcryError),
gcry_strerror(gcryError));
return;
}
gcryError = gcry_cipher_setkey(gcryCipherHd, aesSymKey, keyLength);
if (gcryError)
{
printf("gcry_cipher_setkey failed: %s/%s\n",
gcry_strsource(gcryError),
gcry_strerror(gcryError));
return;
}
gcryError = gcry_cipher_setiv(gcryCipherHd, aesIV, blkLength);
if (gcryError)
{
printf("gcry_cipher_setiv failed: %s/%s\n",
gcry_strsource(gcryError),
gcry_strerror(gcryError));
return;
}
gcryError = gcry_cipher_decrypt(
gcryCipherHd, // gcry_cipher_hd_t
outBuffer, // void *
blkLength, // size_t
aesPass, // const void *
8); // size_t
if (gcryError)
{
printf("gcry_cipher_decrypt failed: %s/%s\n",
gcry_strsource(gcryError),
gcry_strerror(gcryError));
return;
}
gcryError = gcry_cipher_checktag(
gcryCipherHd,
aesTag,
blkLength);
if (gcryError)
{
printf("gcry_cipher_checktag failed: %s/%s\n",
gcry_strsource(gcryError),
gcry_strerror(gcryError));
return;
}
printf("Decrypted cipher = %s\n", outBuffer);
// clean up after ourselves
gcry_cipher_close(gcryCipherHd);
free(outBuffer);
return 0;
}
EDIT: Just to be clear, the steps to decrypt I'm searching for is for the ciphertext
output of the Crypto++ encryption function shown above; the encrypt_data()
. So I won't accept any answer where it can't be applied to successfully decrypt ciphertext
.
回答1:
Part 2 of 2 for the answer. This is the Gcrpyt decryptor. It consumes the parameters from Part 1.
In the code below, the call to gcry_cipher_decrypt
gets the decrypted text. But I don't know how to get the size of the decrypted text from the library. It does not matter for GCM mode, but it will matter for other modes, like CBC. See this Stack Overflow question: Determine size of decrypted data from gcry_cipher_decrypt?.
The ROUNDUP
is for rounding up to a multiple of the cipher's block size. I read it was a requirement for the decryption buffer at Working with Ciphers, but it may not apply here. I left it in place because "things worked", but you should knob turn on it further if it bothers you.
If you turn knobs on the AE
or AAD
preprocessor macro, then you will need to generate new parameters with the Crypto++ encryption routine.
/* gcc -g3 -O1 -Wall -Wextra -std=c99 gcm-gcrypt-decrypt.c /usr/local/lib/libgcrypt.a /usr/local/lib/libgpg-error.a -o gcm-gcrypt-decrypt.exe */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <gcrypt.h>
typedef unsigned char byte;
/* All of this was generated in Crypto++ */
const byte key[] = { 0x73,0x12,0xBB,0xDB,0x86,0x73,0x65,0xF7,0x68,0x7D,0xE9,0x2B,0xF8,0xEE,0x66,0xF1 };
const byte iv[] = { 0x8C,0x70,0x54,0x17,0xD6,0xD9,0x7B,0x18,0x39,0xDC,0x5B,0xBC,0x21,0xDF,0x30,0x74 };
const byte plain[] = { 0x4E,0x6F,0x77,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x66,0x6F,0x72,0x20,0x61,0x6C,0x6C,0x20,0x67,0x6F,0x6F,0x64,0x20,0x6D,0x65,0x6E,0x20,0x74,0x6F,0x20,0x63,0x6F,0x6D,0x65,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x61,0x69,0x64,0x65,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x75,0x6E,0x74,0x72,0x79,0x2E,0x00 };
const byte aad[] = { 0x41,0x74,0x74,0x61,0x63,0x6B,0x20,0x61,0x74,0x20,0x64,0x61,0x77,0x6E,0x21,0x00 };
const byte cipher[] = { 0xE8,0x0E,0xEA,0x10,0x32,0x26,0x7D,0xD1,0x75,0xF3,0x33,0x0F,0x30,0xBB,0x36,0xFB,0x3F,0x95,0x24,0x31,0x90,0xD2,0x2C,0xB1,0x34,0x5B,0x69,0x42,0x1E,0x98,0xC4,0x65,0x3B,0x06,0x5D,0x45,0xB6,0xC7,0x7E,0x26,0x7E,0xBC,0xFF,0xB7,0x7F,0xF4,0x11,0xF8,0xF3,0x8B,0x19,0x08,0xE6,0xAE,0x36,0x44,0xEF,0x3F,0xA6,0xC3,0xAE,0x34,0x08,0xB9,0x33,0xD3,0x33,0x63,0x46 };
const byte tag[] = { 0x00,0xAE,0xDC,0x12,0x55,0xF8,0x87,0xB5,0x10,0x75,0x20,0xB5,0x94,0xCA,0x91,0xDF };
#define COUNTOF(x) ( sizeof(x) / sizeof(x[0]) )
#define ROUNDUP(x, b) ( (x) ? (((x) + (b - 1)) / b) * b : b)
byte recovered[ ROUNDUP(COUNTOF(cipher), 16) ];
#define GCRY_CIPHER GCRY_CIPHER_AES128
#define GCRY_MODE GCRY_CIPHER_MODE_GCM
#define AE 1
#define AAD 1
int main(){
gcry_error_t err;
gcry_cipher_hd_t handle;
memset(recovered, 0x00, COUNTOF(recovered));
fprintf(stdout, "Plaintext size: %d\n", (int)COUNTOF(plain));
fprintf(stdout, "Ciphertext size: %d\n", (int)COUNTOF(cipher));
fprintf(stdout, "Recovered size: %d\n", (int)COUNTOF(recovered));
assert(COUNTOF(key) == gcry_cipher_get_algo_keylen(GCRY_CIPHER));
assert(COUNTOF(iv) == gcry_cipher_get_algo_blklen(GCRY_CIPHER));
assert(COUNTOF(recovered) % gcry_cipher_get_algo_blklen(GCRY_CIPHER) == 0);
if (!gcry_check_version(GCRYPT_VERSION))
{
fputs("libgcrypt version mismatch\n", stderr);
exit(2);
}
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
if(!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
{
fputs("libgcrypt has not been initialized\n", stderr);
abort();
}
err = gcry_cipher_open(
&handle, // gcry_cipher_hd_t *
GCRY_CIPHER, // int
GCRY_MODE, // int
0); // unsigned int
if (err)
{
printf("gcry_cipher_open failed: %s/%s\n",
gcry_strsource(err),
gcry_strerror(err));
return 1;
}
err = gcry_cipher_setkey(handle, key, COUNTOF(key));
if (err)
{
printf("gcry_cipher_setkey failed: %s/%s\n",
gcry_strsource(err),
gcry_strerror(err));
return 1;
}
err = gcry_cipher_setiv(handle, iv, COUNTOF(iv));
if (err)
{
printf("gcry_cipher_setiv failed: %s/%s\n",
gcry_strsource(err),
gcry_strerror(err));
return 1;
}
#if defined(AAD)
err = gcry_cipher_authenticate(
handle, // gcry_cipher_hd_t
aad, // void *
COUNTOF(aad)); // size_t
if (err)
{
printf("gcry_cipher_authenticate failed: %s/%s\n",
gcry_strsource(err),
gcry_strerror(err));
return 1;
}
#endif
#if defined(AE)
err = gcry_cipher_decrypt(
handle, // gcry_cipher_hd_t
recovered, // void *
COUNTOF(recovered), // size_t
cipher, // const void *
COUNTOF(cipher)); // size_t
if (err)
{
printf("gcry_cipher_decrypt failed: %s/%s\n",
gcry_strsource(err),
gcry_strerror(err));
return 1;
}
#endif
err = gcry_cipher_checktag(
handle,
tag,
COUNTOF(tag));
if (err)
{
printf("gcry_cipher_checktag failed: %s/%s\n",
gcry_strsource(err),
gcry_strerror(err));
return 1;
}
#if defined(AE)
fprintf(stdout, "Decrypted = %s\n", recovered);
#endif
#if defined(AAD)
fprintf(stdout, "Additional data = %s\n", (char*)aad);
#endif
gcry_cipher_close(handle);
return 0;
}
It produces output similar to:
$ ./gcm-gcrypt-decrypt.exe
Plaintext size: 69
Ciphertext size: 69
Recovered size: 80
Decrypted = Now is the time for all good men to come to the aide of the country.
Additional data = Attack at dawn!
回答2:
Part 1 of 2 for the answer. This is the Crypto++ encryptor. It also prints the parameters it operates upon.
If you turn knobs on the AE
or AAD
preprocessor macro, then you will need to generate new parameters for the Gcrypt decryption routine.
// g++ -g3 -O1 -Wall -Wextra gcm-cryptopp-encrypt.cpp /usr/local/lib/libcryptopp.a -o gcm-cryptopp-encrypt.exe
#include <iostream>
using std::cout;
using std::endl;
#include <string>
using std::string;
#include <cryptopp/cryptlib.h>
using CryptoPP::DEFAULT_CHANNEL;
using CryptoPP::AAD_CHANNEL;
#include <cryptopp/osrng.h>
using CryptoPP::OS_GenerateRandomBlock;
#include <cryptopp/aes.h>
using CryptoPP::AES;
#include <cryptopp/gcm.h>
using CryptoPP::GCM;
#include <cryptopp/secblock.h>
using CryptoPP::SecByteBlock;
#include <cryptopp/hex.h>
using CryptoPP::HexEncoder;
#include <cryptopp/filters.h>
using CryptoPP::StringSink;
using CryptoPP::AuthenticatedEncryptionFilter;
#define UNUSED(x) ((void)x)
#define AE 1
#define AAD 1
int main(int argc, char* argv[])
{
UNUSED(argc); UNUSED(argv);
string hexPre = " { 0x", hexPost = " };";
string plain = "Now is the time for all good men to come to the aide of the country.";
string aad = "Attack at dawn!";
HexEncoder hex(NULL, true, 2, ",0x");
size_t res = 0;
SecByteBlock key(AES::DEFAULT_KEYLENGTH), iv(AES::BLOCKSIZE);
static const size_t TAG_SIZE = AES::BLOCKSIZE;
// Generate random key and iv
OS_GenerateRandomBlock(false, key, key.size());
OS_GenerateRandomBlock(false, iv, iv.size());
string s1(hexPre), s2(hexPre);
hex.Detach(new StringSink(s1));
hex.Put(key, key.size());
hex.MessageEnd();
s1 += hexPost;
hex.Detach(new StringSink(s2));
hex.Put(iv, iv.size());
hex.MessageEnd();
s2 += hexPost;
cout << "const byte key[] = " << s1 << endl;
cout << "const byte iv[] = " << s2 << endl;
/////////////////////////////////////////
string s3(hexPre), s4(hexPre);
#if defined(AE)
hex.Detach(new StringSink(s3));
hex.Put(reinterpret_cast<const byte*>(plain.data()), plain.size() + 1 /*NULL*/);
hex.MessageEnd();
s3 += hexPost;
cout << "const byte plain[] = " << s3 << endl;
#endif
#if defined(AAD)
hex.Detach(new StringSink(s4));
hex.Put(reinterpret_cast<const byte*>(aad.data()), aad.size() + 1 /*NULL*/);
hex.MessageEnd();
s4 += hexPost;
cout << "const byte aad[] = " << s4 << endl;
#endif
/////////////////////////////////////////
GCM<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key, key.size(), iv, iv.size());
AuthenticatedEncryptionFilter filter(encryptor);
#if defined(AAD)
filter.ChannelPut(AAD_CHANNEL, reinterpret_cast<const byte*>(aad.data()), aad.size() + 1 /*NULL*/);
#endif
#if defined(AE)
filter.ChannelPut(DEFAULT_CHANNEL, reinterpret_cast<const byte*>(plain.data()), plain.size() + 1 /*NULL*/);
#endif
filter.MessageEnd();
res= filter.MaxRetrievable();
SecByteBlock cipher(res - TAG_SIZE), tag(TAG_SIZE);
#if defined(AE)
res = filter.Get(cipher, cipher.size());
cipher.resize(res);
#endif
res = filter.Get(tag, tag.size());
tag.resize(res);
/////////////////////////////////////////
string s5(hexPre), s6(hexPre);
hex.Detach(new StringSink(s5));
hex.Put(cipher.data(), cipher.size());
hex.MessageEnd();
s5 += hexPost;
hex.Detach(new StringSink(s6));
hex.Put(tag.data(), tag.size());
hex.MessageEnd();
s6 += hexPost;
#if defined(AE)
cout << "const byte cipher[] = " << s5 << endl;
#endif
cout << "const byte tag[] = " << s6 << endl;
return 0;
}
Its output will be similar to:
$ ./gcm-cryptopp-encrypt.exe
const byte key[] = { 0xD1,0xB8,0xDC,0xB8,0xF9,0x83,0x8E,0xB8,0xE5,0x0B,0x48,0xB2,0xF5,0x1A,0x71,0x46 };
const byte iv[] = { 0x05,0x2E,0xAF,0x03,0x23,0xFE,0xFD,0x5C,0xF5,0x90,0x7B,0xDD,0x09,0xBF,0x0A,0x71 };
const byte plain[] = { 0x4E,0x6F,0x77,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x66,0x6F,0x72,0x20,0x61,0x6C,0x6C,0x20,0x67,0x6F,0x6F,0x64,0x20,0x6D,0x65,0x6E,0x20,0x74,0x6F,0x20,0x63,0x6F,0x6D,0x65,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x61,0x69,0x64,0x65,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x75,0x6E,0x74,0x72,0x79,0x2E,0x00 };
const byte aad[] = { 0x41,0x74,0x74,0x61,0x63,0x6B,0x20,0x61,0x74,0x20,0x64,0x61,0x77,0x6E,0x21,0x00 };
const byte cipher[] = { 0xD0,0x6D,0x69,0x0F,0x6A,0xDE,0x61,0x81,0x42,0x5A,0xA1,0xF8,0x29,0xFE,0x70,0xCC,0xCC,0x63,0xE4,0xFE,0x8C,0x32,0x58,0xFE,0xB8,0xC1,0x0F,0x38,0xBC,0x3F,0x27,0x2F,0x51,0xC3,0xB4,0x38,0x19,0x8E,0x24,0x97,0x54,0xCA,0xE6,0xA4,0xE6,0x22,0xDA,0x85,0x02,0x17,0xFE,0x76,0x89,0x55,0x85,0xEC,0x94,0x1D,0xD8,0xB4,0x0B,0x79,0x4A,0xE1,0xD6,0x5A,0x6A,0xA4,0x9A };
const byte tag[] = { 0xA8,0x11,0x3D,0x86,0xE8,0xCA,0x2F,0xAF,0xED,0x09,0x90,0x44,0xCD,0x48,0xC1,0x06 };
回答3:
The Crpto++ encryption implementation executing this code to set the IV:
encryptor.SetKeyWithIV(&key[0], key.size(), &iv[0]);
Since the IV size is not passed, the default length is used which is 12. This is based on the recommended IV size by the specification which is 96bits.
So in order for my libgrcrypt to decode the cipher correctly, I just need to change this line:
gcryError = gcry_cipher_setiv(gcryCipherHd, aesIV, blkLength);
into this:
gcryError = gcry_cipher_setiv(gcryCipherHd, aesIV, 12);
So I'll get the expected output:
$ ./decrypt
Decrypted cipher = password
来源:https://stackoverflow.com/questions/28756446/steps-to-decrypt-encrypted-data-in-crypto-in-libgcrypt