Reading PEM-formatted RSA keyfile with the OpenSSL C API - c

I am trying to read in an AES-128-CBC encrypted PEM key file generated using Ruby with the OpenSSL API. The code that generated the PEM key is the following:
OpenSSL::PKey::RSA.new(2048).to_pem(OpenSSL::Cipher::AES.new('128-CBC'), "password")
Here's the code that reads the PEM file:
RSA *rsa;
BIO *pem = BIO_new(BIO_s_mem());
BIO_puts(pem, "-----BEGIN RSA PRIVATE KEY-----\n"
"Proc-Type: 4,ENCRYPTED\n"
"DEK-Info: AES-128-CBC,BB13D39833DD6ED1FF9843644E7981EE\n\n
"Eugt8JZNKQKErsabWkwfm3wQhU/Tmp9T0QaP5HM8VIWpZwKlDmRlSUDptADU6RPD\n"
"5VtG3DPieXcf+6deyARImid9sBBmQ9mK2omkNRcTMemqTOhAuaKBu78TMt9G4YSf\n"
"RjoXWSqu3jMwrlcGkpn7bIum8wImITRZ3p28oSzk9aDUNBrIU/2Si8DM4RYIZ/fK\n"
"Uvvgdok9dgcd0SjvucivX2HaGeg/IUz23q1jg9inDpimZvFJD1FJfGEUWDgyfJfa\n"
"M8JIxTKbWOPEopONDkT7u4dC5VcSjK29MVbfd7iCKFPMh5UN+c96rPxTng/OWyW5\n"
"0tvzHyyyvAG9p0Hx5Lr4pDbv21GHyu43sA6wbs9jWyqO3AB7CaoEEQhumwfLsdjj\n"
"YGrX6bWThpYv/XNBDmmvltHlKFfe01NCybivOb4KwBnvi45x21PBqaZCKDTFdEkL\n"
"iwDMTiG2iTxSUvPFLy30VFozE+pGyMcGDUyZDVqjsaqI/MRj8khnn5nyubXc27G3\n"
"8Kbsnlix2SW2M0VDxqiy9dyjcxXrkFRSnOFYVs1PFlgjFVTG4Mwh6CZxKw8mFVbi\n"
"EmLvUYwzoDZ1ve4VXSPp/vrKEh33JuHhM0vJOpqI6wqw0QR0I2o6etM1ZRJClPcw\n"
"VIcgcvwenEgLOkoHDqOr0IZQAtYWvAuqq822wKt258hc6z8+ALQf5iMroqk7ADd4\n"
"FlRLz4XTwqlg7pPtTde/emI1DT8dQWzq++QI0lr0CS/N1GXJKqTQDvauXLIiI3Qy\n"
"KfFYFpV9jyYfRfTjNtisI/edPtp98auK0mb9o/wS/hruFI9behgv63iW1IwAOXCW\n"
"ZlkWgobUH13gS864rL+AcrAXreo2j4dDQouTeRaJUEG0HoYTP65Zun/VsCi2aSOH\n"
"JwSnnmHz9OxvcGY80WJDN3kqOCBRIJoDKBv6jcOxGVCsVK+WSdGZ7cfb8lwp7aA8\n"
"8ND1bwL9FYkwkeIsoakj91iinqv4o3+3PUPgCU5oe68WYvAFjuU+criyf+EhmXJV\n"
"JQ1vFFZPrGzgntJz19uXXh1h2iwQPggRouJm2RozYwvv1nz4eQ40Y3eT1F9UOYJU\n"
"CKEhOtI2NpLeVOayqo8g9wO2oC+CQVhZhdYBE5o7pM7akFnYLvRg9s1UsWdcvT0G\n"
"IpFmejLSRJ/F954aQMHTUc6vBOJZH/VNC5Qt+ulFXl634Sr9wQQK2qlqSJyA04TR\n"
"1ixbCNOX71esvpFImsrlsO5oTA22T3h2GyJPUM10XhqGtDXtsTnal6smLna9U9B3\n"
"gTVxFWWukQOF5Lm8ZFQipo2loHWjkozTBc4REPYP44SoXJXstv7k4pt1cK7x6/2H\n"
"ElspXzjveqMhcrveWv1KaA2OGd+hGfUiNsCoIdapJjLz1Bd/+oIQ/ZWQeo0nRowE\n"
"R/HlbbED3V+fRIdJpgydFEAw6gK5E9sYJcgF7uf/n2NabFxxEZL3g6MJQ64Dtusg\n"
"DEH/MpvIYDSX4Navh1gTwCtOeG1CzW3diYaqbZK+UZCBLFU7j27YvVPSd6F2+Wud\n"
"WnAqU3S5BCPqk5OD3wqZv+sEcqJgGPGy1Gv0tl8ARJomdKAru03KsRn2eIWqR5/C\n"
"-----END RSA PRIVATE KEY-----\n");
// Retrieve RSA key from PEM file.
rsa = PEM_read_bio_RSAPrivateKey(pem, NULL, pem_password_callback, "password");
And here's a dummy password callback (not sure about this function's purpose, but I think it may return the length of the password):
int pem_password_callback(char *buf, int max_len, int flag, void *pwd)
{
return 8;
}
Currently, the rsa = ... part does not throw an error, but doesn't return a well-formed result either.

