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.
Related
Hello i want my function to write a pem file from my RSA.
void write_privatekey(RSA *rsa, BIO *keybio)
{
EVP_PKEY *pkey;
BIO *bio_private;
pkey = PEM_read_bio_PrivateKey(keybio, &pkey, 0, 0);
bio_private = BIO_new_file("private_new.pem", "w+");
PEM_write_bio_PrivateKey(bio_private, pkey, NULL, NULL, 0, NULL, NULL);
}
but when i try to run this code it segfault
[1] 48767 segmentation fault ./corsair public.key
The OpenSSL APIs are not the most intuitive to use. However it should be a huge warning sign for you, that you passed a pointer to pkey to PEM_read_bio_PrivateKey and also assign its return value to it.
If you look at the reference manual the suggested stanza is
key = PEM_read_bio_PrivateKey(bp, NULL, pass_cb, …);
if( key == NULL ){
/* Error */
}
Your code snipped lacks a couple of things: It doesn't provide a pass phrase callback and it doesn't perform error checking. You absolutely must do both things.
After messing with the OpenSSL lib. and learned a way to dive into it documentation.
here it is the answer to write the PEM file from RSA object.
BIO *bio_private;
bio_private = BIO_new_file("private_new.pem", "w+");
PEM_write_bio_RSAPrivateKey(bio_private, rsa, 0, 0, 0, 0, 0);
I have the following code trying to generate a DSA key pair.
OpenSSL_add_all_algorithms();
ctx=EVP_PKEY_CTX_new_id(EVP_PKEY_DSA,NULL); EVP_PKEY_keygen_init(ctx);
if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx,1024)<=0) ERR_print_errors_fp(stderr);
and I get the following error
3073906944:error:06089094:digital envelope
routines:EVP_PKEY_CTX_ctrl:invalid operation:pmeth_lib.c:398:
Any clue on what I am doing wrong? thanks
You need two contexts; one for the params, and one for the actual keygen. You should do the following two sets of operations :
Parameter Generation
Create a param generator using EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL)
Set the bits in the param generator context using EVP_PKEY_CTX_set_dsa_paramgen_bits; not the keygen context (which, if you'r'e doing this right, doesn't even exist yet).
Initialize the param generator using EVP_PKEY_paramgen_init
Finally, generate the parameters using EVP_PKEY_paramgen. The result is a EVP_PKEY object (I'll call it pkey_params) that contains the input parameters for the upcoming key generation
Once the above is done, then you move on to the actual key generation, which is considerably simpler:
Key Generation
Create a new context using EVP_PKEY_CTX_new(pkey_params, NULL) Note the pkey_params is from the prior series of steps.
Initialize the generator context using EVP_PKEY_keygen_init
Generate the actual key using EVP_PKEY_keygen
Once done, all resources except the final pkey from above should be properly destroyed. Free the final key once done using it. That's it.
Example
This has no error checking whatsoever, but the order of operations is what is important here. So pay attention to that.
#include <stdio.h>
#include <openssl/evp.h>
#include <openssl/dsa.h>
#include <openssl/pem.h>
// required for any BIO standard stream IO.
#include <openssl/applink.c>
int main()
{
OPENSSL_init();
OpenSSL_add_all_algorithms();
// build parameters first
EVP_PKEY_CTX *ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
EVP_PKEY_paramgen_init(ctx_params);
EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx_params, 1024);
EVP_PKEY* pkey_params = NULL;
EVP_PKEY_paramgen(ctx_params, &pkey_params);
// using parameters, build DSA keypair
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey_params, NULL);
EVP_PKEY_keygen_init(ctx);
EVP_PKEY* pkey = NULL;
EVP_PKEY_keygen(ctx, &pkey);
// cleanup everything but the final key
EVP_PKEY_free(pkey_params);
EVP_PKEY_CTX_free(ctx_params);
EVP_PKEY_CTX_free(ctx);
// TODO: whatever you want with the generator pkey. in this
// example we're just dumping the full unencrypted key to
// stdout.
DSA* dsa = EVP_PKEY_get1_DSA(pkey);
BIO* bio = BIO_new_fp(stdout, BIO_NOCLOSE);
PEM_write_bio_DSAPrivateKey(bio, dsa, NULL, NULL, 0, NULL, NULL);
BIO_flush(bio);
DSA_free(dsa);
EVP_PKEY_free(pkey);
return 0;
}
Output (varies, obviously)
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQDJ+NoL8SZeTcqVA83WI7CCO6INYLw18DiALLMewPqXEPm99mof
RX2693WJfpbWIjuHi/KXzH6vQ/0sQU+2z1CqgWhudVhQTofGNcsPrUbPpShTDMcP
OoTx9dRb8rXWbxg7dfhGZ9z2pEhzRtPWpI2y81VxYhGXzVSC3zqW6+ec2QIVALaE
fynSMqc56gPqDPZfRz1rlq3dAoGAL+vbbYu+gSy8zGqoLykqhG+Vl4/Eh/zQIWoB
t64bfh7GU6o0wvgTQgcdGZK3/laa9Msa6J3iEGZcP3dd9x4fTQ5vzxDGIYikcC8I
L8s2JbNi1Jxbr5dw3/sOKsdHIt95rFZ03+gMzaV+9pc8LpATnaXMtp5mmH+lRgsJ
SIEdLqcCgYAVGpwZHaFUnttqQAf3/ohMtqIQG+RBp/yUf2EA7rcoHpA7bCBADApx
mG5hH/F4dKjCSciKdHq4Ibf60ctAJNL2sobPKNArTMo/GNuzE+J79Wj6s/b7zwt7
AF+27H9PAiXB08ftMmCSesXkX7v926EHRxDgSlVAgCPSfkXKNQn1XwIVALF2MF2N
GRdMtFUxZFnIk2GnqC1R
-----END DSA PRIVATE KEY-----
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.
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
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.