Use OpenSSL RSA key with .Net - c++

I am using openssl 0.9.6g and I have created public/private keypair using RSA_generate_key(). When I save the key with PEM_write_bio_RSAPublicKey, it gives me keys like:
-----BEGIN RSA PUBLIC KEY-----
...
-----END RSA PUBLIC KEY-----
I have another module in .NET which throws an exception when passed in this key due to its format. It takes format like:
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
How to convert my keys to this format. I am using C++.
In .NET, I am using openssl.net, the code is as follows:
string publicKey = #"-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAKGtqUVBBqcGCRYa7Sb6JVQirOX3hggWP2k7CzEtbF/soOONK510Kefm
omXBrGn2t79ES+hAcCvGSiiVZGuEb3UPiznzbiY150SME5nRC+zU0vvdX64ni0Mu
DeUlGcxM1eWSpozO71at6mxLloEMUg0oSWHfAlS5a4LVaURrJqXfAgMBAAE=
-----END RSA PUBLIC KEY-----";
Encoding enc = Encoding.ASCII;
string text = "hello world";
byte[] msg = enc.GetBytes(text);
CryptoKey key = CryptoKey.FromPublicKey(publicKey, "");
RSA rsa = key.GetRSA();
byte[] res = rsa.PublicEncrypt(msg, RSA.Padding.PKCS1);
The exception comes in line:
CryptoKey key = CryptoKey.FromPublicKey(publicKey, "");
If I use the key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbhcU+fhYmYLESZQAj1hKBXsNY
si0kYHNkxpP7ftxZiTFowWUVXHzQgkcYiCNnp3pt1eG6Vt0WDzyFYXqUUqugvX41
gkaIrKQw/sRiWEx49krcz7Vxr3dufL6Mg3eK7NyWDGsqwFrx/qVNqdhsHg12PGNx
IMY4UBtxin2A8pd4OwIDAQAB
-----END PUBLIC KEY-----
It works fine.
I was looking around for this issue. I think what I am looking for is "how to convert rsa public key from pkcs#1 to x509 format.

I am using openssl 0.9.6g and I have created public/private keypair
using RSA_generate_key(). It gives me keys like:
-----BEGIN RSA PUBLIC KEY-----
...
-----END RSA PUBLIC KEY-----
I think what I am looking for is "how to convert rsa public key from pkcs#1 to x509 format.
Yeah, .Net can consume some ASN.1/DER encoded keys, and some PEM encoded keys. The difference is PKCS encoding versus Traditional encoding (OpenSSL calls it "Traditional"). The traditional encoding is the SubjectPublicKeyInfo and it includes the OID and the public key.
So you are looking for either an ASN.1/DER encoding or a PEM encoding that writes SubjectPublicKeyInfo, and not just the public key.
I have another module in .NET which throws an exception when passed in
this key due to its format. It takes format like:
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
In this case, use PEM_write_bio_PUBKEY rather than PEM_write_bio_RSAPublicKey.
PEM_write_bio_PUBKEY writes the SubjectPublicKeyInfo; while PEM_write_bio_RSAPublicKey writes only the public key.
You will need an EVP_PKEY, so use EVP_PKEY_set1_RSA to convert it.
This is a PKCS key in OpenSSL. Its just the public key. You would use PEM_write_RSAPublicKey to write it:
-----BEGIN RSA PUBLIC KEY-----
And this is a Traditional key in OpenSSL. Its the SubjectPublicKeyInfo, and it includes an OID for the algorithm (rsaEncryption) and the public key. You would use PEM_write_bio_PUBKEY to write it:
-----BEGIN PUBLIC KEY-----
Instead of saving the key with PEM_write_RSAPublicKey, you should write out the SubjectPublicKeyInfo structure in ASN.1/DER format with i2d_RSA_PUBKEY_bio; or write it out in PEM format with PEM_write_bio_PUBKEY.
The program below creates a RSA key pair, and then writes out the public key in all the formats. Be sure to save the private key, too.
(And I'm glad you have the C++ tag. unique_ptr makes this exercise so much easier).
#include <memory>
using std::unique_ptr;
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <cassert>
#define ASSERT assert
using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
using EVP_KEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
using BIO_FILE_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
int main(int argc, char* argv[])
{
int rc;
RSA_ptr rsa(RSA_new(), ::RSA_free);
BN_ptr bn(BN_new(), ::BN_free);
BIO_FILE_ptr pem1(BIO_new_file("rsa-public-1.pem", "w"), ::BIO_free);
BIO_FILE_ptr pem2(BIO_new_file("rsa-public-2.pem", "w"), ::BIO_free);
BIO_FILE_ptr der1(BIO_new_file("rsa-public-1.der", "w"), ::BIO_free);
BIO_FILE_ptr der2(BIO_new_file("rsa-public-2.der", "w"), ::BIO_free);
rc = BN_set_word(bn.get(), RSA_F4);
ASSERT(rc == 1);
// Generate key
rc = RSA_generate_key_ex(rsa.get(), 2048, bn.get(), NULL);
ASSERT(rc == 1);
// Convert RSA key to PKEY
EVP_KEY_ptr pkey(EVP_PKEY_new(), ::EVP_PKEY_free);
rc = EVP_PKEY_set1_RSA(pkey.get(), rsa.get());
ASSERT(rc == 1);
//////////
// Write just the public key in ASN.1/DER
// Load with d2i_RSAPublicKey_bio
rc = i2d_RSAPublicKey_bio(der1.get(), rsa.get());
ASSERT(rc == 1);
// Write just the public key in PEM
// Load with PEM_read_bio_RSAPublicKey
rc = PEM_write_bio_RSAPublicKey(pem1.get(), rsa.get());
ASSERT(rc == 1);
// Write SubjectPublicKeyInfo with OID and public key in ASN.1/DER
// Load with d2i_RSA_PUBKEY_bio
rc = i2d_RSA_PUBKEY_bio(der2.get(), rsa.get());
ASSERT(rc == 1);
// Write SubjectPublicKeyInfo with OID and public key in PEM
// Load with PEM_read_bio_PUBKEY
rc = PEM_write_bio_PUBKEY(pem2.get(), pkey.get());
ASSERT(rc == 1);
return 0;
}
The set1 in EVP_PKEY_set1_RSA bumps the reference count, so you don't get a segfault on a double free.
After executing the program, you get the expected PEM and ASN.1/DER:
$ cat rsa-public-1.pem
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA0cgFv6wEcqoOhPtHdVmX4YFlCwodnSqooeCxFF1XadTS4sZkVJTC
kszHmRqXiXL2NmqnuDQsq6nLd+sNoU5yJJ+W1hwo7UToCyJ/81tS4n6mXvF8oilP
8YudD5QnBdW9LhqttBIN4Gk+Cxun+HG1rSJLGP9yiPPFd7DPiFz0Gd+juyWznWnP
gapDIWEKqANKma3j6b9eopBDWB0XAgU0HQ71MSNbcsPvDd23Ftx0re/7jG53V7Bn
eBy7fQsPmxcn4c74Lz4CvhOr7VdQpeBzNeG2CtkefKWyTk7Vu4FZnAgNd/202XAr
c6GmEQqD2M2zXH/nVZg5oLznECDVQ1x/pwIDAQAB
-----END RSA PUBLIC KEY-----
$ cat rsa-public-2.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0cgFv6wEcqoOhPtHdVmX
4YFlCwodnSqooeCxFF1XadTS4sZkVJTCkszHmRqXiXL2NmqnuDQsq6nLd+sNoU5y
JJ+W1hwo7UToCyJ/81tS4n6mXvF8oilP8YudD5QnBdW9LhqttBIN4Gk+Cxun+HG1
rSJLGP9yiPPFd7DPiFz0Gd+juyWznWnPgapDIWEKqANKma3j6b9eopBDWB0XAgU0
HQ71MSNbcsPvDd23Ftx0re/7jG53V7BneBy7fQsPmxcn4c74Lz4CvhOr7VdQpeBz
NeG2CtkefKWyTk7Vu4FZnAgNd/202XArc6GmEQqD2M2zXH/nVZg5oLznECDVQ1x/
pwIDAQAB
-----END PUBLIC KEY-----
$ dumpasn1 rsa-public-1.der
0 266: SEQUENCE {
4 257: INTEGER
: 00 D1 C8 05 BF AC 04 72 AA 0E 84 FB 47 75 59 97
: E1 81 65 0B 0A 1D 9D 2A A8 A1 E0 B1 14 5D 57 69
: D4 D2 E2 C6 64 54 94 C2 92 CC C7 99 1A 97 89 72
: F6 36 6A A7 B8 34 2C AB A9 CB 77 EB 0D A1 4E 72
: 24 9F 96 D6 1C 28 ED 44 E8 0B 22 7F F3 5B 52 E2
: 7E A6 5E F1 7C A2 29 4F F1 8B 9D 0F 94 27 05 D5
: BD 2E 1A AD B4 12 0D E0 69 3E 0B 1B A7 F8 71 B5
: AD 22 4B 18 FF 72 88 F3 C5 77 B0 CF 88 5C F4 19
: [ Another 129 bytes skipped ]
265 3: INTEGER 65537
: }
0 warnings, 0 errors.
$ dumpasn1 rsa-public-2.der
0 290: SEQUENCE {
4 13: SEQUENCE {
6 9: OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
17 0: NULL
: }
19 271: BIT STRING, encapsulates {
24 266: SEQUENCE {
28 257: INTEGER
: 00 D1 C8 05 BF AC 04 72 AA 0E 84 FB 47 75 59 97
: E1 81 65 0B 0A 1D 9D 2A A8 A1 E0 B1 14 5D 57 69
: D4 D2 E2 C6 64 54 94 C2 92 CC C7 99 1A 97 89 72
: F6 36 6A A7 B8 34 2C AB A9 CB 77 EB 0D A1 4E 72
: 24 9F 96 D6 1C 28 ED 44 E8 0B 22 7F F3 5B 52 E2
: 7E A6 5E F1 7C A2 29 4F F1 8B 9D 0F 94 27 05 D5
: BD 2E 1A AD B4 12 0D E0 69 3E 0B 1B A7 F8 71 B5
: AD 22 4B 18 FF 72 88 F3 C5 77 B0 CF 88 5C F4 19
: [ Another 129 bytes skipped ]
289 3: INTEGER 65537
: }
: }
: }
0 warnings, 0 errors.
Related, see How to generate RSA private key using openssl?. It shows you how to write a RSA public and private key in a number of formats.

Related

How to generate RSA public key with no any API. Just using modulus and public exponent

How to generate RSA public key with no any API. Just using modulus and public exponent.
In our project we are implementing RSA PKCS#8 public key by modulus and public exponent.
However we don't know meaning of ASN.1 binary of PKCS#8 public key.
For understanding PKCS#8 public key ASN.1, we did blow.
1. openssl genrsa -out hoge.key 2048
2. openssl rsa -pubout -in hoge.key
Here we get below infomation.
> -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7z2oyePt5vNbH7Pbieiw
BOgRnCUyyUvUo6Wi+uqUWvMxrji1vH21ViTZYLjg40RrulSCGFwjzwnI4AMtEdIZ
7uOol12E3xOZYNgwTBaDNCT9p0IYYuFVGfQyxlavr7oSIaaNmlSRy+0os1xi7IiI
PCHE/7nfifDQiqGtb6b6TBOwP3QXg5IdrXiqQJAlk+8S0XPhbnwwzWOhlrR3Wftq
jylBPSGSDJQoF0fJ5h2cA2yJiGqTV37YRTThPWmQEuz8Njx4bTaEaTul5/UNhSel
s7khd/IvHV9oN6T2o4V//fAsyjRZlYKEUHldb3ML/QHxWs7+hqWSa9NCwwXZGhEl
wwIDAQAB
-----END PUBLIC KEY-----
> 0000000 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01
0000010 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01
0000020 00 ef 3d a8 c9 e3 ed e6 f3 5b 1f b3 db 89 e8 b0
0000030 04 e8 11 9c 25 32 c9 4b d4 a3 a5 a2 fa ea 94 5a
0000040 f3 31 ae 38 b5 bc 7d b5 56 24 d9 60 b8 e0 e3 44
0000050 6b ba 54 82 18 5c 23 cf 09 c8 e0 03 2d 11 d2 19
0000060 ee e3 a8 97 5d 84 df 13 99 60 d8 30 4c 16 83 34
0000070 24 fd a7 42 18 62 e1 55 19 f4 32 c6 56 af af ba
0000080 12 21 a6 8d 9a 54 91 cb ed 28 b3 5c 62 ec 88 88
0000090 3c 21 c4 ff b9 df 89 f0 d0 8a a1 ad 6f a6 fa 4c
00000a0 13 b0 3f 74 17 83 92 1d ad 78 aa 40 90 25 93 ef
00000b0 12 d1 73 e1 6e 7c 30 cd 63 a1 96 b4 77 59 fb 6a
00000c0 8f 29 41 3d 21 92 0c 94 28 17 47 c9 e6 1d 9c 03
00000d0 6c 89 88 6a 93 57 7e d8 45 34 e1 3d 69 90 12 ec
00000e0 fc 36 3c 78 6d 36 84 69 3b a5 e7 f5 0d 85 27 a5
00000f0 b3 b9 21 77 f2 2f 1d 5f 68 37 a4 f6 a3 85 7f fd
0000100 f0 2c ca 34 59 95 82 84 50 79 5d 6f 73 0b fd 01
0000110 f1 5a ce fe 86 a5 92 6b d3 42 c3 05 d9 1a 11 25
0000120 c3 02 03 01 00 01
0000126
we understand above binary as below. However we don't understand some binary.So We would like to know meaning of ASN.1 binary of PKCS#8 public key.
By the way, We have already refer below information. However this does not mention about public key.
https://datatracker.ietf.org/doc/html/rfc5208
30: SEQUEENCE
82: next 2byte is sequence length
01 22: 0x122byte
30: SEWUENCE
0d:?
06:?
09:?
2a 86.. 01 01 01: PKCS#1 rsa Encryption
05: ?
00: ?
03: ?
82: next 2byte is length of bit string
01 0f: length of bit string
00: ?
30 82....: PKCS#1 public key
Taking a huge gamble that what you're really asking is how to obtain the modulus and public exponent from a RSA public key PEM encoding, I invite you on this tryst. If your looking to build this encoding given only the modulus and public exponent, this dissection should show you how it's laid out, and this a somewhat reverse roadmap to get there. Either way, read on.
First of all: "next 2byte is length of bit string" - that's rather assumptive of you. ASN.1 is all about type-length-value (TLV for short) and does not waver from that model. The only reason the "next 2 byte is length of bit string" is because the ASN.1 said it is. (more on that later).
ASN.1 in incredibly flexible. I recently had a colleague jokingly tell me "ASN.1 is just JSON for boomers". Yeah, I took that personally. Since JSON followed ASN.1 by decades, I like to think "JSON is just ASN.1 for millennials".
Anyway, funny thing about ASN.1 (and JSON, for that matter): sometimes there are TLV encodings within TLV encodings, and that is the case here. Specifically an RSAPublicKey sequence will be encoded in an ASN.1 bit-string provided the setup is correct (i.e. the OID matches), and that seems to be the part you're struggling with: how to find it.
I'm going to leave the actual code to do this (which will be tedious, no-doubt fraught with hackery, and ultimately leave you wishing you had just used a crypto-library) as a task. That said, on to the act ripping apart your public key. First, your PEM:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7z2oyePt5vNbH7Pbieiw
BOgRnCUyyUvUo6Wi+uqUWvMxrji1vH21ViTZYLjg40RrulSCGFwjzwnI4AMtEdIZ
7uOol12E3xOZYNgwTBaDNCT9p0IYYuFVGfQyxlavr7oSIaaNmlSRy+0os1xi7IiI
PCHE/7nfifDQiqGtb6b6TBOwP3QXg5IdrXiqQJAlk+8S0XPhbnwwzWOhlrR3Wftq
jylBPSGSDJQoF0fJ5h2cA2yJiGqTV37YRTThPWmQEuz8Njx4bTaEaTul5/UNhSel
s7khd/IvHV9oN6T2o4V//fAsyjRZlYKEUHldb3ML/QHxWs7+hqWSa9NCwwXZGhEl
wwIDAQAB
-----END PUBLIC KEY-----
Decoding the base64 to DER, then displayed through xxd, it looks like this:
00000000: 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0.."0...*.H.....
00000010: 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 ........0.......
00000020: 00 ef 3d a8 c9 e3 ed e6 f3 5b 1f b3 db 89 e8 b0 ..=......[......
00000030: 04 e8 11 9c 25 32 c9 4b d4 a3 a5 a2 fa ea 94 5a ....%2.K.......Z
00000040: f3 31 ae 38 b5 bc 7d b5 56 24 d9 60 b8 e0 e3 44 .1.8..}.V$.`...D
00000050: 6b ba 54 82 18 5c 23 cf 09 c8 e0 03 2d 11 d2 19 k.T..\#.....-...
00000060: ee e3 a8 97 5d 84 df 13 99 60 d8 30 4c 16 83 34 ....]....`.0L..4
00000070: 24 fd a7 42 18 62 e1 55 19 f4 32 c6 56 af af ba $..B.b.U..2.V...
00000080: 12 21 a6 8d 9a 54 91 cb ed 28 b3 5c 62 ec 88 88 .!...T...(.\b...
00000090: 3c 21 c4 ff b9 df 89 f0 d0 8a a1 ad 6f a6 fa 4c <!..........o..L
000000a0: 13 b0 3f 74 17 83 92 1d ad 78 aa 40 90 25 93 ef ..?t.....x.#.%..
000000b0: 12 d1 73 e1 6e 7c 30 cd 63 a1 96 b4 77 59 fb 6a ..s.n|0.c...wY.j
000000c0: 8f 29 41 3d 21 92 0c 94 28 17 47 c9 e6 1d 9c 03 .)A=!...(.G.....
000000d0: 6c 89 88 6a 93 57 7e d8 45 34 e1 3d 69 90 12 ec l..j.W~.E4.=i...
000000e0: fc 36 3c 78 6d 36 84 69 3b a5 e7 f5 0d 85 27 a5 .6<xm6.i;.....'.
000000f0: b3 b9 21 77 f2 2f 1d 5f 68 37 a4 f6 a3 85 7f fd ..!w./._h7......
00000100: f0 2c ca 34 59 95 82 84 50 79 5d 6f 73 0b fd 01 .,.4Y...Py]os...
00000110: f1 5a ce fe 86 a5 92 6b d3 42 c3 05 d9 1a 11 25 .Z.....k.B.....%
00000120: c3 02 03 01 00 01 ......
Regular ASN.1 encoding, as expected. For a PKCS8 encoded key, it should look like this:
PublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
PublicKey BIT STRING
}
Sending our base64-decoded DER data through ASN.1 decoding, we see::
openssl asn1parse -in pubkey.der -inform DER
0:d=0 hl=4 l= 290 cons: SEQUENCE
4:d=1 hl=2 l= 13 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING
That looks about right. For an RSA public key, the OID is 1.2.840.113549.1.1.1, the NULL, then what should be a RSAPublicKey as the PublicKey key data bit-string, which will look like this:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
From the aforementioned decoding, we know at offset 19 is the actual bit string, which should contain the RSAPublicKey ASN.1 structure. Great, but how? Well, the original parse has the following at offset-19 of the file:
00000010: 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 ........0.......
^^^^^^^^^^^^^^
From this we get:
The ASN.1 tag byte, 03, says its a bit-string. Good. We expected that. then...
The length is calculated. Per ASN.1 rules, if the length octet has the high bit lit, it means the remaining bits of that octet tell how many subsequent octets make up the actual length (i.e it can be a multi-byte length). 82 means two octets follow, indicating
the bit-string overall object length: 010f, e.g. 271 bytes.
Immediately following the length description will be a single octet stating the number of padding bits of the bit-string. I can honestly say in a quarter century of staring at these I have never seen this any value other than zero (00) in any RSA encoded public key, but still worth mentioning.
Thereafter is the actual RSAPublicKey ASN.1 structure itself. Past the bit-string type-length-padding description, we can parse the actual RSAPublicKey (finally!). The bit-string content after the type-length and padding bits indicator should be just that. Well, that's at offset 24 of our DER-encoding, so:
openssl asn1parse -in pubkey.der -inform DER -offset 24
0:d=0 hl=4 l= 266 cons: SEQUENCE
4:d=1 hl=4 l= 257 prim: INTEGER :EF3DA8C9E3EDE6F35B1FB3DB89E8B004E8119C2532C94BD4A3A5A2FAEA945AF331AE38B5BC7DB55624D960B8E0E3446BBA5482185C23CF09C8E0032D11D219EEE3A8975D84DF139960D8304C16833424FDA7421862E15519F432C656AFAFBA1221A68D9A5491CBED28B35C62EC88883C21C4FFB9DF89F0D08AA1AD6FA6FA4C13B03F741783921DAD78AA40902593EF12D173E16E7C30CD63A196B47759FB6A8F29413D21920C94281747C9E61D9C036C89886A93577ED84534E13D699012ECFC363C786D3684693BA5E7F50D8527A5B3B92177F22F1D5F6837A4F6A3857FFDF02CCA345995828450795D6F730BFD01F15ACEFE86A5926BD342C305D91A1125C3
265:d=1 hl=2 l= 3 prim: INTEGER :010001
Sure enough, there it is, the modulus and the exponent as expected. Extracting and decoding from there is just a matter of code, but at least now it is code based on decoding by calculation and not guessing.
Fair Warning
The only reason why this worked is because we knew the RSAPublicKey was in the bit string, and the only reason we knew that was because the OID says it was. The form stored in the bit-string is dependent on that OID, so make sure to check it first before assuming the bit-string is RSAPublicKey material.
You are asking how to generate a public key, but I don't understand why you show some binary data you are trying to decode. You can not learn how to generate a public or private key from looking at binary data which i assume is already encrypted.
Why not use a library to generate a key pair and be done with it? Trying to code something yourself, from scratch, is likely (very likely in fact) to have errors which lead to really insecure conditions.
In any event, you can download the source code to ssl libraries like openssl if you want to see what you can learn from them.

BER decode error

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);
...

OpenSSL's rsautl cannot load public key created with PEM_write_RSAPublicKey

I've generated a public key using openssl
BIGNUM* e = BN_new();
BN_set_word(e, 17);
RSA* rsa = RSA_new();
if(!RSA_generate_key_ex(rsa, 2048, e, NULL)) {
LOG(security, debug) << "failed to generate private key";
}
And these are written to files:
FILE* pubwriter = fopen("key.pub", "wb");
int err = PEM_write_RSAPublicKey(pubwriter, key);
if(!err) {
throw new std::runtime_error("Failed to store public key");
}
FILE* privwriter = fopen("key.priv", "wb");
std::string password = "password";
err = PEM_write_RSAPrivateKey(privwriter, key, EVP_des_ede3_cbc(),
(unsigned char*)password.c_str(),
password.size(), NULL, NULL);
And they seem to be stored correctly, key.pub then contains something like
-----BEGIN RSA PUBLIC KEY-----
MIIBCAKCAQEA0rG1b0g3NIsDT8HkzgTx8BUI9LhueWbH1NuAvIh9qTF57GzEXuTu
jxBcuSzWXlR83ci4oITp7VqV6KLVoJryf8orGXBVi9A73JYOQVB6FEzARKym/g8E
fSEwSmdQ4NfiTESwOCtIextdu3x8pANSYDyyqdiWsSHy0SiZmkbvdVYomIBJZOV9
jhb3mkmD0WUYie9AXziTbli97YqDiN168kMI+7ePpbNJFSVSIUkPPocSgvgcAux/
HuDqftzBgyAF3NGb3AAra1A8T7yPOqLyYyXdIJmF+/Svk5PdMbZVE/U76cjBThch
Q9AiLo25hOjkmtuEQubCuwrUDleblr93aQIBEQ==
-----END RSA PUBLIC KEY-----
Now I want to try these to make sure I haven't got anything wrong but it wont' load:
$ openssl rsautl -encrypt -inkey key.pub -pubin -in data.txt -out enc.txt
unable to load Public Key
What am I missing?
int err = PEM_write_RSAPublicKey(pubwriter, key);
PEM_write_RSAPublicKey writes just the public key. You can make the command work using PEM_write_PUBKEY. The various *_PUBKEY routines write the SubjectPublicKeyInfo, which includes the algorithm OID and public key.
When you write the SubjectPublicKeyInfo, OpenSSL calls it "traditional" format. It will also have the header -----BEGIN PUBLIC KEY----- (and not -----BEGIN RSA PUBLIC KEY-----).
Below, I used PEM_write_PUBKEY to save the public key rsa-public.pem:
$ openssl rsautl -encrypt -inkey rsa-public.pem -pubin -in data.txt -out enc.bin
$ hexdump enc.bin
0000000 45 53 31 ad 9d 6a c4 37 1e 22 4b 83 c6 27 c8 3c
0000010 df cb 87 a4 60 d8 63 9a 83 9f ee ca e5 8f 8e dd
0000020 d4 d0 98 97 1c b3 36 55 f1 84 ea 7f fe bf 22 b6
0000030 93 20 a2 d5 b2 bd 20 cc 52 8e c7 1b 33 e6 40 40
0000040 cb 7d 6f 17 f1 eb f1 d4 9d 66 fb 67 eb 67 ba 2a
0000050 44 c2 52 15 54 8d 79 76 ad 26 61 35 27 9c bb 6c
0000060 5b 0e 79 b3 d3 27 0b a9 72 17 0d 2d 19 d7 60 19
0000070 16 46 80 4b c0 ae 75 53 9e 6f f5 24 d9 1a a3 6a
0000080 2f 38 13 f6 72 19 20 94 de 40 75 20 51 f4 08 f4
0000090 74 b8 ac 49 01 d6 f8 f4 e5 79 38 88 2d 02 b7 bd
00000a0 f7 63 c1 e1 e5 ec 39 a1 fa 7c ce 0f 83 16 70 7e
00000b0 cd 7e f5 6b 51 c2 db d7 f6 c4 46 5d e5 93 d3 3d
00000c0 ab e6 3b 1a 97 d4 c9 54 e7 aa 90 2d 0a b9 c2 4b
00000d0 3c 58 fd 26 58 5a 63 c0 8c ae b9 72 24 a1 68 5d
00000e0 83 d7 5b ae 56 2a 78 46 8c f4 21 96 bd d3 0c 93
00000f0 8e 35 61 9c b8 56 2e 3a 4e 05 d9 1e 0b 59 14 11
0000100
PEM_write_PUBKEY requires a EVP_PKEY. Just use something like:
EVP_PKEY* pkey = EVP_PKEY_new();
ASSERT(pkey != NULL);
int rc = EVP_PKEY_set1_RSA(pkey, rsa);
ASSERT(rc == 1);
...
EVP_PKEY_free(pkey);
The set1 bumps the reference count on the RSA key, so you have to free it through EVP_PKEY_free.
The difference between PEM_write_RSAPublicKey and PEM_write_PUBKEY is very obvious when you save in ASN.1/DER. But it gets lost in the PEM encoding.
Here's the non-traditional key in ASN.1/DER and dumped. Its the ASN.1 equivalent of PEM_write_RSAPublicKey. Its just {n,e}:
$ dumpasn1 rsa-public-1.der
0 266: SEQUENCE {
4 257: INTEGER
: 00 D1 C8 05 BF AC 04 72 AA 0E 84 FB 47 75 59 97
: E1 81 65 0B 0A 1D 9D 2A A8 A1 E0 B1 14 5D 57 69
: D4 D2 E2 C6 64 54 94 C2 92 CC C7 99 1A 97 89 72
: F6 36 6A A7 B8 34 2C AB A9 CB 77 EB 0D A1 4E 72
: 24 9F 96 D6 1C 28 ED 44 E8 0B 22 7F F3 5B 52 E2
: 7E A6 5E F1 7C A2 29 4F F1 8B 9D 0F 94 27 05 D5
: BD 2E 1A AD B4 12 0D E0 69 3E 0B 1B A7 F8 71 B5
: AD 22 4B 18 FF 72 88 F3 C5 77 B0 CF 88 5C F4 19
: [ Another 129 bytes skipped ]
265 3: INTEGER 65537
: }
0 warnings, 0 errors.
Here's the traditional public key in ASN.1/DER and dumped. Its the ASN.1 equivalent of PEM_write_PUBKEY. Its the one that writes the SubjectPublicKeyInfo, and it includes an algorithm OID and public key:
$ dumpasn1 rsa-public-2.der
0 290: SEQUENCE {
4 13: SEQUENCE {
6 9: OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
17 0: NULL
: }
19 271: BIT STRING, encapsulates {
24 266: SEQUENCE {
28 257: INTEGER
: 00 D1 C8 05 BF AC 04 72 AA 0E 84 FB 47 75 59 97
: E1 81 65 0B 0A 1D 9D 2A A8 A1 E0 B1 14 5D 57 69
: D4 D2 E2 C6 64 54 94 C2 92 CC C7 99 1A 97 89 72
: F6 36 6A A7 B8 34 2C AB A9 CB 77 EB 0D A1 4E 72
: 24 9F 96 D6 1C 28 ED 44 E8 0B 22 7F F3 5B 52 E2
: 7E A6 5E F1 7C A2 29 4F F1 8B 9D 0F 94 27 05 D5
: BD 2E 1A AD B4 12 0D E0 69 3E 0B 1B A7 F8 71 B5
: AD 22 4B 18 FF 72 88 F3 C5 77 B0 CF 88 5C F4 19
: [ Another 129 bytes skipped ]
289 3: INTEGER 65537
: }
: }
: }
0 warnings, 0 errors.
err = PEM_write_RSAPrivateKey(privwriter, key, EVP_des_ede3_cbc(),
(unsigned char*)password.c_str(),
password.size(), NULL, NULL);
I believe the OpenSSL folks recommend you use PEM_write_PKCS8PrivateKey. See PEM(3) and pkcs8(1).
Now I want to try these to make sure I haven't got anything wrong but it wont' load:
$ openssl rsautl -encrypt -inkey key.pub -pubin -in data.txt -out enc.txt
You can understand the behavior by looking at <openssl src>/apps/rsautl.c. Here are the relevant lines:
else if (!strcmp(*argv, "-pubin")) {
key_type = KEY_PUBKEY;
}
...
case KEY_PUBKEY:
pkey = load_pubkey(bio_err, keyfile, keyform, 0, NULL, e, "Public Key");
break;
...
Then, in apps.c:
if (format == FORMAT_ASN1) {
pkey = d2i_PUBKEY_bio(key, NULL);
}
...
else if (format == FORMAT_PEM) {
pkey = PEM_read_bio_PUBKEY(key, NULL, ...);
}
...
The observation above is the routines are using *_PUBKEY.
There's also a code path based on format == FORMAT_PEMRSA that calls PEM_read_bio_RSAPublicKey, but I don't know how to trigger it. Looking at rsautl(1), I don't think you can because there's no switch that exposes it.
If it was going to trigger, it would be based on a combination of the -keyform option combined with format == FORMAT_PEMRSA. But apps.c's str2fmt does not return a FORMAT_PEMRSA.
I think that means your only option is to use a SubjectPublicKeyInfo. And that means using PEM_write_PUBKEY (or convert the key after the fact).
Now related: OpenSSL Bug Report, Issue 3887: rsautl and intelligent retry for Public Key parse after Traditional/Subject Public Key Info parse fails.
I faced the same problem and managed to debug by following #jww answer above. In the process I discover a simple command that does not require a EVP_PKEY.
Just use PEM_write_bio_RSA_PUBKEY(BIO * bp, RSA * x) directly. Read PEM_write documentation for details.

Load RSA private key from a PEM encoded private key

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.

ElGamal encryption example?

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.