And here's a dummy password callback (not sure about this function's
purpose, but I think it may return the length of the password):
int pem_password_callback(char *buf, int max_len, int flag, void *pwd)
{
return 8;
}
No, its not a dummy. In your example, you just returned a buffer with 8 junk characters (whatever happened to be in buf).
The password callback is where you programmatically plug in your password. You are supplied with a buff of max_len, and you need to copy the password into the buffer and return the number of bytes copied.
int pem_password_callback(char *buf, int max_len, int flag, void *ctx)
{
const char* PASSWD = "password";
int len = strlen(PASSWD);
if(len > max_len)
return 0;
memcpy(buf, PASSWD, len);
return len;
}
The flag is a read/write flag to denote if you are reading a key or writing a key. In practice, I have never used it.
You will use it similar to:
FILE* file = ...;
EVP_PKEY* pkey = PEM_read_PrivateKey(file, NULL, pem_password_callback, NULL);
Unlike the write routine (which needs an EVP_* cipher), the read routine knows what you used to encrypt the key with because its encoded in the private key.
In my systems, I actually use the context for a label to ensure the same passwords arrive at different derived keys:
EVP_PKEY* pkey = PEM_read_PrivateKey(file, NULL, pem_password_callback, "Some Context");
Then, in my password callback:
int pem_password_callback(char *buf, int max_len, int flag, void *ctx)
{
// "Some Context" in the example above
char* label = (char*)ctx;
// Hash password and label
// ...
// Copy hash to buffer, return length
...
}

Related

In C code using OpenSSL 3, how do you convert DES_crypt to use EVP APIs?

