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, gnutls_openpgp_keyid_t* keyid) {
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;
43 gnutls_openpgp_keyid_t curkeyid;
48 /* FIXME: actually respect keyid argument. At the moment, we just
49 emit the primary key. */
62 subkeycount = gnutls_openpgp_privkey_get_subkey_count(*pgp_privkey);
63 if (subkeycount < 0) {
64 err(0,"Could not determine subkey count (got value %d)\n", subkeycount);
68 if ((keyid == NULL) &&
70 err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
75 ret = gnutls_openpgp_privkey_get_key_id(*pgp_privkey, curkeyid);
77 err(0,"Could not get keyid (error: %d)\n", ret);
81 if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
82 /* we want to export the primary key: */
83 err(0,"exporting primary key\n");
85 /* FIXME: this is almost identical to the block below for subkeys.
86 This clumsiness seems inherent in the gnutls OpenPGP API,
88 pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits);
90 err(0, "failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
93 if (pgp_algo == GNUTLS_PK_RSA) {
94 err(0,"OpenPGP RSA Key, with %d bits\n", pgp_bits);
95 ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u);
96 if (GNUTLS_E_SUCCESS != ret) {
97 err(0, "failed to export RSA key parameters (error: %d)\n", ret);
101 } else if (pgp_algo == GNUTLS_PK_DSA) {
102 err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits);
103 ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x);
104 if (GNUTLS_E_SUCCESS != ret) {
105 err(0,"failed to export DSA key parameters (error: %d)\n", ret);
111 /* lets trawl through the subkeys until we find the one we want: */
112 for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
113 ret = gnutls_openpgp_privkey_get_subkey_id(*pgp_privkey, subkeyidx, curkeyid);
115 err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
118 if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
119 err(0,"exporting subkey index %d\n", subkeyidx);
121 /* FIXME: this is almost identical to the block above for the
123 pgp_algo = gnutls_openpgp_privkey_get_subkey_pk_algorithm(*pgp_privkey, subkeyidx, &pgp_bits);
125 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", pgp_algo);
127 } else if (pgp_algo == GNUTLS_PK_RSA) {
128 err(0,"OpenPGP RSA key, with %d bits\n", pgp_bits);
129 ret = gnutls_openpgp_privkey_export_subkey_rsa_raw(*pgp_privkey, subkeyidx, &m, &e, &d, &p, &q, &u);
130 if (GNUTLS_E_SUCCESS != ret) {
131 err(0,"failed to export RSA key parameters (error: %d)\n", ret);
134 } else if (pgp_algo == GNUTLS_PK_DSA) {
135 err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits);
136 ret = gnutls_openpgp_privkey_export_subkey_dsa_raw(*pgp_privkey, subkeyidx, &p, &q, &g, &y, &x);
137 if (GNUTLS_E_SUCCESS != ret) {
138 err(0,"failed to export DSA key parameters (error: %d)\n", ret);
148 err(0,"Could not find key in input\n");
152 if (pgp_algo == GNUTLS_PK_RSA) {
153 ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u);
154 if (GNUTLS_E_SUCCESS != ret) {
155 err(0, "failed to import RSA key parameters (error: %d)\n", ret);
158 } else if (pgp_algo == GNUTLS_PK_DSA) {
159 ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x);
160 if (GNUTLS_E_SUCCESS != ret) {
161 err(0,"failed to import DSA key parameters (error: %d)\n", ret);
165 err(0,"OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
169 ret = gnutls_x509_privkey_fix(*output);
171 err(0,"failed to fix up the private key in X.509 format (error: %d)\n", ret);
178 /* FIXME: keyid should be const also */
179 int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_openpgp_keyid_t* keyid) {
180 gnutls_openpgp_keyid_t curkeyid;
185 gnutls_datum_t m, e, p, q, g, y, algolabel;
187 gnutls_pk_algorithm_t algo;
188 const gnutls_datum_t* all[5];
189 const char* algoname;
191 /* output_data must be at least 2 chars longer than the maximum possible
193 char output_data[20];
195 /* variables for the output conversion: */
197 int pipefd, child_pid;
198 char* const b64args[] = {"/usr/bin/base64", "--wrap=0", NULL};
205 init_datum(&algolabel);
208 /* figure out if we've got the right thing: */
209 subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt);
210 if (subkeycount < 0) {
211 err(0,"Could not determine subkey count (got value %d)\n", subkeycount);
215 if ((keyid == NULL) &&
217 err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
222 ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid);
224 err(0,"Could not get keyid (error: %d)\n", ret);
228 if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
229 /* we want to export the primary key: */
230 err(0,"exporting primary key\n");
232 /* FIXME: this is almost identical to the block below for subkeys.
233 This clumsiness seems inherent in the gnutls OpenPGP API,
235 algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits);
237 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
239 } else if (algo == GNUTLS_PK_RSA) {
240 err(0,"OpenPGP RSA certificate, with %d bits\n", bits);
241 ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e);
242 if (GNUTLS_E_SUCCESS != ret) {
243 err(0,"failed to export RSA certificate parameters (error: %d)\n", ret);
246 } else if (algo == GNUTLS_PK_DSA) {
247 err(0,"OpenPGP DSA certificate, with %d bits\n", bits);
248 ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y);
249 if (GNUTLS_E_SUCCESS != ret) {
250 err(0,"failed to export DSA certificate parameters (error: %d)\n", ret);
257 /* lets trawl through the subkeys until we find the one we want: */
258 for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
259 ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid);
261 err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
264 if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
265 err(0,"exporting subkey index %d\n", subkeyidx);
267 /* FIXME: this is almost identical to the block above for the
269 algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits);
271 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
273 } else if (algo == GNUTLS_PK_RSA) {
274 err(0,"OpenPGP RSA certificate, with %d bits\n", bits);
275 ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e);
276 if (GNUTLS_E_SUCCESS != ret) {
277 err(0,"failed to export RSA certificate parameters (error: %d)\n", ret);
280 } else if (algo == GNUTLS_PK_DSA) {
281 err(0,"OpenPGP DSA certificate, with %d bits\n", bits);
282 ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y);
283 if (GNUTLS_E_SUCCESS != ret) {
284 err(0,"failed to export DSA certificate parameters (error: %d)\n", ret);
295 err(0,"Could not find key in input\n");
299 /* if we made it this far, we've got MPIs, and we've got the
300 algorithm, so we just need to emit the info */
301 if (algo == GNUTLS_PK_RSA) {
302 algoname = "ssh-rsa";
308 } else if (algo == GNUTLS_PK_DSA) {
309 algoname = "ssh-dss";
318 err(0,"Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo);
322 if (ret = datum_from_string(&algolabel, algoname), ret) {
323 err(0,"couldn't label string (error: %d)\n", ret);
327 snprintf(output_data, sizeof(output_data), "%s ", algoname);
329 pipefd = create_writing_pipe(&child_pid, b64args[0], b64args);
331 err(0,"failed to create a writing pipe (returned %d)\n", pipefd);
335 write(1, output_data, strlen(output_data));
337 if (0 != write_data_fd_with_length(pipefd, all, mpicount)) {
338 err(0,"was not able to write out RSA key data\n");
342 if (child_pid != waitpid(child_pid, &pipestatus, 0)) {
343 err(0,"could not wait for child process to return for some reason.\n");
346 if (pipestatus != 0) {
347 err(0,"base64 pipe died with return code %d\n", pipestatus);
356 int main(int argc, char* argv[]) {
359 gnutls_x509_privkey_t x509_privkey;
360 gnutls_openpgp_privkey_t pgp_privkey;
361 gnutls_openpgp_crt_t pgp_crt;
363 char output_data[10240];
364 size_t ods = sizeof(output_data);
366 gnutls_openpgp_keyid_t keyid;
367 gnutls_openpgp_keyid_t* use_keyid;
371 /* figure out what keyid we should be looking for: */
373 if (argv[1] != NULL) {
374 ret = convert_string_to_keyid(keyid, argv[1]);
383 /* slurp in the key from stdin */
384 if (ret = set_datum_fd(&data, 0), ret) {
385 err(0,"didn't read file descriptor 0\n");
390 if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
391 err(0,"Failed to initialized OpenPGP private key (error: %d)\n", ret);
394 /* check whether it's a private key or a public key, by trying them: */
395 if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) ||
396 (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) {
397 /* we're dealing with a private key */
398 err(0,"Translating private key\n");
399 if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
400 err(0,"Failed to initialize X.509 private key for output (error: %d)\n", ret);
404 ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, use_keyid);
406 gnutls_openpgp_privkey_deinit(pgp_privkey);
410 ret = gnutls_x509_privkey_export (x509_privkey,
415 write(1, output_data, ods);
417 gnutls_x509_privkey_deinit(x509_privkey);
420 if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) {
421 err(0,"Failed to initialized OpenPGP certificate (error: %d)\n", ret);
425 if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) ||
426 (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) {
427 /* we're dealing with a public key */
428 err(0,"Translating public key\n");
430 ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid);
433 /* we have no idea what kind of key this is at all anyway! */
434 err(0,"Input does contain any form of OpenPGP key I recognize.\n");
442 gnutls_global_deinit();