Major openpgp2ssh overhaul. It's an unforgiving and brittle tool, but
authorDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Thu, 12 Jun 2008 21:25:55 +0000 (17:25 -0400)
committerDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Thu, 12 Jun 2008 21:25:55 +0000 (17:25 -0400)
it should do what we expect it to do, and its major limitations should
be documented in the man page.

man/man1/openpgp2ssh.1
src/keytrans/gnutls-helpers.c
src/keytrans/gnutls-helpers.h
src/keytrans/openpgp2ssh.c

index 62671419488d7129b519a849e8d974385240a6c9..1a02b3819393f386a17ae54392261670e8ecaa19 100644 (file)
@@ -12,16 +12,16 @@ openpgp2ssh
 
 .Nm gpg --export-secret-key $KEYID | openpgp2ssh $KEYID
 .Sh DESCRIPTION
-openpgp2ssh takes OpenPGP-formatted RSA and DSA keys on standard
-input, and spits out the requested equivalent SSH-style key on
-standard output.
+openpgp2ssh takes an OpenPGP-formatted primary key and associated
+subkeys on standard input, and spits out the requested equivalent
+SSH-style key on standard output.
 
-If the data on standard input contains only a single key, you can
-invoke openpgp2ssh without arguments.  If the data on standard input
-contains multiple keys (e.g. a primary key and associated subkeys),
-you must specify a specific OpenPGP keyid (e.g. CCD2ED94D21739E9) or
+If the data on standard input contains no subkeys, you can invoke
+openpgp2ssh without arguments.  If the data on standard input contains
+multiple keys (e.g. a primary key and associated subkeys), you must
+specify a specific OpenPGP keyid (e.g. CCD2ED94D21739E9) or
 fingerprint as the first argument to indicate which key to export.
-The keyid must be at least 8 hex characters.
+The keyid must be exactly 16 hex characters.
 
 If the input contains an OpenPGP RSA or DSA public key, it will be
 converted to the OpenSSH-style single-line keystring, prefixed with
@@ -31,20 +31,29 @@ insertion into known_hosts files and authorized_keys files.
 If the input contains an OpenPGP RSA or DSA secret key, it will be
 converted to the equivalent PEM-encoded private key.
 
-Note that the keys produced by this process are stripped of all
-identifying information, including certifications, self-signatures,
-etc.
-
 openpgp2ssh is part of the
 .Xr monkeysphere 1
 framework for providing a PKI for SSH.
+.Sh CAVEATS
+The keys produced by this process are stripped of all identifying
+information, including certifications, self-signatures, etc.  This is
+intentional, since ssh attaches no inherent significance to these
+features.
+
+openpgp2ssh only works with RSA or DSA keys, because those are the
+only ones which work with ssh.
+
+Assuming a valid key type, though, openpgp2ssh will produce output for
+any requested key.  This means, among other things, that it will
+happily export revoked keys, unverifiable keys, expired keys, etc.
+Make sure you do your own key validation before using this tool!
 .Sh EXAMPLES
 .Nm gpg --export-secret-key $KEYID | openpgp2ssh $KEYID | ssh-add -c /dev/stdin
 
 This pushes the secret key into the active
 .Xr ssh-agent 1 . 
-Tools (such as 
-.Xr ssh 1 )
+Tools such as 
+.Xr ssh 1
 which know how to talk to the 
 .Xr ssh-agent 1
 can now rely on the key.
@@ -58,8 +67,14 @@ lsh(1) and putty(1).
 
 Secret key output is currently not passphrase-protected.
 
-This program is not yet implemented, and this man page currently only
-describes expected functionality.
+openpgp2ssh currently cannot handle passphrase-protected secret keys on input.
+
+It would be nice to be able to use keyids shorter or longer than 16
+hex characters.
+
+openpgp2ssh only acts on keys associated with the first primary key
+passed in.  If you send it more than one primary key, it will silently
+ignore later ones.
 .Sh SEE ALSO
 .Xr monkeysphere 1 ,
 .Xr ssh 1 ,
