summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2010-07-02 13:35:01 +1000
committerDamien Miller <djm@mindrot.org>2010-07-02 13:35:01 +1000
commit44b25040110a224a79ff371ee548be9a10ba8bfa (patch)
tree19e7e606355d900658326870a984fac97e6d3940
parentb96c441ee2e4d8ffae756d0d74e2777149c91067 (diff)
- djm@cvs.openbsd.org 2010/06/29 23:15:30
[ssh-keygen.1 ssh-keygen.c] allow import (-i) and export (-e) of PEM and PKCS#8 encoded keys; bz#1749; ok markus@
-rw-r--r--ChangeLog4
-rw-r--r--ssh-keygen.147
-rw-r--r--ssh-keygen.c281
3 files changed, 266 insertions, 66 deletions
diff --git a/ChangeLog b/ChangeLog
index 20b39f0d..ee40f10e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,10 @@
- djm@cvs.openbsd.org 2010/06/26 23:04:04
[ssh.c]
oops, forgot to #include <canohost.h>; spotted and patch from chl@
+ - djm@cvs.openbsd.org 2010/06/29 23:15:30
+ [ssh-keygen.1 ssh-keygen.c]
+ allow import (-i) and export (-e) of PEM and PKCS#8 encoded keys;
+ bz#1749; ok markus@
20100627
- (tim) [openbsd-compat/port-uw.c] Reorder includes. auth-options.h now needs
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index 26ae31f5..0d62255b 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ssh-keygen.1,v 1.94 2010/04/16 06:47:04 jmc Exp $
+.\" $OpenBSD: ssh-keygen.1,v 1.95 2010/06/29 23:15:30 djm Exp $
.\"
.\" -*- nroff -*-
.\"
@@ -37,7 +37,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: April 16 2010 $
+.Dd $Mdocdate: June 29 2010 $
.Dt SSH-KEYGEN 1
.Os
.Sh NAME
@@ -59,9 +59,11 @@
.Op Fl f Ar keyfile
.Nm ssh-keygen
.Fl i
+.Op Fl m Ar key_format
.Op Fl f Ar input_keyfile
.Nm ssh-keygen
.Fl e
+.Op Fl m Ar key_format
.Op Fl f Ar input_keyfile
.Nm ssh-keygen
.Fl y
@@ -215,11 +217,13 @@ Download the RSA public keys provided by the PKCS#11 shared library
.Ar pkcs11 .
.It Fl e
This option will read a private or public OpenSSH key file and
-print the key in
-RFC 4716 SSH Public Key File Format
-to stdout.
-This option allows exporting keys for use by several commercial
-SSH implementations.
+print to stdout the key in one of the formats specified by the
+.Fl m
+option.
+The default export format is
+.Dq RFC4716 .
+This option allows exporting OpenSSH key for use by other programs, including
+several commercial SSH implementations.
.It Fl F Ar hostname
Search for the specified
.Ar hostname
@@ -270,13 +274,14 @@ Please see the
section for details.
.It Fl i
This option will read an unencrypted private (or public) key file
-in SSH2-compatible format and print an OpenSSH compatible private
+in the format specified by the
+.Fl m
+option and print an OpenSSH compatible private
(or public) key to stdout.
-.Nm
-also reads the
-RFC 4716 SSH Public Key File Format.
-This option allows importing keys from several commercial
-SSH implementations.
+This option allows importing keys from other software, including several
+commercial SSH implementations.
+The default import format is
+.Dq RFC4716 .
.It Fl L
Prints the contents of a certificate.
.It Fl l
@@ -288,6 +293,22 @@ tries to find the matching public key file and prints its fingerprint.
If combined with
.Fl v ,
an ASCII art representation of the key is supplied with the fingerprint.
+.It Fl m Ar key_format
+Specify a key format for the
+.Fl i
+(import) or
+.Fl e
+(export) coversion options.
+The supported key formats are:
+.Dq RFC4716
+(RFC4716/SSH2 public or private key),
+.Dq PKCS8
+(PEM PKCS8 public key)
+or
+.Dq PEM
+(PEM public key).
+The default conversion format is
+.Dq RFC4716 .
.It Fl M Ar memory
Specify the amount of memory to use (in megabytes) when generating
candidate moduli for DH-GEX.
diff --git a/ssh-keygen.c b/ssh-keygen.c
index de7c4409..be08fbda 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.192 2010/06/23 02:59:02 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.193 2010/06/29 23:15:30 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -133,14 +133,20 @@ u_int32_t certflags_flags = CERTOPT_DEFAULT;
char *certflags_command = NULL;
char *certflags_src_addr = NULL;
-/* Dump public key file in format used by real and the original SSH 2 */
-int convert_to_ssh2 = 0;
-int convert_from_ssh2 = 0;
+/* Conversion to/from various formats */
+int convert_to = 0;
+int convert_from = 0;
+enum {
+ FMT_RFC4716,
+ FMT_PKCS8,
+ FMT_PEM
+} convert_format = FMT_RFC4716;
int print_public = 0;
int print_generic = 0;
char *key_type_name = NULL;
+
/* argv0 */
extern char *__progname;
@@ -215,30 +221,12 @@ load_identity(char *filename)
#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
static void
-do_convert_to_ssh2(struct passwd *pw)
+do_convert_to_ssh2(struct passwd *pw, Key *k)
{
- Key *k;
u_int len;
u_char *blob;
char comment[61];
- struct stat st;
- if (!have_identity)
- ask_filename(pw, "Enter file in which the key is");
- if (stat(identity_file, &st) < 0) {
- perror(identity_file);
- exit(1);
- }
- if ((k = key_load_public(identity_file, NULL)) == NULL) {
- if ((k = load_identity(identity_file)) == NULL) {
- fprintf(stderr, "load failed\n");
- exit(1);
- }
- }
- if (k->type == KEY_RSA1) {
- fprintf(stderr, "version 1 keys are not supported\n");
- exit(1);
- }
if (key_to_blob(k, &blob, &len) <= 0) {
fprintf(stderr, "key_to_blob failed\n");
exit(1);
@@ -259,6 +247,81 @@ do_convert_to_ssh2(struct passwd *pw)
}
static void
+do_convert_to_pkcs8(Key *k)
+{
+ switch (key_type_plain(k->type)) {
+ case KEY_RSA:
+ if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
+ fatal("PEM_write_RSA_PUBKEY failed");
+ break;
+ case KEY_DSA:
+ if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
+ fatal("PEM_write_DSA_PUBKEY failed");
+ break;
+ default:
+ fatal("%s: unsupported key type %s", __func__, key_type(k));
+ }
+ exit(0);
+}
+
+static void
+do_convert_to_pem(Key *k)
+{
+ switch (key_type_plain(k->type)) {
+ case KEY_RSA:
+ if (!PEM_write_RSAPublicKey(stdout, k->rsa))
+ fatal("PEM_write_RSAPublicKey failed");
+ break;
+#if notyet /* OpenSSH 0.9.8 lacks this function */
+ case KEY_DSA:
+ if (!PEM_write_DSAPublicKey(stdout, k->dsa))
+ fatal("PEM_write_DSAPublicKey failed");
+ break;
+#endif
+ default:
+ fatal("%s: unsupported key type %s", __func__, key_type(k));
+ }
+ exit(0);
+}
+
+static void
+do_convert_to(struct passwd *pw)
+{
+ Key *k;
+ struct stat st;
+
+ if (!have_identity)
+ ask_filename(pw, "Enter file in which the key is");
+ if (stat(identity_file, &st) < 0)
+ fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
+ if ((k = key_load_public(identity_file, NULL)) == NULL) {
+ if ((k = load_identity(identity_file)) == NULL) {
+ fprintf(stderr, "load failed\n");
+ exit(1);
+ }
+ }
+ if (k->type == KEY_RSA1) {
+ fprintf(stderr, "version 1 keys are not supported\n");
+ exit(1);
+ }
+
+ switch (convert_format) {
+ case FMT_RFC4716:
+ do_convert_to_ssh2(pw, k);
+ break;
+ case FMT_PKCS8:
+ do_convert_to_pkcs8(k);
+ break;
+ case FMT_PEM:
+ do_convert_to_pem(k);
+ break;
+ default:
+ fatal("%s: unknown key format %d", __func__, convert_format);
+ }
+ exit(0);
+}
+
+static void
buffer_get_bignum_bits(Buffer *b, BIGNUM *value)
{
u_int bignum_bits = buffer_get_int(b);
@@ -396,24 +459,16 @@ get_line(FILE *fp, char *line, size_t len)
}
static void
-do_convert_from_ssh2(struct passwd *pw)
+do_convert_from_ssh2(struct passwd *pw, Key **k, int *private)
{
- Key *k;
int blen;
u_int len;
char line[1024];
u_char blob[8096];
char encoded[8096];
- struct stat st;
- int escaped = 0, private = 0, ok;
+ int escaped = 0;
FILE *fp;
- if (!have_identity)
- ask_filename(pw, "Enter file in which the key is");
- if (stat(identity_file, &st) < 0) {
- perror(identity_file);
- exit(1);
- }
if ((fp = fopen(identity_file, "r")) == NULL)
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
encoded[0] = '\0';
@@ -423,7 +478,7 @@ do_convert_from_ssh2(struct passwd *pw)
if (strncmp(line, "----", 4) == 0 ||
strstr(line, ": ") != NULL) {
if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
- private = 1;
+ *private = 1;
if (strstr(line, " END ") != NULL) {
break;
}
@@ -448,26 +503,130 @@ do_convert_from_ssh2(struct passwd *pw)
fprintf(stderr, "uudecode failed.\n");
exit(1);
}
- k = private ?
+ *k = *private ?
do_convert_private_ssh2_from_blob(blob, blen) :
key_from_blob(blob, blen);
- if (k == NULL) {
+ if (*k == NULL) {
fprintf(stderr, "decode blob failed.\n");
exit(1);
}
- ok = private ?
- (k->type == KEY_DSA ?
- PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) :
- PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) :
- key_write(k, stdout);
+ fclose(fp);
+}
+
+static void
+do_convert_from_pkcs8(Key **k, int *private)
+{
+ EVP_PKEY *pubkey;
+ FILE *fp;
+
+ if ((fp = fopen(identity_file, "r")) == NULL)
+ fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
+ if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
+ fatal("%s: %s is not a recognised public key format", __func__,
+ identity_file);
+ }
+ fclose(fp);
+ switch (EVP_PKEY_type(pubkey->type)) {
+ case EVP_PKEY_RSA:
+ *k = key_new(KEY_UNSPEC);
+ (*k)->type = KEY_RSA;
+ (*k)->rsa = EVP_PKEY_get1_RSA(pubkey);
+ break;
+ case EVP_PKEY_DSA:
+ *k = key_new(KEY_UNSPEC);
+ (*k)->type = KEY_DSA;
+ (*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
+ break;
+ default:
+ fatal("%s: unsupported pubkey type %d", __func__,
+ EVP_PKEY_type(pubkey->type));
+ }
+ EVP_PKEY_free(pubkey);
+ return;
+}
+
+static void
+do_convert_from_pem(Key **k, int *private)
+{
+ FILE *fp;
+ RSA *rsa;
+#ifdef notyet
+ DSA *dsa;
+#endif
+
+ if ((fp = fopen(identity_file, "r")) == NULL)
+ fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
+ if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
+ *k = key_new(KEY_UNSPEC);
+ (*k)->type = KEY_RSA;
+ (*k)->rsa = rsa;
+ fclose(fp);
+ return;
+ }
+#if notyet /* OpenSSH 0.9.8 lacks this function */
+ rewind(fp);
+ if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
+ *k = key_new(KEY_UNSPEC);
+ (*k)->type = KEY_DSA;
+ (*k)->dsa = dsa;
+ fclose(fp);
+ return;
+ }
+#endif
+ fatal("%s: unrecognised raw private key format", __func__);
+}
+
+static void
+do_convert_from(struct passwd *pw)
+{
+ Key *k = NULL;
+ int private = 0, ok;
+ struct stat st;
+
+ if (!have_identity)
+ ask_filename(pw, "Enter file in which the key is");
+ if (stat(identity_file, &st) < 0)
+ fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
+
+ switch (convert_format) {
+ case FMT_RFC4716:
+ do_convert_from_ssh2(pw, &k, &private);
+ break;
+ case FMT_PKCS8:
+ do_convert_from_pkcs8(&k, &private);
+ break;
+ case FMT_PEM:
+ do_convert_from_pem(&k, &private);
+ break;
+ default:
+ fatal("%s: unknown key format %d", __func__, convert_format);
+ }
+
+ if (!private)
+ ok = key_write(k, stdout);
+ if (ok)
+ fprintf(stdout, "\n");
+ else {
+ switch (k->type) {
+ case KEY_DSA:
+ ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
+ NULL, 0, NULL, NULL);
+ break;
+ case KEY_RSA:
+ ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
+ NULL, 0, NULL, NULL);
+ break;
+ default:
+ fatal("%s: unsupported key type %s", __func__,
+ key_type(k));
+ }
+ }
+
if (!ok) {
fprintf(stderr, "key write failed\n");
exit(1);
}
key_free(k);
- if (!private)
- fprintf(stdout, "\n");
- fclose(fp);
exit(0);
}
@@ -1525,7 +1684,7 @@ usage(void)
#ifdef ENABLE_PKCS11
fprintf(stderr, " -D pkcs11 Download public key from pkcs11 token.\n");
#endif
- fprintf(stderr, " -e Convert OpenSSH to RFC 4716 key file.\n");
+ fprintf(stderr, " -e Export OpenSSH to foreign format key file.\n");
fprintf(stderr, " -F hostname Find hostname in known hosts file.\n");
fprintf(stderr, " -f filename Filename of the key file.\n");
fprintf(stderr, " -G file Generate candidates for DH-GEX moduli.\n");
@@ -1533,9 +1692,10 @@ usage(void)
fprintf(stderr, " -H Hash names in known_hosts file.\n");
fprintf(stderr, " -h Generate host certificate instead of a user certificate.\n");
fprintf(stderr, " -I key_id Key identifier to include in certificate.\n");
- fprintf(stderr, " -i Convert RFC 4716 to OpenSSH key file.\n");
+ fprintf(stderr, " -i Import foreign format to OpenSSH key file.\n");
fprintf(stderr, " -L Print the contents of a certificate.\n");
fprintf(stderr, " -l Show fingerprint of key file.\n");
+ fprintf(stderr, " -m key_fmt Conversion format for -e/-i (PEM|PKCS8|RFC4716).\n");
fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n");
fprintf(stderr, " -n name,... User/host principal names to include in certificate\n");
fprintf(stderr, " -N phrase Provide new passphrase.\n");
@@ -1603,7 +1763,7 @@ main(int argc, char **argv)
exit(1);
}
- while ((opt = getopt(argc, argv, "degiqpclBHLhvxXyF:b:f:t:D:I:P:N:n:"
+ while ((opt = getopt(argc, argv, "degiqpclBHLhvxXyF:b:f:t:D:I:P:m:N:n:"
"O:C:r:g:R:T:G:M:S:s:a:V:W:z:")) != -1) {
switch (opt) {
case 'b':
@@ -1635,6 +1795,21 @@ main(int argc, char **argv)
case 'B':
print_bubblebabble = 1;
break;
+ case 'm':
+ if (strcasecmp(optarg, "RFC4716") == 0 ||
+ strcasecmp(optarg, "ssh2") == 0) {
+ convert_format = FMT_RFC4716;
+ break;
+ }
+ if (strcasecmp(optarg, "PKCS8") == 0) {
+ convert_format = FMT_PKCS8;
+ break;
+ }
+ if (strcasecmp(optarg, "PEM") == 0) {
+ convert_format = FMT_PEM;
+ break;
+ }
+ fatal("Unsupported conversion format \"%s\"", optarg);
case 'n':
cert_principals = optarg;
break;
@@ -1671,7 +1846,7 @@ main(int argc, char **argv)
case 'e':
case 'x':
/* export key */
- convert_to_ssh2 = 1;
+ convert_to = 1;
break;
case 'h':
cert_key_type = SSH2_CERT_TYPE_HOST;
@@ -1680,7 +1855,7 @@ main(int argc, char **argv)
case 'i':
case 'X':
/* import key */
- convert_from_ssh2 = 1;
+ convert_from = 1;
break;
case 'y':
print_public = 1;
@@ -1796,10 +1971,10 @@ main(int argc, char **argv)
do_change_passphrase(pw);
if (change_comment)
do_change_comment(pw);
- if (convert_to_ssh2)
- do_convert_to_ssh2(pw);
- if (convert_from_ssh2)
- do_convert_from_ssh2(pw);
+ if (convert_to)
+ do_convert_to(pw);
+ if (convert_from)
+ do_convert_from(pw);
if (print_public)
do_print_public(pw);
if (rr_hostname != NULL) {