[crypto,key] add function to export PEM and create a key

* freerdp_key_generate creates a new key
* freerdp_key_get_pem exports the key as PEM
This commit is contained in:
akallabeth
2025-04-22 10:56:13 +02:00
parent 0eefdbdb40
commit 2fb2e5f9c5
10 changed files with 192 additions and 19 deletions

View File

@@ -35,16 +35,62 @@ extern "C"
WINPR_ATTR_MALLOC(freerdp_key_free, 1)
FREERDP_API rdpPrivateKey* freerdp_key_new(void);
WINPR_ATTR_MALLOC(freerdp_key_free, 1)
FREERDP_API rdpPrivateKey* freerdp_key_new_from_file(const char* keyfile);
WINPR_DEPRECATED_VAR(
"[since 3.16.0] use freerdp_key_new_from_file_enc",
WINPR_ATTR_MALLOC(freerdp_key_free, 1)
FREERDP_API rdpPrivateKey* freerdp_key_new_from_file(const char* keyfile));
WINPR_DEPRECATED_VAR("[since 3.16.0] use freerdp_key_new_from_pem_enc",
WINPR_ATTR_MALLOC(freerdp_key_free, 1)
FREERDP_API rdpPrivateKey* freerdp_key_new_from_pem(const char* pem));
/** @brief Create a private key from file \b keyfile with optional password \b password
*
* @param keyfile The file to read the key from
* @param password The optional password the key is enecrypted with, \b NULL for unencrypted
* @return An allocated private key, \b NULL in case of failure.
* @since version 3.16.0
*/
FREERDP_API rdpPrivateKey* freerdp_key_new_from_file_enc(const char* keyfile,
const char* password);
/** @brief Create a private key from a PEM file with optional \b password
*
* @param pem The PEM string to use
* @param password The optional password, use \b NULL if no encryption is used.
* @return An allocated private key, \b NULL in case of failure.
* @since version 3.16.0
*/
WINPR_ATTR_MALLOC(freerdp_key_free, 1)
FREERDP_API rdpPrivateKey* freerdp_key_new_from_pem(const char* pem);
FREERDP_API rdpPrivateKey* freerdp_key_new_from_pem_enc(const char* pem, const char* password);
FREERDP_API BOOL freerdp_key_is_rsa(const rdpPrivateKey* key);
FREERDP_API size_t freerdp_key_get_bits(const rdpPrivateKey* key);
/** @brief Create a PEM from a private key
*
* @param key The key to convert
* @param plen Optional pointer, value set to strlen of the PEM
* @param password Optional password string. If \b NULL an unencrypted PEM is written.
* @return A PEM string or \b NULL in case of errors
*
* @since version 3.16.0
*/
WINPR_ATTR_MALLOC(free, 1)
FREERDP_API char* freerdp_key_get_pem(const rdpPrivateKey* key, size_t* plen,
const char* password);
/** @brief Create a new private key
*
* @param key The key to initialize
* @param type The key type (RSA, ...)
* @param count The number of arguments following, depends on type
* @return \b TRUE for success, \b FALSE otherwise
* @since version 3.16.0
*/
FREERDP_API BOOL freerdp_key_generate(rdpPrivateKey* key, const char* type, size_t count, ...);
#ifdef __cplusplus
}
#endif

View File

@@ -586,7 +586,7 @@ int aad_recv(rdpAad* aad, wStream* s)
static BOOL generate_rsa_2048(rdpAad* aad)
{
WINPR_ASSERT(aad);
return freerdp_key_generate(aad->key, 2048);
return freerdp_key_generate(aad->key, "RSA", 1, 2048);
}
static char* generate_rsa_digest_base64_str(rdpAad* aad, const char* input, size_t ilen)

View File

@@ -37,6 +37,7 @@
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include "privatekey.h"
#include "cert_common.h"
@@ -118,7 +119,8 @@ fail:
}
#endif
static EVP_PKEY* evp_pkey_utils_from_pem(const char* data, size_t len, BOOL fromFile)
static EVP_PKEY* evp_pkey_utils_from_pem(const char* data, size_t len, BOOL fromFile,
const char* password)
{
EVP_PKEY* evp = NULL;
BIO* bio = NULL;
@@ -137,7 +139,7 @@ static EVP_PKEY* evp_pkey_utils_from_pem(const char* data, size_t len, BOOL from
return NULL;
}
evp = PEM_read_bio_PrivateKey(bio, NULL, NULL, 0);
evp = PEM_read_bio_PrivateKey(bio, NULL, NULL, WINPR_CAST_CONST_PTR_AWAY(password, void*));
BIO_free_all(bio);
if (!evp)
WLog_ERR(TAG, "PEM_read_bio_PrivateKey returned NULL [input length %" PRIuz "]", len);
@@ -226,11 +228,16 @@ fail:
}
rdpPrivateKey* freerdp_key_new_from_pem(const char* pem)
{
return freerdp_key_new_from_pem_enc(pem, NULL);
}
rdpPrivateKey* freerdp_key_new_from_pem_enc(const char* pem, const char* password)
{
rdpPrivateKey* key = freerdp_key_new();
if (!key || !pem)
goto fail;
key->evp = evp_pkey_utils_from_pem(pem, strlen(pem), FALSE);
key->evp = evp_pkey_utils_from_pem(pem, strlen(pem), FALSE, password);
if (!key->evp)
goto fail;
if (!key_read_private(key))
@@ -243,12 +250,16 @@ fail:
rdpPrivateKey* freerdp_key_new_from_file(const char* keyfile)
{
return freerdp_key_new_from_file_enc(keyfile, NULL);
}
rdpPrivateKey* freerdp_key_new_from_file_enc(const char* keyfile, const char* password)
{
rdpPrivateKey* key = freerdp_key_new();
if (!key || !keyfile)
goto fail;
key->evp = evp_pkey_utils_from_pem(keyfile, strlen(keyfile), TRUE);
key->evp = evp_pkey_utils_from_pem(keyfile, strlen(keyfile), TRUE, password);
if (!key->evp)
goto fail;
if (!key_read_private(key))
@@ -378,10 +389,30 @@ size_t freerdp_key_get_bits(const rdpPrivateKey* key)
return WINPR_ASSERTING_INT_CAST(size_t, rc);
}
BOOL freerdp_key_generate(rdpPrivateKey* key, size_t key_length)
BOOL freerdp_key_generate(rdpPrivateKey* key, const char* type, size_t count, ...)
{
BOOL rc = FALSE;
if (!type)
{
WLog_ERR(TAG, "Invalid argument type=%s", type);
return FALSE;
}
if (strncmp("RSA", type, 4) != 0)
{
WLog_ERR(TAG, "Argument type=%s is currently not supported, aborting", type);
return FALSE;
}
if (count != 1)
{
WLog_ERR(TAG, "Argument type=%s requires count=1, got %" PRIuz ", aborting", type, count);
return FALSE;
}
va_list ap;
va_start(ap, count);
const int key_length = va_arg(ap, int);
va_end(ap);
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
RSA* rsa = NULL;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
@@ -423,7 +454,7 @@ BOOL freerdp_key_generate(rdpPrivateKey* key, size_t key_length)
rc = TRUE;
#else
EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, type, NULL);
if (!pctx)
return FALSE;
@@ -433,7 +464,7 @@ BOOL freerdp_key_generate(rdpPrivateKey* key, size_t key_length)
if (key_length > INT_MAX)
goto fail;
if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, (int)key_length) != 1)
if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, key_length) != 1)
goto fail;
EVP_PKEY_free(key->evp);
@@ -550,3 +581,101 @@ WINPR_DIGEST_CTX* freerdp_key_digest_sign(rdpPrivateKey* key, WINPR_MD_TYPE dige
}
return md_ctx;
}
static BOOL bio_read_pem(BIO* bio, char** ppem, size_t* plength)
{
BOOL rc = FALSE;
WINPR_ASSERT(bio);
WINPR_ASSERT(ppem);
const size_t blocksize = 2048;
size_t offset = 0;
size_t length = blocksize;
char* pem = NULL;
*ppem = NULL;
if (plength)
*plength = 0;
while (offset < length)
{
char* tmp = realloc(pem, length + 1);
if (!tmp)
goto fail;
pem = tmp;
ERR_clear_error();
const int status = BIO_read(bio, &pem[offset], (int)(length - offset));
if (status < 0)
{
WLog_ERR(TAG, "failed to read certificate");
goto fail;
}
if (status == 0)
break;
offset += (size_t)status;
if (length - offset > 0)
break;
length += blocksize;
}
if (pem)
{
if (offset >= length)
goto fail;
pem[offset] = '\0';
}
*ppem = pem;
if (plength)
*plength = offset;
rc = TRUE;
fail:
if (!rc)
free(pem);
return rc;
}
char* freerdp_key_get_pem(const rdpPrivateKey* key, size_t* plen, const char* password)
{
WINPR_ASSERT(key);
if (!key->evp)
return NULL;
/**
* Don't manage certificates internally, leave it up entirely to the external client
* implementation
*/
BIO* bio = BIO_new(BIO_s_mem());
if (!bio)
{
WLog_ERR(TAG, "BIO_new() failure");
return NULL;
}
char* pem = NULL;
const EVP_CIPHER* enc = NULL;
if (password)
enc = EVP_aes_256_cbc_hmac_sha256();
const int status = PEM_write_bio_PrivateKey(bio, key->evp, enc, NULL, 0, 0,
WINPR_CAST_CONST_PTR_AWAY(password, void*));
if (status < 0)
{
WLog_ERR(TAG, "PEM_write_bio_PrivateKey failure: %d", status);
goto fail;
}
(void)bio_read_pem(bio, &pem, plen);
fail:
BIO_free_all(bio);
return pem;
}

