From 66430207a4de2160082d84bb69feedde9cf7ac83 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Sat, 5 Feb 2000 21:07:56 +0000 Subject: [PATCH] Add support for some broken PKCS#8 formats. --- CHANGES | 4 + apps/pkcs8.c | 19 ++- crypto/asn1/p8_pkey.c | 2 - crypto/evp/evp_pkey.c | 261 +++++++++++++++++++++++++++++------------- crypto/x509/x509.h | 7 +- doc/apps/pkcs8.pod | 32 +++++- util/libeay.num | 1 + 7 files changed, 232 insertions(+), 94 deletions(-) diff --git a/CHANGES b/CHANGES index 5480029ca3..131faa9ba6 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ Changes between 0.9.4 and 0.9.5 [xx XXX 2000] + *) Add support for various broken PKCS#8 formats, and command line + options to produce them. + [Steve Henson] + *) New functions BN_CTX_start(), BN_CTX_get() and BT_CTX_end() to get temporary BIGNUMs from a BN_CTX. [Ulf Möller] diff --git a/apps/pkcs8.c b/apps/pkcs8.c index ccd7b44d1c..be1e0c177e 100644 --- a/apps/pkcs8.c +++ b/apps/pkcs8.c @@ -124,6 +124,8 @@ int MAIN(int argc, char **argv) else if (!strcmp (*args, "-noiter")) iter = 1; else if (!strcmp (*args, "-nocrypt")) nocrypt = 1; else if (!strcmp (*args, "-nooct")) p8_broken = PKCS8_NO_OCTET; + else if (!strcmp (*args, "-nsdb")) p8_broken = PKCS8_NS_DB; + else if (!strcmp (*args, "-embed")) p8_broken = PKCS8_EMBEDDED_PARAM; else if (!strcmp(*args,"-passin")) { if (!args[1]) goto bad; @@ -183,7 +185,9 @@ int MAIN(int argc, char **argv) BIO_printf(bio_err, "-passout arg input file pass phrase\n"); BIO_printf(bio_err, "-envpassout arg environment variable containing input file pass phrase\n"); BIO_printf(bio_err, "-topk8 output PKCS8 file\n"); - BIO_printf(bio_err, "-nooct use (broken) no octet form\n"); + BIO_printf(bio_err, "-nooct use (nonstandard) no octet format\n"); + BIO_printf(bio_err, "-embed use (nonstandard) embedded DSA parameters format\n"); + BIO_printf(bio_err, "-nsdb use (nonstandard) DSA Netscape DB format\n"); BIO_printf(bio_err, "-noiter use 1 as iteration count\n"); BIO_printf(bio_err, "-nocrypt use or expect unencrypted private key\n"); BIO_printf(bio_err, "-v2 alg use PKCS#5 v2.0 and cipher \"alg\"\n"); @@ -224,12 +228,11 @@ int MAIN(int argc, char **argv) return (1); } BIO_free(in); - if (!(p8inf = EVP_PKEY2PKCS8(pkey))) { + if (!(p8inf = EVP_PKEY2PKCS8_broken(pkey, p8_broken))) { BIO_printf(bio_err, "Error converting key\n", outfile); ERR_print_errors(bio_err); return (1); } - PKCS8_set_broken(p8inf, p8_broken); if(nocrypt) { if(outformat == FORMAT_PEM) PEM_write_bio_PKCS8_PRIV_KEY_INFO(out, p8inf); @@ -316,7 +319,15 @@ int MAIN(int argc, char **argv) BIO_printf(bio_err, "Warning: broken key encoding: "); switch (p8inf->broken) { case PKCS8_NO_OCTET: - BIO_printf(bio_err, "No Octet String\n"); + BIO_printf(bio_err, "No Octet String in PrivateKey\n"); + break; + + case PKCS8_EMBEDDED_PARAM: + BIO_printf(bio_err, "DSA parameters included in PrivateKey\n"); + break; + + case PKCS8_NS_DB: + BIO_printf(bio_err, "DSA public key include in PrivateKey\n"); break; default: diff --git a/crypto/asn1/p8_pkey.c b/crypto/asn1/p8_pkey.c index cf38a6aaf8..59cfbe7f28 100644 --- a/crypto/asn1/p8_pkey.c +++ b/crypto/asn1/p8_pkey.c @@ -109,8 +109,6 @@ PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO(PKCS8_PRIV_KEY_INFO **a, M_ASN1_D2I_get_IMP_set_opt_type(X509_ATTRIBUTE, ret->attributes, d2i_X509_ATTRIBUTE, X509_ATTRIBUTE_free, 0); - if (ASN1_TYPE_get(ret->pkey) == V_ASN1_SEQUENCE) - ret->broken = PKCS8_NO_OCTET; M_ASN1_D2I_Finish(a, PKCS8_PRIV_KEY_INFO_free, ASN1_F_D2I_PKCS8_PRIV_KEY_INFO); } diff --git a/crypto/evp/evp_pkey.c b/crypto/evp/evp_pkey.c index 75fde554fc..d5e6f5880f 100644 --- a/crypto/evp/evp_pkey.c +++ b/crypto/evp/evp_pkey.c @@ -62,19 +62,22 @@ #include #include +static int dsa_pkey2pkcs8(PKCS8_PRIV_KEY_INFO *p8inf, EVP_PKEY *pkey); + /* Extract a private key from a PKCS8 structure */ EVP_PKEY *EVP_PKCS82PKEY (PKCS8_PRIV_KEY_INFO *p8) { - EVP_PKEY *pkey; + EVP_PKEY *pkey = NULL; #ifndef NO_RSA - RSA *rsa; + RSA *rsa = NULL; #endif #ifndef NO_DSA - DSA *dsa; - ASN1_INTEGER *dsapriv; - STACK *ndsa; - BN_CTX *ctx; + DSA *dsa = NULL; + ASN1_INTEGER *privkey; + ASN1_TYPE *t1, *t2, *param = NULL; + STACK *ndsa = NULL; + BN_CTX *ctx = NULL; int plen; #endif X509_ALGOR *a; @@ -82,21 +85,14 @@ EVP_PKEY *EVP_PKCS82PKEY (PKCS8_PRIV_KEY_INFO *p8) int pkeylen; char obj_tmp[80]; - switch (p8->broken) { - case PKCS8_OK: + if(p8->pkey->type == V_ASN1_OCTET_STRING) { + p8->broken = PKCS8_OK; p = p8->pkey->value.octet_string->data; pkeylen = p8->pkey->value.octet_string->length; - break; - - case PKCS8_NO_OCTET: + } else { + p8->broken = PKCS8_NO_OCTET; p = p8->pkey->value.sequence->data; pkeylen = p8->pkey->value.sequence->length; - break; - - default: - EVPerr(EVP_F_EVP_PKCS82PKEY,EVP_R_PKCS8_UNKNOWN_BROKEN_TYPE); - return NULL; - break; } if (!(pkey = EVP_PKEY_new())) { EVPerr(EVP_F_EVP_PKCS82PKEY,ERR_R_MALLOC_FAILURE); @@ -121,65 +117,83 @@ EVP_PKEY *EVP_PKCS82PKEY (PKCS8_PRIV_KEY_INFO *p8) * be recalculated. */ - /* Check for broken Netscape Database DSA PKCS#8, UGH! */ + /* Check for broken DSA PKCS#8, UGH! */ if(*p == (V_ASN1_SEQUENCE|V_ASN1_CONSTRUCTED)) { if(!(ndsa = ASN1_seq_unpack(p, pkeylen, - (char *(*)())d2i_ASN1_INTEGER, - ASN1_STRING_free))) { + (char *(*)())d2i_ASN1_TYPE, + ASN1_TYPE_free))) { EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_DECODE_ERROR); - return NULL; + goto dsaerr; } if(sk_num(ndsa) != 2 ) { EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_DECODE_ERROR); - sk_pop_free(ndsa, ASN1_STRING_free); - return NULL; + goto dsaerr; } - dsapriv = (ASN1_INTEGER *) sk_pop(ndsa); - sk_pop_free(ndsa, ASN1_STRING_free); - } else if (!(dsapriv=d2i_ASN1_INTEGER (NULL, &p, pkeylen))) { + /* Handle Two broken types: + * SEQUENCE {parameters, priv_key} + * SEQUENCE {pub_key, priv_key} + */ + + t1 = (ASN1_TYPE *)sk_value(ndsa, 0); + t2 = (ASN1_TYPE *)sk_value(ndsa, 1); + if(t1->type == V_ASN1_SEQUENCE) { + p8->broken = PKCS8_EMBEDDED_PARAM; + param = t1; + } else if(a->parameter->type == V_ASN1_SEQUENCE) { + p8->broken = PKCS8_NS_DB; + param = a->parameter; + } else { EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_DECODE_ERROR); - return NULL; - } - /* Retrieve parameters */ - if (a->parameter->type != V_ASN1_SEQUENCE) { - EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_NO_DSA_PARAMETERS); - return NULL; + goto dsaerr; + } + + if(t2->type != V_ASN1_INTEGER) { + EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_DECODE_ERROR); + goto dsaerr; + } + privkey = t2->value.integer; + } else if (!(privkey=d2i_ASN1_INTEGER (NULL, &p, pkeylen))) { + EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_DECODE_ERROR); + goto dsaerr; } - p = a->parameter->value.sequence->data; - plen = a->parameter->value.sequence->length; + p = param->value.sequence->data; + plen = param->value.sequence->length; if (!(dsa = d2i_DSAparams (NULL, &p, plen))) { EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_DECODE_ERROR); - return NULL; + goto dsaerr; } /* We have parameters now set private key */ - if (!(dsa->priv_key = ASN1_INTEGER_to_BN(dsapriv, NULL))) { + if (!(dsa->priv_key = ASN1_INTEGER_to_BN(privkey, NULL))) { EVPerr(EVP_F_EVP_PKCS82PKEY,EVP_R_BN_DECODE_ERROR); - DSA_free (dsa); - return NULL; + goto dsaerr; } /* Calculate public key (ouch!) */ if (!(dsa->pub_key = BN_new())) { EVPerr(EVP_F_EVP_PKCS82PKEY,ERR_R_MALLOC_FAILURE); - DSA_free (dsa); - return NULL; + goto dsaerr; } if (!(ctx = BN_CTX_new())) { EVPerr(EVP_F_EVP_PKCS82PKEY,ERR_R_MALLOC_FAILURE); - DSA_free (dsa); - return NULL; + goto dsaerr; } if (!BN_mod_exp(dsa->pub_key, dsa->g, dsa->priv_key, dsa->p, ctx)) { EVPerr(EVP_F_EVP_PKCS82PKEY,EVP_R_BN_PUBKEY_ERROR); - BN_CTX_free (ctx); - DSA_free (dsa); - return NULL; + goto dsaerr; } - EVP_PKEY_assign_DSA (pkey, dsa); + EVP_PKEY_assign_DSA(pkey, dsa); BN_CTX_free (ctx); + sk_pop_free(ndsa, ASN1_TYPE_free); + break; + dsaerr: + BN_CTX_free (ctx); + sk_pop_free(ndsa, ASN1_TYPE_free); + DSA_free(dsa); + EVP_PKEY_free(pkey); + return NULL; break; #endif default: @@ -193,30 +207,35 @@ EVP_PKEY *EVP_PKCS82PKEY (PKCS8_PRIV_KEY_INFO *p8) return pkey; } +PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey) +{ + return EVP_PKEY2PKCS8_broken(pkey, PKCS8_OK); +} + /* Turn a private key into a PKCS8 structure */ -PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey) +PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8_broken(EVP_PKEY *pkey, int broken) { PKCS8_PRIV_KEY_INFO *p8; -#ifndef NO_DSA - ASN1_INTEGER *dpkey; - unsigned char *p, *q; - int len; -#endif + if (!(p8 = PKCS8_PRIV_KEY_INFO_new())) { EVPerr(EVP_F_EVP_PKEY2PKCS8,ERR_R_MALLOC_FAILURE); return NULL; } + p8->broken = broken; ASN1_INTEGER_set (p8->version, 0); if (!(p8->pkeyalg->parameter = ASN1_TYPE_new ())) { EVPerr(EVP_F_EVP_PKEY2PKCS8,ERR_R_MALLOC_FAILURE); PKCS8_PRIV_KEY_INFO_free (p8); return NULL; } + p8->pkey->type = V_ASN1_OCTET_STRING; switch (EVP_PKEY_type(pkey->type)) { #ifndef NO_RSA case EVP_PKEY_RSA: + if(p8->broken == PKCS8_NO_OCTET) p8->pkey->type = V_ASN1_SEQUENCE; + p8->pkeyalg->algorithm = OBJ_nid2obj(NID_rsaEncryption); p8->pkeyalg->parameter->type = V_ASN1_NULL; if (!ASN1_pack_string ((char *)pkey, i2d_PrivateKey, @@ -229,36 +248,11 @@ PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey) #endif #ifndef NO_DSA case EVP_PKEY_DSA: - p8->pkeyalg->algorithm = OBJ_nid2obj(NID_dsa); - - /* get parameters and place in AlgorithmIdentifier */ - len = i2d_DSAparams (pkey->pkey.dsa, NULL); - if (!(p = Malloc(len))) { - EVPerr(EVP_F_EVP_PKEY2PKCS8,ERR_R_MALLOC_FAILURE); + if(!dsa_pkey2pkcs8(p8, pkey)) { PKCS8_PRIV_KEY_INFO_free (p8); return NULL; } - q = p; - i2d_DSAparams (pkey->pkey.dsa, &q); - p8->pkeyalg->parameter->type = V_ASN1_SEQUENCE; - p8->pkeyalg->parameter->value.sequence = ASN1_STRING_new(); - ASN1_STRING_set(p8->pkeyalg->parameter->value.sequence, p, len); - Free(p); - /* Get private key into an integer and pack */ - if (!(dpkey = BN_to_ASN1_INTEGER (pkey->pkey.dsa->priv_key, NULL))) { - EVPerr(EVP_F_EVP_PKEY2PKCS8,EVP_R_ENCODE_ERROR); - PKCS8_PRIV_KEY_INFO_free (p8); - return NULL; - } - - if (!ASN1_pack_string((char *)dpkey, i2d_ASN1_INTEGER, - &p8->pkey->value.octet_string)) { - EVPerr(EVP_F_EVP_PKEY2PKCS8,ERR_R_MALLOC_FAILURE); - M_ASN1_INTEGER_free (dpkey); - PKCS8_PRIV_KEY_INFO_free (p8); - return NULL; - } - M_ASN1_INTEGER_free (dpkey); + break; #endif default: @@ -266,7 +260,6 @@ PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey) PKCS8_PRIV_KEY_INFO_free (p8); return NULL; } - p8->pkey->type = V_ASN1_OCTET_STRING; RAND_add(p8->pkey->value.octet_string->data, p8->pkey->value.octet_string->length, 0); return p8; @@ -295,4 +288,112 @@ PKCS8_PRIV_KEY_INFO *PKCS8_set_broken(PKCS8_PRIV_KEY_INFO *p8, int broken) } } +#ifndef NO_DSA +static int dsa_pkey2pkcs8(PKCS8_PRIV_KEY_INFO *p8, EVP_PKEY *pkey) +{ + ASN1_STRING *params; + ASN1_INTEGER *prkey; + ASN1_TYPE *ttmp; + STACK *ndsa; + unsigned char *p, *q; + int len; + p8->pkeyalg->algorithm = OBJ_nid2obj(NID_dsa); + len = i2d_DSAparams (pkey->pkey.dsa, NULL); + if (!(p = Malloc(len))) { + EVPerr(EVP_F_EVP_PKEY2PKCS8,ERR_R_MALLOC_FAILURE); + PKCS8_PRIV_KEY_INFO_free (p8); + return 0; + } + q = p; + i2d_DSAparams (pkey->pkey.dsa, &q); + params = ASN1_STRING_new(); + ASN1_STRING_set(params, p, len); + Free(p); + /* Get private key into integer */ + if (!(prkey = BN_to_ASN1_INTEGER (pkey->pkey.dsa->priv_key, NULL))) { + EVPerr(EVP_F_EVP_PKEY2PKCS8,EVP_R_ENCODE_ERROR); + return 0; + } + + switch(p8->broken) { + case PKCS8_OK: + case PKCS8_NO_OCTET: + + if (!ASN1_pack_string((char *)prkey, i2d_ASN1_INTEGER, + &p8->pkey->value.octet_string)) { + EVPerr(EVP_F_EVP_PKEY2PKCS8,ERR_R_MALLOC_FAILURE); + M_ASN1_INTEGER_free (prkey); + return 0; + } + + M_ASN1_INTEGER_free (prkey); + p8->pkeyalg->parameter->value.sequence = params; + p8->pkeyalg->parameter->type = V_ASN1_SEQUENCE; + + break; + + case PKCS8_NS_DB: + + p8->pkeyalg->parameter->value.sequence = params; + p8->pkeyalg->parameter->type = V_ASN1_SEQUENCE; + ndsa = sk_new_null(); + ttmp = ASN1_TYPE_new(); + if (!(ttmp->value.integer = BN_to_ASN1_INTEGER (pkey->pkey.dsa->pub_key, NULL))) { + EVPerr(EVP_F_EVP_PKEY2PKCS8,EVP_R_ENCODE_ERROR); + PKCS8_PRIV_KEY_INFO_free(p8); + return 0; + } + ttmp->type = V_ASN1_INTEGER; + sk_push(ndsa, (char *)ttmp); + + ttmp = ASN1_TYPE_new(); + ttmp->value.integer = prkey; + ttmp->type = V_ASN1_INTEGER; + sk_push(ndsa, (char *)ttmp); + + p8->pkey->value.octet_string = ASN1_OCTET_STRING_new(); + + if (!ASN1_seq_pack(ndsa, i2d_ASN1_TYPE, + &p8->pkey->value.octet_string->data, + &p8->pkey->value.octet_string->length)) { + + EVPerr(EVP_F_EVP_PKEY2PKCS8,ERR_R_MALLOC_FAILURE); + sk_pop_free(ndsa, ASN1_TYPE_free); + M_ASN1_INTEGER_free(prkey); + return 0; + } + sk_pop_free(ndsa, ASN1_TYPE_free); + break; + + case PKCS8_EMBEDDED_PARAM: + + p8->pkeyalg->parameter->type = V_ASN1_NULL; + ndsa = sk_new_null(); + ttmp = ASN1_TYPE_new(); + ttmp->value.sequence = params; + ttmp->type = V_ASN1_SEQUENCE; + sk_push(ndsa, (char *)ttmp); + + ttmp = ASN1_TYPE_new(); + ttmp->value.integer = prkey; + ttmp->type = V_ASN1_INTEGER; + sk_push(ndsa, (char *)ttmp); + + p8->pkey->value.octet_string = ASN1_OCTET_STRING_new(); + + if (!ASN1_seq_pack(ndsa, i2d_ASN1_TYPE, + &p8->pkey->value.octet_string->data, + &p8->pkey->value.octet_string->length)) { + + EVPerr(EVP_F_EVP_PKEY2PKCS8,ERR_R_MALLOC_FAILURE); + sk_pop_free(ndsa, ASN1_TYPE_free); + M_ASN1_INTEGER_free (prkey); + return 0; + } + sk_pop_free(ndsa, ASN1_TYPE_free); + break; + } + return 1; +} +#endif diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index 3a02194401..3931710ae1 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -433,8 +433,10 @@ X509_ALGOR *prf; typedef struct pkcs8_priv_key_info_st { int broken; /* Flag for various broken formats */ -#define PKCS8_OK 0 -#define PKCS8_NO_OCTET 1 +#define PKCS8_OK 0 +#define PKCS8_NO_OCTET 1 +#define PKCS8_EMBEDDED_PARAM 2 +#define PKCS8_NS_DB 3 ASN1_INTEGER *version; X509_ALGOR *pkeyalg; ASN1_TYPE *pkey; /* Should be OCTET STRING but some are broken */ @@ -1105,6 +1107,7 @@ void PKCS8_PRIV_KEY_INFO_free(PKCS8_PRIV_KEY_INFO *a); EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *p8); PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey); +PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8_broken(EVP_PKEY *pkey, int broken); PKCS8_PRIV_KEY_INFO *PKCS8_set_broken(PKCS8_PRIV_KEY_INFO *p8, int broken); int X509_check_trust(X509 *x, int id, int flags); diff --git a/doc/apps/pkcs8.pod b/doc/apps/pkcs8.pod index a7d2932100..359eb6f898 100644 --- a/doc/apps/pkcs8.pod +++ b/doc/apps/pkcs8.pod @@ -19,6 +19,8 @@ B B [B<-noiter>] [B<-nocrypt>] [B<-nooct>] +[B<-embed>] +[B<-nsdb>] [B<-v2 alg>] [B<-v1 alg>] @@ -93,11 +95,24 @@ code signing software used unencrypted private keys. =item B<-nooct> -This option generates private keys in a broken format that some software +This option generates RSA private keys in a broken format that some software uses. Specifically the private key should be enclosed in a OCTET STRING but some software just includes the structure itself without the surrounding OCTET STRING. +=item B<-embed> + +This option generates DSA keys in a broken format. The DSA parameters are +embedded inside the PrivateKey structure. In this form the OCTET STRING +contains an ASN1 SEQUENCE consisting of two structures: a SEQUENCE containing +the parameters and an ASN1 INTEGER containing the private key. + +=item B<-nsdb> + +This option generates DSA keys in a broken format compatible with Netscape +private key databases. The PrivateKey contains a SEQUENCE consisting of +the public and private keys respectively. + =item B<-v2 alg> This option enables the use of PKCS#5 v2.0 algorithms. Normally PKCS#8 @@ -202,11 +217,16 @@ Convert a private key from any PKCS#8 format to traditional format: =head1 STANDARDS -Test vectors from this implementation were posted to the pkcs-tng mailing -list using triple DES, DES and RC2 with high iteration counts, several -people confirmed that they could decrypt the private keys produced and -Therefore it can be assumed that the PKCS#5 v2.0 implementation is -reasonably accurate at least as far as these algorithms are concerned. +Test vectors from this PKCS#5 v2.0 implementation were posted to the +pkcs-tng mailing list using triple DES, DES and RC2 with high iteration +counts, several people confirmed that they could decrypt the private +keys produced and Therefore it can be assumed that the PKCS#5 v2.0 +implementation is reasonably accurate at least as far as these +algorithms are concerned. + +The format of PKCS#8 DSA (and other) private keys is not well documented: +it is hidden away in PKCS#11 v2.01, section 11.9. OpenSSL's DSA private +key format complies with this standard. =head1 BUGS diff --git a/util/libeay.num b/util/libeay.num index 1d345d3e4b..580077f3bd 100755 --- a/util/libeay.num +++ b/util/libeay.num @@ -2216,3 +2216,4 @@ BN_is_prime_fasttest 2240 BN_CTX_end 2241 BN_CTX_start 2242 BN_CTX_get 2243 +EVP_PKEY2PKCS8_broken 2244 -- GitLab