]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: support PKCS8 as an optional format for storage of
authordjm@openbsd.org <djm@openbsd.org>
Mon, 15 Jul 2019 13:16:29 +0000 (13:16 +0000)
committerDamien Miller <djm@mindrot.org>
Mon, 15 Jul 2019 13:21:18 +0000 (23:21 +1000)
private keys, enabled via "ssh-keygen -m PKCS8" on operations that save
private keys to disk.

The OpenSSH native key format remains the default, but PKCS8 is a
superior format to PEM if interoperability with non-OpenSSH software
is required, as it may use a less terrible KDF (IIRC PEM uses a single
round of MD5 as a KDF).

adapted from patch by Jakub Jelen via bz3013; ok markus

OpenBSD-Commit-ID: 027824e3bc0b1c243dc5188504526d73a55accb1

authfile.c
ssh-keygen.1
ssh-keygen.c
sshkey.c
sshkey.h

index 2166c16898e46c2f5620808192cc6a426d2c5f9f..851c1a8a1ae5b0af6be93e292007d418f8b22fa7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: authfile.c,v 1.132 2019/06/28 13:35:04 deraadt Exp $ */
+/* $OpenBSD: authfile.c,v 1.133 2019/07/15 13:16:29 djm Exp $ */
 /*
  * Copyright (c) 2000, 2013 Markus Friedl.  All rights reserved.
  *
@@ -74,7 +74,7 @@ sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
 int
 sshkey_save_private(struct sshkey *key, const char *filename,
     const char *passphrase, const char *comment,
-    int force_new_format, const char *new_format_cipher, int new_format_rounds)
+    int format, const char *openssh_format_cipher, int openssh_format_rounds)
 {
        struct sshbuf *keyblob = NULL;
        int r;
@@ -82,7 +82,7 @@ sshkey_save_private(struct sshkey *key, const char *filename,
        if ((keyblob = sshbuf_new()) == NULL)
                return SSH_ERR_ALLOC_FAIL;
        if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
-           force_new_format, new_format_cipher, new_format_rounds)) != 0)
+           format, openssh_format_cipher, openssh_format_rounds)) != 0)
                goto out;
        if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
                goto out;
index f42127c60cae21d6651cf22f99a0653690298ebe..8184a1797ecde559d8de51f1bc8f8d485449ae66 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ssh-keygen.1,v 1.160 2019/05/20 06:01:59 jmc Exp $
+.\"    $OpenBSD: ssh-keygen.1,v 1.161 2019/07/15 13:16:29 djm Exp $
 .\"
 .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
 .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -35,7 +35,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: May 20 2019 $
+.Dd $Mdocdate: July 15 2019 $
 .Dt SSH-KEYGEN 1
 .Os
 .Sh NAME
@@ -419,11 +419,12 @@ The supported key formats are:
 .Dq RFC4716
 (RFC 4716/SSH2 public or private key),
 .Dq PKCS8
-(PEM PKCS8 public key)
+(PKCS8 public or private key)
 or
 .Dq PEM
 (PEM public key).
-The default conversion format is
+By default OpenSSH will write newly-generated private keys in its own
+format, but when converting public keys for export the default format is
 .Dq RFC4716 .
 Setting a format of
 .Dq PEM
index b019a02ff927da66e0a44fa4cb19c857f1cbb0b2..5dcad1f61e8aefb6aa64d889373a3547f20ed5f8 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.335 2019/07/05 07:32:01 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.336 2019/07/15 13:16:29 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -147,11 +147,11 @@ static char *key_type_name = NULL;
 /* Load key from this PKCS#11 provider */
 static char *pkcs11provider = NULL;
 
-/* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */
-static int use_new_format = 1;
+/* Format for writing private keys */
+static int private_key_format = SSHKEY_PRIVATE_OPENSSH;
 
 /* Cipher for new-format private keys */
