I am using the AES cipher with Crypto++. I have a key that recorded in the file during encryption. I extract key from file to string and try to use for decrypt.
Is there any way i can convert the string that contains the key string s1 to CryptoPP::byte? There fragment of code.
encoded.clear();
StringSource(key, sizeof(key), true,
new HexEncoder(
new StringSink(encoded)
));
ofstream fout1("key.txt");
fout1 << encoded;
fout1.close();
string s1;
ifstream TextFile1("key.txt");
while (!TextFile1.eof())
{
if (TextFile1.eof())
break;
TextFile1 >> s1;
}
I have a key that recorded in the file during encryption. I extract key from file to string and try to use for decrypt.
It sounds like you have a file structured similar to below:
[...key...][...iv...][...encrypted data...]
It is a bit unusual to store a key like that, but here is how it goes... Below I assume you are using AES-128 in CTR mode. That means [...key...] is 16 bytes, and [...iv...] is 16 bytes. The remainder is encrypted data.
I generate the sample file with:
$ head -c 128 < /dev/urandom > message.enc
$ hexdump -C message.enc
00000000 17 44 79 6b e6 96 ff d0 9e 3e 8c c4 fe 57 56 a2 |.Dyk.....>...WV.|
00000010 bb 59 9c a6 fb ab 73 de a7 a9 4a 22 14 6e c4 af |.Y....s...J".n..|
00000020 31 13 04 4d f2 79 f8 7c 7a 0b 16 2c bd be 6e 4c |1..M.y.|z..,..nL|
00000030 b6 61 0a 6c 33 d3 f0 73 25 44 ec f5 cd f5 cd da |.a.l3..s%D......|
00000040 3d 13 72 98 65 19 e1 c5 f8 49 1e 07 c7 dc ac b7 |=.r.e....I......|
00000050 ce 03 d1 90 94 08 aa 9d a0 8b b0 cd ff 9c b9 67 |...............g|
00000060 8a 2c 6f d9 7e fa d2 07 0f a0 48 99 57 77 2b d1 |.,o.~.....H.Ww+.|
00000070 c7 28 2a bc 80 22 21 fb 4a ba cb b2 0e b6 2c ff |.(*.."!.J.....,.|
The key is 17 44 ... 56 a2. The iv is bb 59 ... c4 af. The encrypted data starts at 31 13 04 4d....
And the program:
$ cat test.cxx
#include "filters.h"
#include "files.h"
#include "modes.h"
#include "aes.h"
#include "hex.h"
#include <iostream>
int main()
{
using namespace CryptoPP;
// Key and iv are stored at the head of the file
SecByteBlock key(16), iv(16);
FileSource fs("message.enc", false /* DO NOT Pump All */);
// Attach new filter
ArraySink ak(key, key.size());
fs.Detach(new Redirector(ak));
fs.Pump(16); // Pump first 16 bytes
// Attach new filter
ArraySink av(iv, iv.size());
fs.Detach(new Redirector(av));
fs.Pump(16); // Pump next 16 bytes
CTR_Mode<AES>::Decryption decryptor;
decryptor.SetKeyWithIV(key, key.size(), iv, iv.size());
// Detach previously attached filter, attach new filter
ByteQueue queue;
fs.Detach(new StreamTransformationFilter(decryptor, new Redirector(queue)));
fs.PumpAll(); // Pump remainder of bytes
std::cout << "Key: ";
StringSource(key, key.size(), true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
std::cout << "IV: ";
StringSource(iv, iv.size(), true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
std::cout << "Message: ";
HexEncoder hex(new FileSink(std::cout));
queue.TransferTo(hex);
std::cout << std::endl;
return 0;
}
You can call either Attach or Detach to attach a new filter. Both attach a new filter. The difference is, Attach returns the old filter and you have to free it. Detach deletes the old filter for you.
Finally:
$ g++ -Wall -I . test.cxx ./libcryptopp.a -o test.exe
$ ./test.exe
Key: 1744796BE696FFD09E3E8CC4FE5756A2
IV: BB599CA6FBAB73DEA7A94A22146EC4AF
Message: 84F6DC079CA04BDFACB645CB11CC2F828573F1841B1B9267CB296B6A977BE19D68B05FA
AF41AB73498F45629EE050B132174A2798C12C29A7033ADD1999BECD00B101F2616112D7E6968EA0
A1BE159CD0EE43549BA6534C8D4AB8F5E7D9E3E44
Unrelated to Crypto++, you usually want to avoid this:
while (!TextFile1.eof())
{
if (TextFile1.eof())
break;
TextFile1 >> s1;
}
I believe it is usually better to follow Why is iostream::eof inside a loop condition considered wrong? (I even think TextFile1 >> s1; should be in between the TextFile1.eof() checks. You don't reach eof() until you try to perform a read).
Related
I have trouble creating signatures with OpenSSL on a Raspberry Pi. RSA, ECDSA and EdDSA fail. Here is a small example:
#pragma once
#include <vector>
#include <string>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
void main()
{
// message to be sign
std::string msg_str = "my messsage";
std::vector<uint8_t> msg(msg_str.begin(), msg_str.end());
// generate a key pair
EVP_PKEY* key = EVP_PKEY_Q_keygen(nullptr, nullptr, "RSA", 2048);
// signature
std::vector<uint8_t> sig_out(1024); // reserve some memory
// signing process
EVP_MD_CTX* md_ctx(EVP_MD_CTX_new());
size_t out_len = 0; // final signature length (calculated by openssl)
if (EVP_DigestSignInit(md_ctx, nullptr, EVP_sha1(), nullptr, key) == 0)
{
std::cout << "EVP_DigestSignInit error: " << ERR_error_string(ERR_get_error(), NULL) << std::endl;
}
if (EVP_DigestSign(md_ctx, sig_out.data(), &out_len, msg.data(), msg.size()) == 0)
{
std::cout << "EVP_DigestSign error: " << ERR_error_string(ERR_get_error(), NULL) << std::endl;
}
else
{
sig_out.resize(out_len);
std::cout << "signature length: " << sig_out .size() << " data: " << std::endl;
for (size_t i = 0; i < out_len; i++)
{
printf("%.2X ", sig_out[i]);
}
}
EVP_PKEY_free(key);
EVP_MD_CTX_free(md_ctx);
return;
}
On my desktop PC all signatures work but not on the Raspberry Pi. Here is the screen output:
Windows 10, AMD CPU, x64 system, OpenSSL 3.0.0:
signature length: 256 data:
59 A9 45 F5 2B 97 51 F5 53 A8 AE 17 16 7A 26 28
F5 68 3F 1F 3D B2 05 4F 0E 28 AF F2 F5 0E DA FF
37 71 50 DD DA E1 DE F0 91 05 0A 07 79 30 00 03
A4 1E F5 60 F5 7E 47 97 EF 88 9C 27 70 CE 64 63
0B 6C 2E 50 7B D7 89 48 B6 73 44 AD 7A 02 EA 49
BC D3 95 67 B8 E6 D9 E4 A1 4F 2B E8 F4 5C F8 73
B5 53 B0 A5 FB BB 7A 81 1C 25 23 6F 30 D8 8F D8
EC 9E 02 00 C2 0D 7C 9C 23 66 D7 44 62 FF 51 1A
94 3F 6F FB D7 B2 C5 2B A4 03 09 E5 10 44 D4 AE
A2 69 F3 EB 31 1B CB 2A 14 1D 76 CD 11 09 B9 76
99 59 42 5A 74 3D 14 98 B7 87 FD 98 16 17 AC 9E
DA 55 82 0B 93 3D 24 28 4F 09 EB EA AE 82 77 47
B2 E2 C8 1E 62 FF E4 90 E6 18 E8 88 94 B4 F0 AF
DF A2 2B D7 79 32 BD C5 0F B1 03 36 B6 D8 44 9A
FA DB 02 EB 7D FE D5 D7 15 34 77 72 4D 4E 44 A8
E7 DA D9 2B 49 80 43 58 1F AA F4 1D 27 80 1C EE
Raspberry Pi, ARM CPU, Debian (bullseye), x64 system, OpenSSL 3.1.0:
EVP_DigestSign error: error:1C8000B3:Provider routines::invalid signature size
does anyone have any idea what the problem may be? I am now a bit desperate.
best regards,
SBond
solved!
this error occurs (only on ARM) when out_len is smaller than the final signature length. I need to change the code as follows:
from
if (EVP_DigestSign(md_ctx, sig_out.data(), &out_len, msg.data(), msg.size()) == 0)
{
std::cout << "EVP_DigestSign error: " << ERR_error_string(ERR_get_error(), NULL) << std::endl;
}
to
EVP_DigestSign(md_ctx, nullptr, &out_len, msg.data(), msg.size()); // calculates the final length (does not perform signature; therfore a fast process)
sig_out.resize(out_len);
if (EVP_DigestSign(md_ctx, sig_out.data(), &out_len, msg.data(), msg.size()) == 0)
{
std::cout << "EVP_DigestSign error: " << ERR_error_string(ERR_get_error(), NULL) << std::endl;
}
now I'm happy :)
I'm trying to sign a file with my private key using the following function:
void Signer::SignFile(const std::string& file)
{
RSASS<PSSR, Whirlpool>::Signer signer(rsaPrivate);
// Encrypt and write
FileSource ss1(file.c_str(), true,
new SignerFilter(rng, signer,
new FileSink(file.c_str(), true)
, true));
}
As an outcome my target file gets overwritten with the signature. Why does that happen? Obviously I'd like to append it to the file, that's why I also added an extra "true" parameter for the "putMessage".
FileSource ss1(file.c_str(), true,
new SignerFilter(rng, signer,
new FileSink(file.c_str(), true), true));
I [personally] have never seen someone use the same file as a source and sink. Usually the file data and signature are separate like with a detached signature.
It looks like one file/two streams is implementation defined: C++ read and write to same file using different streams. I guess you should expect seemingly random results on different platforms.
As an outcome my target file gets overwritten with the signature. Why does that happen?
The FileSink opens with std::ios_base::tunc. Also see FileSink on the Crypto++ wiki.
You should probably do something like this. It saves the signature to an intermediate ByteQueue, and then writes the queue to the file once the file is closed.
#include "cryptlib.h"
#include "filters.h"
#include "osrng.h"
#include "files.h"
#include "pssr.h"
#include "rsa.h"
#include "whrlpool.h"
#include <iostream>
#include <fstream>
#include <string>
int main(int argc, char* argv[])
{
using namespace CryptoPP;
AutoSeededRandomPool prng;
std::string fname("test.bin");
///// Create test message /////
{
std::string msg("Yoda said, Do or do not. There is no try.");
std::ofstream out(fname.c_str());
out.write(msg.data(), msg.size());
}
///// Generate a key /////
RSASS<PSSR, Whirlpool>::Signer signer;
signer.AccessKey().GenerateRandomWithKeySize(prng, 2048);
///// Sign the message /////
ByteQueue queue;
{
FileSource source(fname.c_str(), true,
new SignerFilter(prng, signer,
new Redirector(queue)));
}
///// Append the signature /////
{
std::ofstream out(fname.c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::app);
queue.TransferTo(FileSink(out).Ref());
}
///// Create a verifier /////
RSASS<PSSR, Whirlpool>::Verifier verifier(signer);
///// Verify the message /////
{
FileSource source(fname.c_str(), true,
new SignatureVerificationFilter(verifier, NULLPTR,
SignatureVerificationFilter::THROW_EXCEPTION));
}
std::cout << "Verified signature on message" << std::endl;
return 0;
}
It does not crash and it appends the message as expected:
$ ./test.exe
Verified signature on message
$ hexdump -C test.bin
00000000 59 6f 64 61 20 73 61 69 64 2c 20 44 6f 20 6f 72 |Yoda said, Do or|
00000010 20 64 6f 20 6e 6f 74 2e 20 54 68 65 72 65 20 69 | do not. There i|
00000020 73 20 6e 6f 20 74 72 79 2e 19 f2 1c 8f f9 cb 2f |s no try......./|
00000030 f2 38 9f a8 3b a9 0b 8b 62 25 56 a8 ea 81 7e 60 |.8..;...b%V...~`|
00000040 22 55 38 ce 79 7f 32 95 a5 1a 75 c1 80 ad b2 c2 |"U8.y.2...u.....|
00000050 6f ce a5 f7 bd 4b d3 3f e4 b3 69 00 21 60 d7 09 |o....K.?..i.!`..|
00000060 a8 71 9b 5f 41 d6 66 b1 80 f1 de 00 26 19 34 01 |.q._A.f.....&.4.|
00000070 b3 65 1b 78 e2 32 71 be bc 07 25 78 36 6b 56 4e |.e.x.2q...%x6kVN|
00000080 26 4e 12 9e a8 bb 72 86 ee 0d 70 b2 f1 bd a3 2c |&N....r...p....,|
00000090 14 fd 12 61 35 98 4a 80 9f ee 3c 31 d3 70 26 0f |...a5.J...<1.p&.|
000000a0 73 a0 5d 36 ef 96 56 65 f8 ac 3a fb 44 c3 04 76 |s.]6..Ve..:.D..v|
000000b0 e5 2f ae 92 84 be 40 34 f6 4b b8 84 aa bd 67 74 |./....#4.K....gt|
000000c0 05 43 91 d2 e6 b1 50 dd 6d 64 47 cc 3e 3c 3a 9d |.C....P.mdG.><:.|
000000d0 67 ff 4f 38 c1 a5 a6 d5 92 45 bc 2d ff 96 30 3a |g.O8.....E.-..0:|
000000e0 1d 3a 42 4f 8c 13 2d 4c 3f e9 ad 08 a6 b3 5e fa |.:BO..-L?.....^.|
000000f0 46 08 24 17 43 ce ed ec f7 1a 38 62 e7 bf 42 93 |F.$.C.....8b..B.|
00000100 84 44 b6 05 22 9e e3 bd 80 a6 08 b0 34 d0 a4 89 |.D..".......4...|
00000110 78 48 20 7f 7b 33 1c 51 9d 48 b5 b7 f7 de 2f dd |xH .{3.Q.H..../.|
00000120 d7 74 7b af 04 cd 92 fc 1c |.t{......|
I was not able to get this to work. I'm pretty sure it is a dead end.
std::fstream inout(fname.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary | std::ios_base::app);
FileSource fs(inout, true,
new SignerFilter(prng, signer,
new FileSink(inout), true));
Obviously I'd like to append it to the file, that's why ...
Also, because you are using PSSR, you don't need the original message. PSSR is "probabilistic signature scheme with recovery". The message is include in the signature with a mask function.
You would need the original message with a SSA scheme. SSA is "signature scheme with appendix". In a SSA scheme you need to provide both the original message and the signature.
(From the comments) Here is an example that uses a Signature Scheme with Appendix (SSA). It also uses std::iftream and std::ofstream directly so FileSource and FileSink work as you expect. std::ofstream includes std::ios_base::app so the signature is appended.
#include "cryptlib.h"
#include "filters.h"
#include "osrng.h"
#include "files.h"
#include "oaep.h"
#include "rsa.h"
#include "sha.h"
#include <iostream>
#include <fstream>
#include <string>
int main(int argc, char* argv[])
{
using namespace CryptoPP;
AutoSeededRandomPool prng;
std::string fname("test.bin");
///// Create test message /////
{
std::string msg("Yoda said, Do or do not. There is no try.");
std::ofstream out(fname.c_str());
out.write(msg.data(), msg.size());
}
///// Generate a key /////
RSASS<PKCS1v15, SHA256>::Signer signer;
signer.AccessKey().GenerateRandomWithKeySize(prng, 2048);
{
///// Create fstreams for input and output /////
std::ifstream fin(fname.c_str(), std::ios_base::in | std::ios_base::binary);
std::ofstream fout(fname.c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::app);
///// Sign the message /////
FileSource source(fin, true,
new SignerFilter(prng, signer,
new FileSink(fout)));
}
///// Create a verifier /////
RSASS<PKCS1v15, SHA256>::Verifier verifier(signer);
///// Verify the message /////
{
FileSource source(fname.c_str(), true,
new SignatureVerificationFilter(verifier, NULLPTR,
SignatureVerificationFilter::THROW_EXCEPTION));
}
std::cout << "Verified signature on message" << std::endl;
return 0;
}
And then:
$ ./test.exe
Verified signature on message
$ hexdump -C test.bin
00000000 59 6f 64 61 20 73 61 69 64 2c 20 44 6f 20 6f 72 |Yoda said, Do or|
00000010 20 64 6f 20 6e 6f 74 2e 20 54 68 65 72 65 20 69 | do not. There i|
00000020 73 20 6e 6f 20 74 72 79 2e c7 b3 6f 84 1d fd bf |s no try...o....|
00000030 c7 c8 38 7c 89 b1 f3 42 ee 5e f8 10 de a8 01 7f |..8|...B.^......|
00000040 7f a5 24 3d 27 7e 55 16 bc 80 8b 21 21 75 3d ed |..$='~U....!!u=.|
00000050 41 05 84 b1 3d bf d3 ae 3a 2f a8 81 7a e7 e4 ae |A...=...:/..z...|
00000060 50 d7 9b 25 04 17 a6 a3 1d 12 e2 8e cd 7a 02 42 |P..%.........z.B|
00000070 91 c0 d7 fc 43 09 94 a2 66 d9 67 95 55 5e dc 8c |....C...f.g.U^..|
00000080 eb bc 20 af e8 5c d4 63 05 d4 2c 48 57 6d f1 fe |.. ..\.c..,HWm..|
00000090 26 16 80 c3 41 11 58 8e 8d b0 cb 48 95 b9 ed 94 |&...A.X....H....|
000000a0 84 cc 86 0f a4 7e a3 6a ff 0d 0d 24 17 82 13 94 |.....~.j...$....|
000000b0 54 cb 8a ca 04 1e 65 18 c3 ab a2 3f 4d 44 de 42 |T.....e....?MD.B|
000000c0 32 07 29 e4 95 83 cc ff 39 85 08 bf d5 61 46 db |2.).....9....aF.|
000000d0 e0 96 d6 69 25 b9 ce 1e 3e bc 63 81 e5 16 bd 12 |...i%...>.c.....|
000000e0 a0 78 02 19 60 96 80 36 7d a5 79 be 0f 45 54 f4 |.x..`..6}.y..ET.|
000000f0 92 af f0 d8 74 65 7d 45 98 c7 bb 7f 6e 9b e3 cd |....te}E....n...|
00000100 c0 60 91 0f 78 aa 7c 77 a7 f5 4e 7d 6e ed e1 4c |.`..x.|w..N}n..L|
00000110 8e 5e 96 ac cd 30 16 e0 2d be 9e 2d 68 d4 25 46 |.^...0..-..-h.%F|
00000120 86 77 87 be 68 ef 06 26 55 |.w..h..&U|
I am doing the RSA signature verify, I will always get this error
terminate called after throwing an instance of 'CryptoPP::BERDecodeErr'
what(): BER decode error
Aborted (core dumped)
I don't understand why I get this error, I never call the BERDecode before.
This my code segment, I had tried to trace the error it happens in the second line of the code:
FileSource pubFile(publicKey_file.c_str(), true, new HexDecoder);
RSASS<PSSR, SHA1>::Verifier pub(pubFile);
FileSource signatureFile(sig_file.c_str(), true, new HexDecoder);
if (signatureFile.MaxRetrievable() != pub.SignatureLength()) {
return false;
}
SecByteBlock signature(pub.SignatureLength());
signatureFile.Get(signature, signature.size());
bool result = pub.VerifyMessage((const byte*)messages_file.c_str(),
messages_file.length(), signature, signature.size());
return result;
I could define what's going wrong, hope to get some help. Thank you.
You are trying to load a public key, which needs to be parsed before you can do any verification. For this you give the public key data to the Verifier constructor. This constructor tries to parse the public keys.
Now public keys are generally encoded data structures. These data structures are described by a data description language called ASN.1 or Abstract Syntax Notation 1 and encoded using a scheme called BER, the Basic Encoding Rules for ASN.1.
So what happens is that the constructor tries to read the public key and therefore calls BERDecode so make sense of the input. Unfortunately the input is likely not binary BER encoded at all.
So to resolve this you need to either use additional calls in your application to convert to BER or public key object, or you need to convert your key to BER. If the key is ASCII armored (PEM format) then you can use:
$ openssl rsa -pubin -inform PEM -in <filename of key in PEM format> -outform DER -out <filename of key in DER format>
If this doesn't work please include the public key into your question so we can check why it doesn't parse.
According to:
$ echo "30819D300D06092A864886F70D010101050003818B0030818702818100DD2CED773D6F9A
E4A63F2DAEEF9019C056D4A35F338764FAAE85EDCBFB13FC9E53F13CEFADEF58C65B501C3D2D13DC
DE65282B7781C45259065F991C4184E6E6DEDB3087472B4AC4BDD74FDF4D3C893257D68722326516
53A4882588C61C0F4FB096C5906F2F88E0480513A2B1BA6418869DB01C9D9A2FB4BECADE54658D55
2F020111" | xxd -r -p > key.ber
And then:
$ dumpasn1 key.ber
0 157: SEQUENCE {
3 13: SEQUENCE {
5 9: OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
16 0: NULL
: }
18 139: BIT STRING, encapsulates {
22 135: SEQUENCE {
25 129: INTEGER
: 00 DD 2C ED 77 3D 6F 9A E4 A6 3F 2D AE EF 90 19
: C0 56 D4 A3 5F 33 87 64 FA AE 85 ED CB FB 13 FC
: 9E 53 F1 3C EF AD EF 58 C6 5B 50 1C 3D 2D 13 DC
: DE 65 28 2B 77 81 C4 52 59 06 5F 99 1C 41 84 E6
: E6 DE DB 30 87 47 2B 4A C4 BD D7 4F DF 4D 3C 89
: 32 57 D6 87 22 32 65 16 53 A4 88 25 88 C6 1C 0F
: 4F B0 96 C5 90 6F 2F 88 E0 48 05 13 A2 B1 BA 64
: 18 86 9D B0 1C 9D 9A 2F B4 BE CA DE 54 65 8D 55
: 2F
157 1: INTEGER 17
: }
: }
: }
You have a subjectPublicKeyInfo. I believe all you need to do is call Load, but it assumes you have a well-formed key:
RSASS<PSSR, SHA1>::Verifier pub;
pub.AccessKey().Load(pubFile);
Here's the whole program:
$ cat test.cxx
#include "cryptlib.h"
#include "filters.h"
#include "pssr.h"
#include "sha.h"
#include "rsa.h"
#include "hex.h"
#include <string>
#include <iostream>
int main()
{
using namespace CryptoPP;
std::string encodedKey = "30819D300D06092A864886F70D010101050003818B00"
"30818702818100DD2CED773D6F9AE4A63F2DAEEF9019C056D4A35F338764FAAE8"
"5EDCBFB13FC9E53F13CEFADEF58C65B501C3D2D13DCDE65282B7781C45259065F"
"991C4184E6E6DEDB3087472B4AC4BDD74FDF4D3C893257D6872232651653A4882"
"588C61C0F4FB096C5906F2F88E0480513A2B1BA6418869DB01C9D9A2FB4BECADE"
"54658D552F020111";
StringSource hexKey(encodedKey, true, new HexDecoder);
RSASS<PSSR, SHA1>::Verifier pub;
pub.AccessKey().Load(hexKey);
std::cout << "n: " << std::hex << pub.AccessKey().GetModulus() << std::endl;
std::cout << "e: " << std::dec << pub.AccessKey().GetPublicExponent() << std::endl;
return 0;
}
And finally:
$ ./test.exe
n: dd2ced773d6f9ae4a63f2daeef9019c056d4a35f338764faae85edcbfb13fc9e53f13cefadef5
8c65b501c3d2d13dcde65282b7781c45259065f991c4184e6e6dedb3087472b4ac4bdd74fdf4d3c8
93257d6872232651653a4882588c61c0f4fb096c5906f2f88e0480513a2b1ba6418869db01c9d9a2
fb4becade54658d552fh
e: 17.
This may a bit too restrictive:
if (signatureFile.MaxRetrievable() != pub.SignatureLength()) {
return false;
}
The actual signature length may be a tad bit shorter then MaxSignatureLength(), depending on how many leading 0's the exponentiation produces.
You might consider following one of the wiki examples. For example, from RSA Signature Schemes:
...
// Signing
RSASS<PSSR, SHA1>::Signer signer(privateKey);
RSASS<PSSR, SHA1>::Verifier verifier(publicKey);
// Setup
byte message[] = "RSA-PSSR Test";
size_t messageLen = sizeof(message);
////////////////////////////////////////////////
// Sign and Encode
SecByteBlock signature(signer.MaxSignatureLength(messageLen));
size_t signatureLen = signer.SignMessageWithRecovery(rng, message, messageLen, NULL, 0, signature);
// Resize now we know the true size of the signature
signature.resize(signatureLen);
////////////////////////////////////////////////
// Verify and Recover
SecByteBlock recovered(
verifier.MaxRecoverableLengthFromSignatureLength(signatureLen)
);
DecodingResult result = verifier.RecoverMessage(recovered, NULL, 0, signature, signatureLen);
if (!result.isValidCoding) {
throw Exception(Exception::OTHER_ERROR, "Invalid Signature");
}
////////////////////////////////////////////////
// Use recovered message
// MaxSignatureLength is likely larger than messageLength
recovered.resize(result.messageLength);
...
I'm trying to load an RSA private key from a std::string that contains the private key in PEM format, like this:
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAK8Q+ToR4tWGshaKYRHKJ3ZmMUF6jjwCS/u1A8v1tFbQiVpBlxYB
paNcT2ENEXBGdmWqr8VwSl0NBIKyq4p0rhsCAQMCQHS1+3wL7I5ZzA8G62Exb6RE
INZRtCgBh/0jV91OeDnfQUc07SE6vs31J8m7qw/rxeB3E9h6oGi9IVRebVO+9zsC
IQDWb//KAzrSOo0P0yktnY57UF9Q3Y26rulWI6LqpsxZDwIhAND/cmlg7rUz34Pf
SmM61lJEmMEjKp8RB/xgghzmCeI1AiEAjvVVMVd8jCcItTdwyRO0UjWU4JOz0cnw
5BfB8cSIO18CIQCLVPbw60nOIpUClNxCJzmMLbsrbMcUtgVS6wFomVvsIwIhAK+A
YqT6WwsMW2On5l9di+RPzhDT1QdGyTI5eFNS+GxY
-----END RSA PRIVATE KEY-----
And I wonder if anyone can help me to use this key instead of generating a random with the following statement.
CryptoPP::RSA::PrivateKey rsaPrivate;
rsaPrivate.GenerateRandomWithKeySize (rnd, 512);
The key is PEM encoded. You need to strip the PEM header and footer, then convert from Base64 back to DER/BER, and finally use Crypto++'s BERDecodePrivateKey.
There's some reading on the subject at the Crypto++ wiki under Keys and Formats. Below is the code to perform the conversion (I don't believe Stack Overflow has a working example of it in Crypto++).
string RSA_PRIV_KEY =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIBOgIBAAJBAK8Q+ToR4tWGshaKYRHKJ3ZmMUF6jjwCS/u1A8v1tFbQiVpBlxYB\n"
"paNcT2ENEXBGdmWqr8VwSl0NBIKyq4p0rhsCAQMCQHS1+3wL7I5ZzA8G62Exb6RE\n"
"INZRtCgBh/0jV91OeDnfQUc07SE6vs31J8m7qw/rxeB3E9h6oGi9IVRebVO+9zsC\n"
"IQDWb//KAzrSOo0P0yktnY57UF9Q3Y26rulWI6LqpsxZDwIhAND/cmlg7rUz34Pf\n"
"SmM61lJEmMEjKp8RB/xgghzmCeI1AiEAjvVVMVd8jCcItTdwyRO0UjWU4JOz0cnw\n"
"5BfB8cSIO18CIQCLVPbw60nOIpUClNxCJzmMLbsrbMcUtgVS6wFomVvsIwIhAK+A\n"
"YqT6WwsMW2On5l9di+RPzhDT1QdGyTI5eFNS+GxY\n"
"-----END RSA PRIVATE KEY-----";
static string HEADER = "-----BEGIN RSA PRIVATE KEY-----";
static string FOOTER = "-----END RSA PRIVATE KEY-----";
size_t pos1, pos2;
pos1 = RSA_PRIV_KEY.find(HEADER);
if(pos1 == string::npos)
throw runtime_error("PEM header not found");
pos2 = RSA_PRIV_KEY.find(FOOTER, pos1+1);
if(pos2 == string::npos)
throw runtime_error("PEM footer not found");
// Start position and length
pos1 = pos1 + HEADER.length();
pos2 = pos2 - pos1;
string keystr = RSA_PRIV_KEY.substr(pos1, pos2);
// Base64 decode, place in a ByteQueue
ByteQueue queue;
Base64Decoder decoder;
decoder.Attach(new Redirector(queue));
decoder.Put((const byte*)keystr.data(), keystr.length());
decoder.MessageEnd();
// Write to file for inspection
FileSink fs("decoded-key.der");
queue.CopyTo(fs);
fs.MessageEnd();
try
{
CryptoPP::RSA::PrivateKey rsaPrivate;
rsaPrivate.BERDecodePrivateKey(queue, false /*paramsPresent*/, queue.MaxRetrievable());
// BERDecodePrivateKey is a void function. Here's the only check
// we have regarding the DER bytes consumed.
ASSERT(queue.IsEmpty());
}
catch (const Exception& ex)
{
cerr << ex.what() << endl;
exit (1);
}
After loading the key, you can validate it with:
AutoSeededRandomPool prng;
bool valid = rsaPrivate.Validate(prng, 3);
if(!valid)
cerr << "RSA private key is not valid" << endl;
And print it with:
cout << "N: " << rsaPrivate.GetModulus() << endl << endl;
cout << "E: " << rsaPrivate.GetPublicExponent() << endl << endl;
cout << "D: " << rsaPrivate.GetPrivateExponent() << endl << endl;
If the key is password protected, then Crypto++ cannot decode it. The library lacks the support to perform the decryption. In this case, you can convert it to BER/DER using the following OpenSSL command. Then you can use the key material with Crypto++.
openssl pkcs8 -nocrypt -in rsa-key.pem -inform PEM -topk8 -outform DER -out rsa-key.der
The sample program wrote the key to file with this:
FileSink fs("decoded-key.der");
queue.CopyTo(fs);
fs.MessageEnd();
The CopyTo leaves the bytes in the queue for use later. You can dump the file with an ASN.1 tool, like Gutmann's dumpasn1:
$ dumpasn1 decoded-key.der
0 314: SEQUENCE {
4 1: INTEGER 0
7 65: INTEGER
: 00 AF 10 F9 3A 11 E2 D5 86 B2 16 8A 61 11 CA 27
: 76 66 31 41 7A 8E 3C 02 4B FB B5 03 CB F5 B4 56
: D0 89 5A 41 97 16 01 A5 A3 5C 4F 61 0D 11 70 46
: 76 65 AA AF C5 70 4A 5D 0D 04 82 B2 AB 8A 74 AE
: 1B
74 1: INTEGER 3
77 64: INTEGER
: 74 B5 FB 7C 0B EC 8E 59 CC 0F 06 EB 61 31 6F A4
: 44 20 D6 51 B4 28 01 87 FD 23 57 DD 4E 78 39 DF
: 41 47 34 ED 21 3A BE CD F5 27 C9 BB AB 0F EB C5
: E0 77 13 D8 7A A0 68 BD 21 54 5E 6D 53 BE F7 3B
143 33: INTEGER
: 00 D6 6F FF CA 03 3A D2 3A 8D 0F D3 29 2D 9D 8E
: 7B 50 5F 50 DD 8D BA AE E9 56 23 A2 EA A6 CC 59
: 0F
178 33: INTEGER
: 00 D0 FF 72 69 60 EE B5 33 DF 83 DF 4A 63 3A D6
: 52 44 98 C1 23 2A 9F 11 07 FC 60 82 1C E6 09 E2
: 35
213 33: INTEGER
: 00 8E F5 55 31 57 7C 8C 27 08 B5 37 70 C9 13 B4
: 52 35 94 E0 93 B3 D1 C9 F0 E4 17 C1 F1 C4 88 3B
: 5F
248 33: INTEGER
: 00 8B 54 F6 F0 EB 49 CE 22 95 02 94 DC 42 27 39
: 8C 2D BB 2B 6C C7 14 B6 05 52 EB 01 68 99 5B EC
: 23
283 33: INTEGER
: 00 AF 80 62 A4 FA 5B 0B 0C 5B 63 A7 E6 5F 5D 8B
: E4 4F CE 10 D3 D5 07 46 C9 32 39 78 53 52 F8 6C
: 58
: }
0 warnings, 0 errors.
I apologise in advance for the n00bishness of asking this question, but I've been stuck for ages and I'm struggling to figure out what to do next. Essentially, I am trying to perform ElGamal encryption on some data. I have been given the public part of an ephemeral key pair and a second static key, as well as some data. If my understanding is correct, this is all I need to perform the encryption, but I'm struggling to figure out how using Crypto++.
I've looked endlessly for examples, but I can find literally zero on Google. Ohloh is less than helpful as I just get back endless pages of the cryptopp ElGamal source files, which I can't seem to be able to figure out (I'm relatively new to using Crypto++ and until about 3 days ago hadn't even heard of ElGamal).
The closest I've been able to find as an example comes from the CryptoPP package itself, which is as follows:
bool ValidateElGamal()
{
cout << "\nElGamal validation suite running...\n\n";
bool pass = true;
{
FileSource fc("TestData/elgc1024.dat", true, new HexDecoder);
ElGamalDecryptor privC(fc);
ElGamalEncryptor pubC(privC);
privC.AccessKey().Precompute();
ByteQueue queue;
privC.AccessKey().SavePrecomputation(queue);
privC.AccessKey().LoadPrecomputation(queue);
pass = CryptoSystemValidate(privC, pubC) && pass;
}
return pass;
}
However, this doesn't really seem to help me much as I'm unaware of how to plug in my already computed values. I am not sure if I'm struggling with my understanding of how Elgamal works (entirely possible) or if I'm just being an idiot when it comes to using what I've got with CryptoPP. Can anyone help point me in the right direction?
I have been given the public part of an ephemeral key pair and a second static key, as well as some data.
We can't really help you here because we know nothing about what is supposed to be done.
The ephemeral key pair is probably for simulating key exchange, and the static key is long term for signing the ephemeral exchange. Other than that, its anybody's guess as to what's going on.
Would you happen to know what the keys are? is the ephemeral key a Diffie-Hellman key and the static key an ElGamal signing key?
If my understanding is correct, this is all I need to perform the encryption, but I'm struggling to figure out how using Crypto++.
For the encryption example, I'm going to cheat a bit and use an RSA encryption example and port it to ElGamal. This is about as difficult as copy and paste because both RSA encryption and ElGamal encryption adhere to the the PK_Encryptor and PK_Decryptor interfaces. See the PK_Encryptor and PK_Decryptor classes for details. (And keep in mind, you might need an ElGamal or Nyberg-Rueppel (NR) signing example).
Crypto++ has a cryptosystem built on ElGamal. The cryptosystem will encrypt a large block of plain text under a symmetric key, and then encrypt the symmetric key under the ElGamal key. I'm not sure what standard it follows, though (likely IEEE's P1363). See SymmetricEncrypt and SymmetricDecrypt in elgamal.h.
The key size is artificially small so the program runs quickly. ElGamal is a discrete log problem, so its key size should be 2048-bits or higher in practice. 2048-bits is blessed by ECRYPT (Asia), ISO/IEC (Worldwide), NESSIE (Europe), and NIST (US).
If you need to save/persist/load the keys you generate, then see Keys and Formats on the Crypto++ wiki. The short answer is to call decryptor.Save() and decryptor.Load(); and stay away from the {BER|DER} encodings.
If you want, you can use a standard string rather than a SecByteBlock. The string will be easier if you are interested in printing stuff to the terminal via cout and friends.
Finally, there's now a page on the Crypto++ Wiki covering the topic with the source code for the program below. See Crypto++'s ElGamal Encryption.
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
#include <cryptopp/osrng.h>
using CryptoPP::AutoSeededRandomPool;
#include <cryptopp/secblock.h>
using CryptoPP::SecByteBlock;
#include <cryptopp/elgamal.h>
using CryptoPP::ElGamal;
using CryptoPP::ElGamalKeys;
#include <cryptopp/cryptlib.h>
using CryptoPP::DecodingResult;
int main(int argc, char* argv[])
{
////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;
cout << "Generating private key. This may take some time..." << endl;
ElGamal::Decryptor decryptor;
decryptor.AccessKey().GenerateRandomWithKeySize(rng, 512);
const ElGamalKeys::PrivateKey& privateKey = decryptor.AccessKey();
ElGamal::Encryptor encryptor(decryptor);
const PublicKey& publicKey = encryptor.AccessKey();
////////////////////////////////////////////////
// Secret to protect
static const int SECRET_SIZE = 16;
SecByteBlock plaintext( SECRET_SIZE );
memset( plaintext, 'A', SECRET_SIZE );
////////////////////////////////////////////////
// Encrypt
// Now that there is a concrete object, we can validate
assert( 0 != encryptor.FixedMaxPlaintextLength() );
assert( plaintext.size() <= encryptor.FixedMaxPlaintextLength() );
// Create cipher text space
size_t ecl = encryptor.CiphertextLength( plaintext.size() );
assert( 0 != ecl );
SecByteBlock ciphertext( ecl );
encryptor.Encrypt( rng, plaintext, plaintext.size(), ciphertext );
////////////////////////////////////////////////
// Decrypt
// Now that there is a concrete object, we can check sizes
assert( 0 != decryptor.FixedCiphertextLength() );
assert( ciphertext.size() <= decryptor.FixedCiphertextLength() );
// Create recovered text space
size_t dpl = decryptor.MaxPlaintextLength( ciphertext.size() );
assert( 0 != dpl );
SecByteBlock recovered( dpl );
DecodingResult result = decryptor.Decrypt( rng, ciphertext, ciphertext.size(), recovered );
// More sanity checks
assert( result.isValidCoding );
assert( result.messageLength <= decryptor.MaxPlaintextLength( ciphertext.size() ) );
// At this point, we can set the size of the recovered
// data. Until decryption occurs (successfully), we
// only know its maximum size
recovered.resize( result.messageLength );
// SecByteBlock is overloaded for proper results below
assert( plaintext == recovered );
// If the assert fires, we won't get this far.
if(plaintext == recovered)
cout << "Recovered plain text" << endl;
else
cout << "Failed to recover plain text" << endl;
return !(plaintext == recovered);
}
You can also create the Decryptor from a PrivateKey like so:
ElGamalKeys::PrivateKey k;
k.GenerateRandomWithKeySize(rng, 512);
ElGamal::Decryptor d(k);
...
And an Encryptor from a PublicKey:
ElGamalKeys::PublicKey pk;
privateKey.MakePublicKey(pk);
ElGamal::Encryptor e(pk);
You can save and load keys to and from disk as follows:
ElGamalKeys::PrivateKey privateKey1;
privateKey1.GenerateRandomWithKeySize(prng, 2048);
privateKey1.Save(FileSink("elgamal.der", true /*binary*/).Ref());
ElGamalKeys::PrivateKey privateKey2;
privateKey2.Load(FileSource("elgamal.der", true /*pump*/).Ref());
privateKey2.Validate(prng, 3);
ElGamal::Decryptor decryptor(privateKey2);
// ...
The keys are ASN.1 encoded, so you can dump them with something like Peter Gutmann's dumpasn1:
$ ./cryptopp-elgamal-keys.exe
Generating private key. This may take some time...
$ dumpasn1 elgamal.der
0 556: SEQUENCE {
4 257: INTEGER
: 00 C0 8F 5A 29 88 82 8C 88 7D 00 AE 08 F0 37 AC
: FA F3 6B FC 4D B2 EF 5D 65 92 FD 39 98 04 C7 6D
: 6D 74 F5 FA 84 8F 56 0C DD B4 96 B2 51 81 E3 A1
: 75 F6 BE 82 46 67 92 F2 B3 EC 41 00 70 5C 45 BF
: 40 A0 2C EC 15 49 AD 92 F1 3E 4D 06 E2 89 C6 5F
: 0A 5A 88 32 3D BD 66 59 12 A1 CB 15 B1 72 FE F3
: 2D 19 DD 07 DF A8 D6 4C B8 D0 AB 22 7C F2 79 4B
: 6D 23 CE 40 EC FB DF B8 68 A4 8E 52 A9 9B 22 F1
: [ Another 129 bytes skipped ]
265 1: INTEGER 3
268 257: INTEGER
: 00 BA 4D ED 20 E8 36 AC 01 F6 5C 9C DA 62 11 BB
: E9 71 D0 AB B7 E2 D3 61 37 E2 7B 5C B3 77 2C C9
: FC DE 43 70 AE AA 5A 3C 80 0A 2E B0 FA C9 18 E5
: 1C 72 86 46 96 E9 9A 44 08 FF 43 62 95 BE D7 37
: F8 99 16 59 7D FA 3A 73 DD 0D C8 CA 19 B8 6D CA
: 8D 8E 89 52 50 4E 3A 84 B3 17 BD 71 1A 1D 38 9E
: 4A C4 04 F3 A2 1A F7 1F 34 F0 5A B9 CD B4 E2 7F
: 8C 40 18 22 58 85 14 40 E0 BF 01 2D 52 B7 69 7B
: [ Another 129 bytes skipped ]
529 29: INTEGER
: 01 61 40 24 1F 48 00 4C 35 86 0B 9D 02 8C B8 90
: B1 56 CF BD A4 75 FE E2 8E 0B B3 66 08
: }
0 warnings, 0 errors.