As stated in OpenSSL 3, DES_crypt macro is deprecated.
We are encouraged to use the sequence EVP_EncryptInit_ex(3), EVP_EncryptUpdate(3) and EVP_EncryptFinal_ex(3) as stated here:
https://www.openssl.org/docs/manmaster/man3/DES_crypt.html
However, it doesn't state where the old parameters are going to be placed.
Where is the "buf" and "salt" going on which EVP API? Which is the *In, Key, IV value on the EVP APIs?
Which cipher should I use?
Sample of our old code:
char* theDESCrypt (const char *myBuf, const char *mySalt)
{
// some code here
// ...
return DES_crypt(myBuf, mySalt);
}
This is what I tried to use. Is the placement of buffer and salt correct on this one?
char* theDESCrypt (const char *myBuf, const char *mySalt)
{
int myRetLen = 0;
int myRetLenFinal = 0;
unsigned char *myRet = malloc(strlen(myBuf) + 1);
/* fetch cipher */
EVP_CIPHER *des_cipher = EVP_CIPHER_fetch(NULL, "DES", NULL);
/* setup context */
EVP_CIPHER_CTX *p_Ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(p_Ctx);
/* encrypt data */
EVP_EncryptInit_ex2(p_Ctx, des_cipher, NULL, mySalt, NULL);
EVP_EncryptUpdate(p_Ctx, myRet, &myRetLen, (unsigned char*)myBuf, strlen(myBuf)+1);
EVP_EncryptFinal_ex(p_Ctx, &myRet[myRetLen], &myRetLenFinal);
/* cleanup contexts*/
EVP_CIPHER_free(des_cipher);
EVP_CIPHER_CTX_free(p_Ctx);
return myBuf;
}
I tried using the above code and it fails our unit tests so I'm not sure if my changes are correct.

Write Cert to DER

I'm trying to write a X509 Cert to DER format in memory.
Writing it to a file works perfectly.
I need the Cert in PEM format without the "-----BEGIN PRIVATE KEY-----" header, footer or newlines. I can't figure out how to do it directly so...
I'm outputting to der and base64 encoding.
THIS WORKS.
int X509_to_DER_file(X509 *cert) {
int res=0;
out = BIO_new(BIO_s_file());
if (NULL != out) {
if(BIO_write_filename(out, "my.der") > 0) {
res = i2d_X509_bio(out, cert);
}
BIO_free_all(out);
}
return (tres);
}
THIS DOES NOT.
It returns and mallocs the correct number of bytes and appears to write out to memory correctly but the resulting string is incorrect (the first 15 or so positions are correct).
char *X509_to_DER_mem(X509 *cert) {
char *der = NULL;
bio = BIO_new(BIO_s_mem());
if (NULL != bio) {
//load cert into bio
if (0 == i2d_X509_bio(bio, cert)) {
BIO_flush(bio);
BIO_free(bio);
return NULL;
}
der = (char *) malloc(bio->num_write + 1);
if (NULL == der) {
BIO_free(bio);
return NULL;
}
memset(der, 0, bio->num_write + 1);
BIO_read(bio, der, bio->num_write);
// Appears to work put "der" is incomplete.
BIO_free(bio);
}
return der;
}
It returns and mallocs the correct number of bytes and appears to
write out to memory correctly but the resulting string is incorrect
The result of i2d_X509_bio() is not a (zero-terminated) string, but a bunch of bytes. If you try to write it to a file as a string, it might look incomplete because you might encounter a 0-byte at a location before you reach the end. So in addition to the char * result, your function X509_to_DER_mem() will have to return the number of bytes that make up the result.
With regard to the memory BIO, another way of obtaining its data is with the BIO_get_mem_data() function. Something like this:
char *ptr = NULL;
long len = BIO_get_mem_data(bio, &ptr);
der = malloc(len);
memcpy(der, ptr, len);
Finally, your actual question is
I need the Cert in PEM format without the "-----BEGIN PRIVATE
KEY-----" header, footer or newlines.
Writing the certificate in DER format does not seem to give you what you need. This answer to another SO question explains how you could use the function PEM_read_bio() in combination with EVP_EncodeBlock() for that purpose.

PKCS#7 Signature Verification