-static char *new_format_cipher = NULL;
+static char *openssh_format_cipher = NULL;
 
 /*
  * Number of KDF rounds to derive new format keys /
@@ -1048,7 +1048,8 @@ do_gen_all_hostkeys(struct passwd *pw)
                snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
                    hostname);
                if ((r = sshkey_save_private(private, prv_tmp, "",
-                   comment, use_new_format, new_format_cipher, rounds)) != 0) {
+                   comment, private_key_format, openssh_format_cipher,
+                   rounds)) != 0) {
                        error("Saving key \"%s\" failed: %s",
                            prv_tmp, ssh_err(r));
                        goto failnext;
@@ -1391,7 +1392,7 @@ do_change_passphrase(struct passwd *pw)
 
        /* Save the file using the new passphrase. */
        if ((r = sshkey_save_private(private, identity_file, passphrase1,
-           comment, use_new_format, new_format_cipher, rounds)) != 0) {
+           comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
                error("Saving key \"%s\" failed: %s.",
                    identity_file, ssh_err(r));
                explicit_bzero(passphrase1, strlen(passphrase1));
@@ -1480,7 +1481,7 @@ do_change_comment(struct passwd *pw, const char *identity_comment)
        }
 
        if (private->type != KEY_ED25519 && private->type != KEY_XMSS &&
-           !use_new_format) {
+           private_key_format != SSHKEY_PRIVATE_OPENSSH) {
                error("Comments are only supported for keys stored in "
                    "the new format (-o).");
                explicit_bzero(passphrase, strlen(passphrase));
@@ -1514,7 +1515,8 @@ do_change_comment(struct passwd *pw, const char *identity_comment)
 
        /* Save the file using the new passphrase. */
        if ((r = sshkey_save_private(private, identity_file, passphrase,
-           new_comment, use_new_format, new_format_cipher, rounds)) != 0) {
+           new_comment, private_key_format, openssh_format_cipher,
+           rounds)) != 0) {
                error("Saving key \"%s\" failed: %s",
                    identity_file, ssh_err(r));
                explicit_bzero(passphrase, strlen(passphrase));
@@ -2525,11 +2527,12 @@ main(int argc, char **argv)
                        }
                        if (strcasecmp(optarg, "PKCS8") == 0) {
                                convert_format = FMT_PKCS8;
+                               private_key_format = SSHKEY_PRIVATE_PKCS8;
                                break;
                        }
                        if (strcasecmp(optarg, "PEM") == 0) {
                                convert_format = FMT_PEM;
-                               use_new_format = 0;
+                               private_key_format = SSHKEY_PRIVATE_PEM;
                                break;
                        }
                        fatal("Unsupported conversion format \"%s\"", optarg);
@@ -2567,7 +2570,7 @@ main(int argc, char **argv)
                        add_cert_option(optarg);
                        break;
                case 'Z':
-                       new_format_cipher = optarg;
+                       openssh_format_cipher = optarg;
                        break;
                case 'C':
                        identity_comment = optarg;
@@ -2912,7 +2915,7 @@ passphrase_again:
 
        /* Save the key with the given passphrase and comment. */
        if ((r = sshkey_save_private(private, identity_file, passphrase1,
-           comment, use_new_format, new_format_cipher, rounds)) != 0) {
+           comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
                error("Saving key \"%s\" failed: %s",
                    identity_file, ssh_err(r));
                explicit_bzero(passphrase1, strlen(passphrase1));
index 6b5ff04852efe7ce9c82d435474b8fa5ed567e9f..a0cea92578c030f5c23066a4355801bd6bca9c76 100644 (file)
--- a/sshkey.c
+++ b/sshkey.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.c,v 1.79 2019/07/07 01:05:00 dtucker Exp $ */
+/* $OpenBSD: sshkey.c,v 1.80 2019/07/15 13:16:29 djm Exp $ */
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  * Copyright (c) 2008 Alexander von Gernler.  All rights reserved.
@@ -3975,10 +3975,10 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
 
 
 #ifdef WITH_OPENSSL
-/* convert SSH v2 key in OpenSSL PEM format */
+/* convert SSH v2 key to PEM or PKCS#8 format */
 static int
-sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf,
-    const char *_passphrase, const char *comment)
+sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf,
+    int format, const char *_passphrase, const char *comment)
 {
        int was_shielded = sshkey_is_shielded(key);
        int success, r;
@@ -3988,32 +3988,49 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf,
        char *bptr;
        BIO *bio = NULL;
        struct sshbuf *blob;
+       EVP_PKEY *pkey = NULL;
 
        if (len > 0 && len <= 4)
                return SSH_ERR_PASSPHRASE_TOO_SHORT;
        if ((blob = sshbuf_new()) == NULL)
                return SSH_ERR_ALLOC_FAIL;
-       if ((bio = BIO_new(BIO_s_mem())) == NULL) {
-               sshbuf_free(blob);
-               return SSH_ERR_ALLOC_FAIL;
+       if ((bio = BIO_new(BIO_s_mem())) == NULL) {
+               r = SSH_ERR_ALLOC_FAIL;
+               goto out;
        }
+       if (format == SSHKEY_PRIVATE_PKCS8 && (pkey = EVP_PKEY_new()) == NULL) {
+               r = SSH_ERR_ALLOC_FAIL;
+               goto out;
+       }
        if ((r = sshkey_unshield_private(key)) != 0)
                goto out;
 
        switch (key->type) {
        case KEY_DSA:
-               success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
-                   cipher, passphrase, len, NULL, NULL);
+               if (format == SSHKEY_PRIVATE_PEM) {
+                       success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
+                           cipher, passphrase, len, NULL, NULL);
+               } else {
+                       success = EVP_PKEY_set1_DSA(pkey, key->dsa);
+               }
                break;
 #ifdef OPENSSL_HAS_ECC
        case KEY_ECDSA:
-               success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
-                   cipher, passphrase, len, NULL, NULL);
+               if (format == SSHKEY_PRIVATE_PEM) {
+                       success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
+                           cipher, passphrase, len, NULL, NULL);
+               } else {
+                       success = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa);
+               }
                break;
 #endif
        case KEY_RSA:
-               success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
-                   cipher, passphrase, len, NULL, NULL);
+               if (format == SSHKEY_PRIVATE_PEM) {
+                       success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
+                           cipher, passphrase, len, NULL, NULL);
+               } else {
+                       success = EVP_PKEY_set1_RSA(pkey, key->rsa);
+               }
                break;
        default:
                success = 0;
@@ -4023,6 +4040,13 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf,
                r = SSH_ERR_LIBCRYPTO_ERROR;
                goto out;
        }
+       if (format == SSHKEY_PRIVATE_PKCS8) {
+               if ((success = PEM_write_bio_PrivateKey(bio, pkey, cipher,
+                   passphrase, len, NULL, NULL)) == 0) {
+                       r = SSH_ERR_LIBCRYPTO_ERROR;
+                       goto out;
+               }
+       }
        if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) {
                r = SSH_ERR_INTERNAL_ERROR;
                goto out;
@@ -4035,8 +4059,9 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf,
                r = sshkey_shield_private(key);
        if (r == 0)
                r = sshbuf_putb(buf, blob);
-       sshbuf_free(blob);
 
+       EVP_PKEY_free(pkey);
+       sshbuf_free(blob);
        BIO_free(bio);
        return r;
 }
@@ -4046,29 +4071,38 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf,
 int
 sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
     const char *passphrase, const char *comment,
-    int force_new_format, const char *new_format_cipher, int new_format_rounds)
+    int format, const char *openssh_format_cipher, int openssh_format_rounds)
 {
        switch (key->type) {
 #ifdef WITH_OPENSSL
        case KEY_DSA:
        case KEY_ECDSA:
        case KEY_RSA:
-               if (force_new_format) {
-                       return sshkey_private_to_blob2(key, blob, passphrase,
-                           comment, new_format_cipher, new_format_rounds);
-               }
-               return sshkey_private_pem_to_blob(key, blob,
-                   passphrase, comment);
+               break; /* see below */
 #endif /* WITH_OPENSSL */
        case KEY_ED25519:
 #ifdef WITH_XMSS
        case KEY_XMSS:
 #endif /* WITH_XMSS */
                return sshkey_private_to_blob2(key, blob, passphrase,
-                   comment, new_format_cipher, new_format_rounds);
+                   comment, openssh_format_cipher, openssh_format_rounds);
        default:
                return SSH_ERR_KEY_TYPE_UNKNOWN;
        }
+
+#ifdef WITH_OPENSSL
+       switch (format) {
+       case SSHKEY_PRIVATE_OPENSSH:
+               return sshkey_private_to_blob2(key, blob, passphrase,
+                   comment, openssh_format_cipher, openssh_format_rounds);
+       case SSHKEY_PRIVATE_PEM:
+       case SSHKEY_PRIVATE_PKCS8:
+               return sshkey_private_to_blob_pem_pkcs8(key, blob,
+                   format, passphrase, comment);
+       default:
+               return SSH_ERR_INVALID_ARGUMENT;
+       }
+#endif /* WITH_OPENSSL */
 }
 
 
index 41d159a1bf4f1cab6d65962ee50e8ccadfc2b6b2..d30a69cc9966c456d0af21716d0855116810d702 100644 (file)
--- a/sshkey.h
+++ b/sshkey.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.h,v 1.32 2019/06/21 04:21:05 djm Exp $ */
+/* $OpenBSD: sshkey.h,v 1.33 2019/07/15 13:16:29 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -88,6 +88,13 @@ enum sshkey_serialize_rep {
        SSHKEY_SERIALIZE_INFO = 254,
 };
 
+/* Private key disk formats */
+enum sshkey_private_format {
+       SSHKEY_PRIVATE_OPENSSH = 0,
+       SSHKEY_PRIVATE_PEM = 1,
+       SSHKEY_PRIVATE_PKCS8 = 2,
+};
+
 /* key is stored in external hardware */
 #define SSHKEY_FLAG_EXT                0x0001
 
@@ -221,7 +228,7 @@ int sshkey_private_deserialize(struct sshbuf *buf,  struct sshkey **keyp);
 /* private key file format parsing and serialisation */
 int    sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
     const char *passphrase, const char *comment,
-    int force_new_format, const char *new_format_cipher, int new_format_rounds);
+    int format, const char *openssh_format_cipher, int openssh_format_rounds);
 int    sshkey_parse_private_fileblob(struct sshbuf *buffer,
     const char *passphrase, struct sshkey **keyp, char **commentp);
 int    sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,