index 6eae29e0aa3a32352c62212587a33118d5ca973f..7c342bb4c65b78ab610726cf9e681c70757291cc 100644 (file)
@@ -12,6 +12,9 @@
 /* for isalnum() */
 #include <ctype.h>
 
+/* for exit() */
+#include <unistd.h>
+
 int loglevel = 0;
 
 
@@ -46,6 +49,71 @@ void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
   }
 }
 
+unsigned char hex2bin(unsigned char x) {
+  if ((x >= '0') && (x <= '9')) 
+    return x - '0';
+  if ((x >= 'A') && (x <= 'F')) 
+    return 10 + x - 'A';
+  if ((x >= 'a') && (x <= 'f')) 
+    return 10 + x - 'a';
+  return 0xff;
+}
+
+void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
+  unsigned int pkix = 0, outkix = 0;
+  
+  while (pkix < sizeof(printable_keyid)) {
+    unsigned hi = hex2bin(in[pkix]);
+    unsigned lo = hex2bin(in[pkix + 1]);
+    if (hi == 0xff) {
+      err("character '%c' is not a hex char\n", in[pkix]);
+      exit(1);
+    }
+    if (lo == 0xff) {
+      err("character '%c' is not a hex char\n", in[pkix + 1]);
+      exit(1);
+    }
+    out[outkix] = lo | (hi << 4);
+
+    pkix += 2;
+    outkix++;
+  }
+}
+
+int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
+  printable_keyid p;
+  int ret;
+
+  ret = convert_string_to_printable_keyid(p, str);
+  if (ret == 0) 
+    collapse_printable_keyid(out, p);
+  return ret;
+}
+int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) {
+  int arglen, x;
+  arglen = 0;
+  x = 0;
+  while ((arglen <= sizeof(printable_keyid)) &&
+        (str[x] != '\0')) {
+    if (isxdigit(str[x])) {
+      if (arglen == sizeof(printable_keyid)) {
+       err("There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str);
+       return 1;
+      }
+      pkeyid[arglen] = str[x];
+      arglen++;
+    }
+    x++;
+  }
+  
+  if (arglen != sizeof(printable_keyid)) {
+    err("keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid));
+    return 1;
+  }
+  return 0;
+}
+
+
 
 int init_gnutls() {
   const char* version = NULL;
index 9ea22a3491c90e8266fcd5a9b4c3c7c3f9a9732d..00cdec7f81fae54bd042f8a3b06e23c11fb24bfb 100644 (file)
@@ -44,6 +44,10 @@ typedef unsigned char printable_keyid[16];
 
 void init_keyid(gnutls_openpgp_keyid_t keyid);
 void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid);
+void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in);
+int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str);
+int convert_string_to_printable_keyid(printable_keyid out, const char* str);
+
 
 /* functions to get data into datum objects: */
 
index d6bac685399c090749c50eeb33e80b4ad735b6ce..30e19d5e1790f1c42305afb604a55c62a56a1b75 100644 (file)
@@ -3,47 +3,47 @@
 #include <gnutls/openpgp.h>
 #include <gnutls/x509.h>
 
