Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/crypto/privatekey.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Private key Handling
4
 *
5
 * Copyright 2011 Jiten Pathy
6
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7
 * Copyright 2015 Thincast Technologies GmbH
8
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
9
 * Copyright 2023 Armin Novak <anovak@thincast.com>
10
 * Copyright 2023 Thincast Technologies GmbH
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 *     http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24
25
#include <freerdp/config.h>
26
27
#include <errno.h>
28
#include <stdio.h>
29
#include <string.h>
30
31
#include <winpr/assert.h>
32
#include <winpr/wtypes.h>
33
#include <winpr/crt.h>
34
#include <winpr/file.h>
35
#include <winpr/crypto.h>
36
37
#include <openssl/pem.h>
38
#include <openssl/rsa.h>
39
#include <openssl/bn.h>
40
#include <openssl/err.h>
41
42
#include "privatekey.h"
43
#include "cert_common.h"
44
45
#include <freerdp/crypto/privatekey.h>
46
47
#include <openssl/evp.h>
48
49
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
50
#include <openssl/core_names.h>
51
#endif
52
53
#include "x509_utils.h"
54
#include "crypto.h"
55
#include "opensslcompat.h"
56
57
#define TAG FREERDP_TAG("crypto")
58
59
struct rdp_private_key
60
{
61
  EVP_PKEY* evp;
62
63
  rdpCertInfo cert;
64
  BYTE* PrivateExponent;
65
  DWORD PrivateExponentLength;
66
};
67
68
/*
69
 * Terminal Services Signing Keys.
70
 * Yes, Terminal Services Private Key is publicly available.
71
 */
72
73
static BYTE tssk_modulus[] = { 0x3d, 0x3a, 0x5e, 0xbd, 0x72, 0x43, 0x3e, 0xc9, 0x4d, 0xbb, 0xc1,
74
                             0x1e, 0x4a, 0xba, 0x5f, 0xcb, 0x3e, 0x88, 0x20, 0x87, 0xef, 0xf5,
75
                             0xc1, 0xe2, 0xd7, 0xb7, 0x6b, 0x9a, 0xf2, 0x52, 0x45, 0x95, 0xce,
76
                             0x63, 0x65, 0x6b, 0x58, 0x3a, 0xfe, 0xef, 0x7c, 0xe7, 0xbf, 0xfe,
77
                             0x3d, 0xf6, 0x5c, 0x7d, 0x6c, 0x5e, 0x06, 0x09, 0x1a, 0xf5, 0x61,
78
                             0xbb, 0x20, 0x93, 0x09, 0x5f, 0x05, 0x6d, 0xea, 0x87 };
79
80
static BYTE tssk_privateExponent[] = {
81
  0x87, 0xa7, 0x19, 0x32, 0xda, 0x11, 0x87, 0x55, 0x58, 0x00, 0x16, 0x16, 0x25, 0x65, 0x68, 0xf8,
82
  0x24, 0x3e, 0xe6, 0xfa, 0xe9, 0x67, 0x49, 0x94, 0xcf, 0x92, 0xcc, 0x33, 0x99, 0xe8, 0x08, 0x60,
83
  0x17, 0x9a, 0x12, 0x9f, 0x24, 0xdd, 0xb1, 0x24, 0x99, 0xc7, 0x3a, 0xb8, 0x0a, 0x7b, 0x0d, 0xdd,
84
  0x35, 0x07, 0x79, 0x17, 0x0b, 0x51, 0x9b, 0xb3, 0xc7, 0x10, 0x01, 0x13, 0xe7, 0x3f, 0xf3, 0x5f
85
};
86
87
static const rdpPrivateKey tssk = { .PrivateExponent = tssk_privateExponent,
88
                                  .PrivateExponentLength = sizeof(tssk_privateExponent),
89
                                  .cert = { .Modulus = tssk_modulus,
90
                                            .ModulusLength = sizeof(tssk_modulus) } };
