diff --git a/example/client-simple/estclient-simple.c b/example/client-simple/estclient-simple.c index 852831a..baa25ff 100644 --- a/example/client-simple/estclient-simple.c +++ b/example/client-simple/estclient-simple.c @@ -68,81 +68,6 @@ static void show_usage_and_exit (void) } -/* - * This function generates an EC public/private key - * pair that will be used with the certificate - * we provision. - */ -static EVP_PKEY * generate_private_key (void) -{ - EC_KEY *eckey; - EC_GROUP *group = NULL; - BIO *out; - unsigned char *tdata; - unsigned char *key_data; - char file_name[MAX_FILENAME_LEN] = "./new_key.pem"; - int key_len; - BIO *keyin; - EVP_PKEY *new_priv_key; - int asn1_flag = OPENSSL_EC_NAMED_CURVE; - point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED; - - /* - * Generate an EC key - */ - - group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); - EC_GROUP_set_asn1_flag(group, asn1_flag); - EC_GROUP_set_point_conversion_form(group, form); - eckey = EC_KEY_new(); - EC_KEY_set_group(eckey, group); - if (!EC_KEY_generate_key(eckey)) { - printf("Failed to generate EC key\n"); - exit(1); - } - out = BIO_new(BIO_s_mem()); - PEM_write_bio_ECPKParameters(out, group); - PEM_write_bio_ECPrivateKey(out, eckey, NULL, NULL, 0, NULL, NULL); - key_len = BIO_get_mem_data(out, &tdata); - key_data = malloc(key_len+1); - memcpy(key_data, tdata, key_len); - EC_KEY_free(eckey); - BIO_free(out); - - - /* - * We'll write this out to a local file called new_key.pem. - * Your application should persist the key somewhere safe. - */ - write_binary_file(file_name, key_data, key_len); - free(key_data); - - /* - * read it back in to an EVP_PKEY struct - */ - keyin = BIO_new(BIO_s_file_internal()); - if (BIO_read_filename(keyin, file_name) <= 0) { - printf("\nUnable to read newly generated client private key file %s\n", file_name); - exit(1); - } - - /* - * This reads in the private key file, which is expected to be a PEM - * encoded private key. If using DER encoding, you would invoke - * d2i_PrivateKey_bio() instead. - */ - new_priv_key = PEM_read_bio_PrivateKey(keyin, NULL, NULL, NULL); - if (new_priv_key == NULL) { - printf("\nError while reading PEM encoded private key file %s\n", file_name); - ERR_print_errors_fp(stderr); - exit(1); - } - BIO_free(keyin); - - return (new_priv_key); -} - - /* * auth_credentials_token_cb() is the application layer callback function that will * return a token based authentication credential when called. It's registered @@ -359,10 +284,20 @@ int main (int argc, char **argv) /* * Create a public/private key pair that will be used for - * the enrollment. We'll write this out to a local - * file called new_key.pem. + * the enrollment. + */ + char *key_data = ossl_generate_private_EC_key(NID_X9_62_prime256v1, NULL/* no password_cb */); + /* + * We write this out to a local file called new_key.pem. + * Your application should persist the key somewhere safe. */ - key = generate_private_key(); + write_binary_file("./new_key.pem", (unsigned char *)key_data, strlen(key_data)); + key = ossl_load_private_key_PEM(key_data); // not using password_cb + if (!key) { + printf("\nUnable to convert newly created key from PEM\n"); + exit(1); + } + free(key_data); ectx = setup_est_context(); if (!ectx) { diff --git a/example/client/estclient.c b/example/client/estclient.c index c51fdd0..11c84cb 100644 --- a/example/client/estclient.c +++ b/example/client/estclient.c @@ -19,6 +19,7 @@ #include #include #include +#include // for PEM_def_callback #include #include #include @@ -31,6 +32,7 @@ #define MAX_SERVER_LEN 255 #define MAX_FILENAME_LEN 255 #define MAX_CN 64 +#define PRIV_KEY_PASS "pass:" /* * Global variables to hold command line options @@ -52,8 +54,6 @@ static char priv_key_file[MAX_FILENAME_LEN]; static char client_key_file[MAX_FILENAME_LEN]; static char client_cert_file[MAX_FILENAME_LEN]; static int read_timeout = EST_SSL_READ_TIMEOUT_DEF; -static unsigned char *new_pkey = NULL; -static int new_pkey_len = 0; static unsigned char *cacerts = NULL; static int cacerts_len = 0; static char out_dir[MAX_FILENAME_LEN]; @@ -67,6 +67,8 @@ static unsigned char *c_key = NULL; static int c_cert_len = 0; static int c_key_len = 0; +static char priv_key_pwd[MAX_PWD_LEN]; +static pem_password_cb *priv_key_cb = NULL; EVP_PKEY *priv_key = NULL; EVP_PKEY *client_priv_key = NULL; X509 *client_cert = NULL; @@ -115,6 +117,7 @@ static void show_usage_and_exit (void) " --common-name Specify the common name to use in the Suject Name field of the new certificate.\n" " 127.0.0.1 will be used if this option is not specified\n" " --pem-output Convert the new certificate to PEM format\n" + " --keypass stdin|"PRIV_KEY_PASS" Specify en-/decryption of private key, password read from STDIN or argument\n" " --srp Enable TLS-SRP cipher suites. Use with --srp-user and --srp-password options\n" " --srp-user Specify the SRP user name\n" " --srp-password Specify the SRP password\n" @@ -124,32 +127,6 @@ static void show_usage_and_exit (void) } -/* - * When the -x option isn't used from the CLI, we will implicitly generate - * an RSA key to be used to sign the CSR. - */ -static unsigned char * generate_private_key (int *key_len) -{ - RSA *rsa = RSA_new(); - BIGNUM *bn = BN_new(); - BIO *out; - unsigned char *tdata; - unsigned char *key_data; - - BN_set_word(bn, 0x10001); - - RSA_generate_key_ex(rsa, SRP_MINIMAL_N, bn, NULL); - out = BIO_new(BIO_s_mem()); - PEM_write_bio_RSAPrivateKey(out, rsa, NULL, NULL, 0, NULL, NULL); - *key_len = BIO_get_mem_data(out, &tdata); - key_data = malloc(*key_len + 1); - memcpy(key_data, tdata, *key_len); - BIO_free(out); - RSA_free(rsa); - BN_free(bn); - return (key_data); -} - /* * Takes as input the name of the file to write the cert to on the * local file system (full path name expected). @@ -407,35 +384,6 @@ static int populate_x509_csr (X509_REQ *req, EVP_PKEY *pkey, char *cn) return (0); } -static EVP_PKEY *read_private_key (char *key_file) -{ - BIO *keyin; - EVP_PKEY *priv_key; - - /* - * Read in the private key - */ - keyin = BIO_new(BIO_s_file_internal()); - if (BIO_read_filename(keyin, key_file) <= 0) { - printf("\nUnable to read private key file %s\n", key_file); - return (NULL); - } - /* - * This reads in the private key file, which is expected to be a PEM - * encoded private key. If using DER encoding, you would invoke - * d2i_PrivateKey_bio() instead. - */ - priv_key = PEM_read_bio_PrivateKey(keyin, NULL, NULL, NULL); - if (priv_key == NULL) { - printf("\nError while reading PEM encoded private key file %s\n", key_file); - ERR_print_errors_fp(stderr); - return (NULL); - } - BIO_free(keyin); - - return (priv_key); -} - static int regular_csr_attempt (EST_CTX *ectx) { int rv; @@ -843,12 +791,18 @@ static void do_operation () } +static int string_password_cb(char *buf, int size, int wflag, void *data) +{ + strncpy(buf, priv_key_pwd, size); + // printf("string_password_cb: wflag=%d, password=%.*s\n", wflag, size, buf); + return(strnlen(buf, size)); +} + int main (int argc, char **argv) { signed char c; int set_fips_return = 0; char file_name[MAX_FILENAME_LEN]; - BIO *keyin; BIO *certin; static struct option long_options[] = { { "trustanchor", 1, 0, 0 }, @@ -858,6 +812,7 @@ int main (int argc, char **argv) { "auth-token", 1, 0, 0 }, { "common-name", 1, 0, 0 }, { "pem-output", 0, 0, 0 }, + { "keypass", 1, 0, 0 }, { NULL, 0, NULL, 0 } }; int option_index = 0; @@ -911,6 +866,17 @@ int main (int argc, char **argv) if (!strncmp(long_options[option_index].name, "common-name", strlen("common-name"))) { strncpy(subj_cn, optarg, MAX_CN); } + else if (!strcmp(long_options[option_index].name, "keypass")) { + if (!strcmp(optarg, "stdin")) { + priv_key_cb = PEM_def_callback; + } else if (!strncmp(optarg, PRIV_KEY_PASS, strlen(PRIV_KEY_PASS))) { + strncpy(priv_key_pwd, optarg+strlen(PRIV_KEY_PASS), MAX_PWD_LEN); + priv_key_cb = string_password_cb; + } else { + printf("Error: The --keypass option takes as argument either \'stdin\' or \'pass:\' immediately followed by a string.\n"); + exit(1); + } + } if (!strncmp(long_options[option_index].name, "pem-output", strlen("pem-output"))) { pem_out = 1; } @@ -1066,6 +1032,14 @@ int main (int argc, char **argv) } } + est_apps_startup(); // need to do this before decrypting private keys + +#if DEBUG_OSSL_LEAKS + CRYPTO_malloc_debug_init(); + CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif + /* * Read in the current client certificate */ @@ -1091,33 +1065,14 @@ int main (int argc, char **argv) * Read in the client's private key */ if (client_key_file[0]) { - keyin = BIO_new(BIO_s_file_internal()); - if (BIO_read_filename(keyin, client_key_file) <= 0) { - printf("\nUnable to read client private key file %s\n", client_key_file); - exit(1); - } - /* - * This reads in the private key file, which is expected to be a PEM - * encoded private key. If using DER encoding, you would invoke - * d2i_PrivateKey_bio() instead. - */ - client_priv_key = PEM_read_bio_PrivateKey(keyin, NULL, NULL, NULL); + client_priv_key = ossl_read_private_key(client_key_file, priv_key_cb); if (client_priv_key == NULL) { printf("\nError while reading PEM encoded private key file %s\n", client_key_file); ERR_print_errors_fp(stderr); exit(1); } - BIO_free(keyin); } - est_apps_startup(); - -#if DEBUG_OSSL_LEAKS - CRYPTO_malloc_debug_init(); - CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL); - CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); -#endif - if (verbose) { est_init_logger(EST_LOG_LVL_INFO, &test_logger_stdout); est_enable_backtrace(1); @@ -1126,14 +1081,18 @@ int main (int argc, char **argv) } if (!priv_key_file[0] && enroll && !csr_file[0]) { - printf("\nA private key is required for enrolling. Creating a new RSA key pair since you didn't provide a key using the -x option."); + printf("A private key is required for enrolling. Creating a new RSA key pair since you didn't provide a key using the -x option.\n"); /* * Create a private key that will be used for the * enroll operation. */ - new_pkey = generate_private_key(&new_pkey_len); + char *new_pkey = ossl_generate_private_RSA_key(SRP_MINIMAL_N, priv_key_cb); + if (new_pkey == NULL) { + printf("\nError creating key pair\n"); + exit(1); + } snprintf(file_name, MAX_FILENAME_LEN, "%s/newkey.pem", out_dir); - write_binary_file(file_name, new_pkey, new_pkey_len); + write_binary_file(file_name, (unsigned char *)new_pkey, strlen(new_pkey)); free(new_pkey); /* @@ -1145,7 +1104,11 @@ int main (int argc, char **argv) if (enroll && !csr_file[0]) { /* Read in the private key file */ - priv_key = read_private_key(priv_key_file); + priv_key = ossl_read_private_key(priv_key_file, priv_key_cb); + if (priv_key == NULL) { + printf("\nError reading CSR private key from file %s\n", priv_key_file); + exit(1); + } } diff --git a/example/proxy/estproxy.c b/example/proxy/estproxy.c index 9982327..446b77e 100644 --- a/example/proxy/estproxy.c +++ b/example/proxy/estproxy.c @@ -315,7 +315,7 @@ int main (int argc, char **argv) char c; int i, size; EVP_PKEY *priv_key; - BIO *certin, *keyin; + BIO *certin; X509 *x; EST_ERROR rv; int sleep_delay = 0; @@ -478,23 +478,12 @@ int main (int argc, char **argv) /* * Read in the server's private key */ - keyin = BIO_new(BIO_s_file_internal()); - if (BIO_read_filename(keyin, keyfile) <= 0) { - printf("\nUnable to read server private key file %s\n", keyfile); - exit(1); - } - /* - * This reads in the private key file, which is expected to be a PEM - * encoded private key. If using DER encoding, you would invoke - * d2i_PrivateKey_bio() instead. - */ - priv_key = PEM_read_bio_PrivateKey(keyin, NULL, NULL, NULL); + priv_key = read_private_key(keyfile); // not using password_cb if (priv_key == NULL) { printf("\nError while reading PEM encoded private key file %s\n", keyfile); ERR_print_errors_fp(stderr); exit(1); } - BIO_free(keyin); est_init_logger(EST_LOG_LVL_INFO, NULL); if (verbose) { diff --git a/example/server/estserver.c b/example/server/estserver.c index 4d1d322..c17930b 100644 --- a/example/server/estserver.c +++ b/example/server/estserver.c @@ -25,12 +25,14 @@ #include #include #include +#include // for PEM_def_callback #include #include "ossl_srv.h" #include "../util/utils.h" #include "../util/simple_server.h" #define MAX_FILENAME_LEN 255 +#define PRIV_KEY_PASS "pass:" /* * The OpenSSL CA needs this BIO to send errors too @@ -160,6 +162,7 @@ static void show_usage_and_exit (void) " -6 Enable IPv6\n" " -w Dump the CSR to '/tmp/csr.p10' allowing for manual attribute capture on server\n" " -? Print this help message and exit\n" + " --keypass stdin|"PRIV_KEY_PASS" Decrytion password for server key, read from STDIN or argument\n" " --srp Enable TLS-SRP authentication of client using the specified SRP parameters file\n" " --enforce-csr Enable CSR attributes enforcement. The client must provide all the attributes in the CSR.\n" " --token Use HTTP Bearer Token auth.\n" @@ -640,6 +643,14 @@ void cleanup (void) } +static char priv_key_pwd[MAX_PWD_LEN]; +static int string_password_cb(char *buf, int size, int wflag, void *data) +{ + strncpy(buf, priv_key_pwd, size); + // printf("string_password_cb: wflag=%d, password=%.*s\n", wflag, size, buf); + return(strnlen(buf, size)); +} + /* * This is the main entry point into the example EST server. * This routine parses the command line options, reads in the @@ -653,8 +664,9 @@ int main (int argc, char **argv) int i; int size; X509 *x; + pem_password_cb *priv_key_cb = NULL; EVP_PKEY *priv_key; - BIO *certin, *keyin; + BIO *certin; DH *dh; EST_ERROR rv; int sleep_delay = 0; @@ -665,6 +677,7 @@ int main (int argc, char **argv) {"srp", 1, NULL, 0}, {"enforce-csr", 0, NULL, 0}, {"token", 1, 0, 0}, + {"keypass", 1, 0, 0}, {NULL, 0, NULL, 0} }; @@ -696,6 +709,17 @@ int main (int argc, char **argv) memset(valid_token_value, 0, MAX_AUTH_TOKEN_LEN+1); strncpy(&(valid_token_value[0]), optarg, MAX_AUTH_TOKEN_LEN); } + else if (!strcmp(long_options[option_index].name, "keypass")) { + if (!strcmp(optarg, "stdin")) { + priv_key_cb = PEM_def_callback; + } else if (!strncmp(optarg, PRIV_KEY_PASS, strlen(PRIV_KEY_PASS))) { + strncpy(priv_key_pwd, optarg+strlen(PRIV_KEY_PASS), MAX_PWD_LEN); + priv_key_cb = string_password_cb; + } else { + printf("Error: The --keypass option takes as argument either \'stdin\' or \'pass:\' immediately followed by a string.\n"); + exit(1); + } + } break; #ifndef DISABLE_TSEARCH case 'm': @@ -829,24 +853,12 @@ int main (int argc, char **argv) /* * Read in the server's private key */ - keyin = BIO_new(BIO_s_file_internal()); - if (BIO_read_filename(keyin, keyfile) <= 0) { - printf("\nUnable to read server private key file %s\n", keyfile); - exit(1); - } - /* - * This reads in the private key file, which is expected to be a PEM - * encoded private key. If using DER encoding, you would invoke - * d2i_PrivateKey_bio() instead. - */ - priv_key = PEM_read_bio_PrivateKey(keyin, NULL, NULL, NULL); + priv_key = ossl_read_private_key(keyfile, priv_key_cb); if (priv_key == NULL) { - printf("\nError while reading PEM encoded private key file %s\n", keyfile); + printf("\nError while reading PEM encoded server private key file %s\n", keyfile); ERR_print_errors_fp(stderr); exit(1); } - BIO_free(keyin); - bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); if (!bio_err) { diff --git a/src/est/est.c b/src/est/est.c index 4626afd..50fb49f 100644 --- a/src/est/est.c +++ b/src/est/est.c @@ -295,63 +295,6 @@ X509_REQ *est_read_x509_request (unsigned char *csr, int csr_len, return (req); } -/*! @brief est_load_key() is a helper function that reads - * a char* and converts it to an OpenSSL EVP_PKEY*. The char* data - * can be either PEM or DER encoded. - - @param key This is the char* that contains the PEM or DER encoded - key pair. - @param key_len This is the length of the key char*. DER encoded data - may contain zeros, which requires the length to be provided - by the application layer. - @param key_format This parameter specifies the encoding method of the - key char* that was provided. Set this to either EST_FORMAT_PEM - or EST_FORMAT_DER. - - This function converts a PEM or DER encoded char* to the OpenSSL - EVP_PKEY* structure. This function will return NULL if the PEM/DER - data is corrupted or unable to be parsed by the OpenSSL library. - This function will allocate memory for the EVP_PKEY data. You must - free the memory in your application when it's no longer needed by - calling EVP_PKEY_free(). - - @return EVP_PKEY* - */ -EVP_PKEY *est_load_key (unsigned char *key, int key_len, int format) -{ - BIO *in = NULL; - EVP_PKEY *pkey = NULL; - - if (key == NULL) { - EST_LOG_ERR("no key data provided"); - return NULL; - } - - in = BIO_new_mem_buf(key, key_len); - if (in == NULL) { - EST_LOG_ERR("Unable to open the provided key buffer"); - return (NULL); - } - - switch (format) { - case EST_FORMAT_PEM: - pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); - break; - case EST_FORMAT_DER: - pkey = d2i_PrivateKey_bio(in, NULL); - break; - default: - EST_LOG_ERR("Invalid key format"); - BIO_free(in); - return NULL; - break; - } - BIO_free(in); - - return (pkey); -} - - /* * This function is used to read the CERTS in a BIO and build a diff --git a/src/est/est.h b/src/est/est.h index 05a6095..6332d08 100644 --- a/src/est/est.h +++ b/src/est/est.h @@ -433,7 +433,15 @@ EST_ERROR est_decode_attributes_helper(char *csrattrs, int csrattrs_len, */ X509_REQ *est_read_x509_request(unsigned char *csr, int csr_len, EST_CERT_FORMAT csr_format); -EVP_PKEY *est_load_key(unsigned char *key, int key_len, int format); +#define EST_PRIVATE_KEY_ENC EVP_aes_128_cbc() // The key wrap algorithm optionally used to protect private keys +char *ossl_generate_private_RSA_key (int key_size, pem_password_cb *cb); +char *ossl_generate_private_EC_key (int curve_nid, pem_password_cb *cb); +char *ossl_private_key_to_PEM (const EVP_PKEY* pkey, pem_password_cb *cb); +EVP_PKEY *ossl_load_private_key (const unsigned char *key, int key_len, int format, pem_password_cb *cb); +#define est_load_key(key, key_len, format) ossl_load_private_key(key,key_len,format, NULL) +#define ossl_load_private_key_PEM(key) ossl_load_private_key((unsigned char*)(key),strlen(key),EST_FORMAT_PEM, NULL) +EVP_PKEY *ossl_read_private_key (const char *key_file, pem_password_cb *cb); +#define read_private_key(key_file) ossl_read_private_key(key_file, NULL) int est_convert_p7b64_to_pem(unsigned char *certs_p7, int certs_len, unsigned char **pem); /* diff --git a/src/est/est_ossl_util.c b/src/est/est_ossl_util.c index b7689f8..49f64fa 100644 --- a/src/est/est_ossl_util.c +++ b/src/est/est_ossl_util.c @@ -248,6 +248,247 @@ void ossl_dump_ssl_errors () } +/*! @brief ossl_generate_private_RSA key() generates a new RSA key + of the given size and returns it as a PEM encoded character string. + A callback may be provided which is then used to encrypt the key data. + + @param key_size This the requested bit size of the new RSA key. + @param cb If not NULL, this must be a pointer to a function that sets a + password to be used for encrypting the newly generated key data. + A suitable value is the pre-defined function reading from STDIN: + int PEM_def_callback(char *buf, int size, int wflag, void *data); + + This function will return NULL if an error occurs. Otherwise + this function allocates memory for the key data. You must free() + the memory in your application when it's no longer needed. + + @return char* + */ +char *ossl_generate_private_RSA_key (int key_size, pem_password_cb *cb) +{ + char *key_data = NULL; + + RSA *rsa = RSA_new(); + if (!rsa) { + return NULL; + } + BIGNUM *bn = BN_new(); + if (!bn) { + RSA_free(rsa); + return NULL; + } + + BN_set_word(bn, 0x10001); + RSA_generate_key_ex(rsa, key_size, bn, NULL); + + do { + BIO *out = BIO_new(BIO_s_mem()); + if (!out) { + break; + } + PEM_write_bio_RSAPrivateKey(out, rsa, cb ? EST_PRIVATE_KEY_ENC : NULL, NULL, 0, cb, NULL); + key_data = (char *)est_ossl_BIO_copy_data(out, NULL); + BIO_free(out); + if (key_data && !key_data[0]) { // happens if passphrase entered via STDIN does not verify or has less than 4 characters + free(key_data); + key_data = NULL; + } + } while (cb && !key_data); + + RSA_free(rsa); + BN_free(bn); + return (key_data); +} + +/*! @brief ossl_generate_private_EC key() generates a new EC key + of the given curve and returns it as a PEM encoded character string. + A callback may be provided which is then used to encrypt the key data. + + @param curve_nid This the NID of the requested curve type of the new EC key. + @param cb If not NULL, this must be a pointer to a function that sets a + password to be used for encrypting the newly generated key data. + A suitable value is the pre-defined function reading from STDIN: + int PEM_def_callback(char *buf, int size, int wflag, void *data); + + This function will return NULL if an error occurs. Otherwise + this function allocates memory for the key data. You must free() + the memory in your application when it's no longer needed. + + @return char* + */ +char *ossl_generate_private_EC_key (int curve_nid, pem_password_cb *cb) +{ + EC_KEY *eckey; + EC_GROUP *group = NULL; + char *key_data = NULL; + int asn1_flag = OPENSSL_EC_NAMED_CURVE; + point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED; + + /* + * Generate an EC key + */ + + eckey = EC_KEY_new(); + if (!eckey) { + return NULL; + } + + group = EC_GROUP_new_by_curve_name(curve_nid); + EC_GROUP_set_asn1_flag(group, asn1_flag); + EC_GROUP_set_point_conversion_form(group, form); + EC_KEY_set_group(eckey, group); + if (!EC_KEY_generate_key(eckey)) { + return (NULL); + } + + do { + BIO *out = BIO_new(BIO_s_mem()); + if (!out) { + break; + } + PEM_write_bio_ECPKParameters(out, group); + PEM_write_bio_ECPrivateKey(out, eckey, cb ? EST_PRIVATE_KEY_ENC : NULL, NULL, 0, cb, NULL); + key_data = (char *)est_ossl_BIO_copy_data(out, NULL); + BIO_free(out); + if (key_data && !strstr(key_data, "-----BEGIN EC PRIVATE KEY-----")) { // happens if passphrase entered via STDIN does not verify or has less than 4 characters + free(key_data); + key_data = NULL; + } + } while (cb && !key_data); + + EC_KEY_free(eckey); + return (key_data); +} + +/*! @brief ossl_private_key_to_PEM() is a helper function that converts + an OpenSSL EVP_PKEY* to a PEM encoded character string. + A callback may be provided which is then used to encrypt the key data. + + @param pkey This is the private key to be converted. + @param cb If not NULL, this must be a pointer to a function that sets a + password to be used for encrypting the key data. + A suitable value is the pre-defined function reading from STDIN: + int PEM_def_callback(char *buf, int size, int wflag, void *data); + + This function will return NULL if an error occurs. Otherwise + this function allocates memory for the string. You must free() + the memory in your application when it's no longer needed. + + @return char* + */ +// currently unused, but helpful when outputting a private key +char *ossl_private_key_to_PEM (const EVP_PKEY* pkey, pem_password_cb *cb) { + char *pkey_pem = NULL; + BIO *out = BIO_new(BIO_s_mem()); + if (out) { + PEM_write_bio_PrivateKey(out, (EVP_PKEY *)pkey, cb ? EST_PRIVATE_KEY_ENC : NULL, NULL, 0, cb, NULL); + pkey_pem = (char *)est_ossl_BIO_copy_data(out, NULL); + BIO_free(out); + } + return (pkey_pem); +} + +/*! @brief ossl_load_private_key() is a helper function that reads + a character string and converts it to an OpenSSL EVP_PKEY*. + The unsigned char* data can be either PEM or DER encoded. + A callback may be provided which is then used to decrypt the key data. + + @param key This is the string that contains the PEM or DER encoded key. + @param key_len This is the length of the key string. DER encoded data + may contain zeros, which requires the length to be provided + by the application layer. + @param key_format This specifies the encoding method of the key string + provided. Set this to either EST_FORMAT_PEM or EST_FORMAT_DER. + @param cb If not NULL, this must be a pointer to a function that sets a + password to be used for decrypting the key data. + A suitable value is the pre-defined function reading from STDIN: + int PEM_def_callback(char *buf, int size, int wflag, void *data); + + This function will return NULL if the PEM/DER data is corrupted or + unable to be parsed by the OpenSSL library. This function will allocate + memory for the EVP_PKEY data. You must free the memory in your application + when it's no longer needed by calling EVP_PKEY_free(). + + @return EVP_PKEY* + */ +EVP_PKEY *ossl_load_private_key (const unsigned char *key, int key_len, int format, pem_password_cb *cb) +{ + BIO *in = NULL; + EVP_PKEY *pkey = NULL; + + if (key == NULL) { + EST_LOG_ERR("No key data provided"); + return NULL; + } + + in = BIO_new_mem_buf((unsigned char *)key, key_len); + if (in == NULL) { + EST_LOG_ERR("Unable to open the provided key buffer"); + return (NULL); + } + + switch (format) { + case EST_FORMAT_PEM: + pkey = PEM_read_bio_PrivateKey(in, NULL, cb, NULL); + break; + case EST_FORMAT_DER: + pkey = d2i_PrivateKey_bio(in, NULL); + break; + default: + EST_LOG_ERR("Invalid key format"); + break; + } + BIO_free(in); + + return (pkey); +} + +/*! @brief ossl_read_private_key() is a helper function that reads a PEM + encoded string from the given file converting it to an OpenSSL EVP_PKEY*. + A callback may be provided which is then used to decrypt the key data. + + @param key This is the name of the file containing the PEM encoded key. + @param cb If not NULL, this must be a pointer to a function that sets a + password to be used for decrypting the key data. + A suitable value is the pre-defined function reading from STDIN: + int PEM_def_callback(char *buf, int size, int wflag, void *data); + + This function will return NULL if the file is unreadable, the PEM data is + corrupted or cannot be parsed by the OpenSSL library. This function will + allocate memory for the EVP_PKEY data. You must free the memory in your + application when it's no longer needed by calling EVP_PKEY_free(). + + @return EVP_PKEY* + */ +EVP_PKEY *ossl_read_private_key(const char *key_file, pem_password_cb *cb) +{ + BIO *keyin; + EVP_PKEY *priv_key; + + /* + * Read in the private key + */ + keyin = BIO_new(BIO_s_file_internal()); + if (BIO_read_filename(keyin, key_file) <= 0) { + EST_LOG_ERR("Unable to read private key file %s", key_file); + return(NULL); + } + /* + * This reads in the private key file, which is expected to be a PEM + * encoded private key. If using DER encoding, you would invoke + * d2i_PrivateKey_bio() instead. + */ + priv_key = PEM_read_bio_PrivateKey(keyin, NULL, cb, NULL); + if (priv_key == NULL) { + EST_LOG_ERR("Error while parsing PEM encoded private key from file %s", key_file); + ossl_dump_ssl_errors(); + } + BIO_free(keyin); + + return (priv_key); +} + + /*! @brief est_convert_p7b64_to_pem() converts the base64 encoded PKCS7 response from the EST server into PEM format. diff --git a/test/util/test_utils.c b/test/util/test_utils.c index 1c03363..53be2cb 100644 --- a/test/util/test_utils.c +++ b/test/util/test_utils.c @@ -195,37 +195,6 @@ BIO *open_tcp_socket_ipv4 (char *ipaddr, char *port) } - -EVP_PKEY *read_private_key(char *key_file) -{ - BIO *keyin; - EVP_PKEY *priv_key; - - /* - * Read in the private key - */ - keyin = BIO_new(BIO_s_file_internal()); - if (BIO_read_filename(keyin, key_file) <= 0) { - printf("\nUnable to read private key file %s\n", key_file); - return(NULL); - } - /* - * This reads in the private key file, which is expected to be a PEM - * encoded private key. If using DER encoding, you would invoke - * d2i_PrivateKey_bio() instead. - */ - priv_key = PEM_read_bio_PrivateKey(keyin, NULL, NULL, NULL); - if (priv_key == NULL) { - printf("\nError while reading PEM encoded private key file %s\n", key_file); - ERR_print_errors_fp(stderr); - return(NULL); - } - BIO_free(keyin); - - return (priv_key); -} - - void dumpbin (char *buf, size_t len) { int i; diff --git a/test/util/test_utils.h b/test/util/test_utils.h index 29c78cc..f7dc475 100644 --- a/test/util/test_utils.h +++ b/test/util/test_utils.h @@ -20,7 +20,6 @@ int read_binary_file(char *filename, unsigned char **contents); int write_binary_file(char *filename, unsigned char *contents, int len); BIO *open_tcp_socket(char *ipaddr, char *port); -EVP_PKEY *read_private_key(char *key_file); #endif