FreeRDP/winpr/tools/makecert/makecert.c
2024-10-16 13:32:15 +02:00

1165 lines
29 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* WinPR: Windows Portable Runtime
* makecert replacement
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/path.h>
#include <winpr/file.h>
#include <winpr/cmdline.h>
#include <winpr/sysinfo.h>
#include <winpr/crypto.h>
#ifdef WITH_OPENSSL
#include <openssl/crypto.h>
#include <openssl/conf.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>
#include <openssl/bn.h>
#endif
#include <winpr/tools/makecert.h>
struct S_MAKECERT_CONTEXT
{
int argc;
char** argv;
#ifdef WITH_OPENSSL
X509* x509;
EVP_PKEY* pkey;
PKCS12* pkcs12;
#endif
BOOL live;
BOOL silent;
BOOL crtFormat;
BOOL pemFormat;
BOOL pfxFormat;
char* password;
char* output_file;
char* output_path;
char* default_name;
char* common_name;
int duration_years;
int duration_months;
};
static char* makecert_read_str(BIO* bio, size_t* pOffset)
{
int status = -1;
size_t offset = 0;
size_t length = 0;
char* x509_str = NULL;
while (offset >= length)
{
size_t new_len = 0;
size_t readBytes = 0;
char* new_str = NULL;
new_len = length * 2;
if (new_len == 0)
new_len = 2048;
if (new_len > INT_MAX)
{
status = -1;
break;
}
new_str = (char*)realloc(x509_str, new_len);
if (!new_str)
{
status = -1;
break;
}
length = new_len;
x509_str = new_str;
ERR_clear_error();
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
status = BIO_read_ex(bio, &x509_str[offset], length - offset, &readBytes);
#else
status = BIO_read(bio, &x509_str[offset], length - offset);
readBytes = status;
#endif
if (status <= 0)
break;
offset += readBytes;
}
if (status < 0)
{
free(x509_str);
if (pOffset)
*pOffset = 0;
return NULL;
}
x509_str[offset] = '\0';
if (pOffset)
*pOffset = offset + 1;
return x509_str;
}
static int makecert_print_command_line_help(COMMAND_LINE_ARGUMENT_A* args, int argc, char** argv)
{
char* str = NULL;
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
if (!argv || (argc < 1))
return -1;
printf("Usage: %s [options] [output file]\n", argv[0]);
printf("\n");
arg = args;
do
{
if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
{
printf(" %s", "-");
printf("%-20s", arg->Name);
printf("\t%s\n", arg->Text);
}
else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
(arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
{
printf(" %s", "-");
if (arg->Format)
{
size_t length = strlen(arg->Name) + strlen(arg->Format) + 2;
str = malloc(length + 1);
if (!str)
return -1;
(void)sprintf_s(str, length + 1, "%s %s", arg->Name, arg->Format);
(void)printf("%-20s", str);
free(str);
}
else
{
printf("%-20s", arg->Name);
}
printf("\t%s\n", arg->Text);
}
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return 1;
}
#ifdef WITH_OPENSSL
static int x509_add_ext(X509* cert, int nid, char* value)
{
X509V3_CTX ctx;
X509_EXTENSION* ext = NULL;
if (!cert || !value)
return 0;
X509V3_set_ctx_nodb(&ctx) X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ext = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
if (!ext)
return 0;
X509_add_ext(cert, ext, -1);
X509_EXTENSION_free(ext);
return 1;
}
#endif
static char* x509_name_parse(char* name, char* txt, size_t* length)
{
char* p = NULL;
char* entry = NULL;
if (!name || !txt || !length)
return NULL;
p = strstr(name, txt);
if (!p)
return NULL;
entry = p + strlen(txt) + 1;
p = strchr(entry, '=');
if (!p)
*length = strlen(entry);
else
*length = (size_t)(p - entry);
return entry;
}
static char* x509_get_default_name(void)
{
CHAR* computerName = NULL;
DWORD nSize = 0;
if (GetComputerNameExA(ComputerNamePhysicalDnsFullyQualified, NULL, &nSize) ||
GetLastError() != ERROR_MORE_DATA)
goto fallback;
computerName = (CHAR*)calloc(1, nSize);
if (!computerName)
goto fallback;
if (!GetComputerNameExA(ComputerNamePhysicalDnsFullyQualified, computerName, &nSize))
goto fallback;
return computerName;
fallback:
free(computerName);
if (GetComputerNameExA(ComputerNamePhysicalNetBIOS, NULL, &nSize) ||
GetLastError() != ERROR_MORE_DATA)
return NULL;
computerName = (CHAR*)calloc(1, nSize);
if (!computerName)
return NULL;
if (!GetComputerNameExA(ComputerNamePhysicalNetBIOS, computerName, &nSize))
{
free(computerName);
return NULL;
}
return computerName;
}
static int command_line_pre_filter(void* pvctx, int index, int argc, LPSTR* argv)
{
MAKECERT_CONTEXT* context = pvctx;
if (!context || !argv || (index < 0) || (argc < 0))
return -1;
if (index == (argc - 1))
{
if (argv[index][0] != '-')
{
context->output_file = _strdup(argv[index]);
if (!context->output_file)
return -1;
return 1;
}
}
return 0;
}
static int makecert_context_parse_arguments(MAKECERT_CONTEXT* context,
COMMAND_LINE_ARGUMENT_A* args, int argc, char** argv)
{
int status = 0;
DWORD flags = 0;
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
if (!context || !argv || (argc < 0))
return -1;
/**
* makecert -r -pe -n "CN=%COMPUTERNAME%" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr LocalMachine
* -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12
*/
CommandLineClearArgumentsA(args);
flags = COMMAND_LINE_SEPARATOR_SPACE | COMMAND_LINE_SIGIL_DASH;
status =
CommandLineParseArgumentsA(argc, argv, args, flags, context, command_line_pre_filter, NULL);
if (status & COMMAND_LINE_STATUS_PRINT_HELP)
{
makecert_print_command_line_help(args, argc, argv);
return 0;
}
arg = args;
errno = 0;
do
{
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
CommandLineSwitchStart(arg)
/* Basic Options */
CommandLineSwitchCase(arg, "silent")
{
context->silent = TRUE;
}
CommandLineSwitchCase(arg, "live")
{
context->live = TRUE;
}
CommandLineSwitchCase(arg, "format")
{
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
if (strcmp(arg->Value, "crt") == 0)
{
context->crtFormat = TRUE;
context->pemFormat = FALSE;
context->pfxFormat = FALSE;
}
else if (strcmp(arg->Value, "pem") == 0)
{
context->crtFormat = FALSE;
context->pemFormat = TRUE;
context->pfxFormat = FALSE;
}
else if (strcmp(arg->Value, "pfx") == 0)
{
context->crtFormat = FALSE;
context->pemFormat = FALSE;
context->pfxFormat = TRUE;
}
else
return -1;
}
CommandLineSwitchCase(arg, "path")
{
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
context->output_path = _strdup(arg->Value);
if (!context->output_path)
return -1;
}
CommandLineSwitchCase(arg, "p")
{
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
context->password = _strdup(arg->Value);
if (!context->password)
return -1;
}
CommandLineSwitchCase(arg, "n")
{
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
context->common_name = _strdup(arg->Value);
if (!context->common_name)
return -1;
}
CommandLineSwitchCase(arg, "y")
{
long val = 0;
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
val = strtol(arg->Value, NULL, 0);
if ((errno != 0) || (val < 0) || (val > INT32_MAX))
return -1;
context->duration_years = (int)val;
}
CommandLineSwitchCase(arg, "m")
{
long val = 0;
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
val = strtol(arg->Value, NULL, 0);
if ((errno != 0) || (val < 0))
return -1;
context->duration_months = (int)val;
}
CommandLineSwitchDefault(arg)
{
}
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return 1;
}
int makecert_context_set_output_file_name(MAKECERT_CONTEXT* context, const char* name)
{
if (!context)
return -1;
free(context->output_file);
context->output_file = NULL;
if (name)
context->output_file = _strdup(name);
if (!context->output_file)
return -1;
return 1;
}
int makecert_context_output_certificate_file(MAKECERT_CONTEXT* context, const char* path)
{
#ifdef WITH_OPENSSL
FILE* fp = NULL;
int status = 0;
size_t length = 0;
size_t offset = 0;
char* filename = NULL;
char* fullpath = NULL;
char* ext = NULL;
int ret = -1;
BIO* bio = NULL;
char* x509_str = NULL;
if (!context)
return -1;
if (!context->output_file)
{
context->output_file = _strdup(context->default_name);
if (!context->output_file)
return -1;
}
/*
* Output Certificate File
*/
length = strlen(context->output_file);
filename = malloc(length + 8);
if (!filename)
return -1;
if (context->crtFormat)
ext = "crt";
else if (context->pemFormat)
ext = "pem";
else if (context->pfxFormat)
ext = "pfx";
else
goto out_fail;
(void)sprintf_s(filename, length + 8, "%s.%s", context->output_file, ext);
if (path)
fullpath = GetCombinedPath(path, filename);
else
fullpath = _strdup(filename);
if (!fullpath)
goto out_fail;
fp = winpr_fopen(fullpath, "w+");
if (fp)
{
if (context->pfxFormat)
{
if (!context->password)
{
context->password = _strdup("password");
if (!context->password)
goto out_fail;
printf("Using default export password \"password\"\n");
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
#else
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
OPENSSL_INIT_LOAD_CONFIG,
NULL);
#endif
context->pkcs12 = PKCS12_create(context->password, context->default_name, context->pkey,
context->x509, NULL, 0, 0, 0, 0, 0);
if (!context->pkcs12)
goto out_fail;
bio = BIO_new(BIO_s_mem());
if (!bio)
goto out_fail;
status = i2d_PKCS12_bio(bio, context->pkcs12);
if (status != 1)
goto out_fail;
x509_str = makecert_read_str(bio, &offset);
if (!x509_str)
goto out_fail;
length = offset;
if (fwrite((void*)x509_str, length, 1, fp) != 1)
goto out_fail;
}
else
{
bio = BIO_new(BIO_s_mem());
if (!bio)
goto out_fail;
if (!PEM_write_bio_X509(bio, context->x509))
goto out_fail;
x509_str = makecert_read_str(bio, &offset);
if (!x509_str)
goto out_fail;
length = offset;
if (fwrite(x509_str, length, 1, fp) != 1)
goto out_fail;
free(x509_str);
x509_str = NULL;
BIO_free_all(bio);
bio = NULL;
if (context->pemFormat)
{
bio = BIO_new(BIO_s_mem());
if (!bio)
goto out_fail;
status = PEM_write_bio_PrivateKey(bio, context->pkey, NULL, NULL, 0, NULL, NULL);
if (status < 0)
goto out_fail;
x509_str = makecert_read_str(bio, &offset);
if (!x509_str)
goto out_fail;
length = offset;
if (fwrite(x509_str, length, 1, fp) != 1)
goto out_fail;
}
}
}
ret = 1;
out_fail:
BIO_free_all(bio);
if (fp)
(void)fclose(fp);
free(x509_str);
free(filename);
free(fullpath);
return ret;
#else
WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
return -1;
#endif
}
int makecert_context_output_private_key_file(MAKECERT_CONTEXT* context, const char* path)
{
#ifdef WITH_OPENSSL
FILE* fp = NULL;
size_t length = 0;
size_t offset = 0;
char* filename = NULL;
char* fullpath = NULL;
int ret = -1;
BIO* bio = NULL;
char* x509_str = NULL;
if (!context->crtFormat)
return 1;
if (!context->output_file)
{
context->output_file = _strdup(context->default_name);
if (!context->output_file)
return -1;
}
/**
* Output Private Key File
*/
length = strlen(context->output_file);
filename = malloc(length + 8);
if (!filename)
return -1;
(void)sprintf_s(filename, length + 8, "%s.key", context->output_file);
if (path)
fullpath = GetCombinedPath(path, filename);
else
fullpath = _strdup(filename);
if (!fullpath)
goto out_fail;
fp = winpr_fopen(fullpath, "w+");
if (!fp)
goto out_fail;
bio = BIO_new(BIO_s_mem());
if (!bio)
goto out_fail;
if (!PEM_write_bio_PrivateKey(bio, context->pkey, NULL, NULL, 0, NULL, NULL))
goto out_fail;
x509_str = makecert_read_str(bio, &offset);
if (!x509_str)
goto out_fail;
length = offset;
if (fwrite((void*)x509_str, length, 1, fp) != 1)
goto out_fail;
ret = 1;
out_fail:
if (fp)
(void)fclose(fp);
BIO_free_all(bio);
free(x509_str);
free(filename);
free(fullpath);
return ret;
#else
WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
return -1;
#endif
}
#ifdef WITH_OPENSSL
static BOOL makecert_create_rsa(EVP_PKEY** ppkey, size_t key_length)
{
BOOL rc = FALSE;
WINPR_ASSERT(ppkey);
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
RSA* rsa = NULL;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
rsa = RSA_generate_key(key_length, RSA_F4, NULL, NULL);
#else
{
BIGNUM* bn = BN_secure_new();
if (!bn)
return FALSE;
rsa = RSA_new();
if (!rsa)
{
BN_clear_free(bn);
return FALSE;
}
BN_set_word(bn, RSA_F4);
const int res = RSA_generate_key_ex(rsa, key_length, bn, NULL);
BN_clear_free(bn);
if (res != 1)
return FALSE;
}
#endif
if (!EVP_PKEY_assign_RSA(*ppkey, rsa))
{
RSA_free(rsa);
return FALSE;
}
rc = TRUE;
#else
EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
if (!pctx)
return FALSE;
if (EVP_PKEY_keygen_init(pctx) != 1)
goto fail;
WINPR_ASSERT(key_length <= UINT_MAX);
unsigned int keylen = (unsigned int)key_length;
const OSSL_PARAM params[] = { OSSL_PARAM_construct_uint("bits", &keylen),
OSSL_PARAM_construct_end() };
if (EVP_PKEY_CTX_set_params(pctx, params) != 1)
goto fail;
if (EVP_PKEY_generate(pctx, ppkey) != 1)
goto fail;
rc = TRUE;
fail:
EVP_PKEY_CTX_free(pctx);
#endif
return rc;
}
#endif
int makecert_context_process(MAKECERT_CONTEXT* context, int argc, char** argv)
{
COMMAND_LINE_ARGUMENT_A args[] = {
/* Custom Options */
{ "rdp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
"Unsupported - Generate certificate with required options for RDP usage." },
{ "silent", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
"Silently generate certificate without verbose output." },
{ "live", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
"Generate certificate live in memory when used as a library." },
{ "format", COMMAND_LINE_VALUE_REQUIRED, "<crt|pem|pfx>", NULL, NULL, -1, NULL,
"Specify certificate file format" },
{ "path", COMMAND_LINE_VALUE_REQUIRED, "<path>", NULL, NULL, -1, NULL,
"Specify certificate file output path" },
{ "p", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL,
"Specify certificate export password" },
/* Basic Options */
{ "n", COMMAND_LINE_VALUE_REQUIRED, "<name>", NULL, NULL, -1, NULL,
"Specifies the subject's certificate name. This name must conform to the X.500 standard. "
"The simplest method is to specify the name in double quotes, preceded by CN=; for "
"example, "
"-n \"CN=myName\"." },
{ "pe", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
"Unsupported - Marks the generated private key as exportable. This allows the private "
"key to "
"be included in the certificate." },
{ "sk", COMMAND_LINE_VALUE_REQUIRED, "<keyname>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the subject's key container location, which contains the "
"private "
"key. "
"If a key container does not exist, it will be created." },
{ "sr", COMMAND_LINE_VALUE_REQUIRED, "<location>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the subject's certificate store location. location can be "
"either "
"currentuser (the default) or localmachine." },
{ "ss", COMMAND_LINE_VALUE_REQUIRED, "<store>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the subject's certificate store name that stores the output "
"certificate." },
{ "#", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
"Specifies a serial number from 1 to 2,147,483,647. The default is a unique value "
"generated "
"by Makecert.exe." },
{ "$", COMMAND_LINE_VALUE_REQUIRED, "<authority>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the signing authority of the certificate, which must be set to "
"either commercial "
"(for certificates used by commercial software publishers) or individual (for "
"certificates "
"used by individual software publishers)." },
/* Extended Options */
{ "a", COMMAND_LINE_VALUE_REQUIRED, "<algorithm>", NULL, NULL, -1, NULL,
"Specifies the signature algorithm. algorithm must be md5, sha1, sha256 (the default), "
"sha384, or sha512." },
{ "b", COMMAND_LINE_VALUE_REQUIRED, "<mm/dd/yyyy>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the start of the validity period. Defaults to the current "
"date." },
{ "crl", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
"Unsupported - Generates a certificate relocation list (CRL) instead of a certificate." },
{ "cy", COMMAND_LINE_VALUE_REQUIRED, "<certType>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the certificate type. Valid values are end for end-entity and "
"authority for certification authority." },
{ "e", COMMAND_LINE_VALUE_REQUIRED, "<mm/dd/yyyy>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the end of the validity period. Defaults to 12/31/2039 11:59:59 "
"GMT." },
{ "eku", COMMAND_LINE_VALUE_REQUIRED, "<oid[,oid…]>", NULL, NULL, -1, NULL,
"Unsupported - Inserts a list of comma-separated, enhanced key usage object identifiers "
"(OIDs) into the certificate." },
{ "h", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the maximum height of the tree below this certificate." },
{ "ic", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the issuer's certificate file." },
{ "ik", COMMAND_LINE_VALUE_REQUIRED, "<keyName>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the issuer's key container name." },
{ "iky", COMMAND_LINE_VALUE_REQUIRED, "<keyType>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the issuer's key type, which must be one of the following: "
"signature (which indicates that the key is used for a digital signature), "
"exchange (which indicates that the key is used for key encryption and key exchange), "
"or an integer that represents a provider type. "
"By default, you can pass 1 for an exchange key or 2 for a signature key." },
{ "in", COMMAND_LINE_VALUE_REQUIRED, "<name>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the issuer's certificate common name." },
{ "ip", COMMAND_LINE_VALUE_REQUIRED, "<provider>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the issuer's CryptoAPI provider name. For information about the "
"CryptoAPI provider name, see the sp option." },
{ "ir", COMMAND_LINE_VALUE_REQUIRED, "<location>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the location of the issuer's certificate store. location can be "
"either currentuser (the default) or localmachine." },
{ "is", COMMAND_LINE_VALUE_REQUIRED, "<store>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the issuer's certificate store name." },
{ "iv", COMMAND_LINE_VALUE_REQUIRED, "<pvkFile>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the issuer's .pvk private key file." },
{ "iy", COMMAND_LINE_VALUE_REQUIRED, "<type>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the issuer's CryptoAPI provider type. For information about the "
"CryptoAPI provider type, see the sy option." },
{ "l", COMMAND_LINE_VALUE_REQUIRED, "<link>", NULL, NULL, -1, NULL,
"Unsupported - Links to policy information (for example, to a URL)." },
{ "len", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
"Specifies the generated key length, in bits." },
{ "m", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
"Specifies the duration, in months, of the certificate validity period." },
{ "y", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
"Specifies the duration, in years, of the certificate validity period." },
{ "nscp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
"Unsupported - Includes the Netscape client-authorization extension." },
{ "r", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
"Unsupported - Creates a self-signed certificate." },
{ "sc", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the subject's certificate file." },
{ "sky", COMMAND_LINE_VALUE_REQUIRED, "<keyType>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the subject's key type, which must be one of the following: "
"signature (which indicates that the key is used for a digital signature), "
"exchange (which indicates that the key is used for key encryption and key exchange), "
"or an integer that represents a provider type. "
"By default, you can pass 1 for an exchange key or 2 for a signature key." },
{ "sp", COMMAND_LINE_VALUE_REQUIRED, "<provider>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the subject's CryptoAPI provider name, which must be defined in "
"the "
"registry subkeys of "
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider. If both sp "
"and "
"sy are present, "
"the type of the CryptoAPI provider must correspond to the Type value of the provider's "
"subkey." },
{ "sv", COMMAND_LINE_VALUE_REQUIRED, "<pvkFile>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the subject's .pvk private key file. The file is created if "
"none "
"exists." },
{ "sy", COMMAND_LINE_VALUE_REQUIRED, "<type>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the subject's CryptoAPI provider type, which must be defined in "
"the "
"registry subkeys of "
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider Types. If "
"both "
"sy and sp are present, "
"the name of the CryptoAPI provider must correspond to the Name value of the provider "
"type "
"subkey." },
{ "tbs", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
"Unsupported - Specifies the certificate or CRL file to be signed." },
/* Help */
{ "?", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "help",
"print help" },
{ "!", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "help-ext",
"print extended help" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
#ifdef WITH_OPENSSL
size_t length = 0;
char* entry = NULL;
int key_length = 0;
long serial = 0;
X509_NAME* name = NULL;
const EVP_MD* md = NULL;
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
int ret = 0;
ret = makecert_context_parse_arguments(context, args, argc, argv);
if (ret < 1)
{
return ret;
}
if (!context->default_name && !context->common_name)
{
context->default_name = x509_get_default_name();
if (!context->default_name)
return -1;
}
else
{
context->default_name = _strdup(context->common_name);
if (!context->default_name)
return -1;
}
if (!context->common_name)
{
context->common_name = _strdup(context->default_name);
if (!context->common_name)
return -1;
}
if (!context->pkey)
context->pkey = EVP_PKEY_new();
if (!context->pkey)
return -1;
if (!context->x509)
context->x509 = X509_new();
if (!context->x509)
return -1;
key_length = 2048;
arg = CommandLineFindArgumentA(args, "len");
if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
{
unsigned long val = strtoul(arg->Value, NULL, 0);
if ((errno != 0) || (val > INT_MAX))
return -1;
key_length = (int)val;
}
if (!makecert_create_rsa(&context->pkey, key_length))
return -1;
X509_set_version(context->x509, 2);
arg = CommandLineFindArgumentA(args, "#");
if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
{
serial = strtol(arg->Value, NULL, 0);
if (errno != 0)
return -1;
}
else
serial = (long)GetTickCount64();
ASN1_INTEGER_set(X509_get_serialNumber(context->x509), serial);
{
ASN1_TIME* before = NULL;
ASN1_TIME* after = NULL;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
before = X509_get_notBefore(context->x509);
after = X509_get_notAfter(context->x509);
#else
before = X509_getm_notBefore(context->x509);
after = X509_getm_notAfter(context->x509);
#endif
X509_gmtime_adj(before, 0);
long duration = context->duration_months * 31l + context->duration_years * 365l;
duration *= 60l * 60l * 24l;
X509_gmtime_adj(after, duration);
}
X509_set_pubkey(context->x509, context->pkey);
name = X509_get_subject_name(context->x509);
arg = CommandLineFindArgumentA(args, "n");
if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
{
entry = x509_name_parse(arg->Value, "C", &length);
if (entry)
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_UTF8, (const unsigned char*)entry,
(int)length, -1, 0);
entry = x509_name_parse(arg->Value, "ST", &length);
if (entry)
X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_UTF8, (const unsigned char*)entry,
(int)length, -1, 0);
entry = x509_name_parse(arg->Value, "L", &length);
if (entry)
X509_NAME_add_entry_by_txt(name, "L", MBSTRING_UTF8, (const unsigned char*)entry,
(int)length, -1, 0);
entry = x509_name_parse(arg->Value, "O", &length);
if (entry)
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_UTF8, (const unsigned char*)entry,
(int)length, -1, 0);
entry = x509_name_parse(arg->Value, "OU", &length);
if (entry)
X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_UTF8, (const unsigned char*)entry,
(int)length, -1, 0);
entry = context->common_name;
length = strlen(entry);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (const unsigned char*)entry,
(int)length, -1, 0);
}
else
{
entry = context->common_name;
length = strlen(entry);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (const unsigned char*)entry,
(int)length, -1, 0);
}
X509_set_issuer_name(context->x509, name);
x509_add_ext(context->x509, NID_ext_key_usage, "serverAuth");
arg = CommandLineFindArgumentA(args, "a");
md = EVP_sha256();
if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
{
md = EVP_get_digestbyname(arg->Value);
if (!md)
return -1;
}
if (!X509_sign(context->x509, context->pkey, md))
return -1;
/**
* Print certificate
*/
if (!context->silent)
{
BIO* bio = NULL;
int status = 0;
char* x509_str = NULL;
bio = BIO_new(BIO_s_mem());
if (!bio)
return -1;
status = X509_print(bio, context->x509);
if (status < 0)
{
BIO_free_all(bio);
return -1;
}
x509_str = makecert_read_str(bio, NULL);
if (!x509_str)
{
BIO_free_all(bio);
return -1;
}
printf("%s", x509_str);
free(x509_str);
BIO_free_all(bio);
}
/**
* Output certificate and private key to files
*/
if (!context->live)
{
if (!winpr_PathFileExists(context->output_path))
{
if (!CreateDirectoryA(context->output_path, NULL))
return -1;
}
if (makecert_context_output_certificate_file(context, context->output_path) != 1)
return -1;
if (context->crtFormat)
{
if (makecert_context_output_private_key_file(context, context->output_path) < 0)
return -1;
}
}
return 0;
#else
WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
return -1;
#endif
}
MAKECERT_CONTEXT* makecert_context_new(void)
{
MAKECERT_CONTEXT* context = (MAKECERT_CONTEXT*)calloc(1, sizeof(MAKECERT_CONTEXT));
if (context)
{
context->crtFormat = TRUE;
context->duration_years = 1;
}
return context;
}
void makecert_context_free(MAKECERT_CONTEXT* context)
{
if (context)
{
free(context->password);
free(context->default_name);
free(context->common_name);
free(context->output_file);
free(context->output_path);
#ifdef WITH_OPENSSL
X509_free(context->x509);
EVP_PKEY_free(context->pkey);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
CRYPTO_cleanup_all_ex_data();
#endif
#endif
free(context);
}
}