Use PGP public key to cipher with Windows CryptoApi in C++ - c++

I don't know if this is possible at all. A pair of RSA keys are generated with GNUPG, and the public key is exported to a file. My program receives such file and then it has to encrypt some data with the public RSA key in that file. The program is written in C/C++ and it won't use external libraries, so all I can use is Windows CryptoApi functions (CryptStringToBinary, CryptDecodeObjectEx, CryptImportPublicKeyInfo, etc).
This method works with a public 2048 bit RSA key in PEM format encoded in base64:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnCEy2jOlwK8qVxAHddaD
J6u8u/D0h8nOexco6Xg8iu7DnZOrKPL/1pTL1pwH5GLp0bsb/NfkxetijIb/C4h7
37y6bZPC8V+Koi2jz2lNCNOF4jWuD9Dw8mYnOeH+HpVkKTDVry824i2+qihWM1s/
DwVNUh4C50asnFl64Qd9ycbE3jDr4+yzeBDC7Pirm21OFVUZhTzNzuT5UQzGidvw
2pomYnDM6NOwoIyrBOP0J4CCGbJnZMsf+Dsya/t9tR0cKgFl1Zh0W/V1eJ8Ud7Yq
vIwGeStNeIcjoVkPGh4Hu1Uj0YHXZeTyy4LYo8OUWIipQEJ/dL4TLd0/uD8cr1LR
TwIDAQAB
-----END PUBLIC KEY-----
But the key exported with GPG looks quite different:
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2
mQENBFSIwoUBCACpyzbyoFLtg8uPMOVl0a4oRdfSSyyNpVuZiDENvj60JINSLhQq
gkyfRW6KbSxp9rEPjsUWnjEVcZ8EYcTZjalGajaG090WrAM0iop4zagKSK3EjpTO
sdkA0wtX7abeK+s9WBiC4hbFk2Ds6iRHtVCU5zsYzZ2S/lD7+PA6UXjFYSpNpCGr
XjyATh7tiYelFjij7ocgK9MWCCDPv6ti9yRVqmsBJPqIqIfvjyyLxFncSOCAc4+E
2cLFUsDr5rGYZF8OsJ0AICxAWwJn50IXpEpuQVQNAOd5yZsrRj4tOJ6qCo/bL24a
UkzF9+zDkFQ6kPSKqUnpOx7wdby5625eBImTABEBAAG0ImdudXBnX2tleXMgPGdu
dXBna2V5c0B5b3BtYWlsLmNvbT6JATkEEwECACMFAlSIwoUCGwMHCwkIBwMCAQYV
CAIJCgsEFgIDAQIeAQIXgAAKCRD3/5Jn+Ps9o7JfCACfQqOVorlkcpya/N9uT/L0
2RA3Y2CwlMEdCpzxUtlEC9KlcAWEMBYfS68Cvq550VKKA+bz9v1XBta1rGPN5T/4
o8lxa7fEhQYCRcUJ4qBzHHOPJoLd7oYDNjGcMS0LNmt7L8Apm2+WguwufO1x61OA
bfOsxCzPt7kl/PjzQMLeNY3F03SuzgtkSQwDc0CpBgoRYhlbCyorxEuTIdRioZiL
h6G/Wvp4Me9prQiMpEqPkHHhp61+LKdAGKjaCcyOwDUB3Ec1GtY1CRKb3/VRveXQ
nN8hm9+VnbqVBy6HurMsz5rEM9rKbIWl1i2A3CFY4EKbJBcYdiGTB16p/QJ+Ll0o
uQENBFSIwoUBCACfH0Up/2Zf+WwH70Tk4WVmaEMNhEiiN1ivYsPT1RYSgSzsgRzx
LgD9CTWCFl9jf6Ko2YCHujZimTpx4Bd2hGNj07zF8VWl2fpW8nA964HkWg1isk5s
XYiSYRSG6foH6tn8D42fYsJad0A4yZo1P5OoPzql9MTJpH1nVjaWnxTOTRgoYmMo
mPW7DimIDnoKRp/A7yCdw3HiUogKqRedqWTxLzs2odhx1NKDx9A3lA81UQtNSA78
o6h1JGPQUHUU5yl3+EgntDL+qmcx4fW2J/PQ2ingIq+VueeDpUKYNomGcrvR7vvR
EAIUCD9UbQTos3xgzUAa6TZY+sLC/x6lTpq9ABEBAAGJAR8EGAECAAkFAlSIwoUC
GwwACgkQ9/+SZ/j7PaPd0gf/TNqUmBgQQaY+kgfUL2rCauNkBZboku59pNxJu6iJ
W+IEMYLbRg8qzIVS1ui9zxMY8pper88QX82jfZ27Xo5nbct9ZZCjDeWZRX5xJULx
CsK2fHlMA/CdvGZJdm5KMNmVFni+vVJlLzpij5eQ52j+8NvHAPFgL3NlcmdwWVhy
/y3XjG687bB+DnVhlfOb7JijA/WHThjXS6AFH659jlt/Z1FRti6O3cxEJSTN0rQU
bCXkjJPsqQNgEbsBDQ3f6hwZKnpqpQZt417qRahb/LrfIgxAJhiWLyFFWKp3XuX3
mR0t01lrPzIXTQMaY9lce3G3XSoQx+1gu29fBm/rkHvQIQ==
=R8UZ
-----END PGP PUBLIC KEY BLOCK-----
Although it's a 2048 bit key as well. If I trim the blank lines and the header, CryptStringToBinary succeeds and translates it into a binary format, but CryptDecodeObjectEx fails (GetLastError() returns 0x8009310B). Removing the checksum didn't work either.
I'm a bit lost here so, basically, Is there a way to obtain a RSA public key exported with GPG and use it to encrypt data with Windows CryptoApi?
Thank you.

