upstream: support PKCS8 as an optional format for storage of
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
This commit is contained in:
parent
e18a27eedc
commit
eb0d8e708a
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
25
ssh-keygen.c
25
ssh-keygen.c
|
@ -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));
|
||||
|
|
78
sshkey.c
78
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 */
|
||||
}
|
||||
|
||||
|
||||
|
|
11
sshkey.h
11
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,
|
||||
|
|
Loading…
Reference in New Issue