/* rsa.c * * Functions for RSA private key reading and use * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 2007 Gerald Combs * * This program 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 2 * of the License, or (at your option) any later version. * * This program 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include "rsa.h" #include #include "filesystem.h" #include "file_util.h" #include "log.h" #include #ifdef HAVE_LIBGNUTLS #include #include /* RSA private key file processing {{{ */ #define RSA_PARS 6 gcry_sexp_t rsa_privkey_to_sexp(gnutls_x509_privkey_t priv_key, char **err) { gnutls_datum_t rsa_datum[RSA_PARS]; /* m, e, d, p, q, u */ size_t tmp_size; gcry_error_t gret; gcry_sexp_t rsa_priv_key = NULL; gint i; gcry_mpi_t rsa_params[RSA_PARS]; *err = NULL; /* RSA get parameter */ if (gnutls_x509_privkey_export_rsa_raw(priv_key, &rsa_datum[0], &rsa_datum[1], &rsa_datum[2], &rsa_datum[3], &rsa_datum[4], &rsa_datum[5]) != 0) { *err = g_strdup("can't export rsa param (is a rsa private key file ?!?)"); return NULL; } /* convert each rsa parameter to mpi format*/ for(i=0; i 0) { /* p, q = q, p */ gcry_mpi_swap(rsa_params[3], rsa_params[4]); /* due to swapping p and q, u = p^-1 mod p which happens to be needed. */ } /* libgcrypt expects u = p^-1 mod q (for OpenPGP), but the u parameter * says u = q^-1 mod p. Recompute u = p^-1 mod q. Do this unconditionally as * at least GnuTLS 2.12.23 computes an invalid value. */ gcry_mpi_invm(rsa_params[5], rsa_params[3], rsa_params[4]); if (gcry_sexp_build( &rsa_priv_key, NULL, "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params[0], rsa_params[1], rsa_params[2], rsa_params[3], rsa_params[4], rsa_params[5]) != 0) { *err = g_strdup("can't build rsa private key s-exp"); return NULL; } for (i=0; i< 6; i++) gcry_mpi_release(rsa_params[i]); return rsa_priv_key; } gnutls_x509_privkey_t rsa_load_pem_key(FILE *fp, char **err) { /* gnutls makes our work much harder, since we have to work internally with * s-exp formatted data, but PEM loader exports only in "gnutls_datum_t" * format, and a datum -> s-exp convertion function does not exist. */ gnutls_x509_privkey_t priv_key; gnutls_datum_t key; ws_statb64 statbuf; gint ret; guint bytes; *err = NULL; if (ws_fstat64(ws_fileno(fp), &statbuf) == -1) { *err = g_strdup("can't ws_fstat64 file"); return NULL; } if (S_ISDIR(statbuf.st_mode)) { *err = g_strdup("file is a directory"); errno = EISDIR; return NULL; } if (S_ISFIFO(statbuf.st_mode)) { *err = g_strdup("file is a named pipe"); errno = EINVAL; return NULL; } if (!S_ISREG(statbuf.st_mode)) { *err = g_strdup("file is not a regular file"); errno = EINVAL; return NULL; } /* XXX - check for a too-big size */ /* load all file contents into a datum buffer*/ key.data = (unsigned char *)g_malloc((size_t)statbuf.st_size); key.size = (int)statbuf.st_size; bytes = (guint) fread(key.data, 1, key.size, fp); if (bytes < key.size) { *err = g_strdup_printf("can't read from file %d bytes, got %d", key.size, bytes); g_free(key.data); return NULL; } /* init private key data*/ gnutls_x509_privkey_init(&priv_key); /* import PEM data*/ if ((ret = gnutls_x509_privkey_import(priv_key, &key, GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS) { *err = g_strdup_printf("can't import pem data: %s", gnutls_strerror(ret)); g_free(key.data); return NULL; } if (gnutls_x509_privkey_get_pk_algorithm(priv_key) != GNUTLS_PK_RSA) { *err = g_strdup("private key public key algorithm isn't RSA"); g_free(key.data); return NULL; } g_free(key.data); return priv_key; } static const char * BAGTYPE(gnutls_pkcs12_bag_type_t x) { switch (x) { case GNUTLS_BAG_EMPTY: return "Empty"; case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: return "PKCS#8 Encrypted key"; case GNUTLS_BAG_PKCS8_KEY: return "PKCS#8 Key"; case GNUTLS_BAG_CERTIFICATE: return "Certificate"; case GNUTLS_BAG_CRL: return "CRL"; case GNUTLS_BAG_ENCRYPTED: return "Encrypted"; case GNUTLS_BAG_UNKNOWN: return "Unknown"; default: return ""; } } gnutls_x509_privkey_t rsa_load_pkcs12(FILE *fp, const gchar *cert_passwd, char **err) { int i, j, ret; int rest; unsigned char *p; gnutls_datum_t data; gnutls_pkcs12_bag_t bag = NULL; gnutls_pkcs12_bag_type_t bag_type; size_t len; gnutls_pkcs12_t rsa_p12 = NULL; gnutls_x509_privkey_t rsa_pkey = NULL; gnutls_x509_privkey_t priv_key = NULL; *err = NULL; rest = 4096; data.data = (unsigned char *)g_malloc(rest); data.size = rest; p = data.data; while ((len = fread(p, 1, rest, fp)) > 0) { p += len; rest -= (int) len; if (!rest) { rest = 1024; data.data = (unsigned char *)g_realloc(data.data, data.size + rest); p = data.data + data.size; data.size += rest; } } data.size -= rest; if (!feof(fp)) { *err = g_strdup("Error during certificate reading."); g_free(data.data); return 0; } ret = gnutls_pkcs12_init(&rsa_p12); if (ret < 0) { *err = g_strdup_printf("gnutls_pkcs12_init(&st_p12) - %s", gnutls_strerror(ret)); g_free(data.data); return 0; } /* load PKCS#12 in DER or PEM format */ ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_DER, 0); if (ret < 0) { ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_PEM, 0); if (ret < 0) { *err = g_strdup_printf("could not load PKCS#12 in DER or PEM format: %s", gnutls_strerror(ret)); } } g_free(data.data); if (ret < 0) { return 0; } g_log(NULL, G_LOG_LEVEL_INFO, "rsa_privkey_to_sexp: PKCS#12 imported\n"); /* TODO: Use gnutls_pkcs12_simple_parse, since 3.1.0 (August 2012) */ for (i=0; ; i++) { ret = gnutls_pkcs12_bag_init(&bag); if (ret < 0) break; ret = gnutls_pkcs12_get_bag(rsa_p12, i, bag); if (ret < 0) break; for (j=0; j= GNUTLS_BAG_UNKNOWN) goto done; g_log(NULL, G_LOG_LEVEL_INFO, "Bag %d/%d: %s\n", i, j, BAGTYPE(bag_type)); if (bag_type == GNUTLS_BAG_ENCRYPTED) { ret = gnutls_pkcs12_bag_decrypt(bag, cert_passwd); if (ret == 0) { ret = gnutls_pkcs12_bag_get_type(bag, j); if (ret < 0) goto done; bag_type = (gnutls_pkcs12_bag_type_t)ret; if (bag_type >= GNUTLS_BAG_UNKNOWN) goto done; g_log(NULL, G_LOG_LEVEL_INFO, "Bag %d/%d decrypted: %s\n", i, j, BAGTYPE(bag_type)); } } ret = gnutls_pkcs12_bag_get_data(bag, j, &data); if (ret < 0) goto done; switch (bag_type) { case GNUTLS_BAG_PKCS8_KEY: case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: ret = gnutls_x509_privkey_init(&rsa_pkey); if (ret < 0) { *err = g_strdup_printf("gnutls_x509_privkey_init(&rsa_pkey) - %s", gnutls_strerror(ret)); goto done; } ret = gnutls_x509_privkey_import_pkcs8(rsa_pkey, &data, GNUTLS_X509_FMT_DER, cert_passwd, (bag_type==GNUTLS_BAG_PKCS8_KEY) ? GNUTLS_PKCS_PLAIN : 0); if (ret < 0) { *err = g_strdup_printf("Can not decrypt private key - %s", gnutls_strerror(ret)); goto done; } if (gnutls_x509_privkey_get_pk_algorithm(rsa_pkey) != GNUTLS_PK_RSA) { *err = g_strdup("private key public key algorithm isn't RSA"); goto done; } /* Private key found, return it. */ priv_key = rsa_pkey; goto done; break; default: ; } } /* j */ if (bag) { gnutls_pkcs12_bag_deinit(bag); bag = NULL; } } /* i */ done: if (!priv_key && rsa_pkey) gnutls_x509_privkey_deinit(rsa_pkey); if (bag) gnutls_pkcs12_bag_deinit(bag); return priv_key; } void rsa_private_key_free(gpointer key) { gcry_sexp_release((gcry_sexp_t) key); } #else /* ! defined(HAVE_LIBGNUTLS) */ void rsa_private_key_free(gpointer key _U_) { } #endif /* HAVE_LIBGNUTLS */ /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */