1 #include "gnutls-helpers.h"
3 #include <gnutls/openpgp.h>
4 #include <gnutls/x509.h>
11 Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
12 Date: 2008-06-12 13:47:41-0400
13 License: GPL v3 or later
15 monkeysphere key translator: execute this with an OpenPGP key on
16 stdin, (please indicate the specific keyid that you want as the
17 first argument if there are subkeys). At the moment, only public
18 keys and passphraseless secret keys work.
20 For secret keys, it will spit out a PEM-encoded version of the key
21 on stdout, which can be fed into ssh-add like this:
23 gpg --export-secret-keys $KEYID | openpgp2ssh $KEYID | ssh-add -c /dev/stdin
25 For public keys, it will spit out a single line of text that can
26 (with some massaging) be used in an openssh known_hosts or
27 authorized_keys file. For example:
29 echo server.example.org $(gpg --export $KEYID | openpgp2ssh $KEYID) >> ~/.ssh/known_hosts
31 Requirements: I've only built this so far with GnuTLS v2.3.x.
32 GnuTLS 2.2.x does not contain the appropriate functionality.
37 /* FIXME: keyid should be const as well */
38 int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_openpgp_privkey_t* pgp_privkey, const unsigned char* keyfpr, unsigned int fprlen) {
39 gnutls_datum_t m, e, d, p, q, u, g, y, x;
40 gnutls_pk_algorithm_t pgp_algo;
41 unsigned int pgp_bits;
46 unsigned char fingerprint[20];
47 size_t fingerprint_length = sizeof(fingerprint);
59 subkeycount = gnutls_openpgp_privkey_get_subkey_count(*pgp_privkey);
60 if (subkeycount < 0) {
61 err(0,"Could not determine subkey count (got value %d)\n", subkeycount);
65 if ((keyfpr == NULL) &&
67 err(0,"No key identifier passed in, but there were %d keys to choose from\n", subkeycount + 1);
72 ret = gnutls_openpgp_privkey_get_fingerprint(*pgp_privkey, fingerprint, &fingerprint_length);
74 err(0,"Could not get fingerprint (error: %d)\n", ret);
77 if (fprlen > fingerprint_length) {
78 err(0, "Requested key identifier is longer than computed fingerprint\n");
81 if (fingerprint_length > fprlen) {
82 err(0, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
85 if ((keyfpr == NULL) || (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0)) {
86 /* we want to export the primary key: */
87 err(0,"exporting primary key\n");
89 /* FIXME: this is almost identical to the block below for subkeys.
90 This clumsiness seems inherent in the gnutls OpenPGP API,
92 pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits);
94 err(0, "failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
97 if (pgp_algo == GNUTLS_PK_RSA) {
98 err(0,"OpenPGP RSA Key, with %d bits\n", pgp_bits);
99 ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u);
100 if (GNUTLS_E_SUCCESS != ret) {
101 err(0, "failed to export RSA key parameters (error: %d)\n", ret);
105 } else if (pgp_algo == GNUTLS_PK_DSA) {
106 err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits);
107 ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x);
108 if (GNUTLS_E_SUCCESS != ret) {
109 err(0,"failed to export DSA key parameters (error: %d)\n", ret);
115 /* lets trawl through the subkeys until we find the one we want: */
116 for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
117 ret = gnutls_openpgp_privkey_get_subkey_fingerprint(*pgp_privkey, subkeyidx, fingerprint, &fingerprint_length);
119 err(0,"Could not get fingerprint of subkey with index %d (error: %d)\n", subkeyidx, ret);
122 if (fprlen > fingerprint_length) {
123 err(0, "Requested key identifier is longer than computed fingerprint\n");
126 if (fingerprint_length > fprlen) {
127 err(1, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
129 if (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0) {
130 err(0,"exporting subkey index %d\n", subkeyidx);
132 /* FIXME: this is almost identical to the block above for the
134 pgp_algo = gnutls_openpgp_privkey_get_subkey_pk_algorithm(*pgp_privkey, subkeyidx, &pgp_bits);
136 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", pgp_algo);
138 } else if (pgp_algo == GNUTLS_PK_RSA) {
139 err(0,"OpenPGP RSA key, with %d bits\n", pgp_bits);
140 ret = gnutls_openpgp_privkey_export_subkey_rsa_raw(*pgp_privkey, subkeyidx, &m, &e, &d, &p, &q, &u);
141 if (GNUTLS_E_SUCCESS != ret) {
142 err(0,"failed to export RSA key parameters (error: %d)\n", ret);
145 } else if (pgp_algo == GNUTLS_PK_DSA) {
146 err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits);
147 ret = gnutls_openpgp_privkey_export_subkey_dsa_raw(*pgp_privkey, subkeyidx, &p, &q, &g, &y, &x);
148 if (GNUTLS_E_SUCCESS != ret) {
149 err(0,"failed to export DSA key parameters (error: %d)\n", ret);
159 err(0,"Could not find key in input\n");
163 if (pgp_algo == GNUTLS_PK_RSA) {
164 ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u);
165 if (GNUTLS_E_SUCCESS != ret) {
166 err(0, "failed to import RSA key parameters (error: %d)\n", ret);
169 } else if (pgp_algo == GNUTLS_PK_DSA) {
170 ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x);
171 if (GNUTLS_E_SUCCESS != ret) {
172 err(0,"failed to import DSA key parameters (error: %d)\n", ret);
176 err(0,"OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
180 ret = gnutls_x509_privkey_fix(*output);
182 err(0,"failed to fix up the private key in X.509 format (error: %d)\n", ret);
189 /* FIXME: keyid should be const also */
190 int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, const unsigned char* keyfpr, size_t fprlen) {
195 gnutls_datum_t m, e, p, q, g, y, algolabel;
197 gnutls_pk_algorithm_t algo;
198 const gnutls_datum_t* all[5];
199 const char* algoname;
201 /* output_data must be at least 2 chars longer than the maximum possible
203 char output_data[20];
205 unsigned char fingerprint[20];
206 size_t fingerprint_length = sizeof(fingerprint);
208 /* variables for the output conversion: */
210 int pipefd, child_pid;
211 char* const b64args[] = {"sh", "-c", "base64 | tr -c -d '[A-Za-z0-9=+/]'", NULL};
218 init_datum(&algolabel);
221 /* figure out if we've got the right thing: */
222 subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt);
223 if (subkeycount < 0) {
224 err(0,"Could not determine subkey count (got value %d)\n", subkeycount);
228 if ((keyfpr == NULL) &&
230 err(0,"No key identifier passed in, but there were %d keys to choose from\n", subkeycount + 1);
234 if (keyfpr != NULL) {
235 ret = gnutls_openpgp_crt_get_fingerprint(*pgp_crt, fingerprint, &fingerprint_length);
237 err(0,"Could not get key fingerprint (error: %d)\n", ret);
240 if (fprlen > fingerprint_length) {
241 err(0, "Requested key identifier is longer than computed fingerprint\n");
244 if (fingerprint_length > fprlen) {
245 err(0, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
248 if ((keyfpr == NULL) || (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0)) {
249 /* we want to export the primary key: */
250 err(0,"exporting primary key\n");
252 /* FIXME: this is almost identical to the block below for subkeys.
253 This clumsiness seems inherent in the gnutls OpenPGP API,
255 algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits);
257 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
259 } else if (algo == GNUTLS_PK_RSA) {
260 err(0,"OpenPGP RSA certificate, with %d bits\n", bits);
261 ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e);
262 if (GNUTLS_E_SUCCESS != ret) {
263 err(0,"failed to export RSA certificate parameters (error: %d)\n", ret);
266 } else if (algo == GNUTLS_PK_DSA) {
267 err(0,"OpenPGP DSA certificate, with %d bits\n", bits);
268 ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y);
269 if (GNUTLS_E_SUCCESS != ret) {
270 err(0,"failed to export DSA certificate parameters (error: %d)\n", ret);
277 /* lets trawl through the subkeys until we find the one we want: */
278 for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
279 ret = gnutls_openpgp_crt_get_subkey_fingerprint(*pgp_crt, subkeyidx, fingerprint, &fingerprint_length);
281 err(0,"Could not get fingerprint of subkey with index %d (error: %d)\n", subkeyidx, ret);
284 if (fprlen > fingerprint_length) {
285 err(0, "Requested key identifier is longer than computed fingerprint\n");
288 if (fingerprint_length > fprlen) {
289 err(1, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
291 if (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0) {
292 err(0,"exporting subkey index %d\n", subkeyidx);
294 /* FIXME: this is almost identical to the block above for the
296 algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits);
298 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
300 } else if (algo == GNUTLS_PK_RSA) {
301 err(0,"OpenPGP RSA certificate, with %d bits\n", bits);
302 ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e);
303 if (GNUTLS_E_SUCCESS != ret) {
304 err(0,"failed to export RSA certificate parameters (error: %d)\n", ret);
307 } else if (algo == GNUTLS_PK_DSA) {
308 err(0,"OpenPGP DSA certificate, with %d bits\n", bits);
309 ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y);
310 if (GNUTLS_E_SUCCESS != ret) {
311 err(0,"failed to export DSA certificate parameters (error: %d)\n", ret);
322 err(0,"Could not find key in input\n");
326 /* if we made it this far, we've got MPIs, and we've got the
327 algorithm, so we just need to emit the info */
328 if (algo == GNUTLS_PK_RSA) {
329 algoname = "ssh-rsa";
335 } else if (algo == GNUTLS_PK_DSA) {
336 algoname = "ssh-dss";
345 err(0,"Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo);
349 if (ret = datum_from_string(&algolabel, algoname), ret) {
350 err(0,"couldn't label string (error: %d)\n", ret);
354 snprintf(output_data, sizeof(output_data), "%s ", algoname);
356 pipefd = create_writing_pipe(&child_pid, b64args[0], b64args);
358 err(0,"failed to create a writing pipe (returned %d)\n", pipefd);
362 write(1, output_data, strlen(output_data));
364 if (0 != write_data_fd_with_length(pipefd, all, mpicount)) {
365 err(0,"was not able to write out RSA key data\n");
369 if (child_pid != waitpid(child_pid, &pipestatus, 0)) {
370 err(0,"could not wait for child process to return for some reason.\n");
373 if (pipestatus != 0) {
374 err(0,"base64 pipe died with return code %d\n", pipestatus);
383 int main(int argc, char* argv[]) {
386 gnutls_x509_privkey_t x509_privkey;
387 gnutls_openpgp_privkey_t pgp_privkey;
388 gnutls_openpgp_crt_t pgp_crt;
390 char output_data[10240];
391 size_t ods = sizeof(output_data);
393 unsigned char * fingerprint = NULL;
395 char * prettyfpr = NULL;
399 /* figure out what key we should be looking for: */
400 if (argv[1] != NULL) {
401 if (strlen(argv[1]) > 81) {
402 /* safety check to avoid some sort of wacky overflow situation:
403 there's no reason that the key id should be longer than twice
404 a sane fingerprint (one byte between chars, and then another
405 two at the beginning and end) */
406 err(0, "Key identifier is way too long. Please use at most 40 hex digits.\n");
410 fpr_size = hexstring2bin(NULL, argv[1]);
411 if (fpr_size > 40*4) {
412 err(0, "Key identifier is longer than 40 hex digits\n");
415 /* since fpr_size is initially in bits: */
416 if (fpr_size % 8 != 0) {
417 err(0, "Please provide an even number of hex digits for the key identifier\n");
422 fingerprint = malloc(sizeof(unsigned char) * fpr_size);
423 bzero(fingerprint, sizeof(unsigned char) * fpr_size);
424 hexstring2bin(fingerprint, argv[1]);
426 prettyfpr = malloc(sizeof(unsigned char)*fpr_size*2 + 1);
427 if (prettyfpr != NULL) {
428 hex_print_data(prettyfpr, fingerprint, fpr_size);
429 prettyfpr[sizeof(unsigned char)*fpr_size*2] = '\0';
430 err(1, "searching for key with fingerprint '%s'\n", prettyfpr);
435 err(0, "You MUST provide at least 8 hex digits in any key identifier\n");
439 err(0, "You should provide at least 16 hex digits in any key identifier (proceeding with %d digits anyway)\n", fpr_size*2);
446 /* slurp in the key from stdin */
447 if (ret = set_datum_fd(&data, 0), ret) {
448 err(0,"didn't read file descriptor 0\n");
453 if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
454 err(0,"Failed to initialized OpenPGP private key (error: %d)\n", ret);
457 /* check whether it's a private key or a public key, by trying them: */
458 if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) ||
459 (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) {
460 /* we're dealing with a private key */
461 err(0,"Translating private key\n");
462 if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
463 err(0,"Failed to initialize X.509 private key for output (error: %d)\n", ret);
467 ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, fingerprint, fpr_size);
469 gnutls_openpgp_privkey_deinit(pgp_privkey);
473 ret = gnutls_x509_privkey_export (x509_privkey,
478 write(1, output_data, ods);
480 gnutls_x509_privkey_deinit(x509_privkey);
483 if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) {
484 err(0,"Failed to initialized OpenPGP certificate (error: %d)\n", ret);
488 if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) ||
489 (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) {
490 /* we're dealing with a public key */
491 err(0,"Translating public key\n");
493 ret = emit_public_openssh_from_pgp(&pgp_crt, fingerprint, fpr_size);
498 /* we have no idea what kind of key this is at all anyway! */
499 err(0,"Input does not contain any form of OpenPGP key I recognize.\n");
504 gnutls_global_deinit();