diff --git a/include/freerdp/crypto/privatekey.h b/include/freerdp/crypto/privatekey.h index 58fd94b40..6dea57c8b 100644 --- a/include/freerdp/crypto/privatekey.h +++ b/include/freerdp/crypto/privatekey.h @@ -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 diff --git a/libfreerdp/core/aad.c b/libfreerdp/core/aad.c index f7472180e..7a95ffd0e 100644 --- a/libfreerdp/core/aad.c +++ b/libfreerdp/core/aad.c @@ -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) diff --git a/libfreerdp/crypto/privatekey.c b/libfreerdp/crypto/privatekey.c index 75799826f..f1b172857 100644 --- a/libfreerdp/crypto/privatekey.c +++ b/libfreerdp/crypto/privatekey.c @@ -37,6 +37,7 @@ #include #include #include +#include #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; +} diff --git a/libfreerdp/crypto/privatekey.h b/libfreerdp/crypto/privatekey.h index b423c9a4b..dc13af692 100644 --- a/libfreerdp/crypto/privatekey.h +++ b/libfreerdp/crypto/privatekey.h @@ -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. */ diff --git a/libfreerdp/emu/scard/smartcard_virtual_gids.c b/libfreerdp/emu/scard/smartcard_virtual_gids.c index d69f46749..801d0d525 100644 --- a/libfreerdp/emu/scard/smartcard_virtual_gids.c +++ b/libfreerdp/emu/scard/smartcard_virtual_gids.c @@ -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; diff --git a/server/Mac/mf_peer.c b/server/Mac/mf_peer.c index 066912714..f11702e6b 100644 --- a/server/Mac/mf_peer.c +++ b/server/Mac/mf_peer.c @@ -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)) diff --git a/server/Sample/sfreerdp.c b/server/Sample/sfreerdp.c index ba274d646..4e8cb5829 100644 --- a/server/Sample/sfreerdp.c +++ b/server/Sample/sfreerdp.c @@ -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)) diff --git a/server/Windows/wf_peer.c b/server/Windows/wf_peer.c index 1fb60ffb7..69ccaea6f 100644 --- a/server/Windows/wf_peer.c +++ b/server/Windows/wf_peer.c @@ -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) diff --git a/server/proxy/pf_server.c b/server/proxy/pf_server.c index 76a294878..3bb7e69aa 100644 --- a/server/proxy/pf_server.c +++ b/server/proxy/pf_server.c @@ -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; diff --git a/server/shadow/shadow_server.c b/server/shadow/shadow_server.c index 97a136d91..dbc660d7d 100644 --- a/server/shadow/shadow_server.c +++ b/server/shadow/shadow_server.c @@ -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))