I am trying to implement signature verification for PDFs. It is a big topic so I am taking it one step at a time, first I am trying to actually return a positive in the case of a PDF I have signed myself, using all the default values with the current Acrobat — that should be SHA256 for the digest, and a PKCS7 detached signature. So, I crack out openssl, and by reading the byte range given in the PDF and calling the SHA256_* functions I have a hash to compare against. So now I need to read the certificate data etc, and use the PKCS7_* functions. This one looks to be the one I want:
int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, BIO *indata, BIO *out, int flags);
as found in the documentation. Except said documentation doesn't tell me how to construct any of these things. Ok, so I think the BIO *indata can be made with some of the functions in here and the array of certs using these (despite having not worked out the precise details), but what about the PKCS7 *p7, or the STACK_OF(x) called for. I cannot find any documented way of initialising these structures. There are some pkcs7_ctrl functions in the pkcs7.h header:-
long PKCS7_ctrl(PKCS7 *p7, int cmd, long larg, char *parg);
int PKCS7_set_type(PKCS7 *p7, int type);
int PKCS7_set0_type_other(PKCS7 *p7, int type, ASN1_TYPE *other);
int PKCS7_set_content(PKCS7 *p7, PKCS7 *p7_data);
int PKCS7_SIGNER_INFO_set(PKCS7_SIGNER_INFO *p7i, X509 *x509, EVP_PKEY *pkey, const EVP_MD *dgst);
int PKCS7_SIGNER_INFO_sign(PKCS7_SIGNER_INFO *si);
int PKCS7_add_signer(PKCS7 *p7, PKCS7_SIGNER_INFO *p7i);
int PKCS7_add_certificate(PKCS7 *p7, X509 *x509);
int PKCS7_add_crl(PKCS7 *p7, X509_CRL *x509);
int PKCS7_content_new(PKCS7 *p7, int nid);
int PKCS7_dataVerify(X509_STORE *cert_store, X509_STORE_CTX *ctx,
BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si);
int PKCS7_signatureVerify(BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si, X509 *x509);
BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio);
int PKCS7_dataFinal(PKCS7 *p7, BIO *bio);
BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert);
but without some guidelines this doesn't seem like a forest it would be efficacious to start blindly poking around in.
Have I missed something obvious? How do I go about calling this function with the data values I have parsed from the PDF?
Ok, found all this out the (very) hard way. This is how you do it, so that others might learn more easily.
Lets say we have the signature char* sig of length int sig_length, and verification data char* data, int data_length. (There are some subtleties here for PDF signatures but these are well documented in the PDF spec.)
OpenSSL_add_all_algorithms();
OpenSSL_add_all_digests();
EVP_add_digest(EVP_md5());
EVP_add_digest(EVP_sha1());
EVP_add_digest(EVP_sha256());
BIO* sig_BIO = BIO_new_mem_buf(sig, sig_length)
PKCS7* sig_pkcs7 = d2i_PKCS7_bio(sig_BIO, NULL);
BIO* data_BIO = BIO_new_mem_buf(data, data_length)
BIO* data_pkcs7_BIO = PKCS7_dataInit(sig_pkcs7, data_BIO);
// Goto this place in the BIO. Why? No idea!
char unneeded[1024*4];
while (BIO_read(dataPKCS7_BIO, unneeded, sizeof(buffer)) > 0);
int result;
X509_STORE *certificateStore = X509_STORE_new();
X509_STORE_CTX certificateContext;
STACK_OF(PKCS7_SIGNER_INFO) *signerStack = PKCS7_get_signer_info(sig_pkcs7);
int numSignerInfo = sk_PKCS7_SIGNER_INFO_num(signerStack);
for (int i=0; i<numSignerInfo; ++i) {
PKCS7_SIGNER_INFO *signerInfo = sk_PKCS7_SIGNER_INFO_value(signerStack, i);
result = PKCS7_dataVerify(certificateStore, &certificateContext, data_pkcs7_BIO, sig_pkcs7, signerInfo);
}
X509_STORE_CTX_cleanup(&certificateContext);
BIO_free(sig_BIO);
BIO_free(data_BIO);
BIO_free(data_pkcs7_BIO);
PKCS7_free(sig_pkcs7);
X509_STORE_free(certificateStore);
The function that does the work is actually PKCS7_dataVerify, and you don't need to run any digests yourself.
But wait, if you try this, it won't work! Why? Because the verification does both trust and integrity. In addition to this, you will also need to either establish trust by adding certs to the store, which is also complicated and undocumented. If you want fine grain results for you'll want to set a callback on the verification via the certificate store like this:
X509_VERIFY_PARAM_set_flags(certificateStore->param, X509_V_FLAG_CB_ISSUER_CHECK);
X509_STORE_set_verify_cb_func(certificateStore, verificationCallback);
where
static int verificationCallback(int ok, X509_STORE_CTX *ctx) {
switch (ctx->error)
{
case X509_V_ERR_INVALID_PURPOSE: //...
case X509_V_ERR_CERT_HAS_EXPIRED: //...
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: //...
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: //...
// ... etc
default: break;
}
return ok;
}
You can set the error to ok and tell it to verify, for example if you want to ignore expired certs:
static int verificationCallback(int ok, X509_STORE_CTX *ctx) {
switch (ctx->error)
{
case X509_V_ERR_CERT_HAS_EXPIRED:
X509_STORE_CTX_set_error(ctx, X509_V_OK);
ok = 1;
break;
}
return ok;
}

