openSSL: how to initialize keys for public key encryption? - c

For using openSSL API for public key encryption, how is the key (public & private) initialized in a C program, given private key in *.key file format, and public key in *.pem file format:
EVP_PKEY *key;
/* How is key initialized ?
*/
ctx = EVP_PKEY_CTX_new(key);
Thanks.

try this:
EVP_PKEY *pkey;
FILE *f = fopen("<path for your PEM or DER encoded key>", "rb");
if (f == NULL){
// error handling...
}
//if your key is PEM encoded use this
pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL); // pkey now contains the pubKey.
//We are passing NULL to the others parameters because we dont need password to read a public key
//if your key is DER encoded use this
pkey = d2i_PUBKEY_fp(f, NULL);
if (pkey == NULL){
// error handling...
}
I didnt test but should work.

Related

OpenSSL C API - getting keylength of public key in DER encoding

I would like to determine the key length of a public key encoded in DER format while using the OpenSSL API in C.
It is quite straight-forward to do this with a key encoded in PEM format:
FILE *fp = fopen(argv[1], "rb");
RSA *rsa_key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
int keylength = RSA_size(rsa_key);
(assuming that the key to be loaded has been passed as an argument)
Unfortunately there is no equivalent DER_read_RSA_PUBKEY function.
I have been trying it with i2d_RSA_PUBKEY_fp and d2i_RSA_PUBKEY_fp but to no avail - only segmentation faults.
The difference between _RSA_PUBKEY and _RSAPublicKey seems to be that the former uses a SubjectPublicKeyInfo (certificate public key) structure and the latter uses a PKCS#1 RSAPublicKey structure.
The d2i_* function family is available for converting DER to openssl internal datastructures.
So, you should be able to use:
FILE *fp = fopen(argv[1], "rb");
RSA *rsa_key = d2i_RSAPublicKey_fp(fp, NULL);
int keylength = RSA_size(rsa_key);
to achieve what you want.

How to extract public key from private key using openssl in C

I know in command line that given a pem file of private key, you can extract the public key from it.
Wondering how the similar function can be achieved using openssl's C library?
First, read the key:
EVP_PKEY *key;
FILE *fpkey;
OpenSSL_add_all_algorithms();
key = EVP_PKEY_new();
fpkey = fopen ("key.pem", "r");
PEM_read_PrivateKey(fpkey, &key, NULL, NULL);
fclose(fpkey);
Now that you have the EVP_PKEY structure, you can save the public key:
FILE* fppubkey = fopen("pubkey.pem", "wb");
PEM_write_PUBKEY(fppubkey, key);
fclose(fppubkey);
Make sure to add error checking.

Extracting the public certificate from a certificate

I would like to programmatically generate a certificate that contain only the public key using openssl.
So I basically need to do the following but using code.
openssl x509 -in certificate.crt -pubkey
Digging a bit it seem that I need to create a new certificate and copy all the information except the private key in it.
X509 * pNewKey = X509_new();
... Copy all the needed information ...
PEM_write_bio_X509(bioKey, &pNewKey, NULL, NULL);
Does someone have a better method?
I would like to programmatically generate a certificate that contain only the public key
To set the public key, take your public key in EVP_PKEY format and call X509_set_pubkey. Building upon your example code:
X509* x509 = X509_new();
EVP_PKEY* pkey = /***/;
int rc = X509_set_pubkey(x509, pkey);
ASSERT(rc == 1);
BIO* bio = BIO_new_file("filename.txt", "w");
ASSERT(bio != NULL);
rc = PEM_write_bio_X509(bio, x509, NULL, NULL);
ASSERT(rc == 1);
...
BIO_free(bio);
EVP_PKEY_free(pkey);
X509_free(x509);
For completeness, OpenSSL will save a certificate with no attributes, no version, no public key, and no signature.
So I basically need to do the following but using code.
openssl x509 -in certificate.crt -pubkey
OK, this is a different question. The above command will print the public key.
Use x509_get_pubkey:
EVP_PKEY* pkey = X509_get_pubkey(X509* x509);
Once you have the EVP_PKEY, you can get the type. You need to supply this function because OpenSSL does not provide it:
int EVP_PKEY_get_type(EVP_PKEY *pkey)
{
AC_ASSERT(pkey);
if (!pkey)
return NID_undef;
return EVP_PKEY_type(pkey->type);
}
Then, depending on the type, you can print the key:
if(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA2)
{
RSA* rsa = EVP_PKEY_get1_RSA(pkey);
RSA_print_fp(stdout, rsa, 0);
RSA_free(rsa);
}
...
You have to break out the keys by type and then call the respective XXX_print_fp because OpenSSL does not provide an EVP_PKEY_print_fp.
The get1 in EVP_PKEY_get1_RSA means the reference count on the key is bumped. So you have to call a *_free on it. A get0 does not bump the reference count, and does not need a call to *_free.

