/*
* Copyright (C) 2003-2012 Free Software Foundation, Inc.
*
* This file is part of GnuTLS.
*
* GnuTLS is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GnuTLS is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "certtool-common.h"
#include "certtool-args.h"
#include "certtool-cfg.h"
/* Gnulib portability files. */
#include
unsigned char buffer[64 * 1024];
const int buffer_size = sizeof (buffer);
FILE *
safe_open_rw (const char *file, int privkey_op)
{
mode_t omask = 0;
FILE *fh;
if (privkey_op != 0)
{
omask = umask (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
}
fh = fopen (file, "wb");
if (privkey_op != 0)
{
umask (omask);
}
return fh;
}
gnutls_datum_t *
load_secret_key (int mand, common_info_st * info)
{
char raw_key[64];
size_t raw_key_size = sizeof (raw_key);
static gnutls_datum_t key;
gnutls_datum_t hex_key;
int ret;
if (info->verbose)
fprintf (stderr, "Loading secret key...\n");
if (info->secret_key == NULL)
{
if (mand)
error (EXIT_FAILURE, 0, "missing --secret-key");
else
return NULL;
}
hex_key.data = (void *) info->secret_key;
hex_key.size = strlen (info->secret_key);
ret = gnutls_hex_decode (&hex_key, raw_key, &raw_key_size);
if (ret < 0)
error (EXIT_FAILURE, 0, "hex_decode: %s", gnutls_strerror (ret));
key.data = (void*)raw_key;
key.size = raw_key_size;
return &key;
}
const char* get_password(common_info_st * cinfo, unsigned int *flags, int confirm)
{
if (cinfo->null_password)
{
if (flags) *flags |= GNUTLS_PKCS_NULL_PASSWORD;
return NULL;
}
else if (cinfo->password)
{
if (cinfo->password[0] == 0 && flags)
*flags |= GNUTLS_PKCS_PLAIN;
return cinfo->password;
}
else
{
if (confirm)
return get_confirmed_pass (true);
else
return get_pass ();
}
}
static gnutls_privkey_t _load_privkey(gnutls_datum_t *dat, common_info_st * info)
{
int ret;
gnutls_privkey_t key;
unsigned int flags = 0;
const char* pass;
ret = gnutls_privkey_init (&key);
if (ret < 0)
error (EXIT_FAILURE, 0, "privkey_init: %s", gnutls_strerror (ret));
ret = gnutls_privkey_import_x509_raw (key, dat, info->incert_format, NULL, 0);
if (ret == GNUTLS_E_DECRYPTION_FAILED)
{
pass = get_password (info, &flags, 0);
ret = gnutls_privkey_import_x509_raw (key, dat, info->incert_format, pass, flags);
}
if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR)
{
error (EXIT_FAILURE, 0,
"import error: could not find a valid PEM header; "
"check if your key is PKCS #12 encoded");
}
if (ret < 0)
error (EXIT_FAILURE, 0, "importing --load-privkey: %s: %s",
info->privkey, gnutls_strerror (ret));
return key;
}
static gnutls_privkey_t _load_url_privkey(const char* url)
{
int ret;
gnutls_privkey_t key;
ret = gnutls_privkey_init (&key);
if (ret < 0)
error (EXIT_FAILURE, 0, "privkey_init: %s", gnutls_strerror (ret));
ret = gnutls_privkey_import_url(key, url, 0);
if (ret < 0)
error (EXIT_FAILURE, 0, "importing key: %s: %s",
url, gnutls_strerror (ret));
return key;
}
static gnutls_pubkey_t _load_url_pubkey(const char* url)
{
int ret;
gnutls_pubkey_t pubkey;
unsigned int obj_flags = 0;
ret = gnutls_pubkey_init (&pubkey);
if (ret < 0)
{
fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
gnutls_strerror (ret));
exit (1);
}
ret = gnutls_pubkey_import_url (pubkey, url, obj_flags);
if (ret < 0)
{
fprintf (stderr, "Error in %s:%d: %s: %s\n", __func__, __LINE__,
gnutls_strerror (ret), url);
exit (1);
}
return pubkey;
}
/* Load the private key.
* @mand should be non zero if it is required to read a private key.
*/
gnutls_privkey_t
load_private_key (int mand, common_info_st * info)
{
gnutls_privkey_t key;
gnutls_datum_t dat;
size_t size;
if (!info->privkey && !mand)
return NULL;
if (info->privkey == NULL)
error (EXIT_FAILURE, 0, "missing --load-privkey");
if (gnutls_url_is_supported(info->privkey) != 0)
return _load_url_privkey(info->privkey);
dat.data = (void*)read_binary_file (info->privkey, &size);
dat.size = size;
if (!dat.data)
error (EXIT_FAILURE, errno, "reading --load-privkey: %s", info->privkey);
key = _load_privkey(&dat, info);
free (dat.data);
return key;
}
/* Load the private key.
* @mand should be non zero if it is required to read a private key.
*/
gnutls_x509_privkey_t
load_x509_private_key (int mand, common_info_st * info)
{
gnutls_x509_privkey_t key;
int ret;
gnutls_datum_t dat;
size_t size;
unsigned int flags = 0;
const char* pass;
if (!info->privkey && !mand)
return NULL;
if (info->privkey == NULL)
error (EXIT_FAILURE, 0, "missing --load-privkey");
ret = gnutls_x509_privkey_init (&key);
if (ret < 0)
error (EXIT_FAILURE, 0, "privkey_init: %s", gnutls_strerror (ret));
dat.data = (void*)read_binary_file (info->privkey, &size);
dat.size = size;
if (!dat.data)
error (EXIT_FAILURE, errno, "reading --load-privkey: %s", info->privkey);
if (info->pkcs8)
{
pass = get_password (info, &flags, 0);
ret =
gnutls_x509_privkey_import_pkcs8 (key, &dat, info->incert_format,
pass, flags);
}
else
{
ret = gnutls_x509_privkey_import2 (key, &dat, info->incert_format, NULL, 0);
if (ret == GNUTLS_E_DECRYPTION_FAILED)
{
pass = get_password (info, &flags, 0);
ret = gnutls_x509_privkey_import2 (key, &dat, info->incert_format, pass, flags);
}
}
free (dat.data);
if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR)
{
error (EXIT_FAILURE, 0,
"import error: could not find a valid PEM header; "
"check if your key is PKCS #12 encoded");
}
if (ret < 0)
error (EXIT_FAILURE, 0, "importing --load-privkey: %s: %s",
info->privkey, gnutls_strerror (ret));
return key;
}
/* Loads the certificate
* If mand is non zero then a certificate is mandatory. Otherwise
* null will be returned if the certificate loading fails.
*/
gnutls_x509_crt_t
load_cert (int mand, common_info_st * info)
{
gnutls_x509_crt_t *crt;
size_t size;
crt = load_cert_list (mand, &size, info);
return crt ? crt[0] : NULL;
}
#define MAX_CERTS 256
/* Loads a certificate list
*/
gnutls_x509_crt_t *
load_cert_list (int mand, size_t * crt_size, common_info_st * info)
{
FILE *fd;
static gnutls_x509_crt_t crt[MAX_CERTS];
char *ptr;
int ret, i;
gnutls_datum_t dat;
size_t size;
int ptr_size;
*crt_size = 0;
if (info->verbose)
fprintf (stderr, "Loading certificate list...\n");
if (info->cert == NULL)
{
if (mand)
error (EXIT_FAILURE, 0, "missing --load-certificate");
else
return NULL;
}
fd = fopen (info->cert, "r");
if (fd == NULL)
error (EXIT_FAILURE, errno, "%s", info->cert);
size = fread (buffer, 1, sizeof (buffer) - 1, fd);
buffer[size] = 0;
fclose (fd);
ptr = (void*)buffer;
ptr_size = size;
for (i = 0; i < MAX_CERTS; i++)
{
ret = gnutls_x509_crt_init (&crt[i]);
if (ret < 0)
error (EXIT_FAILURE, 0, "crt_init: %s", gnutls_strerror (ret));
dat.data = (void*)ptr;
dat.size = ptr_size;
ret = gnutls_x509_crt_import (crt[i], &dat, info->incert_format);
if (ret < 0 && *crt_size > 0)
break;
if (ret < 0)
error (EXIT_FAILURE, 0, "crt_import: %s", gnutls_strerror (ret));
ptr = strstr (ptr, "---END");
if (ptr == NULL)
break;
ptr++;
ptr_size = size;
ptr_size -=
(unsigned int) ((unsigned char *) ptr - (unsigned char *) buffer);
if (ptr_size < 0)
break;
(*crt_size)++;
}
if (info->verbose)
fprintf (stderr, "Loaded %d certificates.\n", (int) *crt_size);
return crt;
}
/* Load the Certificate Request.
*/
gnutls_x509_crq_t
load_request (common_info_st * info)
{
gnutls_x509_crq_t crq;
int ret;
gnutls_datum_t dat;
size_t size;
if (!info->request)
return NULL;
ret = gnutls_x509_crq_init (&crq);
if (ret < 0)
error (EXIT_FAILURE, 0, "crq_init: %s", gnutls_strerror (ret));
dat.data = (void*)read_binary_file (info->request, &size);
dat.size = size;
if (!dat.data)
error (EXIT_FAILURE, errno, "reading --load-request: %s", info->request);
ret = gnutls_x509_crq_import (crq, &dat, info->incert_format);
if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR)
{
error (EXIT_FAILURE, 0,
"import error: could not find a valid PEM header");
}
free (dat.data);
if (ret < 0)
error (EXIT_FAILURE, 0, "importing --load-request: %s: %s",
info->request, gnutls_strerror (ret));
return crq;
}
/* Load the CA's private key.
*/
gnutls_privkey_t
load_ca_private_key (common_info_st * info)
{
gnutls_privkey_t key;
gnutls_datum_t dat;
size_t size;
if (info->ca_privkey == NULL)
error (EXIT_FAILURE, 0, "missing --load-ca-privkey");
if (gnutls_url_is_supported(info->ca_privkey) != 0)
return _load_url_privkey(info->ca_privkey);
dat.data = (void*)read_binary_file (info->ca_privkey, &size);
dat.size = size;
if (!dat.data)
error (EXIT_FAILURE, errno, "reading --load-ca-privkey: %s",
info->ca_privkey);
key = _load_privkey(&dat, info);
free (dat.data);
return key;
}
/* Loads the CA's certificate
*/
gnutls_x509_crt_t
load_ca_cert (common_info_st * info)
{
gnutls_x509_crt_t crt;
int ret;
gnutls_datum_t dat;
size_t size;
if (info->ca == NULL)
error (EXIT_FAILURE, 0, "missing --load-ca-certificate");
ret = gnutls_x509_crt_init (&crt);
if (ret < 0)
error (EXIT_FAILURE, 0, "crt_init: %s", gnutls_strerror (ret));
dat.data = (void*)read_binary_file (info->ca, &size);
dat.size = size;
if (!dat.data)
error (EXIT_FAILURE, errno, "reading --load-ca-certificate: %s",
info->ca);
ret = gnutls_x509_crt_import (crt, &dat, info->incert_format);
free (dat.data);
if (ret < 0)
error (EXIT_FAILURE, 0, "importing --load-ca-certificate: %s: %s",
info->ca, gnutls_strerror (ret));
return crt;
}
/* Load a public key.
* @mand should be non zero if it is required to read a public key.
*/
gnutls_pubkey_t
load_pubkey (int mand, common_info_st * info)
{
gnutls_pubkey_t key;
int ret;
gnutls_datum_t dat;
size_t size;
if (!info->pubkey && !mand)
return NULL;
if (info->pubkey == NULL)
error (EXIT_FAILURE, 0, "missing --load-pubkey");
if (gnutls_url_is_supported(info->pubkey) != 0)
return _load_url_pubkey(info->pubkey);
ret = gnutls_pubkey_init (&key);
if (ret < 0)
error (EXIT_FAILURE, 0, "privkey_init: %s", gnutls_strerror (ret));
dat.data = (void*)read_binary_file (info->pubkey, &size);
dat.size = size;
if (!dat.data)
error (EXIT_FAILURE, errno, "reading --load-pubkey: %s", info->pubkey);
ret = gnutls_pubkey_import (key, &dat, info->incert_format);
free (dat.data);
if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR)
{
error (EXIT_FAILURE, 0,
"import error: could not find a valid PEM header; "
"check if your key has the PUBLIC KEY header");
}
if (ret < 0)
error (EXIT_FAILURE, 0, "importing --load-pubkey: %s: %s",
info->pubkey, gnutls_strerror (ret));
return key;
}
gnutls_pubkey_t load_public_key_or_import(int mand, gnutls_privkey_t privkey, common_info_st * info)
{
gnutls_pubkey_t pubkey;
int ret;
ret = gnutls_pubkey_init(&pubkey);
if (ret < 0)
error (EXIT_FAILURE, 0, "gnutls_pubkey_init: %s",
gnutls_strerror (ret));
if (!privkey || (ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0)) < 0)
{ /* could not get (e.g. on PKCS #11 */
gnutls_pubkey_deinit(pubkey);
return load_pubkey(mand, info);
}
return pubkey;
}
int
get_bits (gnutls_pk_algorithm_t key_type, int info_bits, const char* info_sec_param, int warn)
{
int bits;
if (info_bits != 0)
{
static int warned = 0;
if (warned == 0 && warn != 0)
{
warned = 1;
fprintf (stderr,
"** Note: Please use the --sec-param instead of --bits\n");
}
bits = info_bits;
}
else
{
if (info_sec_param)
{
bits =
gnutls_sec_param_to_pk_bits (key_type,
str_to_sec_param (info_sec_param));
}
else
bits =
gnutls_sec_param_to_pk_bits (key_type, GNUTLS_SEC_PARAM_NORMAL);
}
return bits;
}
gnutls_sec_param_t str_to_sec_param (const char *str)
{
if (strcasecmp (str, "low") == 0)
{
return GNUTLS_SEC_PARAM_LOW;
}
else if (strcasecmp (str, "legacy") == 0)
{
return GNUTLS_SEC_PARAM_LEGACY;
}
else if (strcasecmp (str, "normal") == 0)
{
return GNUTLS_SEC_PARAM_NORMAL;
}
else if (strcasecmp (str, "high") == 0)
{
return GNUTLS_SEC_PARAM_HIGH;
}
else if (strcasecmp (str, "ultra") == 0)
{
return GNUTLS_SEC_PARAM_ULTRA;
}
else
{
fprintf (stderr, "Unknown security parameter string: %s\n", str);
exit (1);
}
}
#define SPACE "\t"
static void
print_hex_datum (FILE* outfile, gnutls_datum_t * dat, int cprint)
{
unsigned int j;
if (cprint != 0)
{
fprintf (outfile, "\n" SPACE"\"");
for (j = 0; j < dat->size; j++)
{
fprintf (outfile, "\\x%.2x", (unsigned char) dat->data[j]);
if ((j + 1) % 15 == 0)
fprintf (outfile, "\"\n" SPACE"\"");
}
fprintf (outfile, "\";\n\n");
return;
}
fprintf (outfile, "\n" SPACE);
for (j = 0; j < dat->size; j++)
{
fprintf (outfile, "%.2x:", (unsigned char) dat->data[j]);
if ((j + 1) % 15 == 0)
fprintf (outfile, "\n" SPACE);
}
fprintf (outfile, "\n\n");
}
static void print_head(FILE* out, const char* txt, unsigned int size, int cprint)
{
unsigned i;
char* p, * ntxt;
if (cprint != 0)
{
if (size > 0)
asprintf(&ntxt, "const unsigned char %s[%u] =", txt, size);
else
asprintf(&ntxt, "const unsigned char %s[] =\n", txt);
p = strstr(ntxt, "char");
p += 5;
for (i=0;isize, cprint);
print_hex_datum (outfile, x, cprint);
}
print_head (outfile, "public key", y->size, cprint);
print_hex_datum (outfile, y, cprint);
print_head (outfile, "p", p->size, cprint);
print_hex_datum (outfile, p, cprint);
print_head (outfile, "q", q->size, cprint);
print_hex_datum (outfile, q, cprint);
print_head (outfile, "g", g->size, cprint);
print_hex_datum (outfile, g, cprint);
}
void
print_ecc_pkey (FILE* outfile, gnutls_ecc_curve_t curve, gnutls_datum_t* k,
gnutls_datum_t * x, gnutls_datum_t * y, int cprint)
{
if (cprint != 0)
fprintf (outfile, "/* curve: %s */\n", gnutls_ecc_curve_get_name(curve));
else
fprintf (outfile, "curve:\t%s\n", gnutls_ecc_curve_get_name(curve));
if (k)
{
print_head (outfile, "private key", k->size, cprint);
print_hex_datum (outfile, k, cprint);
}
print_head (outfile, "x", x->size, cprint);
print_hex_datum (outfile, x, cprint);
print_head (outfile, "y", y->size, cprint);
print_hex_datum (outfile, y, cprint);
}
void
print_rsa_pkey (FILE* outfile, gnutls_datum_t * m, gnutls_datum_t * e, gnutls_datum_t * d,
gnutls_datum_t * p, gnutls_datum_t * q, gnutls_datum_t * u,
gnutls_datum_t * exp1, gnutls_datum_t * exp2, int cprint)
{
print_head (outfile, "modulus", m->size, cprint);
print_hex_datum (outfile, m, cprint);
print_head (outfile, "public exponent", e->size, cprint);
print_hex_datum (outfile, e, cprint);
if (d)
{
print_head (outfile, "private exponent", d->size, cprint);
print_hex_datum (outfile, d, cprint);
print_head (outfile, "prime1", p->size, cprint);
print_hex_datum (outfile, p, cprint);
print_head (outfile, "prime2", q->size, cprint);
print_hex_datum (outfile, q, cprint);
print_head (outfile, "coefficient", u->size, cprint);
print_hex_datum (outfile, u, cprint);
if (exp1 && exp2)
{
print_head (outfile, "exp1", exp1->size, cprint);
print_hex_datum (outfile, exp1, cprint);
print_head (outfile, "exp2", exp2->size, cprint);
print_hex_datum (outfile, exp2, cprint);
}
}
}
void _pubkey_info(FILE* outfile, gnutls_certificate_print_formats_t format, gnutls_pubkey_t pubkey)
{
gnutls_datum_t data;
int ret;
size_t size;
ret = gnutls_pubkey_print(pubkey, format, &data);
if (ret < 0)
error (EXIT_FAILURE, 0, "pubkey_print error: %s", gnutls_strerror (ret));
fprintf (outfile, "%s\n", data.data);
gnutls_free (data.data);
size = buffer_size;
ret = gnutls_pubkey_export (pubkey, GNUTLS_X509_FMT_PEM, buffer, &size);
if (ret < 0)
error (EXIT_FAILURE, 0, "export error: %s", gnutls_strerror (ret));
fprintf (outfile, "\n%s\n", buffer);
}
static void
print_dh_info (FILE* outfile, gnutls_datum_t * p, gnutls_datum_t * g, unsigned int q_bits, int cprint)
{
if (q_bits > 0)
{
if (cprint != 0)
fprintf (outfile, "\n /* recommended key length: %d bytes */\n\n", (7+q_bits)/8);
else
fprintf (outfile, "\nRecommended key length: %d bits\n\n", q_bits);
}
print_head (outfile, "generator", g->size, cprint);
print_hex_datum (outfile, g, cprint);
print_head (outfile, "prime", p->size, cprint);
print_hex_datum (outfile, p, cprint);
}
void dh_info (FILE* infile, FILE* outfile, common_info_st * ci)
{
gnutls_datum_t params;
size_t size;
int ret;
gnutls_dh_params_t dh_params;
gnutls_datum_t p, g;
unsigned int q_bits = 0;
if (gnutls_dh_params_init (&dh_params) < 0)
{
fprintf (stderr, "Error in dh parameter initialization\n");
exit (1);
}
params.data = (void*)fread_file (infile, &size);
params.size = size;
ret =
gnutls_dh_params_import_pkcs3 (dh_params, ¶ms, GNUTLS_X509_FMT_PEM);
if (ret < 0)
{
fprintf (stderr, "Error parsing dh params: %s\n", gnutls_strerror (ret));
exit (1);
}
ret = gnutls_dh_params_export_raw (dh_params, &p, &g, &q_bits);
if (ret < 0)
{
fprintf (stderr, "Error exporting parameters: %s\n",
gnutls_strerror (ret));
exit (1);
}
print_dh_info (outfile, &p, &g, q_bits, ci->cprint);
if (!ci->cprint)
{ /* generate a PKCS#3 structure */
size_t len = buffer_size;
ret = gnutls_dh_params_export_pkcs3 (dh_params, GNUTLS_X509_FMT_PEM,
buffer, &len);
if (ret == 0)
{
fprintf (outfile, "\n%s", buffer);
}
else
{
fprintf (stderr, "Error: %s\n", gnutls_strerror (ret));
}
}
gnutls_dh_params_deinit(dh_params);
}
/* If how is zero then the included parameters are used.
*/
int
generate_prime (FILE* outfile, int how, common_info_st * info)
{
int ret;
gnutls_dh_params_t dh_params;
gnutls_datum_t p, g;
int bits = get_bits (GNUTLS_PK_DH, info->bits, info->sec_param, 1);
unsigned int q_bits = 0;
gnutls_dh_params_init (&dh_params);
if (how != 0)
{
fprintf (stderr, "Generating DH parameters (%d bits)...\n", bits);
fprintf (stderr, "(might take long time)\n");
}
else
fprintf (stderr, "Retrieving DH parameters...\n");
if (how != 0)
{
ret = gnutls_dh_params_generate2 (dh_params, bits);
if (ret < 0)
{
fprintf (stderr, "Error generating parameters: %s\n",
gnutls_strerror (ret));
exit (1);
}
ret = gnutls_dh_params_export_raw (dh_params, &p, &g, &q_bits);
if (ret < 0)
{
fprintf (stderr, "Error exporting parameters: %s\n",
gnutls_strerror (ret));
exit (1);
}
}
else
{
#ifdef ENABLE_SRP
if (bits <= 1024)
{
p = gnutls_srp_1024_group_prime;
g = gnutls_srp_1024_group_generator;
bits = 1024;
}
else if (bits <= 1536)
{
p = gnutls_srp_1536_group_prime;
g = gnutls_srp_1536_group_generator;
bits = 1536;
}
else if (bits <= 2048)
{
p = gnutls_srp_2048_group_prime;
g = gnutls_srp_2048_group_generator;
bits = 2048;
}
else if (bits <= 3072)
{
p = gnutls_srp_3072_group_prime;
g = gnutls_srp_3072_group_generator;
bits = 3072;
}
else
{
p = gnutls_srp_4096_group_prime;
g = gnutls_srp_4096_group_generator;
bits = 4096;
}
ret = gnutls_dh_params_import_raw (dh_params, &p, &g);
if (ret < 0)
{
fprintf (stderr, "Error exporting parameters: %s\n",
gnutls_strerror (ret));
exit (1);
}
#else
fprintf (stderr, "Parameters unavailable as SRP is disabled.\n");
exit (1);
#endif
}
print_dh_info (outfile, &p, &g, q_bits, info->cprint);
if (!info->cprint)
{ /* generate a PKCS#3 structure */
size_t len = buffer_size;
ret = gnutls_dh_params_export_pkcs3 (dh_params, GNUTLS_X509_FMT_PEM,
buffer, &len);
if (ret == 0)
{
fprintf (outfile, "\n%s", buffer);
}
else
{
fprintf (stderr, "Error: %s\n", gnutls_strerror (ret));
}
}
gnutls_dh_params_deinit(dh_params);
return 0;
}