Reading Public/Private Key from Memory with OpenSSL

I am using Public/Private Keys in my project to encrypt/decrypt some data.
I am hosting a public key ("public.pem") on a server.
"public.pem" looks like this:
-----BEGIN PUBLIC KEY-----
.....
.....
-----END PUBLIC KEY-----
I wrote a client side that downloads this public key and save it to disk and then calls OpenSSL's PEM_read_RSA_PUBKEY() with a File descriptor to that file.
This operation works great and the result is an RSA object that is ready for encryption.
I would like to avoid writing the public key to disk each time (since i have the buffer in memory already).
How can i do the same operation without saving the buffer to disk?
I noticed a function called: PEM_read_bio_RSAPublicKey() but i am not sure of it's usage of BIO structure. Am I on the right path?
So the real question would be: How do I read a public/private key to an RSA object straight from memory and not from a file descriptor.
You are on the right track. You must wrap the PEM key already in memory by means of a BIO buffer via BIO_new_mem_buf(). In other words, something like:
BIO *bufio;
RSA *rsa
bufio = BIO_new_mem_buf((void*)pem_key_buffer, pem_key_buffer_len);
PEM_read_bio_RSAPublicKey(bufio, &rsa, 0, NULL);
The same approach is valid for an RSA private key (via PEM_read_bio_RSAPrivateKey), but in that case you most certainly need to cater for the pass phrase. Check the man page for details.
SquareRootOfTwentyThree's method not work for me. Here is my solution.
BIO* bio = BIO_new(BIO_s_mem());
int len = BIO_write(bio, pem_key_buffer, pem_key_buffer_len);
EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
RSA* rsa = EVP_PKEY_get1_RSA(evp_key);
Here's complete example, showing embedded key, and how to use C++11 unique pointers
to manage OpenSSL resources.
Updated: following on from comments by spectras. No longer using specialisation
of default_delete<T>.
/* compile with:
c++ -Wall -pedantic -std=c++17 main.cc -lssl -lcrypto -o main
*/
#include <memory>
#include <iostream>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <assert.h>
#include <string.h>
/* Custom deletors for use with unique_ptr */
struct EVP_PKEY_deleter {
void operator()(EVP_PKEY* p) const {
if (p)
EVP_PKEY_free(p);
}
};
struct BIO_deleter {
void operator()(BIO* p) const {
if (p)
BIO_free(p);
}
};
/* Smart pointers wrapping OpenSSL resources */
using evp_key_ptr = std::unique_ptr<EVP_PKEY, EVP_PKEY_deleter>;
using bio_ptr = std::unique_ptr<BIO, BIO_deleter>;
/* Create key based on memory contents */
evp_key_ptr load_public_key(const char* buf, size_t len)
{
bio_ptr bp (BIO_new_mem_buf((void*) buf, len));
if (!bp)
throw std::runtime_error("BIO_new_mem_buf failed");
EVP_PKEY * kp = nullptr;
kp = PEM_read_bio_PUBKEY(bp.get(), &kp, nullptr, nullptr);
ERR_print_errors_fp(stderr);
return evp_key_ptr{kp};
}
int main()
{
const char * RSA_PUBLIC_KEY=R"(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA80ZqDPPW5eOH6TWdLsEJ
8qf6hoMJfFZ3BL9Fz+YNGeBpF3zxKmm8UuRrBHHVZZB2Gs1MTo06IU3fqDfFsOyh
J6pHeJF3wyUlYZuYbGAyMlZZ/+M5TOvo92f7lt/A40QThCVf1vS5o+V8sFkgnz3N
C7+VvC4dYrv+fwnmnWGxPy1qfp3orB+81S4OPRiaoy+cQBZs10KCQaNBI/Upzl2R
3dMkWKM+6yQViKTHavT4DRRZ1MKp9995qOR3XfhhJdWuDl4moXcU3RcX4kluvS5q
b8oTnVyd2QB1GkUw6OKLWB/5jN1V1WzeYK447x2h4aPmJfsn5gCFJs6deq2RFQBR
SQIDAQAB
-----END PUBLIC KEY-----
)";
ERR_load_crypto_strings();
ERR_free_strings();
auto pubkey = load_public_key(RSA_PUBLIC_KEY, strlen(RSA_PUBLIC_KEY));
if (pubkey)
std::cout << "load_public_key success" << std::endl;
}

