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;
58 subkeycount = gnutls_openpgp_privkey_get_subkey_count(*pgp_privkey);
59 if (subkeycount < 0) {
60 err(0,"Could not determine subkey count (got value %d)\n", subkeycount);
64 if ((keyid == NULL) &&
66 err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
71 ret = gnutls_openpgp_privkey_get_key_id(*pgp_privkey, curkeyid);
73 err(0,"Could not get keyid (error: %d)\n", ret);
77 if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
78 /* we want to export the primary key: */
79 err(0,"exporting primary key\n");
81 /* FIXME: this is almost identical to the block below for subkeys.
82 This clumsiness seems inherent in the gnutls OpenPGP API,
84 pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits);
86 err(0, "failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
89 if (pgp_algo == GNUTLS_PK_RSA) {
90 err(0,"OpenPGP RSA Key, with %d bits\n", pgp_bits);
91 ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u);
92 if (GNUTLS_E_SUCCESS != ret) {
93 err(0, "failed to export RSA key parameters (error: %d)\n", ret);
97 } else if (pgp_algo == GNUTLS_PK_DSA) {
98 err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits);
99 ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x);
100 if (GNUTLS_E_SUCCESS != ret) {
101 err(0,"failed to export DSA key parameters (error: %d)\n", ret);
107 /* lets trawl through the subkeys until we find the one we want: */
108 for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
109 ret = gnutls_openpgp_privkey_get_subkey_id(*pgp_privkey, subkeyidx, curkeyid);
111 err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
114 if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
115 err(0,"exporting subkey index %d\n", subkeyidx);
117 /* FIXME: this is almost identical to the block above for the
119 pgp_algo = gnutls_openpgp_privkey_get_subkey_pk_algorithm(*pgp_privkey, subkeyidx, &pgp_bits);
121 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", pgp_algo);
123 } else if (pgp_algo == GNUTLS_PK_RSA) {
124 err(0,"OpenPGP RSA key, with %d bits\n", pgp_bits);
125 ret = gnutls_openpgp_privkey_export_subkey_rsa_raw(*pgp_privkey, subkeyidx, &m, &e, &d, &p, &q, &u);
126 if (GNUTLS_E_SUCCESS != ret) {
127 err(0,"failed to export RSA key parameters (error: %d)\n", ret);
130 } else if (pgp_algo == GNUTLS_PK_DSA) {
131 err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits);
132 ret = gnutls_openpgp_privkey_export_subkey_dsa_raw(*pgp_privkey, subkeyidx, &p, &q, &g, &y, &x);
133 if (GNUTLS_E_SUCCESS != ret) {
134 err(0,"failed to export DSA key parameters (error: %d)\n", ret);
144 err(0,"Could not find key in input\n");
148 if (pgp_algo == GNUTLS_PK_RSA) {
149 ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u);
150 if (GNUTLS_E_SUCCESS != ret) {
151 err(0, "failed to import RSA key parameters (error: %d)\n", ret);
154 } else if (pgp_algo == GNUTLS_PK_DSA) {
155 ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x);
156 if (GNUTLS_E_SUCCESS != ret) {
157 err(0,"failed to import DSA key parameters (error: %d)\n", ret);
161 err(0,"OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
165 ret = gnutls_x509_privkey_fix(*output);
167 err(0,"failed to fix up the private key in X.509 format (error: %d)\n", ret);
174 /* FIXME: keyid should be const also */
175 int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_openpgp_keyid_t* keyid) {
176 gnutls_openpgp_keyid_t curkeyid;
181 gnutls_datum_t m, e, p, q, g, y, algolabel;
183 gnutls_pk_algorithm_t algo;
184 const gnutls_datum_t* all[5];
185 const char* algoname;
187 /* output_data must be at least 2 chars longer than the maximum possible
189 char output_data[20];
191 /* variables for the output conversion: */
193 int pipefd, child_pid;
194 char* const b64args[] = {"/usr/bin/base64", "--wrap=0", NULL};
201 init_datum(&algolabel);
204 /* figure out if we've got the right thing: */
205 subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt);
206 if (subkeycount < 0) {
207 err(0,"Could not determine subkey count (got value %d)\n", subkeycount);
211 if ((keyid == NULL) &&
213 err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
218 ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid);
220 err(0,"Could not get keyid (error: %d)\n", ret);
224 if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
225 /* we want to export the primary key: */
226 err(0,"exporting primary key\n");
228 /* FIXME: this is almost identical to the block below for subkeys.
229 This clumsiness seems inherent in the gnutls OpenPGP API,
231 algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits);
233 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
235 } else if (algo == GNUTLS_PK_RSA) {
236 err(0,"OpenPGP RSA certificate, with %d bits\n", bits);
237 ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e);
238 if (GNUTLS_E_SUCCESS != ret) {
239 err(0,"failed to export RSA certificate parameters (error: %d)\n", ret);
242 } else if (algo == GNUTLS_PK_DSA) {
243 err(0,"OpenPGP DSA certificate, with %d bits\n", bits);
244 ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y);
245 if (GNUTLS_E_SUCCESS != ret) {
246 err(0,"failed to export DSA certificate parameters (error: %d)\n", ret);
253 /* lets trawl through the subkeys until we find the one we want: */
254 for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
255 ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid);
257 err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
260 if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
261 err(0,"exporting subkey index %d\n", subkeyidx);
263 /* FIXME: this is almost identical to the block above for the
265 algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits);
267 err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
269 } else if (algo == GNUTLS_PK_RSA) {
270 err(0,"OpenPGP RSA certificate, with %d bits\n", bits);
271 ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e);
272 if (GNUTLS_E_SUCCESS != ret) {
273 err(0,"failed to export RSA certificate parameters (error: %d)\n", ret);
276 } else if (algo == GNUTLS_PK_DSA) {
277 err(0,"OpenPGP DSA certificate, with %d bits\n", bits);
278 ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y);
279 if (GNUTLS_E_SUCCESS != ret) {
280 err(0,"failed to export DSA certificate parameters (error: %d)\n", ret);
291 err(0,"Could not find key in input\n");
295 /* if we made it this far, we've got MPIs, and we've got the
296 algorithm, so we just need to emit the info */
297 if (algo == GNUTLS_PK_RSA) {
298 algoname = "ssh-rsa";
304 } else if (algo == GNUTLS_PK_DSA) {
305 algoname = "ssh-dss";
314 err(0,"Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo);
318 if (ret = datum_from_string(&algolabel, algoname), ret) {
319 err(0,"couldn't label string (error: %d)\n", ret);
323 snprintf(output_data, sizeof(output_data), "%s ", algoname);
325 pipefd = create_writing_pipe(&child_pid, b64args[0], b64args);
327 err(0,"failed to create a writing pipe (returned %d)\n", pipefd);
331 write(1, output_data, strlen(output_data));
333 if (0 != write_data_fd_with_length(pipefd, all, mpicount)) {
334 err(0,"was not able to write out RSA key data\n");
338 if (child_pid != waitpid(child_pid, &pipestatus, 0)) {
339 err(0,"could not wait for child process to return for some reason.\n");
342 if (pipestatus != 0) {
343 err(0,"base64 pipe died with return code %d\n", pipestatus);
352 int main(int argc, char* argv[]) {
355 gnutls_x509_privkey_t x509_privkey;
356 gnutls_openpgp_privkey_t pgp_privkey;
357 gnutls_openpgp_crt_t pgp_crt;
359 char output_data[10240];
360 size_t ods = sizeof(output_data);
362 gnutls_openpgp_keyid_t keyid;
363 gnutls_openpgp_keyid_t* use_keyid;
367 /* figure out what keyid we should be looking for: */
369 if (argv[1] != NULL) {
370 ret = convert_string_to_keyid(keyid, argv[1]);
379 /* slurp in the key from stdin */
380 if (ret = set_datum_fd(&data, 0), ret) {
381 err(0,"didn't read file descriptor 0\n");
386 if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
387 err(0,"Failed to initialized OpenPGP private key (error: %d)\n", ret);
390 /* check whether it's a private key or a public key, by trying them: */
391 if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) ||
392 (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) {
393 /* we're dealing with a private key */
394 err(0,"Translating private key\n");
395 if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
396 err(0,"Failed to initialize X.509 private key for output (error: %d)\n", ret);
400 ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, use_keyid);
402 gnutls_openpgp_privkey_deinit(pgp_privkey);
406 ret = gnutls_x509_privkey_export (x509_privkey,
411 write(1, output_data, ods);
413 gnutls_x509_privkey_deinit(x509_privkey);
416 if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) {
417 err(0,"Failed to initialized OpenPGP certificate (error: %d)\n", ret);
421 if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) ||
422 (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) {
423 /* we're dealing with a public key */
424 err(0,"Translating public key\n");
426 ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid);
429 /* we have no idea what kind of key this is at all anyway! */
430 err(0,"Input does not contain any form of OpenPGP key I recognize.\n");
435 gnutls_global_deinit();