+/* for waitpid() */
+#include <sys/types.h>
+#include <sys/wait.h>
+
 /* 
    Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
-   Date: Tue, 01 Apr 2008
+   Date: 2008-06-12 13:47:41-0400
    License: GPL v3 or later
 
-   monkeysphere private key translator: execute this with an GPG
-   secret key on stdin (at the moment, only passphraseless RSA keys
-   work).
+   monkeysphere key translator: execute this with an OpenPGP key on
+   stdin, (please indicate the specific keyid that you want as the
+   first argument if there are subkeys).  At the moment, only public
+   keys and passphraseless secret keys work.
 
-   It will spit out a PEM-encoded version of the key on stdout, which
-   can be fed into ssh-add like this:
+   For secret keys, it will spit out a PEM-encoded version of the key
+   on stdout, which can be fed into ssh-add like this:
 
-    gpg --export-secret-keys $KEYID | monkeysphere | ssh-add -c /dev/stdin
+    gpg --export-secret-keys $KEYID | openpgp2ssh $KEYID | ssh-add -c /dev/stdin
 
-   Requirements: I've only built this so far with GnuTLS v2.3.4 --
-   version 2.2.0 does not contain the appropriate pieces.
+   For public keys, it will spit out a single line of text that can
+   (with some massaging) be used in an openssh known_hosts or
+   authorized_keys file.  For example:
 
-   Notes: gpgkey2ssh doesn't seem to provide the same public
-   keys. Mighty weird!
+    echo server.example.org $(gpg --export $KEYID | openpgp2ssh $KEYID) >> ~/.ssh/known_hosts
 
-0 wt215@squeak:~/monkeysphere$ gpg --export-secret-keys 1DCDF89F | ~dkg/src/monkeysphere/monkeysphere  | ssh-add -c /dev/stdin
-gnutls version: 2.3.4
-OpenPGP RSA Key, with 1024 bits
-Identity added: /dev/stdin (/dev/stdin)
-The user has to confirm each use of the key
-0 wt215@squeak:~/monkeysphere$ ssh-add -L
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9gWQqfrnhQKDQnND/3eOexpddE64J+1zp9fcyCje7H5LKclb6DBV2HS6WgW32PJhIzvP+fYZM3dzXea3fpv14y1SicXiRBDgF9SnsNA1qWn2RyzkLcKy7PmM0PDYtU1oiLTcQj/xkWcqW2sLKHT/WW+vZP5XP7RMGN/yWNMfE2Q== /dev/stdin
-0 wt215@squeak:~/monkeysphere$ gpgkey2ssh 1DCDF89F
-ssh-rsa AAAAB3NzaC1yc2EAAACBAL2BZCp+ueFAoNCc0P/d457Gl10Trgn7XOn19zIKN7sfkspyVvoMFXYdLpaBbfY8mEjO8/59hkzd3Nd5rd+m/XjLVKJxeJEEOAX1Kew0DWpafZHLOQtwrLs+YzQ8Ni1TWiItNxCP/GRZypbawsodP9Zb69k/lc/tEwY3/JY0x8TZAAAAAwEAAQ== COMMENT
-0 wt215@squeak:~/monkeysphere$ 
+   Requirements: I've only built this so far with GnuTLS v2.3.x.
+   GnuTLS 2.2.x does not contain the appropriate functionality.
 
  */
 
 
-int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) {
-  gnutls_openpgp_privkey_t pgp_privkey;
+/* FIXME: keyid should be const as well */
+int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_openpgp_privkey_t* pgp_privkey, gnutls_openpgp_keyid_t* keyid) {
   gnutls_datum_t m, e, d, p, q, u, g, y, x;
   gnutls_pk_algorithm_t pgp_algo;
   unsigned int pgp_bits;
   int ret;
 
+/* FIXME: actually respect keyid argument.  At the moment, we just
+   emit the primary key.  */
+
   init_datum(&m);
   init_datum(&e);
   init_datum(&d);
@@ -54,34 +54,14 @@ int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) {
   init_datum(&y);
   init_datum(&x);
 
-  if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
-    err("Failed to initialized OpenPGP private key (error: %d)\n", ret);
-    return 1;
-  }
-
-
-  /* format could be either: GNUTLS_OPENPGP_FMT_RAW,
-     GNUTLS_OPENPGP_FMT_BASE64; if MONKEYSPHERE_RAW is set, use RAW,
-     otherwise, use BASE64: */
-
-  if (getenv("MONKEYSPHERE_RAW")) {
-    err("assuming RAW formatted private keys\n");
-    if (ret = gnutls_openpgp_privkey_import(pgp_privkey, input, GNUTLS_OPENPGP_FMT_RAW, NULL, 0), ret)
-      err("failed to import the OpenPGP private key in RAW format (error: %d)\n", ret);
-  } else {
-    err("assuming BASE64 formatted private keys\n");
-    if (ret = gnutls_openpgp_privkey_import (pgp_privkey, input, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0), ret)
-      err("failed to import the OpenPGP private key in BASE64 format (error: %d)\n", ret);
-  }
-
-  pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(pgp_privkey, &pgp_bits);
+  pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits);
   if (pgp_algo < 0) {
     err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
     return 1;
   }
   if (pgp_algo == GNUTLS_PK_RSA) {
     err("OpenPGP RSA Key, with %d bits\n", pgp_bits);
-    ret = gnutls_openpgp_privkey_export_rsa_raw(pgp_privkey, &m, &e, &d, &p, &q, &u);
+    ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u);
     if (GNUTLS_E_SUCCESS != ret) {
       err ("failed to export RSA key parameters (error: %d)\n", ret);
       return 1;
@@ -94,7 +74,7 @@ int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) {
     }
   } else if (pgp_algo == GNUTLS_PK_DSA) {
     err("OpenPGP DSA Key, with %d bits\n", pgp_bits);
-    ret = gnutls_openpgp_privkey_export_dsa_raw(pgp_privkey, &p, &q, &g, &y, &x);
+    ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x);
     if (GNUTLS_E_SUCCESS != ret) {
       err ("failed to export DSA key parameters (error: %d)\n", ret);
       return 1;
@@ -116,10 +96,190 @@ int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) {
     return 1; 
   }
 
-  gnutls_openpgp_privkey_deinit(pgp_privkey);
   return 0;
 }
 
