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 * SPDX-License-Identifier: GPL-2.0-or-later
13 #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
16 #include "filesystem.h"
17 #include "file_util.h"
19 #include <wsutil/wslog.h>
24 #include <gnutls/abstract.h>
25 #include <gnutls/pkcs12.h>
27 /* RSA private key file processing {{{ */
30 rsa_privkey_to_sexp(gnutls_x509_privkey_t priv_key
, char **err
)
32 gnutls_datum_t rsa_datum
[RSA_PARS
]; /* m, e, d, p, q, u */
35 gcry_sexp_t rsa_priv_key
= NULL
;
37 gcry_mpi_t rsa_params
[RSA_PARS
];
40 /* RSA get parameter */
41 if (gnutls_x509_privkey_export_rsa_raw(priv_key
,
47 &rsa_datum
[5]) != 0) {
48 *err
= g_strdup("can't export rsa param (is a rsa private key file ?!?)");
52 /* convert each rsa parameter to mpi format*/
53 for(i
=0; i
<RSA_PARS
; i
++) {
54 gret
= gcry_mpi_scan(&rsa_params
[i
], GCRYMPI_FMT_USG
, rsa_datum
[i
].data
, rsa_datum
[i
].size
,&tmp_size
);
55 /* these buffers were allocated by gnutls_x509_privkey_export_rsa_raw() */
56 g_free(rsa_datum
[i
].data
);
58 *err
= ws_strdup_printf("can't convert m rsa param to int (size %d)", rsa_datum
[i
].size
);
63 /* libgcrypt expects p < q, and gnutls might not return it as such, depending on gnutls version and its crypto backend */
64 if (gcry_mpi_cmp(rsa_params
[3], rsa_params
[4]) > 0)
67 gcry_mpi_swap(rsa_params
[3], rsa_params
[4]);
68 /* due to swapping p and q, u = p^-1 mod p which happens to be needed. */
70 /* libgcrypt expects u = p^-1 mod q (for OpenPGP), but the u parameter
71 * says u = q^-1 mod p. Recompute u = p^-1 mod q. Do this unconditionally as
72 * at least GnuTLS 2.12.23 computes an invalid value. */
73 gcry_mpi_invm(rsa_params
[5], rsa_params
[3], rsa_params
[4]);
75 if (gcry_sexp_build( &rsa_priv_key
, NULL
,
76 "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params
[0],
77 rsa_params
[1], rsa_params
[2], rsa_params
[3], rsa_params
[4],
78 rsa_params
[5]) != 0) {
79 *err
= g_strdup("can't build rsa private key s-exp");
84 gcry_mpi_release(rsa_params
[i
]);
89 rsa_load_pem_key(FILE *fp
, char **err
)
91 /* gnutls makes our work much harder, since we have to work internally with
92 * s-exp formatted data, but PEM loader exports only in "gnutls_datum_t"
93 * format, and a datum -> s-exp conversion function does not exist.
95 gnutls_x509_privkey_t priv_key
;
102 if (ws_fstat64(ws_fileno(fp
), &statbuf
) == -1) {
103 *err
= ws_strdup_printf("can't ws_fstat64 file: %s", g_strerror(errno
));
106 if (S_ISDIR(statbuf
.st_mode
)) {
107 *err
= g_strdup("file is a directory");
111 if (S_ISFIFO(statbuf
.st_mode
)) {
112 *err
= g_strdup("file is a named pipe");
116 if (!S_ISREG(statbuf
.st_mode
)) {
117 *err
= g_strdup("file is not a regular file");
121 /* XXX - check for a too-big size */
122 /* load all file contents into a datum buffer*/
123 key
.data
= (unsigned char *)g_malloc((size_t)statbuf
.st_size
);
124 key
.size
= (int)statbuf
.st_size
;
125 bytes
= (unsigned) fread(key
.data
, 1, key
.size
, fp
);
126 if (bytes
< key
.size
) {
127 if (bytes
== 0 && ferror(fp
)) {
128 *err
= ws_strdup_printf("can't read from file %d bytes, got error %s",
129 key
.size
, g_strerror(errno
));
131 *err
= ws_strdup_printf("can't read from file %d bytes, got %d",
138 /* init private key data*/
139 gnutls_x509_privkey_init(&priv_key
);
142 if ((ret
= gnutls_x509_privkey_import(priv_key
, &key
, GNUTLS_X509_FMT_PEM
)) != GNUTLS_E_SUCCESS
) {
143 *err
= ws_strdup_printf("can't import pem data: %s", gnutls_strerror(ret
));
145 gnutls_x509_privkey_deinit(priv_key
);
149 if (gnutls_x509_privkey_get_pk_algorithm(priv_key
) != GNUTLS_PK_RSA
) {
150 *err
= g_strdup("private key public key algorithm isn't RSA");
152 gnutls_x509_privkey_deinit(priv_key
);
162 BAGTYPE(gnutls_pkcs12_bag_type_t x
) {
164 case GNUTLS_BAG_EMPTY
: return "Empty";
165 case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY
: return "PKCS#8 Encrypted key";
166 case GNUTLS_BAG_PKCS8_KEY
: return "PKCS#8 Key";
167 case GNUTLS_BAG_CERTIFICATE
: return "Certificate";
168 case GNUTLS_BAG_CRL
: return "CRL";
169 case GNUTLS_BAG_ENCRYPTED
: return "Encrypted";
170 case GNUTLS_BAG_UNKNOWN
: return "Unknown";
171 default: return "<undefined>";
175 gnutls_x509_privkey_t
176 rsa_load_pkcs12(FILE *fp
, const char *cert_passwd
, char **err
)
182 gnutls_pkcs12_bag_t bag
= NULL
;
185 gnutls_pkcs12_t rsa_p12
= NULL
;
187 gnutls_x509_privkey_t priv_key
= NULL
;
191 data
.data
= (unsigned char *)g_malloc(rest
);
194 while ((len
= fread(p
, 1, rest
, fp
)) > 0) {
199 data
.data
= (unsigned char *)g_realloc(data
.data
, data
.size
+ rest
);
200 p
= data
.data
+ data
.size
;
206 *err
= g_strdup("Error during certificate reading.");
211 ret
= gnutls_pkcs12_init(&rsa_p12
);
213 *err
= ws_strdup_printf("gnutls_pkcs12_init(&st_p12) - %s", gnutls_strerror(ret
));
218 /* load PKCS#12 in DER or PEM format */
219 ret
= gnutls_pkcs12_import(rsa_p12
, &data
, GNUTLS_X509_FMT_DER
, 0);
221 ret
= gnutls_pkcs12_import(rsa_p12
, &data
, GNUTLS_X509_FMT_PEM
, 0);
223 *err
= ws_strdup_printf("could not load PKCS#12 in DER or PEM format: %s", gnutls_strerror(ret
));
228 gnutls_pkcs12_deinit(rsa_p12
);
232 ws_debug("grsa_privkey_to_sexp: PKCS#12 imported");
234 /* TODO: Use gnutls_pkcs12_simple_parse, since 3.1.0 (August 2012) */
236 gnutls_pkcs12_bag_type_t bag_type
;
238 ret
= gnutls_pkcs12_bag_init(&bag
);
240 *err
= ws_strdup_printf("gnutls_pkcs12_bag_init failed: %s",
241 gnutls_strerror(ret
));
245 ret
= gnutls_pkcs12_get_bag(rsa_p12
, i
, bag
);
247 *err
= ws_strdup_printf("gnutls_pkcs12_get_bag failed: %s",
248 gnutls_strerror(ret
));
252 for (j
=0; j
<gnutls_pkcs12_bag_get_count(bag
); j
++) {
254 ret
= gnutls_pkcs12_bag_get_type(bag
, j
);
256 *err
= ws_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
257 gnutls_strerror(ret
));
260 bag_type
= (gnutls_pkcs12_bag_type_t
)ret
;
261 if (bag_type
>= GNUTLS_BAG_UNKNOWN
) {
262 *err
= ws_strdup_printf("gnutls_pkcs12_bag_get_type returned unknown bag type %u",
266 ws_debug("Bag %d/%d: %s", i
, j
, BAGTYPE(bag_type
));
267 if (bag_type
== GNUTLS_BAG_ENCRYPTED
) {
268 ret
= gnutls_pkcs12_bag_decrypt(bag
, cert_passwd
);
270 ret
= gnutls_pkcs12_bag_get_type(bag
, j
);
272 *err
= ws_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
273 gnutls_strerror(ret
));
276 bag_type
= (gnutls_pkcs12_bag_type_t
)ret
;
277 if (bag_type
>= GNUTLS_BAG_UNKNOWN
) {
278 *err
= ws_strdup_printf("gnutls_pkcs12_bag_get_type returned unknown bag type %u",
282 ws_debug("Bag %d/%d decrypted: %s", i
, j
, BAGTYPE(bag_type
));
286 ret
= gnutls_pkcs12_bag_get_data(bag
, j
, &data
);
288 *err
= ws_strdup_printf("gnutls_pkcs12_bag_get_data failed: %s",
289 gnutls_strerror(ret
));
295 case GNUTLS_BAG_PKCS8_KEY
:
296 case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY
:
298 gnutls_x509_privkey_t rsa_pkey
;
300 ret
= gnutls_x509_privkey_init(&rsa_pkey
);
302 *err
= ws_strdup_printf("gnutls_x509_privkey_init failed: %s", gnutls_strerror(ret
));
305 ret
= gnutls_x509_privkey_import_pkcs8(rsa_pkey
, &data
, GNUTLS_X509_FMT_DER
, cert_passwd
,
306 (bag_type
==GNUTLS_BAG_PKCS8_KEY
) ? GNUTLS_PKCS_PLAIN
: 0);
308 *err
= ws_strdup_printf("Can not decrypt private key - %s", gnutls_strerror(ret
));
309 gnutls_x509_privkey_deinit(rsa_pkey
);
313 if (gnutls_x509_privkey_get_pk_algorithm(rsa_pkey
) != GNUTLS_PK_RSA
) {
314 *err
= g_strdup("private key public key algorithm isn't RSA");
315 gnutls_x509_privkey_deinit(rsa_pkey
);
319 /* Private key found, return it. */
328 gnutls_pkcs12_bag_deinit(bag
);
334 gnutls_pkcs12_bag_deinit(bag
);
338 * We failed. If we didn't fail with an error, we failed because
339 * we found no PKCS8 key and fell out of the loop; report that
343 *err
= g_strdup("no PKCS8 key found");
345 gnutls_pkcs12_deinit(rsa_p12
);
351 rsa_private_key_free(void * key
)
353 gcry_sexp_release((gcry_sexp_t
) key
);
356 #else /* ! defined(HAVE_LIBGNUTLS) */
359 rsa_private_key_free(void * key _U_
)
363 #endif /* HAVE_LIBGNUTLS */
366 * Editor modelines - https://www.wireshark.org/tools/modelines.html
371 * indent-tabs-mode: nil
374 * vi: set shiftwidth=4 tabstop=8 expandtab:
375 * :indentSize=4:tabSize=8:noTabs=true: