I am trying to port a decryption routine from C# program to C++ using cryptopp, but I have a problem. In the C# program, the key and IV are both 256 bits. So I tried to do something like this:
char *hash1 = "......";
std::string hash2;
CryptoPP::StringSource(hash1, true,new CryptoPP::Base64Decoder(new CryptoPP::StringSink(hash2)));
CryptoPP::Rijndael::Decryption decryptor(key, 32);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( decryptor, iv);
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, (new CryptoPP::StringSink( decryptedData ) ));
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( hash2.c_str() ), hash2.size() );
stfDecryptor.MessageEnd();
and I am getting
StreamTransformationFilter: ciphertext length is not a multiple of block size.
I tried to pass IV size like this:
CryptoPP::CBC_Mode<CryptoPP::Rijndael >::Decryption decr;
decr.SetKeyWithIV(key, 32, iv, 32);
But then I get:
IV length 32 exceeds the maximum of 16.
So, how can I decrypt data, when it was encrypted by IV with length = 32?
Looking at the implementation, the current iterations of crypto++ only support Rijndael with a block size of 16 bytes. As the IV has to be precisely a single block for CBC mode, Rijndael with a 256 bit block size does not seem to be possible.
Related
I have this code that I found on SO and it works. My problem is that encryption and decryption are in the same file. Naturally, I want to separate them into two functions. The problem is the decoder needs the original input length. Isn't it a security vulnerability? How can I decrpyt without knowing the original length of the input?
/* computes the ciphertext from plaintext and key using AES256-CBC algorithm */
string cipher_AES(string key, string message)
{
size_t inputslength = message.length();
unsigned char aes_input[inputslength];
unsigned char aes_key[AES_KEYLENGTH];
memset(aes_input, 0, inputslength/8);
memset(aes_key, 0, AES_KEYLENGTH/8);
strcpy((char*) aes_input, message.c_str());
strcpy((char*) aes_key, key.c_str());
/* init vector */
unsigned char iv[AES_BLOCK_SIZE];
memset(iv, 0x00, AES_BLOCK_SIZE);
// buffers for encryption and decryption
const size_t encslength = ((inputslength + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
unsigned char enc_out[encslength];
unsigned char dec_out[inputslength];
memset(enc_out, 0, sizeof(enc_out));
memset(dec_out, 0, sizeof(dec_out));
AES_KEY enc_key, dec_key;
AES_set_encrypt_key(aes_key, AES_KEYLENGTH, &enc_key);
AES_cbc_encrypt(aes_input, enc_out, inputslength, &enc_key, iv, AES_ENCRYPT);
AES_set_decrypt_key(aes_key, AES_KEYLENGTH, &dec_key);
AES_cbc_encrypt(enc_out, dec_out, encslength, &dec_key, iv, AES_DECRYPT);
stringstream ss;
for(int i = 0; i < encslength; i++)
{
ss << enc_out[i];
}
return ss.str(););
}
First of all, AES encryption takes place 1-to-1 in blocks of 128 bits, so you already know the message size with a 16-byte accuracy by just looking at the ciphertext.
Then, for the last block you just need to determine where the message ends. The standard solution for that is to use padding (e.g. PKCS#7). Or just store the message length at the beginning and encrypt it together with the message.
You can of course continue using OpenSSL AES API, and implement padding or some other mechanism yourself. But OpenSSL already has higher-level API (EVP), which does AES, CBC and PKCS padding automatically.
See EVP Symmetric Encryption and Decryption official OpenSSL wiki page for an example of using the EVP API.
Unrelated notes:
a fixed IV (especially zero IV) is insecure. Consider generating a random IV and storing it together with the ciphertext (e.g. using RAND_bytes).
check out also AES GCM mode for authenticated encryption (encryption + secure checksum), this way the encrypted message additionally becomes tamper-proof. See this example.
I am trying to perform an encryption and decryption in Crypto++ library using AES128 cipher with CCM mode of operation. I have a problem when I try to encrypt string longer than 16777215 bytes.
My code:
const int TAG_SIZE = 8;
CCM< AES, TAG_SIZE >::Encryption e;
CCM< AES, TAG_SIZE >::Decryption d;
e.SetKeyWithIV( key, sizeof(key), iv, sizeof(iv) );
e.SpecifyDataLengths( 0, plain.size(), 0 );
//Encryption
StringSource ss1(
plain,
true,
new AuthenticatedEncryptionFilter(
e,
new StringSink(cipher)
)
);
d.SetKeyWithIV( key, sizeof(key), iv, sizeof(iv) );
d.SpecifyDataLengths( 0, plain.size(), 0 );
//Decryption
AuthenticatedDecryptionFilter df( d,
new StringSink(recovered)
);
StringSource ss2(
cipher,
true,
new Redirector(df)
);
When I try to encrypt/decrypt a plaintext which is size of a CD (737280000), I get the following error:
"terminate called after throwing an instance of 'CryptoPP::InvalidArgument'
what(): AES/CCM: message length 737280000 exceeds the maximum of 16777215"
My question is, how do I encrypt/decrypt a plaintext which is longer than 16777215 bytes?
My question is, how do I encrypt/decrypt a plaintext which is longer than 16777215 bytes?
CCM mode is specified in NIST SP800-38c. Section A.1, Length Requirements, discusses maximum plain text under a security context. A security context is the {key, iv} combination (with some hand waiving).
I believe you have three choices. First, you can increase the length of the IV. The larger the iv, the more plain text you can encrypt. The max iv length is 13 so it does not scale up forever.
Second, you have to re-key or change the iv before you hit the maximum plain text under the context. You can find the maximum plain text length using MaxMessageLength(). Crypto++ tracks the number of bytes processed via m_totalMessageLength, but it is not exposed to user programs. You will have to track it yourself.
Third, you can change algorithms. An algorithm like ChaCha20Poly1305 allows you to encrypt 2^38-1 64-byte blocks. That is just under 2^44 bytes or about 256 GB. You should be safe with ChaCha20Poly1305.
Crypto++ tells you the maximum number of bytes via MaxMessageLength(). In the case of CCM it is based on the iv length, and tracked through m_L in the code below.
lword MaxMessageLength() const
{return m_L<8 ? (W64LIT(1)<<(8*m_L))-1 : W64LIT(0)-1;}
MaxMessageLength() is used in authenc.cpp. ProcessData() throws an exception when the limit is hit:
if (m_state >= State_IVSet && length > MaxMessageLength()-m_totalMessageLength)
throw InvalidArgument(AlgorithmName() + ": message length exceeds maximum");
m_totalMessageLength += length;
const int TAG_SIZE = 8;
CCM< AES, TAG_SIZE >::Encryption e;
CCM< AES, TAG_SIZE >::Decryption d;
Your tag size is a tad bit on the small side. You might want to use the maximum size, if your protocol allows it.
I recommend you switch algorithms. CCM is a bastard mode that got standardized in the early 2000's through some Wireless Working Group. Then, NIST adopted it because it was already standardized.
At the time CCM was standardized there were better Authenticated Encryption modes available, like CWC, OCB, EAX and GCM. Unfortunately the damage was done. And now you have algorithms like Bernstein's ChaChaPoly1305.
You might also want to checkout AEAD Comparison on the Crypto++ wiki. The comparison shows CCM is about the worst of the authenticated encryption modes.
This question already has an answer here:
256-bit Rijndael blocksize?
(1 answer)
Closed 7 years ago.
so i have some old messages encrypted with this old code:
'the ecryption algorithm with specific settings
Dim myRijndael As New RijndaelManaged
myRijndael.Padding = PaddingMode.Zeros
myRijndael.Mode = CipherMode.CBC
myRijndael.KeySize = 256
myRijndael.BlockSize = 256
'declared byte arrays for the message string and the key and IV for the encryption algorithm
Dim encrypted() As Byte
Dim toEncrypt() As Byte
Dim key() As Byte
Dim IV() As Byte
'populating the arryas with the needed bytes for the encryption algorithm
key = System.Text.Encoding.ASCII.GetBytes(prm_key)
IV = System.Text.Encoding.ASCII.GetBytes(prm_iv)
'the actual instance of the ecryption algorithm
Dim encryptor As ICryptoTransform = myRijndael.CreateEncryptor(key, IV)
'streams for the encrypted byte array
Dim msEncrypt As New MemoryStream()
Dim csEncrypt As New CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)
toEncrypt = System.Text.Encoding.ASCII.GetBytes(text_to_encrypt)
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length)
'make sure we have all the blocks
csEncrypt.FlushFinalBlock()
'turn encrypted stream into byte array
encrypted = msEncrypt.ToArray()
'return a base 64 string so we can upload it to the server
Return (Convert.ToBase64String(encrypted))
and i am trying to decrypt it with Crypto++
this is the code that i came up with:
std::string coded = "pCyWPA5Enc3F0NAkowrt206brSfMrOgKMTXI1pKhCUY=";
//std::string coded = "F9uvtbK3Ue67Gbe9si5yvDn8a50bYnTovjfWali+Xjo=";
std::string coded2;
std::string ciphertext;
std::string decryptedtext;
CryptoPP::StringSource sss(coded, true,
new CryptoPP::Base64Decoder(
new CryptoPP::StringSink(ciphertext)
) // Base64Decoder
); // StringSource
CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::MAX_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, iv );
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ),CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING);
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( ciphertext.c_str() ), ciphertext.size() );
stfDecryptor.MessageEnd();
//
// Dump Decrypted Text
//
std::cout << "Decrypted Text: " << std::endl;
std::cout << decryptedtext;
std::cout << std::endl << std::endl;
but all i get in return is gibberish. The padding modes are set, the key is correct. I'm out of ideas.
The commented out "coded" string actually get decrypted, but it was encrypted with c++ and crypto++. The text is identical. so why are the Base64 encrypted stings different?
Im thinking that maybe since VB code encrypted bytes in ASCII that that might be the issue. but i don't know how to make crypto++ use ASCII encoding.
Can someone please point out the issue here?
AES uses a 128 bit block size. The VB code uses Rijndael with 256 bit block size.
I'm working in a new protocol for secure communication and I'm having problems to decrypt the ciphertext.
The data packet is saved in a uint8_t* variable and encrypted. Until this part is all going well. But when I try to decrypt I got the followings problems:
1) If I send the vector and the size (it's really 20 but I just want to decrypt the last 16 bytes):
CBC_Mode< AES >::Decryption decryptor;
decryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );
CryptoPP::StringSource ss( vector+4, 16 , true,
new CryptoPP::StreamTransformationFilter( decryptor,
new CryptoPP::StringSink( decryptedtext ) ) );
I get this:
terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found
2) If I just send the vector without size:
CryptoPP::StringSource ss( vector+4, true,
new CryptoPP::StreamTransformationFilter( decryptor,
new CryptoPP::StringSink( decryptedtext ) ) );
The programs runs but I just get all 00:
Text Encrypted (20 bytes)
8c 97 b7 d8 74 80 3d 9f 9f 62 2e 93 38 c7 d1 b de a4 21 80
Text Decrypted (16 bytes)
0 0 0 0 0 0 0 0 68 0 0 0 0 0 0 0 0 0 0 0
I read that it could be that the key is not generated correctly, but I'm working with a size of 16 and here is how I do it:
byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ], iv[ CryptoPP::AES::BLOCKSIZE ];
memset( key, 0x00, CryptoPP::AES::DEFAULT_KEYLENGTH );
memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE );
3) I also tried to cast the vector to char and send it like an string:
CryptoPP::StringSource ss( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16, true,
new CryptoPP::StreamTransformationFilter( decryptor,
new CryptoPP::StringSink( decryptedtext ) ) );
But again I get the same thing:
terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found
Please help, I have tried for days to figure out what's wrong. This is taking me too long and I can't find the solutions.
Does anyone have any idea on what might be happening?
Let me know if you need further details, code or anything.
Edit:
4) One more thing that I tried was (another way to construct the decrypter):
CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, iv );
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ) );
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16 );
stfDecryptor.MessageEnd();
But I get the same:
terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found
Edit2:
The vector is created with this line (the way the vector is fulled is a quite complicated to put it here because I am using a platform for network encoding) :
uint8_t* vector;
Edit3:
This is how I encrypt the vector.
CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( aesEncryption, iv );
CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink( ciphertext ) );
stfEncryptor.Put( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16 );
stfEncryptor.MessageEnd();
And after that I put the ciphertext again in the vector:
std::cout << std::endl << std::endl;
for(int i=0;i < 16; i++){
*(vector+ i + 4) = (ciphertext[i]) ;
}
terminate called after throwing an instance of CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found
This is easy enough to fix. Catch a Crypto++ InvalidCiphertext exception. See the InvalidCiphertext Class Reference.
-----
If I just send the vector without size:
CryptoPP::StringSource ss( vector+4, true, ...
I think this might be matching the following StringSource constructor, which means the length is true, which probable means 1. To add insult to injury the attached StreamTransformationFilter is being coerced into the bool pumpAll:
StringSource (const byte *string, size_t length, bool pumpAll, BufferedTransformation *attachment=NULL)
You should probably enable warnings to catch these sorts of things. -Wall -Wextra are good choices. (Nothing will help the coercion of the pointer to the bool, though. See, for example, No pointer conversion warning for "bool" in C/C++ on the GCC mailing list).
-----
If I send the vector and the size (it's really 20 but I
just want to decrypt the last 16 bytes):
...
CryptoPP::StringSource ss( vector+4, 16 , true, ...
I don't think this is going to work as expected. CBC mode is not a random access mode (but I may not be parsing things correctly):
CBC_Mode< AES >::Encryption enc;
cout << "Random access: " << enc.IsRandomAccess() << endl;
Results in:
Random access: 0
I'm fairly certain its not going to work at byte sizes. You might be able to do it by adding a layer above the decryptor, but you will have to manage the initialization vector, then decrypt a block, and finally return the offest into the block.
-----
You should probably do something like the following. Its not efficient because it copes a string to a vector, but I wanted to try an use vectors since you seem to be using them.
encrypted.CopyTo(f2) is where you put your socket code. Your code will not need encrypted.CopyTo(f2).
// Creates the memory block and zero's it.
SecByteBlock key(AES::DEFAULT_KEYLENGTH), iv(AES::BLOCKSIZE);
/////////////////////////////////////////////////////////////
string m1;
vector<char> v1;
m1 = "Now is the time for all good men to come to the aide of their country";
copy(m1.begin(), m1.end(), back_inserter(v1));
CBC_Mode< AES >::Encryption enc;
enc.SetKeyWithIV(key, key.size(), iv, iv.size());
ByteQueue encrypted;
StreamTransformationFilter f1(enc, new Redirector(encrypted));
f1.PutWord32((uint32_t)v1.size(), BIG_ENDIAN_ORDER);
f1.Put((const unsigned char *) &v1[0], v1.size());
f1.MessageEnd();
/////////////////////////////////////////////////////////////
string m2;
vector<char> v2;
CBC_Mode< AES >::Decryption dec;
dec.SetKeyWithIV(key, key.size(), iv, iv.size());
ByteQueue decrypted;
StreamTransformationFilter f2(dec, new Redirector(decrypted));
encrypted.CopyTo(f2);
f2.MessageEnd();
uint32_t len;
decrypted.GetWord32(len, BIG_ENDIAN_ORDER);
v2.resize(len);
decrypted.Get((unsigned char *) &v2[0], v2.size());
copy(v2.begin(), v2.end(), back_inserter(m2));
/////////////////////////////////////////////////////////////
cout << "Message: " << m1 << endl;
cout << "Decrypted: " << m2 << endl;
It produces the expected results:
$ ./cryptopp-test.exe
Message: Now is the time for all good men to come to the aide of their country
Decrypted: Now is the time for all good men to come to the aide of their country
-----
Regarding your use of vector + 4: once the cipher text is decrypted, you can seek in it, and do things like read a length from it. That's what this does:
// Ciphertext is already decrypted
uint32_t len;
decrypted.GetWord32(len, BIG_ENDIAN_ORDER);
If you just want to discard the value, then try:
decrypted.Skip(4);
But then you'll have to figure out how to size your vector.
-----
I'm working in a new protocol for secure communication
Secure communications usually provides both confidentiality and authenticity assurances. You seem to be missing the latter since CBC mode only provides confidentiality.
It might be a good idea to use something that already exists, like IPSec (preferred) or TLS (will do in a pinch). Also, key agreement is going to be a pain point. Its another reason to consider using something like IPSec or TLS.
If you can't use something already available, then you should use an Authenticated Encryption mode so you get confidentiality and authenticity combined into a single mode. Try EAX, GCM or CCM mode.
If you do, then then change is fairly trivial:
EAX< AES >::Encryption enc;
enc.SetKeyWithIV(key, key.size(), iv, iv.size());
ByteQueue encrypted;
AuthenticatedEncryptionFilter f1(enc, new Redirector(encrypted));
And:
EAX< AES >::Decryption dec;
dec.SetKeyWithIV(key, key.size(), iv, iv.size());
ByteQueue decrypted;
AuthenticatedDecryptionFilter f2(dec, new Redirector(decrypted));
-----
You can print the cipher text by copying it to an encoder. Copying is non-destructive (unlike moving it with TransferTo):
string encoded;
HexEncoder encoder(new StringSink(encoded));
encrypted.CopyTo(encoder);
encoder.MessageEnd();
Fox CBC mode (less secure):
Encrypted: 7F9FFCAB00704EC79BB5F19C48FE7C668033B16F52E7E00671A38A06F4A7426E7FE31
95CA6A83C7414A76C250B42E63143C93E7A6B97B6304C5782DE3E62BD545706A9F62CD7AD57BC374
19B7510EBED
For EAX mode (more secure):
Encrypted: B75347EB75DF8E1F0424979E91CEECD455F5727B506A8AA932AF07E1DF6A7B037A245
FEC7A2270BFAB8110226769E1C0A12E95C455E9C714AF28DA330A2B01B3F2D541D4E68276193C018
7BA0246166AD26624E848EC8330D3
The extra bytes in EAX mode are the authentication tag. Its used to detect accidental and malicious tampering.
The problem was that I was encoding with padding and trying to decrypt it without. So I add not only in the encryption but in the descryption that it should work without padding.
Creation of the Key and IV:
CBC_Mode< AES >::Encryption encryptor;
encryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );
Encryption:
CryptoPP::StringSource ss( vector + 4 , 16, true,
new CryptoPP::StreamTransformationFilter( encryptor,
new CryptoPP::StringSink( ciphertext ),
CryptoPP::StreamTransformationFilter::NO_PADDING
) // StreamTransformationFilter
); // StringSource
Decryption:
CBC_Mode< AES >::Decryption decryptor;
decryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );
CryptoPP::StringSource ss( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16, true,
new CryptoPP::StreamTransformationFilter( decryptor,
new CryptoPP::StringSink( decryptedtext ), CryptoPP::StreamTransformationFilter::NO_PADDING) );
I did this because I don't want to work with padding anymore.
I have c++ code that encrypts a string as a plaintext using AES_CFB and generates a same size ciphertext, but the problem is the data type of input and output, So could anyone help me to let it encrypts an unsigned int number and generates unsigned int number ciphertext withe keeping the same length for the plaintext and chipertext (length of bits ).
string ENCRYPTOR(const std::string& PlainText)
{
byte key[16]= "1234ff";// byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ];
byte iv[16]= "123456";//byte iv[ CryptoPP::AES::BLOCKSIZE ];
std::string CipherText;
// Encryptor
CryptoPP::CFB_Mode< CryptoPP::AES >::Encryption encryptor( key, sizeof(key), iv);
// Encryption
CryptoPP::StringSource( PlainText, true,
new CryptoPP::StreamTransformationFilter( encryptor,
new CryptoPP::StringSink( CipherText ) ) );
return (CipherText);
}
string DECRYPTOR(const string& CipherText)
{
byte key[16]= "1234ff";
byte iv[16]= "123456";
std::string RecoveredText;
// Decryptor
CryptoPP::CFB_Mode< CryptoPP::AES >::Decryption decryptor( key, sizeof(key), iv );
// Decryption
CryptoPP::StringSource( CipherText, true,
new CryptoPP::StreamTransformationFilter( decryptor,
new CryptoPP::StringSink( RecoveredText ) ) );
return (RecoveredText);
}
int main()
{
string ciphertext;
string plaintext = "3555";
ciphertext= ENCRYPTOR(plaintext);
string retrivdat = DECRYPTOR(ciphertext);
cout<<"The plaintext data is: "<<plaintext<<endl;
cout<<"The ciphertextdata is: "<<ciphertext<<endl;
Coot<<"The retrieved data is: "<<retrivdat<<end;
return 0;
}
The output is
The plaintext data is: 3555
The chepertext data is: ï¥R_
The retrieved data is: 3555
Encrypt unsigned int value in form of bits stream by AES_CFB mode
Igor and Owlstead raised some valid points about size of integers and endianess. The easiest solution to avoid them is probably encode the integer as a string:
unsigned int n = ...;
ostringstream oss;
oss << n;
string plainText = oss.str();
Later, you can convert it back with:
string recovered = ...;
istringstream iss(recovered);
unsigned int n;
iss >> n;
byte key[16]= "1234ff";// byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ];
byte iv[16]= "123456";//byte iv[ CryptoPP::AES::BLOCKSIZE ];
Your key and IV are too small. You should be getting compiler warnings because of it. AES::DEFAULT_KEYLENGTH is 16, so you need at least 16 characters for the key. AES::BLOCKSIZE is 16, so you need at least 16 characters for the initialization vector.
If the code above happens to work, then its purely because of luck. You should probably visit CFB Mode on the Crypto++ wiki. It has a working example.
Alternately, use PBKDF to stretch the short key and short IV. You can find an example at Crypto++ pbkdf2 output is different than Rfc2898DeriveBytes (C#) and crypto.pbkdf2 (JavaScript) on Stack Overflow.
The chepertext data is: ï¥R_
You can make this printable with:
string encoded;
HexEncoder hexer(new StringSink(encoded));
hexer.Put((byte*)cipherText.data(), cipherText.size());
hexer.MessageEnd();
cout << encoded << endl;
Alternately, you can use the following (with pipelines):
string encoded;
StringSource ss(cipherText, true,
new HexEncoder(
new StringSink(encoded)));
cout << encoded << endl;
HexEncoder and HexDecoder are discussed on the Crypto++ wiki, too.
So you can:
encode the number into the minimum number of x bytes, for instance using an unsigned big endian number
encrypt with CFB, resulting in the same number of x bytes
decrypt the number
decode the number from the resulting x bytes (using the same encoding scheme of course)
If you want to see the ciphertext as number you'll have to decode the ciphertext as if it was a (signed or unsigned) number.
Note that you will still have to deal with the uniqueness of the IV. If you need to store the IV then there will be significant overhead.