/* * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * 2011 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-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) { unsigned char raw_key[64]; size_t raw_key_size = sizeof (raw_key); static gnutls_datum_t key; gnutls_datum_t hex_key; int ret; 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 = (char *) 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 = raw_key; key.size = raw_key_size; return &key; } static gnutls_privkey_t _load_privkey(gnutls_datum_t *dat, common_info_st * info) { int ret; gnutls_privkey_t key; gnutls_x509_privkey_t xkey; ret = gnutls_x509_privkey_init (&xkey); if (ret < 0) error (EXIT_FAILURE, 0, "x509_privkey_init: %s", gnutls_strerror (ret)); ret = gnutls_privkey_init (&key); if (ret < 0) error (EXIT_FAILURE, 0, "privkey_init: %s", gnutls_strerror (ret)); if (info->pkcs8) { const char *pass = get_pass (); ret = gnutls_x509_privkey_import_pkcs8 (xkey, dat, info->incert_format, pass, 0); } else ret = gnutls_x509_privkey_import (xkey, 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; " "check if your key is PKCS #8 or PKCS #12 encoded"); } if (ret < 0) error (EXIT_FAILURE, 0, "importing --load-privkey: %s: %s", info->privkey, gnutls_strerror (ret)); ret = gnutls_privkey_import_x509(key, xkey, GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE); if (ret < 0) error (EXIT_FAILURE, 0, "gnutls_privkey_import_x509: %s", gnutls_strerror (ret)); return key; } static gnutls_privkey_t _load_pkcs11_privkey(const char* url) { int ret; gnutls_pkcs11_privkey_t p11key; gnutls_privkey_t key; ret = gnutls_privkey_init (&key); if (ret < 0) error (EXIT_FAILURE, 0, "privkey_init: %s", gnutls_strerror (ret)); ret = gnutls_pkcs11_privkey_init (&p11key); if (ret < 0) error (EXIT_FAILURE, 0, "pkcs11_privkey_init: %s", gnutls_strerror (ret)); ret = gnutls_pkcs11_privkey_import_url(p11key, url, 0); if (ret < 0) error (EXIT_FAILURE, 0, "importing PKCS #11 key: %s: %s", url, gnutls_strerror (ret)); ret = gnutls_privkey_import_pkcs11(key, p11key, GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE); if (ret < 0) error (EXIT_FAILURE, 0, "gnutls_privkey_import_pkcs11: %s", gnutls_strerror (ret)); return key; } static gnutls_pubkey_t _load_pkcs11_pubkey(const char* url) { int ret; gnutls_pkcs11_obj_t obj; gnutls_x509_crt_t xcrt; 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_pkcs11_obj_init (&obj); if (ret < 0) { fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror (ret)); exit (1); } ret = gnutls_pkcs11_obj_import_url (obj, url, obj_flags); if (ret < 0) { fprintf (stderr, "Error in %s:%d: %s: %s\n", __func__, __LINE__, gnutls_strerror (ret), url); exit (1); } switch (gnutls_pkcs11_obj_get_type (obj)) { case GNUTLS_PKCS11_OBJ_X509_CRT: ret = gnutls_x509_crt_init (&xcrt); if (ret < 0) { fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror (ret)); exit (1); } ret = gnutls_x509_crt_import_pkcs11 (xcrt, obj); if (ret < 0) { fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror (ret)); exit (1); } ret = gnutls_pubkey_import_x509 (pubkey, xcrt, 0); if (ret < 0) { fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror (ret)); exit (1); } gnutls_x509_crt_deinit (xcrt); break; case GNUTLS_PKCS11_OBJ_PUBKEY: ret = gnutls_pubkey_import_pkcs11 (pubkey, obj, 0); if (ret < 0) { fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror (ret)); exit (1); } break; default: { fprintf(stderr, "Unsupported PKCS #11 object\n"); exit (1); break; } } gnutls_pkcs11_obj_deinit (obj); 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 (strncmp(info->privkey, "pkcs11:", 7) == 0) return _load_pkcs11_privkey(info->privkey); dat.data = 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; 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 = 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) { const char *pass = get_pass (); ret = gnutls_x509_privkey_import_pkcs8 (key, &dat, info->incert_format, pass, 0); } else ret = gnutls_x509_privkey_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 is PKCS #8 or 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; 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 = 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 = 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)++; } 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 = 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 (strncmp(info->ca_privkey, "pkcs11:", 7) == 0) return _load_pkcs11_privkey(info->ca_privkey); dat.data = 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 = 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 (strncmp(info->privkey, "pkcs11:", 7) == 0) return _load_pkcs11_pubkey(info->pubkey); ret = gnutls_pubkey_init (&key); if (ret < 0) error (EXIT_FAILURE, 0, "privkey_init: %s", gnutls_strerror (ret)); dat.data = 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)); ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0); if (ret < 0) /* could not get (e.g. on PKCS #11 */ { gnutls_pubkey_deinit(pubkey); return load_pubkey(mand, info); } return pubkey; }