91
const rdpPrivateKey* priv_key_tssk = &tssk;
92
93
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
94
static RSA* evp_pkey_to_rsa(const rdpPrivateKey* key)
95
0
{
96
0
  if (!freerdp_key_is_rsa(key))
97
0
  {
98
0
    WLog_WARN(TAG, "Key is no RSA key");
99
0
    return NULL;
100
0
  }
101
102
0
  RSA* rsa = NULL;
103
0
  BIO* bio = BIO_new(
104
#if defined(LIBRESSL_VERSION_NUMBER)
105
      BIO_s_mem()
106
#else
107
0
      BIO_s_secmem()
108
0
#endif
109
0
  );
110
0
  if (!bio)
111
0
    return NULL;
112
0
  const int rc = PEM_write_bio_PrivateKey(bio, key->evp, NULL, NULL, 0, NULL, NULL);
113
0
  if (rc != 1)
114
0
    goto fail;
115
0
  rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
116
0
fail:
117
0
  BIO_free_all(bio);
118
0
  return rsa;
119
0
}
120
#endif
121
122
static EVP_PKEY* evp_pkey_utils_from_pem(const char* data, size_t len, BOOL fromFile,
123
                                         const char* password)
124
0
{
125
0
  EVP_PKEY* evp = NULL;
126
0
  BIO* bio = NULL;
127
0
  if (fromFile)
128
0
    bio = BIO_new_file(data, "rb");
129
0
  else
130
0
  {
131
0
    if (len > INT_MAX)
132
0
      return NULL;
133
0
    bio = BIO_new_mem_buf(data, (int)len);
134
0
  }
135
136
0
  if (!bio)
137
0
  {
138
0
    WLog_ERR(TAG, "BIO_new failed for private key");
139
0
    return NULL;
140
0
  }
141
142
0
  evp = PEM_read_bio_PrivateKey(bio, NULL, NULL, WINPR_CAST_CONST_PTR_AWAY(password, void*));
143
0
  BIO_free_all(bio);
144
0
  if (!evp)
145
0
    WLog_ERR(TAG, "PEM_read_bio_PrivateKey returned NULL [input length %" PRIuz "]", len);
146
147
0
  return evp;
148
0
}
149
150
static BOOL key_read_private(rdpPrivateKey* key)
151
0
{
152
0
  BOOL rc = FALSE;
153
154
0
  WINPR_ASSERT(key);
155
0
  WINPR_ASSERT(key->evp);
156
157
  /* The key is not an RSA key, that means we just return success. */
158
0
  if (!freerdp_key_is_rsa(key))
159
0
    return TRUE;
160
161
0
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
162
0
  RSA* rsa = evp_pkey_to_rsa(key);
163
0
  if (!rsa)
164
0
  {
165
0
    char ebuffer[256] = { 0 };
166
0
    WLog_ERR(TAG, "unable to load RSA key: %s.",
167
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
168
0
    goto fail;
169
0
  }
170
171
0
  switch (RSA_check_key(rsa))
172
0
  {
173
0
    case 0:
174
0
      WLog_ERR(TAG, "invalid RSA key");
175
0
      goto fail;
176
177
0
    case 1:
178
      /* Valid key. */
179
0
      break;
180
181
0
    default:
182
0
    {
183
0
      char ebuffer[256] = { 0 };
184
0
      WLog_ERR(TAG, "unexpected error when checking RSA key: %s.",
185
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
186
0
      goto fail;
187
0
    }
188
0
  }
189
190
0
  const BIGNUM* rsa_e = NULL;
191
0
  const BIGNUM* rsa_n = NULL;
192
0
  const BIGNUM* rsa_d = NULL;
193
194
0
  RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);
195
#else
196
  BIGNUM* rsa_e = NULL;
197
  BIGNUM* rsa_n = NULL;
198
  BIGNUM* rsa_d = NULL;
199
200
  if (!EVP_PKEY_get_bn_param(key->evp, OSSL_PKEY_PARAM_RSA_N, &rsa_n))
201
    goto fail;
202
  if (!EVP_PKEY_get_bn_param(key->evp, OSSL_PKEY_PARAM_RSA_E, &rsa_e))
203
    goto fail;
204
  if (!EVP_PKEY_get_bn_param(key->evp, OSSL_PKEY_PARAM_RSA_D, &rsa_d))
205
    goto fail;
206
#endif
207
0
  if (BN_num_bytes(rsa_e) > 4)
208
0
  {
209
0
    WLog_ERR(TAG, "RSA public exponent too large");
210
0
    goto fail;
211
0
  }
212
213
0
  if (!read_bignum(&key->PrivateExponent, &key->PrivateExponentLength, rsa_d, TRUE))
214
0
    goto fail;
215
216
0
  if (!cert_info_create(&key->cert, rsa_n, rsa_e))
217
0
    goto fail;
218
0
  rc = TRUE;
219
0
fail:
220
0
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
221
0
  RSA_free(rsa);
222
#else
223
  BN_free(rsa_d);
224
  BN_free(rsa_e);
225
  BN_free(rsa_n);
226
#endif
227
0
  return rc;
228
0
}
229
230
rdpPrivateKey* freerdp_key_new_from_pem(const char* pem)
231
0
{
232
0
  return freerdp_key_new_from_pem_enc(pem, NULL);
233
0
}
234
235
rdpPrivateKey* freerdp_key_new_from_pem_enc(const char* pem, const char* password)
236
0
{
237
0
  rdpPrivateKey* key = freerdp_key_new();
238
0
  if (!key || !pem)
239
0
    goto fail;
240
0
  key->evp = evp_pkey_utils_from_pem(pem, strlen(pem), FALSE, password);
241
0
  if (!key->evp)
242
0
    goto fail;
243
0
  if (!key_read_private(key))
244
0
    goto fail;
245
0
  return key;
246
0
fail:
247
0
  freerdp_key_free(key);
248
0
  return NULL;
249
0
}
250
251
rdpPrivateKey* freerdp_key_new_from_file(const char* keyfile)
252
0
{
253
0
  return freerdp_key_new_from_file_enc(keyfile, NULL);
254
0
}
255
256
rdpPrivateKey* freerdp_key_new_from_file_enc(const char* keyfile, const char* password)
257
0
{
258
0
  rdpPrivateKey* key = freerdp_key_new();
259
0
  if (!key || !keyfile)
260
0
    goto fail;
261
262
0
  key->evp = evp_pkey_utils_from_pem(keyfile, strlen(keyfile), TRUE, password);
263
0
  if (!key->evp)
264
0
    goto fail;
265
0
  if (!key_read_private(key))
266
0
    goto fail;
267
0
  return key;
268
0
fail:
269
0
  freerdp_key_free(key);
270
0
  return NULL;
271
0
}
272
273
rdpPrivateKey* freerdp_key_new(void)
274
0
{
275
0
  return calloc(1, sizeof(rdpPrivateKey));
276
0
}
277
278
rdpPrivateKey* freerdp_key_clone(const rdpPrivateKey* key)
279
0
{
280
0
  if (!key)
281
0
    return NULL;
282
283
0
  rdpPrivateKey* _key = (rdpPrivateKey*)calloc(1, sizeof(rdpPrivateKey));
284
285
0
  if (!_key)
286
0
    return NULL;
287
288
0
  if (key->evp)
289
0
  {
290
0
    _key->evp = key->evp;
291
0
    if (!_key->evp)
292
0
      goto out_fail;
293
0
    EVP_PKEY_up_ref(_key->evp);
294
0
  }
295
296
0
  if (key->PrivateExponent)
297
0
  {
298
0
    _key->PrivateExponent = (BYTE*)malloc(key->PrivateExponentLength);
299
300
0
    if (!_key->PrivateExponent)
301
0
      goto out_fail;
302
303
0
    CopyMemory(_key->PrivateExponent, key->PrivateExponent, key->PrivateExponentLength);
304
0
    _key->PrivateExponentLength = key->PrivateExponentLength;
305
0
  }
306
307
0
  if (!cert_info_clone(&_key->cert, &key->cert))
308
0
    goto out_fail;
309
310
0
  return _key;
311
0
out_fail:
312
0
  WINPR_PRAGMA_DIAG_PUSH
313
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
314
0
  freerdp_key_free(_key);
315
0
  WINPR_PRAGMA_DIAG_POP
316
0
  return NULL;
317
0
}
318
319
void freerdp_key_free(rdpPrivateKey* key)
320
0
{
321
0
  if (!key)
322
0
    return;
323
324
0
  EVP_PKEY_free(key->evp);
325
0
  if (key->PrivateExponent)
326
0
    memset(key->PrivateExponent, 0, key->PrivateExponentLength);
327
0
  free(key->PrivateExponent);
328
0
  cert_info_free(&key->cert);
329
0
  free(key);
330
0
}
331
332
const rdpCertInfo* freerdp_key_get_info(const rdpPrivateKey* key)
333
0
{
334
0
  WINPR_ASSERT(key);
335
0
  if (!freerdp_key_is_rsa(key))
336
0
    return NULL;
337
0
  return &key->cert;
338
0
}
339
340
const BYTE* freerdp_key_get_exponent(const rdpPrivateKey* key, size_t* plength)
341
0
{
342
0
  WINPR_ASSERT(key);
343
0
  if (!freerdp_key_is_rsa(key))
344
0
  {
345
0
    if (plength)
346
0
      *plength = 0;
347
0
    return NULL;
348
0
  }
349
350
0
  if (plength)
351
0
    *plength = key->PrivateExponentLength;
352
0
  return key->PrivateExponent;
353
0
}
354
355
EVP_PKEY* freerdp_key_get_evp_pkey(const rdpPrivateKey* key)
356
0
{
357
0
  WINPR_ASSERT(key);
358
359
0
  EVP_PKEY* evp = key->evp;
360
0
  WINPR_ASSERT(evp);
361
0
  EVP_PKEY_up_ref(evp);
362
0
  return evp;
363
0
}
364
365
BOOL freerdp_key_is_rsa(const rdpPrivateKey* key)
366
0
{
367
0
  WINPR_ASSERT(key);
368
0
  if (key == priv_key_tssk)
369
0
    return TRUE;
370
371
0
  WINPR_ASSERT(key->evp);
372
0
  return (EVP_PKEY_id(key->evp) == EVP_PKEY_RSA);
373
0
}
374
375
size_t freerdp_key_get_bits(const rdpPrivateKey* key)
376
0
{
377
0
  int rc = -1;
378
0
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
379
0
  RSA* rsa = evp_pkey_to_rsa(key);
380
0
  if (rsa)
381
0
  {
382
0
    rc = RSA_bits(rsa);
383
0
    RSA_free(rsa);
384
0
  }
385
#else
386
  rc = EVP_PKEY_get_bits(key->evp);
387
#endif
388
389
0
  return WINPR_ASSERTING_INT_CAST(size_t, rc);
390
0
}
391
392
BOOL freerdp_key_generate(rdpPrivateKey* key, const char* type, size_t count, ...)
393
0
{
394
0
  BOOL rc = FALSE;
395
396
0
  if (!type)
397
0
  {
398
0
    WLog_ERR(TAG, "Invalid argument type=%s", type);
399
0
    return FALSE;
400
0
  }
401
0
  if (strncmp("RSA", type, 4) != 0)
402
0
  {
403
0
    WLog_ERR(TAG, "Argument type=%s is currently not supported, aborting", type);
404
0
    return FALSE;
405
0
  }
406
0
  if (count != 1)
407
0
  {
408
0
    WLog_ERR(TAG, "Argument type=%s requires count=1, got %" PRIuz ", aborting", type, count);
409
0
    return FALSE;
410
0
  }
411
0
  va_list ap;
412
0
  va_start(ap, count);
413
0
  const int key_length = va_arg(ap, int);
414
0
  va_end(ap);
415
416
0
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
417
0
  RSA* rsa = NULL;
418
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
419
  rsa = RSA_generate_key(key_length, RSA_F4, NULL, NULL);
420
#else
421
0
  {
422
0
    BIGNUM* bn = BN_secure_new();
423
424
0
    if (!bn)
425
0
      return FALSE;
426
427
0
    rsa = RSA_new();
428
429
0
    if (!rsa)
430
0
    {
431
0
      BN_clear_free(bn);
432
0
      return FALSE;
433
0
    }
434
435
0
    BN_set_word(bn, RSA_F4);
436
0
    const int res = RSA_generate_key_ex(rsa, key_length, bn, NULL);
437
0
    BN_clear_free(bn);
438
439
0
    if (res != 1)
440
0
      return FALSE;
441
0
  }
442
0
#endif
443
444
0
  EVP_PKEY_free(key->evp);
445
0
  key->evp = EVP_PKEY_new();
446
447
0
  if (!EVP_PKEY_assign_RSA(key->evp, rsa))
448
0
  {
449
0
    EVP_PKEY_free(key->evp);
450
0
    key->evp = NULL;
451
0
    RSA_free(rsa);
452
0
    return FALSE;
453
0
  }
454
455
0
  rc = TRUE;
456
#else
457
  EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, type, NULL);
458
  if (!pctx)
459
    return FALSE;
460
461
  if (EVP_PKEY_keygen_init(pctx) != 1)
462
    goto fail;
463
464
  if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, key_length) != 1)