How does one access the raw ECDH public key, private key and params inside OpenSSL's EVP_PKEY structure?

I'm using OpenSSL's c library to generate an elliptic curve Diffie-Hellman (ECDH) key pair, following the first code sample here. It glosses over the actual exchange of public keys with this line:
peerkey = get_peerkey(pkey);
The pkey variable and the return value are both of type EVP *. pkey contains the public key, private key, and params generated earlier, and the return value only contains the peer's public key. So this raises three questions:
How would get_peerkey() actually extract just the public key from pkey for sending to the peer?
How would the code extract the private key and params from pKey to store them for later use after the key exchange?
How would get_peerkey() generate a new EVP_PKEY structure from the peer's raw public key?
I've seen the OpenSSL functions EVP_PKEY_print_public(), EVP_PKEY_print_private(), and EVP_PKEY_print_params() but these are for generating human-readable output. And I haven't found any equivalent for converting a human-readable public key back into an EVP_PKEY structure.
To answer my own question, there's a different path for the private key and the public key.
To serialize the public key:
Pass the EVP_PKEY to EVP_PKEY_get1_EC_KEY() to get an EC_KEY.
Pass the EC_KEY to EC_KEY_get0_public_key() to get an EC_POINT.
Pass the EC_POINT to EC_POINT_point2oct() to get octets, which are just unsigned char *.
To deserialize the public key:
Pass the octets to EC_POINT_oct2point() to get an EC_POINT.
Pass the EC_POINT to EC_KEY_set_public_key() to get an EC_KEY.
Pass the EC_KEY to EVP_PKEY_set1_EC_KEY to get an EVP_KEY.
To serialize the private key:
Pass the EVP_PKEY to EVP_PKEY_get1_EC_KEY() to get an EC_KEY.
Pass the EC_KEY to EC_KEY_get0_private_key() to get a BIGNUM.
Pass the BIGNUM to BN_bn2mpi() to get an mpi, which is a format written to
unsigned char *.
To deserialize the private key:
Pass the mpi to BN_mpi2bn() to get a BIGNUM.
Pass the BIGNUM to EC_KEY_set_private_key() to get an EC_KEY.
Pass the EC_KEY to EVP_PKEY_set1_EC_KEY to get an EVP_KEY.
It is also possible to convert the BIGNUM to hex, decimal, or "bin", although I think that mpi used the fewest bytes.
OpenSSL 3.x.x
To serialize the public key:
// We assume the public and private keys have been already generated.
// EVP_PKEY* keyPair...
// Get the serialized public key length.
size_t serializedPublicKeyLen = 0;
if (EVP_PKEY_get_octet_string_param(keyPair, OSSL_PKEY_PARAM_PUB_KEY,
NULL, 0, &serializedPublicKeyLen) != 1) {
return;
}
// Allocate memory for the serialized public key.
unsigned char* serializedPublicKey = (unsigned char*)OPENSSL_malloc(serializedPublicKeyLen);
if (serializedPublicKey == NULL) {
return;
}
// Get the serialized public key.
if (EVP_PKEY_get_octet_string_param(keyPair, OSSL_PKEY_PARAM_PUB_KEY,
serializedPublicKey, serializedPublicKeyLen, &serializedPublicKeyLen) != 1) {
return;
}
// Deallocate the memory when you finish using the serialized public key.
OPENSSL_free(serializedPublicKey);
To deserialize the public key:
// A parameter build for the public key.
OSSL_PARAM_BLD* paramBuild = OSSL_PARAM_BLD_new();
if (paramBuild == NULL) {
return;
}
// This is just an example. Set the curve
// you used to generate the public and private keys.
const char curveName[] = "secp384r1";
// Set the curve name to the parameter build.
if (OSSL_PARAM_BLD_push_utf8_string(paramBuild,
OSSL_PKEY_PARAM_GROUP_NAME, curveName, 0) != 1) {
OSSL_PARAM_BLD_free(paramBuild);
return;
}
// Set the serialized public key.
if (OSSL_PARAM_BLD_push_octet_string(paramBuild, OSSL_PKEY_PARAM_PUB_KEY,
serializedPublicKey, serializedPublicKeyLen) != 1) {
OSSL_PARAM_BLD_free(paramBuild);
return;
}
// Convert the OSSL_PARAM_BLD to an OSSL_PARAM.
OSSL_PARAM* params = OSSL_PARAM_BLD_to_param(paramBuild);
if (params == NULL) {
OSSL_PARAM_BLD_free(paramBuild);
return;
}
// Create a EVP_PKEY context.
EVP_PKEY_CTX* publicKeyCtx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (publicKeyCtx == NULL) {
OSSL_PARAM_BLD_free(paramBuild);
OSSL_PARAM_free(params);
return;
}
// Initialize the EVP_PKEY context.
if (EVP_PKEY_fromdata_init(publicKeyCtx) <= 0) {
OSSL_PARAM_BLD_free(paramBuild);
OSSL_PARAM_free(params);
EVP_PKEY_CTX_free(publicKeyCtx);
return;
}
// Create the peer public key object.
EVP_PKEY* publicKey = NULL;
if (EVP_PKEY_fromdata(publicKeyCtx, &publicKey,
EVP_PKEY_PUBLIC_KEY, params) <= 0) {
OSSL_PARAM_BLD_free(paramBuild);
OSSL_PARAM_free(params);
EVP_PKEY_CTX_free(publicKeyCtx);
return;
}
// Free auxiliary things...
OSSL_PARAM_BLD_free(paramBuild);
OSSL_PARAM_free(params);
EVP_PKEY_CTX_free(publicKeyCtx);
// Now you can use publicKey for EVP_PKEY_derive_set_peer.
// Call EVP_PKEY_free when you finish using it.
A full C++ OpenSSL 3 example: ec-diffie-hellman-openssl.h, ec-diffie-hellman-openssl.cpp.
A full C++ LibreSSL 3/OpenSSL 1 example: ec-diffie-hellman-libressl.h, ec-diffie-hellman-libressl.cpp.
To serialize the private key, you get BIGNUM instead:
BIGNUM* privateKey = NULL;
EVP_PKEY_get_bn_param(keyPair, OSSL_PKEY_PARAM_PRIV_KEY, &privateKey);
Then, you use one of the BIGNUM serialization function: https://www.openssl.org/docs/man3.0/man3/BN_bn2bin.html
To deserialize the private key, you use one of the BIGNUM deserialization function from the link above, then push it to the parameter build via OSSL_PARAM_BLD_push_BN with OSSL_PKEY_PARAM_PRIV_KEY.
The implementation above seems too complicated. openssl/evp.h has functions i2d_PublicKey() and d2i_PublicKey() to respectively convert to and from a binary representation of the public key (and there are equivalent functions for the private key - see: https://www.openssl.org/docs/manmaster/man3/d2i_PublicKey.html)
A small code example:
vector<unsigned char> ecdhPubkeyData(EVP_PKEY *key)
{
int len = i2d_PublicKey(key, 0); // with 0 as second arg it gives length
vector<unsigned char> ret(len);
unsigned char *ptr = ret.data();
len = i2d_PublicKey(key, &ptr);
return ret;
}
// Make sure you free the returned pointer when you are done with it
EVP_PKEY *ecdhPubkeyFromData(vector <unsigned char> const &pubkeyData)
{ // You do need to put in in an existing EVP_PKEY that is assigned
// an EC_KEY, because it needs to know what curve you use
EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
EVP_PKEY *ret = EVP_PKEY_new();
EVP_PKEY_assign_EC_KEY(ret, ec_key);
unsigned char const *ptr = pubkeyData.data();
d2i_PublicKey(EVP_PKEY_EC, &ret, &ptr, pubkeyData.size());
return ret;
}
// PS: In a real example you want to check if any of these functions
// return NULL or some error code
I am using C++ vectors to contain the binary data, but of course you could just use C-style arrays too :-)
I am absolutely not an OpenSSL expert, so let me know if I am doing something horribly wrong in this implementation :-p

How do I decrypt a private key file and sign some text using openssl calls in C?

I have 2 separate programs (spliced together below). The first generates the key pair and saves to files (works fine). The second opens the private key, decrypting with a pass phrase and then I need it to sign a string of text. The code below fails on the PEM_read_PrivateKey() (last) call (can't see why). Can anyone point me at what I am doing wrong and then what openssl calls I should make to use the private key to sign some text?
int main (int argc, char *argv[])
{
char *priv_pem = "priv.pem";
char *pub_pem = "pub.pem";
char *pass = "Password";
FILE *fp;
int bits = 4096;
unsigned long exp = RSA_F4;
RSA *rsa;
EVP_PKEY *pkey;
// GENERATE KEY
rsa=RSA_generate_key(bits,exp,NULL,NULL);
if (RSA_check_key(rsa)!=1)
Exit(1,"Error whilst checking key","");
pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
// WRITE ENCRYPTED PRIVATE KEY
if (!(fp = fopen(priv_pem, "w")))
Exit(2,"Error opening PEM file",priv_pem);
if (!PEM_write_PrivateKey(fp,pkey,EVP_aes_256_cbc(),NULL,0,NULL,pass))
Exit(3,"Error writing PEM file",priv_pem);
fclose(fp);
// WRITE PUBLIC KEY
if (!(fp = fopen(pub_pem, "w")))
Exit(4,"Error opening PEM file",pub_pem);
if (!PEM_write_PUBKEY(fp, pkey))
Exit(5,"Error writing PEM file",pub_pem);
fclose(fp);
// ------- End of key generation program -------
// ------- Start of text signing program -------
// READ IN ENCRYPTED PRIVATE KEY
if (!(fp = fopen(priv_pem, "r")))
Exit(6,"Error reading encrypted private key file",priv_pem);
if (!PEM_read_PrivateKey(fp,&pkey,NULL,pass))
Exit(7,"Error decrypting private key file",priv_pem);
fclose(fp);
// Sign some text using the private key....
// FREE
RSA_free(rsa);
return 0;
}
Have you initialised pkey to NULL before you pass &pkey to PEM_read_PrivateKey()? If not, it will attempt to re-use the EVP_PKEY structure that pkey points to - and if pkey is uninitialised, it will be looking at a random spot in memory.
You can use ERR_print_errors_fp(stderr); to dump the OpenSSL error stack to stderr when an error occurs - this is often helpful in finding the problem.
Thanks #caf for your help.
By trial and error I fixed PEM_read_PrivateKey() error by adding the following to the start:
if (EVP_get_cipherbyname("aes-256-cbc") == NULL)
OpenSSL_add_all_algorithms();
However, I'm still looking for the best (practice) way of generating the keys and then using the private key for signing. From my limited understanding, I am looking for openssl methods that adhere to RSA's "PKCS #1 v2.0: RSA Cryptography Standard"

Resources