3 * Functions for RSA private key reading and use
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 2007 Gerald Combs
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "filesystem.h"
29 #include "file_util.h"
36 #include <gnutls/abstract.h>
37 #include <gnutls/pkcs12.h>
39 /* RSA private key file processing {{{ */
42 rsa_privkey_to_sexp(gnutls_x509_privkey_t priv_key, char **err)
44 gnutls_datum_t rsa_datum[RSA_PARS]; /* m, e, d, p, q, u */
47 gcry_sexp_t rsa_priv_key = NULL;
49 gcry_mpi_t rsa_params[RSA_PARS];
52 /* RSA get parameter */
53 if (gnutls_x509_privkey_export_rsa_raw(priv_key,
59 &rsa_datum[5]) != 0) {
60 *err = g_strdup("can't export rsa param (is a rsa private key file ?!?)");
64 /* convert each rsa parameter to mpi format*/
65 for(i=0; i<RSA_PARS; i++) {
66 gret = gcry_mpi_scan(&rsa_params[i], GCRYMPI_FMT_USG, rsa_datum[i].data, rsa_datum[i].size,&tmp_size);
67 /* these buffers were allocated by gnutls_x509_privkey_export_rsa_raw() */
68 g_free(rsa_datum[i].data);
70 *err = g_strdup_printf("can't convert m rsa param to int (size %d)", rsa_datum[i].size);
75 /* libgcrypt expects p < q, and gnutls might not return it as such, depending on gnutls version and its crypto backend */
76 if (gcry_mpi_cmp(rsa_params[3], rsa_params[4]) > 0)
79 gcry_mpi_swap(rsa_params[3], rsa_params[4]);
80 /* due to swapping p and q, u = p^-1 mod p which happens to be needed. */
82 /* libgcrypt expects u = p^-1 mod q (for OpenPGP), but the u parameter
83 * says u = q^-1 mod p. Recompute u = p^-1 mod q. Do this unconditionally as
84 * at least GnuTLS 2.12.23 computes an invalid value. */
85 gcry_mpi_invm(rsa_params[5], rsa_params[3], rsa_params[4]);
87 if (gcry_sexp_build( &rsa_priv_key, NULL,
88 "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params[0],
89 rsa_params[1], rsa_params[2], rsa_params[3], rsa_params[4],
90 rsa_params[5]) != 0) {
91 *err = g_strdup("can't build rsa private key s-exp");
96 gcry_mpi_release(rsa_params[i]);
100 gnutls_x509_privkey_t
101 rsa_load_pem_key(FILE *fp, char **err)
103 /* gnutls makes our work much harder, since we have to work internally with
104 * s-exp formatted data, but PEM loader exports only in "gnutls_datum_t"
105 * format, and a datum -> s-exp convertion function does not exist.
107 gnutls_x509_privkey_t priv_key;
114 if (ws_fstat64(ws_fileno(fp), &statbuf) == -1) {
115 *err = g_strdup_printf("can't ws_fstat64 file: %s", g_strerror(errno));
118 if (S_ISDIR(statbuf.st_mode)) {
119 *err = g_strdup("file is a directory");
123 if (S_ISFIFO(statbuf.st_mode)) {
124 *err = g_strdup("file is a named pipe");
128 if (!S_ISREG(statbuf.st_mode)) {
129 *err = g_strdup("file is not a regular file");
133 /* XXX - check for a too-big size */
134 /* load all file contents into a datum buffer*/
135 key.data = (unsigned char *)g_malloc((size_t)statbuf.st_size);
136 key.size = (int)statbuf.st_size;
137 bytes = (guint) fread(key.data, 1, key.size, fp);
138 if (bytes < key.size) {
139 if (bytes == 0 && ferror(fp)) {
140 *err = g_strdup_printf("can't read from file %d bytes, got error %s",
141 key.size, g_strerror(errno));
143 *err = g_strdup_printf("can't read from file %d bytes, got %d",
150 /* init private key data*/
151 gnutls_x509_privkey_init(&priv_key);
154 if ((ret = gnutls_x509_privkey_import(priv_key, &key, GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS) {
155 *err = g_strdup_printf("can't import pem data: %s", gnutls_strerror(ret));
157 gnutls_x509_privkey_deinit(priv_key);
161 if (gnutls_x509_privkey_get_pk_algorithm(priv_key) != GNUTLS_PK_RSA) {
162 *err = g_strdup("private key public key algorithm isn't RSA");
164 gnutls_x509_privkey_deinit(priv_key);
174 BAGTYPE(gnutls_pkcs12_bag_type_t x) {
176 case GNUTLS_BAG_EMPTY: return "Empty";
177 case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: return "PKCS#8 Encrypted key";
178 case GNUTLS_BAG_PKCS8_KEY: return "PKCS#8 Key";
179 case GNUTLS_BAG_CERTIFICATE: return "Certificate";
180 case GNUTLS_BAG_CRL: return "CRL";
181 case GNUTLS_BAG_ENCRYPTED: return "Encrypted";
182 case GNUTLS_BAG_UNKNOWN: return "Unknown";
183 default: return "<undefined>";
187 gnutls_x509_privkey_t
188 rsa_load_pkcs12(FILE *fp, const gchar *cert_passwd, char **err)
194 gnutls_pkcs12_bag_t bag = NULL;
195 gnutls_pkcs12_bag_type_t bag_type;
198 gnutls_pkcs12_t rsa_p12 = NULL;
199 gnutls_x509_privkey_t rsa_pkey = NULL;
201 gnutls_x509_privkey_t priv_key = NULL;
205 data.data = (unsigned char *)g_malloc(rest);
208 while ((len = fread(p, 1, rest, fp)) > 0) {
213 data.data = (unsigned char *)g_realloc(data.data, data.size + rest);
214 p = data.data + data.size;
220 *err = g_strdup("Error during certificate reading.");
225 ret = gnutls_pkcs12_init(&rsa_p12);
227 *err = g_strdup_printf("gnutls_pkcs12_init(&st_p12) - %s", gnutls_strerror(ret));
232 /* load PKCS#12 in DER or PEM format */
233 ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_DER, 0);
235 ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_PEM, 0);
237 *err = g_strdup_printf("could not load PKCS#12 in DER or PEM format: %s", gnutls_strerror(ret));
245 g_log(NULL, G_LOG_LEVEL_INFO, "rsa_privkey_to_sexp: PKCS#12 imported\n");
247 /* TODO: Use gnutls_pkcs12_simple_parse, since 3.1.0 (August 2012) */
250 ret = gnutls_pkcs12_bag_init(&bag);
252 *err = g_strdup_printf("gnutls_pkcs12_bag_init failed: %s",
253 gnutls_strerror(ret));
257 ret = gnutls_pkcs12_get_bag(rsa_p12, i, bag);
259 *err = g_strdup_printf("gnutls_pkcs12_get_bag failed: %s",
260 gnutls_strerror(ret));
264 for (j=0; j<gnutls_pkcs12_bag_get_count(bag); j++) {
266 ret = gnutls_pkcs12_bag_get_type(bag, j);
268 *err = g_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
269 gnutls_strerror(ret));
272 bag_type = (gnutls_pkcs12_bag_type_t)ret;
273 if (bag_type >= GNUTLS_BAG_UNKNOWN) {
274 *err = g_strdup_printf("gnutls_pkcs12_bag_get_type returnd unknown bag type %u",
278 g_log(NULL, G_LOG_LEVEL_INFO, "Bag %d/%d: %s\n", i, j, BAGTYPE(bag_type));
279 if (bag_type == GNUTLS_BAG_ENCRYPTED) {
280 ret = gnutls_pkcs12_bag_decrypt(bag, cert_passwd);
282 ret = gnutls_pkcs12_bag_get_type(bag, j);
284 *err = g_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
285 gnutls_strerror(ret));
288 bag_type = (gnutls_pkcs12_bag_type_t)ret;
289 if (bag_type >= GNUTLS_BAG_UNKNOWN) {
290 *err = g_strdup_printf("gnutls_pkcs12_bag_get_type returnd unknown bag type %u",
294 g_log(NULL, G_LOG_LEVEL_INFO, "Bag %d/%d decrypted: %s\n", i, j, BAGTYPE(bag_type));
298 ret = gnutls_pkcs12_bag_get_data(bag, j, &data);
300 *err = g_strdup_printf("gnutls_pkcs12_bag_get_data failed: %s",
301 gnutls_strerror(ret));
307 case GNUTLS_BAG_PKCS8_KEY:
308 case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
310 ret = gnutls_x509_privkey_init(&rsa_pkey);
312 *err = g_strdup_printf("gnutls_x509_privkey_init(&rsa_pkey) - %s", gnutls_strerror(ret));
315 ret = gnutls_x509_privkey_import_pkcs8(rsa_pkey, &data, GNUTLS_X509_FMT_DER, cert_passwd,
316 (bag_type==GNUTLS_BAG_PKCS8_KEY) ? GNUTLS_PKCS_PLAIN : 0);
318 *err = g_strdup_printf("Can not decrypt private key - %s", gnutls_strerror(ret));
322 if (gnutls_x509_privkey_get_pk_algorithm(rsa_pkey) != GNUTLS_PK_RSA) {
323 *err = g_strdup("private key public key algorithm isn't RSA");
327 /* Private key found, return it. */
335 if (bag) { gnutls_pkcs12_bag_deinit(bag); bag = NULL; }
339 if (!priv_key && rsa_pkey)
340 gnutls_x509_privkey_deinit(rsa_pkey);
342 gnutls_pkcs12_bag_deinit(bag);
348 rsa_private_key_free(gpointer key)
350 gcry_sexp_release((gcry_sexp_t) key);
353 #else /* ! defined(HAVE_LIBGNUTLS) */
356 rsa_private_key_free(gpointer key _U_)
360 #endif /* HAVE_LIBGNUTLS */
363 * Editor modelines - http://www.wireshark.org/tools/modelines.html
368 * indent-tabs-mode: nil
371 * vi: set shiftwidth=4 tabstop=8 expandtab:
372 * :indentSize=4:tabSize=8:noTabs=true: