From: Dr. Stephen Henson <steve@openssl.org>
Date: Sat, 5 Feb 2000 21:07:56 +0000 (+0000)
Subject: Add support for some broken PKCS#8 formats.
X-Git-Tag: OpenSSL_0_9_5beta1~107
X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=66430207a4de2160082d84bb69feedde9cf7ac83;p=oweals%2Fopenssl.git

Add support for some broken PKCS#8 formats.
---

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 <openssl/x509.h>
 #include <openssl/rand.h>
 
+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<openssl> B<pkcs8>
 [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