Converting OpenPGP Keys to PEM
Extracting the RSA public key from an OpenPGP key and conterting it to PEM format is possible. Sysmisc has an article about converting to and from OpenPGP keys in different ways. For the way OpenPGP to PEM, it boils down to:
gpgsm -o secret-gpg-key.p12 --export-secret-key-p12 0xXXXXXXXX
openssl pkcs12 -in secret-gpg-key.p12 -nokeys -out gpg-certs.pem
OpenPGP has its own Message Exchange Format
Yet a caveat, if you want to encrypt to an OpenPGP user, he will not be able to read any (Open, ...)SSL encrypted information using an OpenPGP implementation like GnuPG. OpenPGP not only uses a different key format, but also another message exchange format.
If you want to send OpenPGP messages, use GPGME to interface GnuPG from C, there might also be other libraries for doing so.

Related

Encrypt a RSA private key and write it to a file using Crypto++

I'm trying to export a RSA private key to a file, after encrypting it using RSA.
So far, I tried to export it with 2 methods :
int main()
{
AutoSeededRandomPool rnd;
const std::string publickey_str =
"-----BEGIN PUBLIC KEY-----\n"
"rsa public key there\n"
"-----END PUBLIC KEY-----";
RSA::PublicKey master_publicKey;
StringSource source(publickey_str, true);
PEM_Load(source, master_publicKey);
CryptoPP::RSAES_OAEP_SHA_Encryptor e(master_publicKey);
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rnd, 4096);
RSA::PrivateKey privateKey(params);
FileSink file("exported.bin")
//Method 1
FileSink file("exported.bin")
ArraySource export_private_key(privateKey, sizeof(privateKey),true, AuthenticatedEncryptionFilter(e, new Redirector(file))); //can't find a way to convert privateKey to a valid type for ArraySource
//Method 2
privateKey.Save( new AuthenticatedEncryptionFilter(e, new Redirector(file))); //doesn't work, as Save() expects BufferedTransformation
Is there any way to achieve my goal using one of these 2 methods, or do I have to convert my private key to another format (BER or PEM encoding) before applying RSA on it ?
Thanks in advance
P.S: My question is Windows specific, if it matters in any way.
No.
RSA can only securely encrypt a (much) smaller amount of data than the key size because of the secure padding that is required - in this case OAEP. RSA, like any cipher, encrypts bytes, so changing the key to text certainly won't help. It will also further inflate the key size that needs to be encrypted.
What you can do is to use hybrid encryption: first encrypt with AES in a secure mode of operation with a new random key, then encrypt the AES key using RSA.
You may want to think about your security scheme; the RSA encryption is only useful if the RSA decryption key is kept more secure than the RSA key that you are encrypting. Otherwise you've just shifted the problem to the RSA key pair that wraps and unwraps the private key.
There is a YES with some constraints. In the easiest case you try to encrypt a binary BER string of some private RSA key using a 2nd RSA system. The required key size of the 2nd system is somewhat larger, for the BER string has an overhead of may be 100% and also RSA/OAEP entertains some padding tags of about 42 bytes size. So you would form the BER string and generate a RSA Key Pair with (BER size +42) * 8 bits key length.