Openssl/libcrypto AES 128 encoding using the KEY

I am encrypting a certain string using AES-128-ECB and then save the result in a file e.g test.enc
Here is my method that does the encryption:
int do_crypt(char *outfile) {
unsigned char outbuf[1024];
int outlen, tmplen;
unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
char intext[] = "Some Text";
EVP_CIPHER_CTX ctx;
FILE *out;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, key, NULL);
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext))) {
/* Error */
return 0;
}
/* Buffer passed to EVP_EncryptFinal() must be after data just
* encrypted to avoid overwriting it.
*/
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
{
/* Error */
return 0;
}
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
/* Need binary mode for fopen because encrypted data is
* binary data. Also cannot use strlen() on it because
* it wont be null terminated and may contain embedded
* nulls.
*/
out = fopen(outfile, "wb");
fwrite(outbuf, 1, outlen, out);
fclose(out);
return 1;
}
As you can see the key is the actual password and in order to decode the encrypted file following command line should be executed:
openssl aes-128-ecb -in test.enc -K 000102030405060708090A0B0C0D0E0F -d
"000102030405060708090A0B0C0D0E0F" is a password hex representation I use in the code above 0123456789191112131415. As I understand the password can be encrypted as well using MD5 algorithm.
The question is how to encrypt data using actual KEY derived from password and not the password itself using libcrypto?
Take a look at EVP_BytesToKey.
My comments in an old app tell me that BytesToKey is out of date and you should perhaps consider looking at PKCS5_v2_PBE_keyivgen or similar. But essentially, a highly simplified way of doing it is you derive your Key as a hash from your password and a suitable salt value:
EVP_DigestInit_ex(...)
EVP_DigestUpdate(...Password...)
EVP_DigestUpdate(...Salt...)
EVP_DigestFinal_ex(...)
then you use the newly generated Key to derive your IV by:
EVP_DigestInit_ex(...)
EVP_DigestUpdate(...Key...)
EVP_DigestUpdate(...Password...)
EVP_DigestUpdate(...Salt...)
EVP_DigestFinal_ex(...)
A browse of the OpenSSL source code is most useful for looking up stuff like this.
Disclaimer: I'm not a C programmer (the app in question was Delphi using OpenSSL DLLs) nor a security expert so take these suggestions as a starting point, read the proper docs and use proper functions where possible!!...

Resources