View File

@@ -45,8 +45,6 @@ extern "C"
FREERDP_LOCAL const rdpCertInfo* freerdp_key_get_info(const rdpPrivateKey* key);
FREERDP_LOCAL const BYTE* freerdp_key_get_exponent(const rdpPrivateKey* key, size_t* plength);
FREERDP_LOCAL BOOL freerdp_key_generate(rdpPrivateKey* key, size_t bits);
/** \brief returns a pointer to a EVP_PKEY structure.
* Call EVP_PKEY_free when done.
*/

View File

@@ -1499,7 +1499,7 @@ BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, con
if (!ctx->certificate)
goto init_failed;
ctx->privateKey = freerdp_key_new_from_pem(privateKey);
ctx->privateKey = freerdp_key_new_from_pem_enc(privateKey, NULL);
if (!ctx->privateKey)
goto init_failed;

View File

@@ -376,7 +376,7 @@ static void* mf_peer_main_loop(void* arg)
WINPR_ASSERT(settings);
/* Initialize the real server settings here */
rdpPrivateKey* key = freerdp_key_new_from_file(info->key);
rdpPrivateKey* key = freerdp_key_new_from_file_enc(info->key, NULL);
if (!key)
goto fail;
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))

View File

@@ -1115,7 +1115,7 @@ static DWORD WINAPI test_peer_mainloop(LPVOID arg)
goto fail;
}
rdpPrivateKey* key = freerdp_key_new_from_file(info->key);
rdpPrivateKey* key = freerdp_key_new_from_file_enc(info->key, NULL);
if (!key)
goto fail;
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))

View File

@@ -255,7 +255,7 @@ static BOOL wf_peer_read_settings(freerdp_peer* client)
&(PrivateKeyFile)))
PrivateKeyFile = _strdup("server.key");
rdpPrivateKey* key = freerdp_key_new_from_file(PrivateKeyFile);
rdpPrivateKey* key = freerdp_key_new_from_file_enc(PrivateKeyFile, NULL);
free(PrivateKeyFile);
if (!key)

View File

@@ -480,7 +480,7 @@ static BOOL pf_server_initialize_peer_connection(freerdp_peer* peer)
pdata->module = server->module;
const proxyConfig* config = pdata->config = server->config;
rdpPrivateKey* key = freerdp_key_new_from_pem(config->PrivateKeyPEM);
rdpPrivateKey* key = freerdp_key_new_from_pem_enc(config->PrivateKeyPEM, NULL);
if (!key)
return FALSE;

View File

@@ -925,7 +925,7 @@ static BOOL shadow_server_init_certificate(rdpShadowServer* server)
rdpSettings* settings = server->settings;
WINPR_ASSERT(settings);
rdpPrivateKey* key = freerdp_key_new_from_file(server->PrivateKeyFile);
rdpPrivateKey* key = freerdp_key_new_from_file_enc(server->PrivateKeyFile, NULL);
if (!key)
goto out_fail;
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))