How to load RSA key pair without Openssl or other Library

CCFor a project I was searching for a simple RSA implementation to exchange a small secret via an unsecured but existing communication protocol. To keep it small and easy to portable to different platforms I did not want to link against OpenSSL or Crypto++. I found that as a part of the axTLS project which has a suitable license and an easy to extract RSA algorithm. The rsa function for encryption needs two components (as it usses the public key). The pup_exp is 65537 and modulus is the public part of the key and priv_exp the private one.
void RSA_priv_key_new(RSA_CTX **ctx,
const uint8_t *modulus, int mod_len,
const uint8_t *pub_exp, int pub_len,
const uint8_t *priv_exp, int priv_len
)
For easy use for the user, I want to load a certificate like X.509 or PEM generated by a library like OpenSSL, but in C or C++ code without including the whole OpenSSL stuff. But at the moment I even did not find an understandable documentation of the common key file formats.
There are two types of RSA public key format widely used and they are PKCS#1 and X.509(SubjectPublicKeyInfo).
I have used libtomcrypt(http://libtom.org/?page=features). It supports both the RSA key formats and very much portable. The license is non restrictive.

cryptoapi and openssl

I'm trying to encrypt and sign a file with cryptoapi with some X.509 certificates. I want to verify and decrypt this file with openssl.
On windows I think I need to use the CryptSignAndEncryptMessage function to encrypt and sign data. I used this example from MSDN to create a signed and encrypted message.
How can I decrypt/verify this file using openssl? I removed the first 4 bytes from the message since it contained the length of the message (from the windows blob).
When I call openssl -asn1parse I get some output that indicates it to be parsable by openssl.
When trying to verify the signature with openssl I recieve an error:
openssl rsautl -verify -inkey AlonsoCert.pem -keyform pem -certin -in sandvout-without-4byte.txt
RSA operation error
3073579208:error:0406706C:rsa routines:RSA_EAY_PUBLIC_DECRYPT:data greater than mod len:rsa_eay.c:680:
CryptSignAndEncrypt message seems to use RC4 cipher with empty ASN.1 parameters field and, looking at OpenSSL sources, openssl chokes on try to generate IV (which is not needed for RC4).
Try to use other cipher (AES for example) in CryptAndSignMessage.
Anyway, RC4 is very old, insecure, and obsolete.
Your ASN.1 dump information shows you've created a PKCS#7 CMS output from your CryptoAPI code. As a result you cannot use the basic OpenSSL decryption and verification methods.
Instead, use the cms mode:
openssl cms -decrypt -inform DER -in sandvout-without-4byte.txt
-out decrypted.bin -recip testkey.pfx
(Note: I've not used this mode before, so I think the syntax I've suggested is correct. Either way, this should hopefully be the step in the right direction that solves this.)
Try using openssl smime to verify and/or decrypt. The syntax is fairly straight-forward but you can find the information here: http://www.openssl.org/docs/apps/smime.html

Load PEM encoded private RSA key in Crypto++

Often times, user will have PEM encoded RSA private keys. Crypto++ requires that these keys be in DER format to load. I've been asking people to manually convert their PEM files to DER beforehand using openssl like this:
openssl pkcs8 -in in_file.pem -out out_file.der -topk8 -nocrypt -outform der
That works fine, but some people don't understand how to do that nor do they want to. So I would like to convert PEM files to DER files automatically within the program.
Is it as simple as striping the "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" from the PEM or is some other transformation required as well? I've been told that between those markers that it's just b64 encoded DER. Here's some code that demonstrates the issue:
// load the private key
CryptoPP::RSA::PrivateKey PK;
CryptoPP::ByteQueue bytes;
try
{
CryptoPP::FileSource File( rsa.c_str(), true, new CryptoPP::Base64Decoder() );
File.TransferTo( bytes );
bytes.MessageEnd();
// This line Causes BERDecodeError when a PEM encoded file is used
PK.Load( bytes );
}
catch ( CryptoPP::BERDecodeErr )
{
// Convert PEM to DER and try to load the key again
}
I'd like to avoid making system calls to openssl and do the transformation entirely in Crypto++ so that users can provide either format and things "just work". Thanks for any advice.
Yes, it's a DER stream encoded with Base64. Note though, in addition to striping both BEGIN and END markers, in case of RSA key format you also need to strip any flags that may be inserted between the BEGIN marker and the encoded data. Only the remaining part can be successfully Base64 decoded. It appears that you feed the full certificate file to the decoder and that needs fixing.
... I would like to convert PEM files to DER files automatically within the program.
In July 2014, a PEM Pack was provided for the Crypto++ library. The PEM Pack is a partial implementation of message encryption which allows you to read and write PEM encoded keys and parameters, including encrypted private keys. The additional files include support for RSA, DSA, EC, ECDSA keys and Diffie-Hellman parameters.
Its an add-on to the library, and not part of the library proper. You download a ZIP and add five source files to the library. Then you build the library (Crypto++ automatically picks them up). The ZIP contains five additional source files, a script to create test keys using OpenSSL, a C++ program to test reading and writing the keys, and a script to verify the keys written by Crypto++ using OpenSSL.
Here's how you would use it:
CryptoPP::RSA::PrivateKey pk;
CryptoPP::FileSource file("<rsa-key-file.pem>", true);
CryptoPP::PEM_Load(file, pk);
CryptoPP::AutoSeededRandomPool prng;
bool = pk.Validate(prng, 3);
if (! valid)
throw ...
If the keys is encrypted, then here's how you would load it. The PEM Pack re-implement's OpenSSL's EVP_BytesToKey, so the key derivation will work and you can interop:
CryptoPP::RSA::PrivateKey pk;
CryptoPP::FileSource file("<rsa-key-file.pem>", true);
std::string pass = "<super secret password>";
CryptoPP::PEM_Load(file, pk, pass.data(), pass.size());
CryptoPP::AutoSeededRandomPool prng;
bool = pk.Validate(prng, 3);
if (! valid)
throw ...
There's also a PEM_Save, so you can write the keys directly from Crypto++. For example:
// Generate it or load it from somewhere
CryptoPP::RSA::PrivateKey pk = ...;
CryptoPP::FileSink file("<rsa-key-file.pem>", true);
CryptoPP::PEM_Save(file, pk);
And PEM_Save for an encrypted key (or key you intend to encrypt):
// Generate it or load it from somewhere
CryptoPP::RSA::PrivateKey pk = ...;
CryptoPP::FileSink file("<rsa-key-file.pem>", true);
std::string pass = "<super secret password>";
CryptoPP::PEM_Save(file, pk, "AES-128-CBC", pass.data(), pass.size());
PEM_Load does not need an algorithm because its encoded in the encapsulated header. PEM_Save needs an algorithm because there is no default algorithm.
I know this is an old question but other's might find this useful. Once you strip the markers you're left with the 'inner' key material. According to http://www.cryptopp.com/wiki/Keys_and_Formats#BER_and_DER_Encoding you can use BERDecodePrivateKey to load this. So, to load an openssl key that has had its markers stripped you can do something like
bool LoadKey(RandomNumberGenerator& rng, const std::string& file,
RSA::PrivateKey& key)
{
ByteQueue q;
FileSource KeyFile(file.c_str(), true, new Base64Decoder);
KeyFile.TransferTo(q);
key.BERDecodePrivateKey(q,false,0); // last 2 params unused
return key.Validate(rng, 2);
}

Using Crypto++ generated RSA keys on OpenSSL

Is there a way to use the RSA keys I've generated with the Crypto++ API in OpenSSL? What I am looking for is a way to store the keys in a format that both Crypto++ and OpenSSL can easily open them.
I'm writing a licensing scheme and would want to verify signatures and decrypt files using the Crypto++ API, but to generate the license files I would want to use a web interface (probably using PHP, which only supports OpenSSL) to generate and encrypt/sign the licenses.
I would write both applications using Crypto++ and call it from the PHP, but since the private key will be stored in a encrypted form, a password must be passed to the application and passing it on the command line doesn't seems to be a good idea to me.
Both Crypto++ and OpenSSL can handle PKCS#8 encoded keys. In crypto++, you can generate keys and convert to PKCS#8 buffer like this,
AutoSeededRandomPool rng;
RSAES_OAEP_SHA_Decryptor priv(rng, 2048);
string der;
StringSink der_sink(der);
priv.DEREncode(der_sink);
der_sink.MessageEnd();
// der.data() is the bytes you need
Now you just need to pass the bytes to PHP. You can save it in a file, send in a message.
The only gotcha is that PHP's OpenSSL interface only accepts PEM encoded PKCS#8. You can easily convert DER-encoded buffer into PEM like this in PHP,
<?php
function pkcs8_to_pem($der) {
static $BEGIN_MARKER = "-----BEGIN PRIVATE KEY-----";
static $END_MARKER = "-----END PRIVATE KEY-----";
$value = base64_encode($der);
$pem = $BEGIN_MARKER . "\n";
$pem .= chunk_split($value, 64, "\n");
$pem .= $END_MARKER . "\n";
return $pem;
}
?>
You can also convert PKCS#8 to PEM in C++ if you prefer. The algorithm is very simple as you can see from the PHP code.
OpenSSL is so prevalent nowadays. I don't see any reason to use Crypto++ for common crypto applications like this.
Is there a way to use the RSA keys I've generated with the Crypto++ API in OpenSSL? What I am looking for is a way to store the keys in a format that both Crypto++ and OpenSSL can easily open them.
Yes. In addition to X.509 and PKCS #8 encoded keys (ZZ Coder's answer), you can also use PEM encoded keys including encrypted keys. Support for PEM encoded keys was donated to the project in July, 2014 for OpenSSL interop.
To use the PEM encoded keys, you need to fetch the Crypto++ PEM Pack and recompile the library. The PEM Pack is not part of the Crypto++ library as provided by Wei Dai at the Crypto++ website.
Once you install and recompile, its as simple as:
// Load a RSA public key
FileSource fs1("rsa-pub.pem", true);
RSA::PublicKey k1;
PEM_Load(fs1, k1);
// Load a encrypted RSA private key
FileSource fs2("rsa-enc-priv.pem", true);
RSA::PrivateKey k2;
PEM_Load(fs2, k2, "test", 4);
// Save an EC public key
DL_PublicKey_EC<ECP> k16 = ...;
FileSink fs16("ec-pub-xxx.pem", true);
PEM_Save(fs16, k16);
// Save an encrypted EC private key
DL_PrivateKey_EC<ECP> k18 = ...;
FileSink fs18("ec-enc-priv-xxx.pem", true);
PEM_Save(fs18, k18, "AES-128-CBC", "test", 4);
The keys look like so on-disk:
$ cat rsa-pub.pem
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCg7ovcljEjZCFOdLWENBKE6FSk
Nke6OP79SMJABJw+JoEBpNddK6/v99IvA1qU76V0V4k8qLvhkVUtk9FArhhRsxeF
1fd8UVqgsT8j0YCVFcJ/ZA372ogpXyvc5aK9mZEiKE5TIF8qnDFFZiMWPrad1buk
hg+eFdo78QRLA5plEQIDAQAB
-----END PUBLIC KEY-----
$
$ cat rsa-enc-priv.pem
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,E1A759E11CA515CE34B6E8CE5278C919
slMx02TMblahTedEKsfS+qYYo4nZFaqI3PhCRYmE5zUa9clHm7yo36wIk3oo52OB
f4AhOaJwiPQAbLe/kDHeP77iHd/4+hFNq/Haj6ahWRpXilLVOETLtefbzSGO8va3
ORnwQpPThs2V0EetPU3LB3QcA/XRjWDzyNa7+LydOjKwbQdZnF/jND5NCkEkncNM
iQJ1VWubN+Xs3Rx0CfLu5Chl1n7WnmCNMtLL/LtYeaR1SlRJa6BaF7hNHJJJ3+Jc
8curCKlpobs+XnlDfjyqgTXolkiepn95TnT7KSqi3BqVEpq/5LKMnkDJg6nwUR7A
w0jLNr1f8adWyBEj2Dp0D/jy8eDh65eHdJw4s8G5FZfBud1zWbvRQ3Ah70ISUKa3
4q/6z2vervPgoc+rMVYDvRf/mqa4LMXYhuygsyx50OgPldCC2d0cVVFCg/ljdEzO
UV5rSkK1Qczv8Nc1ZdY3fJA+qYIV8JqPPY+dJ2312R+myPi5Av0/69k8lZN5eIJk
SkiiFQmabhc+o6z4RFA52a3lOud3eGM9L5nbFQGc5COzQVZ6y8t06tLIp9Y5zjA4
KTgNncV5eq3Bau+cWXjP6pJRixFVfwIoy95mAur7B2P1iE4FXyZbvCovPL6vilT5
kSqAo7Znu0RpTjE36tWY6tFt+GU7k8EBrjA3Qi+8xxqyYtr57Ns+H/j+hhJTN8L7
IXoevwS81OPiB0Dmg6wLLXATG1+gCNXb8sd5U2eJhy4LOJA3y54CTgRnPXtM38CH
K+JvnDstyUl9IGTsgUz51ZzyJNZGU9Ro3pt/a3Cs5IJumaygZ0LQ44WBw9m/vja9
-----END RSA PRIVATE KEY-----
$
$ cat ec-pub.pem
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEVwXjdIb2yy25QbIO0XiIHpySXwSpIAcz
v0Wdyq+fZ6BdJjs2jKvbs9pcRJn8yxlASWoz2R4NoHTZ2YokKsDfEg==
-----END PUBLIC KEY-----
$
$ cat ec-enc-priv.pem
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,F1DBC73E26DCD310888932C2762B3512
nikex48SFvtNOIrOEDipwmxaghjn4jtrvwI3d1H/VNq9yp26WqFZxBJCUPFBFLjH
auA+AHeUo3BVkNQPs0VO4FD5xR50mtc2tCJizzhyTTTypLc3lRkxmD1MpeZnWRy2
70foVtNSvLL/QLJqNJGm/G9kl0xPN4zAfOq7Txoscnk=
-----END EC PRIVATE KEY-----
Related: for other useful Crypto++ patches, see the Category:Patch page on the Crypto++ wiki.
Try this link:
http://www.cryptopp.com/fom-serve/cache/62.html
It looks like you'll need to use PKCS#8 and convert from DER to PEM format to be able to use the keys in OpenSSL. I'm not sure if you'll be able to use a single file for both.
I've only used OpenSSL so I'm not sure what options you have with Crypto++. I found the link above by searching Google for these terms: Crypto++ RSA OpenSSL.
DER is OpenSSL's binary format for keys and certificates.
PEM is OpenSSL's text format.