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;
44 /* FIXME: actually respect keyid argument. At the moment, we just
45 emit the primary key. */
57 pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits);
59 err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
62 if (pgp_algo == GNUTLS_PK_RSA) {
63 err("OpenPGP RSA Key, with %d bits\n", pgp_bits);
64 ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u);
65 if (GNUTLS_E_SUCCESS != ret) {
66 err ("failed to export RSA key parameters (error: %d)\n", ret);
70 ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u);
71 if (GNUTLS_E_SUCCESS != ret) {
72 err ("failed to import RSA key parameters (error: %d)\n", ret);
75 } else if (pgp_algo == GNUTLS_PK_DSA) {
76 err("OpenPGP DSA Key, with %d bits\n", pgp_bits);
77 ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x);
78 if (GNUTLS_E_SUCCESS != ret) {
79 err ("failed to export DSA key parameters (error: %d)\n", ret);
83 ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x);
84 if (GNUTLS_E_SUCCESS != ret) {
85 err ("failed to import DSA key parameters (error: %d)\n", ret);
89 err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
93 ret = gnutls_x509_privkey_fix(*output);
95 err("failed to fix up the private key in X.509 format (error: %d)\n", ret);
102 /* FIXME: keyid should be const also */
103 int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_openpgp_keyid_t* keyid) {
104 gnutls_openpgp_keyid_t curkeyid;
109 gnutls_datum_t m, e, p, q, g, y, algolabel;
111 gnutls_pk_algorithm_t algo;
112 const gnutls_datum_t* all[5];
113 const char* algoname;
115 /* output_data must be at least 2 chars longer than the maximum possible
117 char output_data[20];
119 /* variables for the output conversion: */
121 int pipefd, child_pid;
122 char* const b64args[] = {"/usr/bin/base64", "--wrap=0", NULL};
129 init_datum(&algolabel);
132 /* figure out if we've got the right thing: */
133 subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt);
134 if (subkeycount < 0) {
135 err("Could not determine subkey count (got value %d)\n", subkeycount);
139 if ((keyid == NULL) &&
141 err("No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
146 ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid);
148 err("Could not get keyid (error: %d)\n", ret);
152 if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
153 /* we want to export the primary key: */
154 err("exporting primary key\n");
156 /* FIXME: this is almost identical to the block below for subkeys.
157 This clumsiness seems inherent in the gnutls OpenPGP API,
159 algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits);
161 err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
163 } else if (algo == GNUTLS_PK_RSA) {
164 err("OpenPGP RSA certificate, with %d bits\n", bits);
165 ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e);
166 if (GNUTLS_E_SUCCESS != ret) {
167 err ("failed to export RSA key parameters (error: %d)\n", ret);
170 } else if (algo == GNUTLS_PK_DSA) {
171 err("OpenPGP DSA Key, with %d bits\n", bits);
172 ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y);
173 if (GNUTLS_E_SUCCESS != ret) {
174 err ("failed to export DSA key parameters (error: %d)\n", ret);
181 /* lets trawl through the subkeys until we find the one we want: */
182 for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
183 ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid);
185 err("Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
188 if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
189 err("exporting subkey index %d\n", subkeyidx);
191 /* FIXME: this is almost identical to the block above for the
193 algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits);
195 err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
197 } else if (algo == GNUTLS_PK_RSA) {
198 err("OpenPGP RSA certificate, with %d bits\n", bits);
199 ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e);
200 if (GNUTLS_E_SUCCESS != ret) {
201 err ("failed to export RSA key parameters (error: %d)\n", ret);
204 } else if (algo == GNUTLS_PK_DSA) {
205 err("OpenPGP DSA Key, with %d bits\n", bits);
206 ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y);
207 if (GNUTLS_E_SUCCESS != ret) {
208 err ("failed to export DSA key parameters (error: %d)\n", ret);
219 err("Could not find key in input\n");
223 /* if we made it this far, we've got MPIs, and we've got the
224 algorithm, so we just need to emit the info */
225 if (algo == GNUTLS_PK_RSA) {
226 algoname = "ssh-rsa";
232 } else if (algo == GNUTLS_PK_DSA) {
233 algoname = "ssh-dss";
242 err("Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo);
246 if (ret = datum_from_string(&algolabel, algoname), ret) {
247 err("couldn't label string (error: %d)\n", ret);
251 snprintf(output_data, sizeof(output_data), "%s ", algoname);
253 pipefd = create_writing_pipe(&child_pid, b64args[0], b64args);
255 err("failed to create a writing pipe (returned %d)\n", pipefd);
259 write(1, output_data, strlen(output_data));
261 if (0 != write_data_fd_with_length(pipefd, all, mpicount)) {
262 err("was not able to write out RSA key data\n");
266 if (child_pid != waitpid(child_pid, &pipestatus, 0)) {
267 err("could not wait for child process to return for some reason.\n");
270 if (pipestatus != 0) {
271 err("base64 pipe died with return code %d\n", pipestatus);
280 int main(int argc, char* argv[]) {
283 gnutls_x509_privkey_t x509_privkey;
284 gnutls_openpgp_privkey_t pgp_privkey;
285 gnutls_openpgp_crt_t pgp_crt;
287 char output_data[10240];
288 size_t ods = sizeof(output_data);
290 gnutls_openpgp_keyid_t keyid;
291 gnutls_openpgp_keyid_t* use_keyid;
295 /* figure out what keyid we should be looking for: */
297 if (argv[1] != NULL) {
298 ret = convert_string_to_keyid(keyid, argv[1]);
307 /* slurp in the key from stdin */
308 if (ret = set_datum_fd(&data, 0), ret) {
309 err("didn't read file descriptor 0\n");
314 if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
315 err("Failed to initialized OpenPGP private key (error: %d)\n", ret);
318 /* check whether it's a private key or a public key, by trying them: */
319 if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) ||
320 (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) {
321 /* we're dealing with a private key */
322 err("Translating private key\n");
323 if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
324 err("Failed to initialize X.509 private key for output (error: %d)\n", ret);
328 ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, use_keyid);
330 gnutls_openpgp_privkey_deinit(pgp_privkey);
334 ret = gnutls_x509_privkey_export (x509_privkey,
339 write(1, output_data, ods);
341 gnutls_x509_privkey_deinit(x509_privkey);
344 if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) {
345 err("Failed to initialized OpenPGP certificate (error: %d)\n", ret);
349 if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) ||
350 (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) {
351 /* we're dealing with a public key */
352 err("Translating public key\n");
354 ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid);
357 /* we have no idea what kind of key this is at all anyway! */
358 err("Input does contain any form of OpenPGP key I recognize.");
366 gnutls_global_deinit();