Commit 05dba815 authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

Support for traditional format private keys.



Add new function PEM_write_bio_PrivateKey_traditional() to enforce the
use of legacy "traditional" private key format. Add -traditional option
to pkcs8 and pkey utilities.

Reviewed-by: default avatarMatt Caswell <matt@openssl.org>
parent 07930a75
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -23,7 +23,8 @@ typedef enum OPTION_choice {
#ifndef OPENSSL_NO_SCRYPT
    OPT_SCRYPT, OPT_SCRYPT_N, OPT_SCRYPT_R, OPT_SCRYPT_P,
#endif
    OPT_V2, OPT_V1, OPT_V2PRF, OPT_ITER, OPT_PASSIN, OPT_PASSOUT
    OPT_V2, OPT_V1, OPT_V2PRF, OPT_ITER, OPT_PASSIN, OPT_PASSOUT,
    OPT_TRADITIONAL
} OPTION_CHOICE;

OPTIONS pkcs8_options[] = {
@@ -41,6 +42,7 @@ OPTIONS pkcs8_options[] = {
    {"iter", OPT_ITER, 'p', "Specify the iteration count"},
    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
    {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
    {"traditional", OPT_TRADITIONAL, '-', "use traditional format private key"},
#ifndef OPENSSL_NO_ENGINE
    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
#endif
@@ -70,7 +72,7 @@ int pkcs8_main(int argc, char **argv)
    OPTION_CHOICE o;
    int nocrypt = 0, ret = 1, iter = PKCS12_DEFAULT_ITER;
    int informat = FORMAT_PEM, outformat = FORMAT_PEM, topk8 = 0, pbe_nid = -1;
    int private = 0;
    int private = 0, traditional = 0;
#ifndef OPENSSL_NO_SCRYPT
    long scrypt_N = 0, scrypt_r = 0, scrypt_p = 0;
#endif
@@ -110,6 +112,9 @@ int pkcs8_main(int argc, char **argv)
        case OPT_NOCRYPT:
            nocrypt = 1;
            break;
        case OPT_TRADITIONAL:
            traditional = 1;
            break;
        case OPT_V2:
            if (!opt_cipher(opt_arg(), &cipher))
                goto opthelp;
@@ -320,11 +325,15 @@ int pkcs8_main(int argc, char **argv)
    }

    assert(private);
    if (outformat == FORMAT_PEM)
    if (outformat == FORMAT_PEM) {
        if (traditional)
            PEM_write_bio_PrivateKey_traditional(out, pkey, NULL, NULL, 0,
                                                 NULL, passout);
        else
            PEM_write_bio_PrivateKey(out, pkey, NULL, NULL, 0, NULL, passout);
    else if (outformat == FORMAT_ASN1)
    } else if (outformat == FORMAT_ASN1) {
        i2d_PrivateKey_bio(out, pkey);
    else {
    } else {
        BIO_printf(bio_err, "Bad format specified for key\n");
        goto end;
    }
+14 −4
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ typedef enum OPTION_choice {
    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
    OPT_INFORM, OPT_OUTFORM, OPT_PASSIN, OPT_PASSOUT, OPT_ENGINE,
    OPT_IN, OPT_OUT, OPT_PUBIN, OPT_PUBOUT, OPT_TEXT_PUB,
    OPT_TEXT, OPT_NOOUT, OPT_MD
    OPT_TEXT, OPT_NOOUT, OPT_MD, OPT_TRADITIONAL
} OPTION_CHOICE;

OPTIONS pkey_options[] = {
@@ -36,6 +36,8 @@ OPTIONS pkey_options[] = {
    {"text", OPT_TEXT, '-', "Output in plaintext as well"},
    {"noout", OPT_NOOUT, '-', "Don't output the key"},
    {"", OPT_MD, '-', "Any supported cipher"},
    {"traditional", OPT_TRADITIONAL, '-',
     "Use traditional format for private keys"},
#ifndef OPENSSL_NO_ENGINE
    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
#endif
@@ -53,7 +55,7 @@ int pkey_main(int argc, char **argv)
    OPTION_CHOICE o;
    int informat = FORMAT_PEM, outformat = FORMAT_PEM;
    int pubin = 0, pubout = 0, pubtext = 0, text = 0, noout = 0, ret = 1;
    int private = 0;
    int private = 0, traditional = 0;

    prog = opt_init(argc, argv, pkey_options);
    while ((o = opt_next()) != OPT_EOF) {
@@ -105,6 +107,9 @@ int pkey_main(int argc, char **argv)
        case OPT_NOOUT:
            noout = 1;
            break;
        case OPT_TRADITIONAL:
            traditional = 1;
            break;
        case OPT_MD:
            if (!opt_cipher(opt_unknown(), &cipher))
                goto opthelp;
@@ -140,6 +145,11 @@ int pkey_main(int argc, char **argv)
                PEM_write_bio_PUBKEY(out, pkey);
            else {
                assert(private);
                if (traditional)
                    PEM_write_bio_PrivateKey_traditional(out, pkey, cipher,
                                                         NULL, 0, NULL,
                                                         passout);
                else
                    PEM_write_bio_PrivateKey(out, pkey, cipher,
                                             NULL, 0, NULL, passout);
            }
+9 −2
Original line number Diff line number Diff line
@@ -95,11 +95,18 @@ int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
                             unsigned char *kstr, int klen,
                             pem_password_cb *cb, void *u)
{
    char pem_str[80];
    if (!x->ameth || x->ameth->priv_encode)
    if (x->ameth == NULL || x->ameth->priv_encode != NULL)
        return PEM_write_bio_PKCS8PrivateKey(bp, x, enc,
                                             (char *)kstr, klen, cb, u);
    return PEM_write_bio_PrivateKey_traditional(bp, x, enc, kstr, klen, cb, u);
}

int PEM_write_bio_PrivateKey_traditional(BIO *bp, EVP_PKEY *x,
                                         const EVP_CIPHER *enc,
                                         unsigned char *kstr, int klen,
                                         pem_password_cb *cb, void *u)
{
    char pem_str[80];
    BIO_snprintf(pem_str, 80, "%s PRIVATE KEY", x->ameth->pem_str);
    return PEM_ASN1_write_bio((i2d_of_void *)i2d_PrivateKey,
                              pem_str, bp, x, enc, kstr, klen, cb, u);
+50 −24
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ B<openssl> B<pkcs8>
[B<-iter count>]
[B<-noiter>]
[B<-nocrypt>]
[B<-traditional>]
[B<-v2 alg>]
[B<-v2prf alg>]
[B<-v1 alg>]
@@ -43,22 +44,22 @@ Print out a usage message.

=item B<-topk8>

Normally a PKCS#8 private key is expected on input and a traditional format
private key will be written. With the B<-topk8> option the situation is
reversed: it reads a traditional format private key and writes a PKCS#8
format key.
Normally a PKCS#8 private key is expected on input and a private key will be
written to the output file. With the B<-topk8> option the situation is
reversed: it reads a private key and writes a PKCS#8 format key.

=item B<-inform DER|PEM>

This specifies the input format. If a PKCS#8 format key is expected on input
then either a B<DER> or B<PEM> encoded version of a PKCS#8 key will be
expected. Otherwise the B<DER> or B<PEM> format of the traditional format
private key is used.
This specifies the input format: see L<KEY FORMATS> for more details.

=item B<-outform DER|PEM>

This specifies the output format, the options have the same meaning as the
B<-inform> option.
This specifies the output format: see L<KEY FORMATS> for more details.

=item B<-traditional>

When this option is present and B<-topk8> is not a traditional format private
key is written.

=item B<-in filename>

@@ -119,7 +120,7 @@ the B<hmacWithSHA1> option to work.

This option indicates a PKCS#5 v1.5 or PKCS#12 algorithm should be used.  Some
older implementations may not support PKCS#5 v2.0 and may require this option.
If not specified PKCS#5 v2.0 for is used.
If not specified PKCS#5 v2.0 form is used.

=item B<-engine id>

@@ -141,6 +142,27 @@ sets the scrypt B<N>, B<r> or B<p> parameters.

=back

=head1 KEY FORMATS

Various different formats are used by the pkcs8 utility. These are detailed
below.

If a key is being converted from PKCS#8 form (i.e. the B<-topk8> option is
not used) then the input file must be in PKCS#8 format. An encrypted
key is expected unless B<-nocrypt> is included.

If B<-topk8> is not used and B<PEM> mode is set the output file will be an
unencrypted private key in PKCS#8 format. If the B<-traditional> option is
used then a traditional format private key is written instead.

If B<-topk8> is not used and B<DER> mode is set the output file will be an
unencrypted private key in traditional DER format.

If B<-topk8> is used then any supported private key can be used for the input
file in a format specified by B<-inform>. The output file will be encrypted
PKCS#8 format using the specified encryption parameters unless B<-nocrypt>
is included.

=head1 NOTES

By default, when converting a key to PKCS#8 format, PKCS#5 v2.0 using 256 bit
@@ -199,20 +221,28 @@ allow strong encryption algorithms like triple DES or 128 bit RC2 to be used.

=head1 EXAMPLES

Convert a private from traditional to PKCS#5 v2.0 format using triple
DES:
Convert a private key to PKCS#8 format using default parameters (AES with
256 bit key and B<hmacWithSHA256>):

 openssl pkcs8 -in key.pem -topk8 -out enckey.pem

Convert a private key to PKCS#8 unencrypted format:

 openssl pkcs8 -in key.pem -topk8 -nocrypt -out enckey.pem

Convert a private key to PKCS#5 v2.0 format using triple DES:

 openssl pkcs8 -in key.pem -topk8 -v2 des3 -out enckey.pem

Convert a private from traditional to PKCS#5 v2.0 format using AES with
256 bits in CBC mode and B<hmacWithSHA256> PRF:
Convert a private key to PKCS#5 v2.0 format using AES with 256 bits in CBC
mode and B<hmacWithSHA512> PRF:

 openssl pkcs8 -in key.pem -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA256 -out enckey.pem
 openssl pkcs8 -in key.pem -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA512 -out enckey.pem

Convert a private key to PKCS#8 using a PKCS#5 1.5 compatible algorithm
(DES):

 openssl pkcs8 -in key.pem -topk8 -out enckey.pem
 openssl pkcs8 -in key.pem -topk8 -v1 PBE-MD5-DES -out enckey.pem

Convert a private key to PKCS#8 using a PKCS#12 compatible algorithm
(3DES):
@@ -223,14 +253,14 @@ Read a DER unencrypted PKCS#8 format private key:

 openssl pkcs8 -inform DER -nocrypt -in key.der -out key.pem

Convert a private key from any PKCS#8 format to traditional format:
Convert a private key from any PKCS#8 encrypted format to traditional format:

 openssl pkcs8 -in pk8.pem -out key.pem
 openssl pkcs8 -in pk8.pem -traditional -out key.pem

Convert a private key to PKCS#8 format, encrypting with AES-256 and with
one million iterations of the password:

 openssl pkcs8 -in raw.pem -topk8 -v2 aes-256-cbc -iter 1000000 -out pk8.pem
 openssl pkcs8 -in key.pem -topk8 -v2 aes-256-cbc -iter 1000000 -out pk8.pem

=head1 STANDARDS

@@ -250,10 +280,6 @@ PKCS#8 private key format complies with this standard.
There should be an option that prints out the encryption algorithm
in use and other details such as the iteration count.

PKCS#8 using triple DES and PKCS#5 v2.0 should be the default private
key format for OpenSSL: for compatibility several of the utilities use
the old format at present.

=head1 SEE ALSO

L<dsa(1)>, L<rsa(1)>, L<genrsa(1)>,
+7 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ B<openssl> B<pkey>
[B<-passin arg>]
[B<-out filename>]
[B<-passout arg>]
[B<-traditional>]
[B<-cipher>]
[B<-text>]
[B<-text_pub>]
@@ -67,6 +68,12 @@ filename.
the output file password source. For more information about the format of B<arg>
see the B<PASS PHRASE ARGUMENTS> section in L<openssl(1)>.

=item B<-traditional>

normally a private key is written using standard format: this is PKCS#8 form
with the appropriate encryption algorithm (if any). If the B<-traditional>
option is specified then the older "traditional" format is used instead.

=item B<-cipher>

These options encrypt the private key with the supplied cipher. Any algorithm
Loading