+/* FIXME: keyid should be const also */
+int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_openpgp_keyid_t* keyid) {
+  gnutls_openpgp_keyid_t curkeyid;
+  int ret;
+  int subkeyidx;
+  int subkeycount;
+  int found = 0;
+  gnutls_datum_t m, e, p, q, g, y, algolabel;
+  unsigned int bits;
+  gnutls_pk_algorithm_t algo;
+  const gnutls_datum_t* all[5];
+  const char* algoname;
+  int mpicount;
+  /* output_data must be at least 2 chars longer than the maximum possible
+     algorithm name: */
+  char output_data[20];
+
+  /* variables for the output conversion: */
+  int pipestatus;
+  int pipefd, child_pid;
+  char* const b64args[] = {"/usr/bin/base64", "--wrap=0", NULL};
+
+  init_datum(&m);
+  init_datum(&e);
+  init_datum(&p);
+  init_datum(&q);
+  init_datum(&g);
+  init_datum(&algolabel);
+
+
+  /* figure out if we've got the right thing: */
+  subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt);
+  if (subkeycount < 0) {
+    err("Could not determine subkey count (got value %d)\n", subkeycount);
+    return 1;
+  }
+
+  if ((keyid == NULL) && 
+      (subkeycount > 0)) {
+    err("No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
+    return 1;
+  }
+
+  if (keyid != NULL) {
+    ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid);
+    if (ret) {
+      err("Could not get keyid (error: %d)\n", ret);
+      return 1;
+    }
+  }
+  if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
+    /* we want to export the primary key: */
+    err("exporting primary key\n");
+
+    /* FIXME: this is almost identical to the block below for subkeys.
+       This clumsiness seems inherent in the gnutls OpenPGP API,
+       though.  ugh. */
+    algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits);
+    if (algo < 0) {
+      err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
+      return algo;
+    } else if (algo == GNUTLS_PK_RSA) {
+      err("OpenPGP RSA certificate, with %d bits\n", bits);
+      ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e);
+      if (GNUTLS_E_SUCCESS != ret) {
+       err ("failed to export RSA key parameters (error: %d)\n", ret);
+       return 1;
+      }
+    } else if (algo == GNUTLS_PK_DSA) {
+      err("OpenPGP DSA Key, with %d bits\n", bits);
+      ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y);
+      if (GNUTLS_E_SUCCESS != ret) {
+       err ("failed to export DSA key parameters (error: %d)\n", ret);
+       return 1;
+      }
+    }
+    found = 1;
+
+  } else {
+    /* lets trawl through the subkeys until we find the one we want: */
+    for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
+      ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid);
+      if (ret) {
+       err("Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
+       return 1;
+      }
+      if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
+       err("exporting subkey index %d\n", subkeyidx);
+
+       /* FIXME: this is almost identical to the block above for the
+          primary key. */
+       algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits);
+       if (algo < 0) {
+         err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
+         return algo;
+       } else if (algo == GNUTLS_PK_RSA) {
+         err("OpenPGP RSA certificate, with %d bits\n", bits);
+         ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e);
+         if (GNUTLS_E_SUCCESS != ret) {
+           err ("failed to export RSA key parameters (error: %d)\n", ret);
+           return 1;
+         }
+       } else if (algo == GNUTLS_PK_DSA) {
+         err("OpenPGP DSA Key, with %d bits\n", bits);
+         ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y);
+         if (GNUTLS_E_SUCCESS != ret) {
+           err ("failed to export DSA key parameters (error: %d)\n", ret);
+           return 1;
+         }
+       }
+       found = 1;
+       
+      }
+    }
+  }
+
+  if (!found) {
+    err("Could not find key in input\n");
+    return 1;
+  }
+
+  /* if we made it this far, we've got MPIs, and we've got the
+     algorithm, so we just need to emit the info */
+  if (algo == GNUTLS_PK_RSA) {
+    algoname = "ssh-rsa";
+    mpicount = 3;
+
+    all[0] = &algolabel;
+    all[1] = &e;
+    all[2] = &m;
+  } else if (algo == GNUTLS_PK_DSA) {
+    algoname = "ssh-dss";
+    mpicount = 5;
+
+    all[0] = &algolabel;
+    all[1] = &p;
+    all[2] = &q;
+    all[3] = &g;
+    all[4] = &y;
+  } else {
+    err("Key algorithm was neither DSA nor RSA (it was %d).  Can't deal.  Sorry!\n", algo);
+    return 1;
+  }
+
+  if (ret = datum_from_string(&algolabel, algoname), ret) {
+    err("couldn't label string (error: %d)\n", ret);
+    return ret;
+  }
+
+  snprintf(output_data, sizeof(output_data), "%s ", algoname);
+
+  pipefd = create_writing_pipe(&child_pid, b64args[0], b64args);
+  if (pipefd < 0) {
+    err("failed to create a writing pipe (returned %d)\n", pipefd);
+    return pipefd;
+  }
+    
+  write(1, output_data, strlen(output_data));
+
+  if (0 != write_data_fd_with_length(pipefd, all, mpicount)) {
+    err("was not able to write out RSA key data\n");
+    return 1;
+  }
+  close(pipefd);
+  if (child_pid != waitpid(child_pid, &pipestatus, 0)) {
+    err("could not wait for child process to return for some reason.\n");
+    return 1;
+  }
+  if (pipestatus != 0) {
+    err("base64 pipe died with return code %d\n", pipestatus);
+    return pipestatus;
+  }
+
+  write(1, "\n", 1);
+
+  
+  return 0;
+}
+
+
+
 int convert_x509_to_pgp(gnutls_openpgp_privkey_t* output, gnutls_datum_t* input) {
   gnutls_x509_privkey_t x509_privkey;
   gnutls_datum_t m, e, d, p, q, u, g, y, x;
@@ -203,69 +363,88 @@ int main(int argc, char* argv[]) {
   gnutls_datum_t data;
   int ret;
   gnutls_x509_privkey_t x509_privkey;
+  gnutls_openpgp_privkey_t pgp_privkey;
+  gnutls_openpgp_crt_t pgp_crt;
 
   char output_data[10240];
   size_t ods = sizeof(output_data);
+  
+  gnutls_openpgp_keyid_t keyid;
+  gnutls_openpgp_keyid_t* use_keyid;
 
   init_gnutls();
+
+  /* figure out what keyid we should be looking for: */
+  use_keyid = NULL;
+  if (argv[1] != NULL) {
+    ret = convert_string_to_keyid(keyid, argv[1]);
+    if (ret != 0)
+      return ret;
+    use_keyid = &keyid;
+  }
+
   
   init_datum(&data);
 
-  /* slurp in the private key from stdin */
+  /* slurp in the key from stdin */
   if (ret = set_datum_fd(&data, 0), ret) {
     err("didn't read file descriptor 0\n");
     return 1;
   }
 
 
-
-  /* Or, instead, read in key from a file name: 
-  if (ret = set_datum_file(&data, argv[1]), ret) {
-    err("didn't read file '%s'\n", argv[1]);
+  if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
+    err("Failed to initialized OpenPGP private key (error: %d)\n", ret);
     return 1;
   }
-*/
-
-  /* treat the passed file as an X.509 private key, and extract its
-     component values: */
-
-/*   if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { */
-/*     err("Failed to import the X.509 key (error: %d)\n", ret); */
-/*     return 1; */
-/*   } */
-/*   gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */
-
-  /* try to print the PEM-encoded private key: */
-/*   ret = gnutls_x509_privkey_export (x509_privkey, */
-/*                                 GNUTLS_X509_FMT_PEM, */
-/*                                 output_data, */
-/*                                 &ods); */
-/*   printf("ret: %u; ods: %u;\n", ret, ods); */
-/*   if (ret == 0) { */
-/*     write(0, output_data, ods); */
-/*   } */
-
-
-  if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
-    err("Failed to initialize X.509 private key (error: %d)\n", ret);
-    return 1;
+  /* check whether it's a private key or a public key, by trying them: */
+  if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) || 
+      (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) {
+    /* we're dealing with a private key */
+    err("Translating private key\n");
+    if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
+      err("Failed to initialize X.509 private key for output (error: %d)\n", ret);
+      return 1;
+    }
+    
+    ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, use_keyid);
+
+    gnutls_openpgp_privkey_deinit(pgp_privkey);
+    if (ret)
+      return ret;
+
+    ret = gnutls_x509_privkey_export (x509_privkey,
+                                     GNUTLS_X509_FMT_PEM,
+                                     output_data,
+                                     &ods);
+    if (ret == 0) {
+      write(1, output_data, ods);
+    }
+    gnutls_x509_privkey_deinit(x509_privkey);
+  
+  } else {
+    if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) {
+      err("Failed to initialized OpenPGP certificate (error: %d)\n", ret);
+      return 1;
+    }
+    
+    if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) || 
+       (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) {
+      /* we're dealing with a public key */
+      err("Translating public key\n");
+
+      ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid);
+      
+    } else {
+      /* we have no idea what kind of key this is at all anyway! */
+      err("Input does contain any form of OpenPGP key I recognize.");
+      return 1;
+    }
   }
 
-  if (ret = convert_pgp_to_x509(&x509_privkey, &data), ret) {
-    return ret;
-  }
 
-  ret = gnutls_x509_privkey_export (x509_privkey,
-                                   GNUTLS_X509_FMT_PEM,
-                                   output_data,
-                                   &ods);
-  printf("ret: %u; ods: %u;\n", ret, ods);
-  if (ret == 0) {
-    write(1, output_data, ods);
-  }
 
 
-  gnutls_x509_privkey_deinit(x509_privkey);
   gnutls_global_deinit();
   return 0;
 }