465
    goto fail;
466
467
  EVP_PKEY_free(key->evp);
468
  key->evp = NULL;
469
470
  if (EVP_PKEY_generate(pctx, &key->evp) != 1)
471
    goto fail;
472
473
  rc = TRUE;
474
fail:
475
  EVP_PKEY_CTX_free(pctx);
476
#endif
477
0
  return rc;
478
0
}
479
480
BYTE* freerdp_key_get_param(const rdpPrivateKey* key, enum FREERDP_KEY_PARAM param, size_t* plength)
481
0
{
482
0
  BYTE* buf = NULL;
483
484
0
  WINPR_ASSERT(key);
485
0
  WINPR_ASSERT(plength);
486
487
0
  *plength = 0;
488
489
0
  BIGNUM* bn = NULL;
490
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
491
492
  const char* pk = NULL;
493
  switch (param)
494
  {
495
    case FREERDP_KEY_PARAM_RSA_D:
496
      pk = OSSL_PKEY_PARAM_RSA_D;
497
      break;
498
    case FREERDP_KEY_PARAM_RSA_E:
499
      pk = OSSL_PKEY_PARAM_RSA_E;
500
      break;
501
    case FREERDP_KEY_PARAM_RSA_N:
502
      pk = OSSL_PKEY_PARAM_RSA_N;
503
      break;
504
    default:
505
      return NULL;
506
  }
507
508
  if (!EVP_PKEY_get_bn_param(key->evp, pk, &bn))
509
    return NULL;
510
#else
511
0
  {
512
0
    const RSA* rsa = EVP_PKEY_get0_RSA(key->evp);
513
0
    if (!rsa)
514
0
      return NULL;
515
516
0
    const BIGNUM* cbn = NULL;
517
0
    switch (param)
518
0
    {
519
0
      case FREERDP_KEY_PARAM_RSA_D:
520
0
#if OPENSSL_VERSION_NUMBER >= 0x10101007L
521
0
        cbn = RSA_get0_d(rsa);
522
0
#endif
523
0
        break;
524
0
      case FREERDP_KEY_PARAM_RSA_E:
525
0
#if OPENSSL_VERSION_NUMBER >= 0x10101007L
526
0
        cbn = RSA_get0_e(rsa);
527
0
#endif
528
0
        break;
529
0
      case FREERDP_KEY_PARAM_RSA_N:
530
0
#if OPENSSL_VERSION_NUMBER >= 0x10101007L
531
0
        cbn = RSA_get0_n(rsa);
532
0
#endif
533
0
        break;
534
0
      default:
535
0
        return NULL;
536
0
    }
537
0
    if (!cbn)
538
0
      return NULL;
539
0
    bn = BN_dup(cbn);
540
0
    if (!bn)
541
0
      return NULL;
542
0
  }
543
0
#endif
544
545
0
  const int length = BN_num_bytes(bn);
546
0
  if (length < 0)
547
0
    goto fail;
548
549
0
  const size_t alloc_size = (size_t)length + 1ull;
550
0
  buf = calloc(alloc_size, sizeof(BYTE));
551
0
  if (!buf)
552
0
    goto fail;
553
554
0
  const int bnlen = BN_bn2bin(bn, buf);
555
0
  if (bnlen != length)
556
0
  {
557
0
    free(buf);
558
0
    buf = NULL;
559
0
  }
560
0
  else
561
0
    *plength = WINPR_ASSERTING_INT_CAST(size_t, length);
562
563
0
fail:
564
0
  BN_free(bn);
565
0
  return buf;
566
0
}
567
568
WINPR_DIGEST_CTX* freerdp_key_digest_sign(rdpPrivateKey* key, WINPR_MD_TYPE digest)
569
0
{
570
0
  WINPR_DIGEST_CTX* md_ctx = winpr_Digest_New();
571
0
  if (!md_ctx)
572
0
    return NULL;
573
574
0
  if (!winpr_DigestSign_Init(md_ctx, digest, key->evp))
575
0
  {
576
0
    winpr_Digest_Free(md_ctx);
577
0
    return NULL;
578
0
  }
579
0
  return md_ctx;
580
0
}
581
582
static BOOL bio_read_pem(BIO* bio, char** ppem, size_t* plength)
583
0
{
584
0
  BOOL rc = FALSE;
585
586
0
  WINPR_ASSERT(bio);
587
0
  WINPR_ASSERT(ppem);
588
589
0
  const size_t blocksize = 2048;
590
0
  size_t offset = 0;
591
0
  size_t length = blocksize;
592
0
  char* pem = NULL;
593
594
0
  *ppem = NULL;
595
0
  if (plength)
596
0
    *plength = 0;
597
598
0
  while (offset < length)
599
0
  {
600
0
    char* tmp = realloc(pem, length + 1);
601
0
    if (!tmp)
602
0
      goto fail;
603
0
    pem = tmp;
604
605
0
    ERR_clear_error();
606
607
0
    const int status = BIO_read(bio, &pem[offset], (int)(length - offset));
608
0
    if (status < 0)
609
0
    {
610
0
      WLog_ERR(TAG, "failed to read certificate");
611
0
      goto fail;
612
0
    }
613
614
0
    if (status == 0)
615
0
      break;
616
617
0
    offset += (size_t)status;
618
0
    if (length - offset > 0)
619
0
      break;
620
0
    length += blocksize;
621
0
  }
622
623
0
  if (pem)
624
0
  {
625
0
    if (offset >= length)
626
0
      goto fail;
627
0
    pem[offset] = '\0';
628
0
  }
629
0
  *ppem = pem;
630
0
  if (plength)
631
0
    *plength = offset;
632
0
  rc = TRUE;
633
0
fail:
634
0
  if (!rc)
635
0
    free(pem);
636
637
0
  return rc;
638
0
}
639
640
char* freerdp_key_get_pem(const rdpPrivateKey* key, size_t* plen, const char* password)
641
0
{
642
0
  WINPR_ASSERT(key);
643
644
0
  if (!key->evp)
645
0
    return NULL;
646
647
  /**
648
   * Don't manage certificates internally, leave it up entirely to the external client
649
   * implementation
650
   */
651
0
  BIO* bio = BIO_new(BIO_s_mem());
652
653
0
  if (!bio)
654
0
  {
655
0
    WLog_ERR(TAG, "BIO_new() failure");
656
0
    return NULL;
657
0
  }
658
659
0
  char* pem = NULL;
660
661
0
  const EVP_CIPHER* enc = NULL;
662
0
  if (password)
663
0
    enc = EVP_aes_256_xts();
664
665
0
  const int status = PEM_write_bio_PrivateKey(bio, key->evp, enc, NULL, 0, 0,
666
0
                                              WINPR_CAST_CONST_PTR_AWAY(password, void*));
667
0
  if (status < 0)
668
0
  {
669
0
    WLog_ERR(TAG, "PEM_write_bio_PrivateKey failure: %d", status);
670
0
    goto fail;
671
0
  }
672
673
0
  (void)bio_read_pem(bio, &pem, plen);
674
675
0
fail:
676
0
  BIO_free_all(bio);
677
0
  return pem;
678
0
}