]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added ability to import PKCS8 encrypted keys.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 21 Mar 2003 16:46:01 +0000 (16:46 +0000)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 21 Mar 2003 16:46:01 +0000 (16:46 +0000)
13 files changed:
lib/gnutls_global.c
lib/gnutls_mpi.c
lib/gnutls_mpi.h
lib/pkix.asn
lib/pkix_asn1_tab.c
lib/x509/Makefile.am
lib/x509/common.c
lib/x509/common.h
lib/x509/pkcs5.c [new file with mode: 0644]
lib/x509/pkcs5.h [new file with mode: 0644]
lib/x509/privkey.c
lib/x509/privkey.h [new file with mode: 0644]
lib/x509/privkey_pkcs8.c [new file with mode: 0644]

index f92b33ecd22ca3281aba7aa5c2c607159ba6ddb7..237d1b0df56f5d55f9a86976f21bd6c78474d2ff 100644 (file)
@@ -32,7 +32,7 @@ extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[];
 extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
 
 LOG_FUNC _gnutls_log_func;
-int _gnutls_log_level = 1; /* default log level */
+int _gnutls_log_level = 2; /* default log level */
 
 static ASN1_TYPE PKIX1_ASN;
 static ASN1_TYPE GNUTLS_ASN;
index 8e35a62636a17d45680ad39a87f62a4e9c442b86..8406a6fb2104766774ab2dfe993bfedeac88cc6b 100644 (file)
@@ -26,6 +26,7 @@
 #include <gnutls_int.h>
 #include <libtasn1.h>
 #include <gnutls_errors.h>
+#include <gnutls_num.h>
 
 /* Functions that refer to the libgcrypt library.
  */
@@ -90,7 +91,7 @@ int _gnutls_x509_read_int( ASN1_TYPE node, const char* value,
 int len, result;
 size_t s_len;
 
-       len = tmpstr_size - 1;
+       len = tmpstr_size;
        result = asn1_read_value( node, value, tmpstr, &len);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
@@ -106,6 +107,37 @@ size_t s_len;
        return 0;
 }
 
+/* this function reads a (small) unsigned integer
+ * from asn1 structs. Combines the read and the convertion
+ * steps.
+ */
+int _gnutls_x509_read_ui( ASN1_TYPE node, const char* value, 
+       opaque* tmpstr, int tmpstr_size, unsigned int* ret)
+{
+int len, result;
+
+       len = tmpstr_size;
+       result = asn1_read_value( node, value, tmpstr, &len);
+       if (result != ASN1_SUCCESS) {
+               return _gnutls_asn2err(result);
+       }
+
+       if (len == 1)
+               *ret = tmpstr[0];
+       else if (len == 2)
+               *ret = _gnutls_read_uint16(tmpstr);
+       else if (len == 3)
+               *ret = _gnutls_read_uint24(tmpstr);
+       else if (len == 4)
+               *ret = _gnutls_read_uint32(tmpstr);
+       else {
+               gnutls_assert();
+               return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+       }
+
+       return 0;
+}
+
 /* Writes the specified integer into the specified node.
  */
 int _gnutls_x509_write_int( ASN1_TYPE node, const char* value, GNUTLS_MPI mpi)
index 4e0ab1ebf13e762a34288cdf257c09fd64677d23..33f0836e446edee275bb790016f8a413d9104bc3 100644 (file)
@@ -39,4 +39,7 @@ int _gnutls_x509_read_int( ASN1_TYPE node, const char* value,
        char* tmpstr, int tmpstr_size, GNUTLS_MPI* ret_mpi);
 int _gnutls_x509_write_int( ASN1_TYPE node, const char* value, GNUTLS_MPI mpi);
 
+int _gnutls_x509_read_ui( ASN1_TYPE node, const char* value, 
+       opaque* tmpstr, int tmpstr_size, unsigned int* ret);
+
 #endif
index a37f03edd1e6d1d10f50ed480cc76bcec62527bf..626224ca3bfeb07762eb5d518eae8766883e6050 100644 (file)
@@ -484,6 +484,7 @@ X520countryName ::=     PrintableString (SIZE (2)) -- IS 3166 codes
 pkcs-9 OBJECT IDENTIFIER ::=
        { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
 
+
 emailAddress AttributeType      ::= { pkcs-9 1 }
 
 Pkcs9email ::= IA5String (SIZE (1..ub-emailaddress-length))
@@ -1010,4 +1011,64 @@ Pkcs9challengePassword        ::= CHOICE {
       utf8String            UTF8String (SIZE (1..pkcs-9-ub-challengePassword)) }
 
 
+-- PKCS #8 stuff
+
+-- Private-key information syntax
+
+PrivateKeyInfo ::= SEQUENCE {
+  version Version,
+  privateKeyAlgorithm AlgorithmIdentifier,
+  privateKey PrivateKey,
+  attributes [0] Attributes OPTIONAL }
+
+Version ::= INTEGER {v1(0)}
+
+PrivateKey ::= OCTET STRING
+
+Attributes ::= SET OF Attribute
+
+-- Encrypted private-key information syntax
+
+EncryptedPrivateKeyInfo ::= SEQUENCE {
+    encryptionAlgorithm AlgorithmIdentifier,
+    encryptedData EncryptedData 
+}
+
+EncryptedData ::= OCTET STRING
+
+-- PKCS #5 stuff
+
+pkcs-5 OBJECT IDENTIFIER ::=
+       { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 }
+
+pkcs-5-encryptionAlgorithm OBJECT IDENTIFIER ::=
+       { iso(1) member-body(2) us(840) rsadsi(113549) 3 }
+
+pkcs-5-des-EDE3-CBC OBJECT IDENTIFIER ::= {pkcs-5-encryptionAlgorithm 7}
+
+pkcs-5-des-EDE3-CBC-params ::= OCTET STRING (SIZE(8))
+
+pkcs-5-id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
+
+pkcs-5-PBES2-params ::= SEQUENCE {
+  keyDerivationFunc AlgorithmIdentifier,
+  encryptionScheme AlgorithmIdentifier }
+
+-- PBKDF2
+
+pkcs-5-id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12}
+
+-- pkcs-5-algid-hmacWithSHA1 AlgorithmIdentifier ::=
+--  {algorithm pkcs-5-id-hmacWithSHA1, parameters NULL : NULL}
+
+pkcs-5-PBKDF2-params ::= SEQUENCE {
+  salt CHOICE {
+    specified OCTET STRING,
+    otherSource AlgorithmIdentifier
+  },
+  iterationCount INTEGER (1..MAX),
+  keyLength INTEGER (1..MAX) OPTIONAL,
+  prf AlgorithmIdentifier OPTIONAL -- DEFAULT pkcs-5-algid-hmacWithSHA1 
+}
+
 END
index 9a010abca8fd9e4a89a8d815340b47eadb4c6568..2316133a46835e58de8df96c54d55a8e4f15ce30 100644 (file)
@@ -915,10 +915,61 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[]={
   {"challengePassword",1880096780,"AttributeType"},
   {0,1073741825,"pkcs-9"},
   {0,1,"7"},
-  {"Pkcs9challengePassword",536870930,0},
+  {"Pkcs9challengePassword",1610612754,0},
   {"printableString",1612709890,"PrintableString"},
   {"pkcs-9-ub-challengePassword",524298,"1"},
   {"utf8String",538968066,"UTF8String"},
   {"pkcs-9-ub-challengePassword",524298,"1"},
+  {"PrivateKeyInfo",1610612741,0},
+  {"version",1073741826,"Version"},
+  {"privateKeyAlgorithm",1073741826,"AlgorithmIdentifier"},
+  {"privateKey",1073741826,"PrivateKey"},
+  {"attributes",536895490,"Attributes"},
+  {0,4104,"0"},
+  {"Version",1610874883,0},
+  {"v1",1,"0"},
+  {"PrivateKey",1073741831,0},
+  {"Attributes",1610612751,0},
+  {0,2,"Attribute"},
+  {"EncryptedPrivateKeyInfo",1610612741,0},
+  {"encryptionAlgorithm",1073741826,"AlgorithmIdentifier"},
+  {"encryptedData",2,"EncryptedData"},
+  {"EncryptedData",1073741831,0},
+  {"pkcs-5",1879048204,0},
+  {"iso",1073741825,"1"},
+  {"member-body",1073741825,"2"},
+  {"us",1073741825,"840"},
+  {"rsadsi",1073741825,"113549"},
+  {"pkcs",1073741825,"1"},
+  {0,1,"5"},
+  {"pkcs-5-encryptionAlgorithm",1879048204,0},
+  {"iso",1073741825,"1"},
+  {"member-body",1073741825,"2"},
+  {"us",1073741825,"840"},
+  {"rsadsi",1073741825,"113549"},
+  {0,1,"3"},
+  {"pkcs-5-des-EDE3-CBC",1879048204,0},
+  {0,1073741825,"pkcs-5-encryptionAlgorithm"},
+  {0,1,"7"},
+  {"pkcs-5-des-EDE3-CBC-params",1612709895,0},
+  {0,1048586,"8"},
+  {"pkcs-5-id-PBES2",1879048204,0},
+  {0,1073741825,"pkcs-5"},
+  {0,1,"13"},
+  {"pkcs-5-PBES2-params",1610612741,0},
+  {"keyDerivationFunc",1073741826,"AlgorithmIdentifier"},
+  {"encryptionScheme",2,"AlgorithmIdentifier"},
+  {"pkcs-5-id-PBKDF2",1879048204,0},
+  {0,1073741825,"pkcs-5"},
+  {0,1,"12"},
+  {"pkcs-5-PBKDF2-params",536870917,0},
+  {"salt",1610612754,0},
+  {"specified",1073741831,0},
+  {"otherSource",2,"AlgorithmIdentifier"},
+  {"iterationCount",1611137027,0},
+  {"1",10,"MAX"},
+  {"keyLength",1611153411,0},
+  {"1",10,"MAX"},
+  {"prf",16386,"AlgorithmIdentifier"},
   {0,0,0}
 };
index a128b335785cfb5a833689cbcc901a1d518e90bb..f8b48492e9f8578107c4de4405d0f3118a47a543 100644 (file)
@@ -1,6 +1,7 @@
 INCLUDES = -I../ -I../minitasn1/ -I../../includes/
 EXTRA_DIST = dn.h common.h x509.h extensions.h pkcs7.h \
-       x509-api.tex compat.h verify.h mpi.h crq.h sign.h
+       x509-api.tex compat.h verify.h mpi.h crq.h sign.h pkcs5.h \
+       privkey.h
 
 noinst_LTLIBRARIES = libx509.la
 lib_LTLIBRARIES = libgnutls-x509.la
@@ -9,7 +10,7 @@ COBJECTS1 = crl.c dn.c common.c x509.c extensions.c \
        rfc2818_hostname.c verify.c mpi.c privkey.c pkcs7.c
 
 # Extra stuff that will only be put in gnutls-x509 library.
-COBJECTS2 = crq.c xml.c sign.c
+COBJECTS2 = crq.c xml.c sign.c pkcs5.c privkey_pkcs8.c
 
 COMPAT_OBJECTS = compat.c
 
@@ -18,7 +19,7 @@ libx509_la_SOURCES = $(COBJECTS1) $(COMPAT_OBJECTS)
 
 libgnutls_x509_la_SOURCES = $(COBJECTS2)
 
-libgnutls_extra_la_LIBADD = ../lib/libgnutls.la
+libgnutls_x509_la_LIBADD = ../libgnutls.la
 libgnutls_x509_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
 
 x509-api.tex: $(COBJECTS1) $(COBJECTS2)
index 6eaded7e6aa3f1d6e990d550e56eb83be91e484f..b76fc49996cdb3b899cec815cfa4629bb73d9758 100644 (file)
@@ -37,13 +37,6 @@ typedef struct _oid2string {
        int printable;
 } oid2string;
 
-#define PKIX1_RSA_OID "1.2.840.113549.1.1.1"
-#define DSA_OID "1.2.840.10040.4.1"
-
-#define DSA_SHA1_OID "1.2.840.10040.4.3"
-#define RSA_MD5_OID "1.2.840.113549.1.1.4"
-#define RSA_SHA1_OID "1.2.840.113549.1.1.5"
-
 static const oid2string OID2STR[] = {
        {"2.5.4.6", "X520countryName", "C", 0, 1},
        {"2.5.4.12", "X520title", "T", 1, 1},
index 14dcdac7ef37d3672f469ecfc2b87a6c19b6245b..6c484c7bd1ec6a739f6f586a5daf164155fd4c5c 100644 (file)
@@ -7,6 +7,13 @@ void _gnutls_int2str(unsigned int k, char *data);
 #define PEM_X509_CERT2 "CERTIFICATE"
 #define PEM_PKCS7 "PKCS7"
 
+#define PKIX1_RSA_OID "1.2.840.113549.1.1.1"
+#define DSA_OID "1.2.840.10040.4.1"
+
+#define DSA_SHA1_OID "1.2.840.10040.4.3"
+#define RSA_MD5_OID "1.2.840.113549.1.1.4"
+#define RSA_SHA1_OID "1.2.840.113549.1.1.5"
+
 time_t _gnutls_x509_utcTime2gtime(char *ttime);
 time_t _gnutls_x509_generalTime2gtime(char *ttime);
 
diff --git a/lib/x509/pkcs5.c b/lib/x509/pkcs5.c
new file mode 100644 (file)
index 0000000..ed819ae
--- /dev/null
@@ -0,0 +1,202 @@
+/* pkcs5.c     Implementation of Password-Based Cryptography as per PKCS#5
+ * Copyright (C) 2002  Simon Josefsson
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/* XXX what about namespace? */
+
+#include <gcrypt.h>
+#include "pkcs5.h"
+
+/*
+ * 5.2 PBKDF2
+ *
+ *  PBKDF2 applies a pseudorandom function (see Appendix B.1 for an
+ *  example) to derive keys. The length of the derived key is essentially
+ *  unbounded. (However, the maximum effective search space for the
+ *  derived key may be limited by the structure of the underlying
+ *  pseudorandom function. See Appendix B.1 for further discussion.)
+ *  PBKDF2 is recommended for new applications.
+ *
+ *  PBKDF2 (P, S, c, dkLen)
+ *
+ *  Options:        PRF        underlying pseudorandom function (hLen
+ *                             denotes the length in octets of the
+ *                             pseudorandom function output)
+ *
+ *  Input:          P          password, an octet string
+ *                  S          salt, an octet string
+ *                  c          iteration count, a positive integer
+ *                  dkLen      intended length in octets of the derived
+ *                             key, a positive integer, at most
+ *                             (2^32 - 1) * hLen
+ *
+ *  Output:         DK         derived key, a dkLen-octet string
+ */
+
+#define MAX_PRF_BLOCK_LEN 80
+
+int
+_gnutls_pkcs5_pbkdf2 (int PRF,
+       const char *P,
+       size_t Plen,
+       const char *S,
+       size_t Slen, unsigned int c, unsigned int dkLen, char *DK)
+{
+  GCRY_MD_HD prf;
+  char U[MAX_PRF_BLOCK_LEN];
+  char T[MAX_PRF_BLOCK_LEN];
+  unsigned int u;
+  unsigned int hLen = gcry_md_get_algo_dlen (PRF);
+  unsigned int l;
+  unsigned int r;
+  unsigned int t;
+  int rc;
+  unsigned char *p;
+  int i;
+  int k;
+
+  if (hLen == 0 || hLen > MAX_PRF_BLOCK_LEN)
+    return PKCS5_INVALID_PRF;
+
+  if (c == 0)
+    return PKCS5_INVALID_ITERATION_COUNT;
+
+  if (dkLen == 0)
+    return PKCS5_INVALID_DERIVED_KEY_LENGTH;
+
+  /*
+   *
+   *  Steps:
+   *
+   *     1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and
+   *        stop.
+   */
+
+  if (dkLen > 4294967295U)
+    return PKCS5_DERIVED_KEY_TOO_LONG;
+
+  /*
+   *     2. Let l be the number of hLen-octet blocks in the derived key,
+   *        rounding up, and let r be the number of octets in the last
+   *        block:
+   *
+   *                  l = CEIL (dkLen / hLen) ,
+   *                  r = dkLen - (l - 1) * hLen .
+   *
+   *        Here, CEIL (x) is the "ceiling" function, i.e. the smallest
+   *        integer greater than, or equal to, x.
+   */
+
+  l = dkLen / hLen;
+  if (dkLen % hLen)
+    l++;
+  r = dkLen - (l - 1) * hLen;
+
+  /*
+   *     3. For each block of the derived key apply the function F defined
+   *        below to the password P, the salt S, the iteration count c, and
+   *        the block index to compute the block:
+   *
+   *                  T_1 = F (P, S, c, 1) ,
+   *                  T_2 = F (P, S, c, 2) ,
+   *                  ...
+   *                  T_l = F (P, S, c, l) ,
+   *
+   *        where the function F is defined as the exclusive-or sum of the
+   *        first c iterates of the underlying pseudorandom function PRF
+   *        applied to the password P and the concatenation of the salt S
+   *        and the block index i:
+   *
+   *                  F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
+   *
+   *        where
+   *
+   *                  U_1 = PRF (P, S || INT (i)) ,
+   *                  U_2 = PRF (P, U_1) ,
+   *                  ...
+   *                  U_c = PRF (P, U_{c-1}) .
+   *
+   *        Here, INT (i) is a four-octet encoding of the integer i, most
+   *        significant octet first.
+   *
+   *     4. Concatenate the blocks and extract the first dkLen octets to
+   *        produce a derived key DK:
+   *
+   *                  DK = T_1 || T_2 ||  ...  || T_l<0..r-1>
+   *
+   *     5. Output the derived key DK.
+   *
+   *  Note. The construction of the function F follows a "belt-and-
+   *  suspenders" approach. The iterates U_i are computed recursively to
+   *  remove a degree of parallelism from an opponent; they are exclusive-
+   *  ored together to reduce concerns about the recursion degenerating
+   *  into a small set of values.
+   *
+   */
+
+  prf = gcry_md_open (PRF, GCRY_MD_FLAG_HMAC);
+  if (prf == NULL)
+    return PKCS5_INVALID_PRF;
+
+  for (i = 1; i <= l; i++)
+    {
+      memset (T, 0, hLen);
+
+      for (u = 1; u <= c; u++)
+       {
+         int Ulen;
+
+         gcry_md_reset (prf);
+
+         rc = gcry_md_setkey (prf, P, Plen);
+         if (rc != GCRYERR_SUCCESS)
+           return PKCS5_INVALID_PRF;
+
+         if (u == 1)
+           {
+             char tmp[4];
+             memcpy (U, S, Slen);
+             tmp[0] = (i & 0xff000000) >> 24;
+             tmp[1] = (i & 0x00ff0000) >> 16;
+             tmp[2] = (i & 0x0000ff00) >> 8;
+             tmp[3] = (i & 0x000000ff) >> 0;
+             memcpy (U + Slen, tmp, 4);
+             Ulen = Slen + 4;
+           }
+         else
+           Ulen = hLen;
+
+         gcry_md_write (prf, U, Ulen);
+
+         p = gcry_md_read (prf, PRF);
+         if (p == NULL)
+           return PKCS5_INVALID_PRF;
+
+         memcpy (U, p, hLen);
+
+         for (k = 0; k < hLen; k++)
+           T[k] ^= U[k];
+       }
+
+      memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen);
+    }
+
+  gcry_md_close (prf);
+
+  return PKCS5_OK;
+}
diff --git a/lib/x509/pkcs5.h b/lib/x509/pkcs5.h
new file mode 100644 (file)
index 0000000..733ba34
--- /dev/null
@@ -0,0 +1,60 @@
+/* pkcs5.h     header file for pkcs5 functions                       -*- c -*-
+ * Copyright (C) 2002  Simon Josefsson
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PKCS5_H
+#define PKCS5_H
+
+/* This should be discarded as soon as this functionality moved
+ * to libgcrypt.
+ */
+
+/* PRF types */
+enum
+{
+  /* XXX must be synchronized with libgcrypt */
+  PKCS5_PRF_MD5 = 1,
+  PKCS5_PRF_SHA1 = 2,
+  PKCS5_PRF_RMD160 = 3,
+  PKCS5_PRF_MD2 = 5,
+  PKCS5_PRF_TIGER = 6,
+  PKCS5_PRF_HAVAL = 7,
+  PKCS5_PRF_SHA256 = 8,
+  PKCS5_PRF_SHA384 = 9,
+  PKCS5_PRF_SHA512 = 10,
+  PKCS5_PRF_MD4 = 11
+};
+
+/* Error codes */
+enum
+{
+  PKCS5_OK = 0,
+  PKCS5_INVALID_PRF,
+  PKCS5_INVALID_ITERATION_COUNT,
+  PKCS5_INVALID_DERIVED_KEY_LENGTH,
+  PKCS5_DERIVED_KEY_TOO_LONG
+};
+
+extern int
+_gnutls_pkcs5_pbkdf2 (int PRF,
+       const char *P,
+       size_t Plen,
+       const char *S,
+       size_t Slen, unsigned int c, unsigned int dkLen, char *DK);
+
+#endif /* PKCS5_H */
index ad3971d2363ad85edcb2f7fba9077a9b705459a7..4dc64b4ae884cbb903009d9e4f21ea45f848a129 100644 (file)
@@ -77,7 +77,7 @@ int i;
 /* Converts an RSA PKCS#1 key to
  * an internal structure (gnutls_private_key)
  */
-static ASN1_TYPE decode_pkcs1_rsa_key( const gnutls_datum *raw_key, 
+ASN1_TYPE _gnutls_privkey_decode_pkcs1_rsa_key( const gnutls_datum *raw_key, 
        gnutls_x509_privkey pkey) 
 {
        int result;
@@ -259,7 +259,7 @@ static ASN1_TYPE decode_dsa_key( const gnutls_datum* raw_key,
 #define PEM_KEY_RSA "RSA PRIVATE KEY"
 
 /**
-  * gnutls_x509_privkey_import - This function will import a DER or PEM encoded Certificate
+  * gnutls_x509_privkey_import - This function will import a DER or PEM encoded key
   * @key: The structure to store the parsed key
   * @data: The DER or PEM encoded certificate.
   * @format: One of DER or PEM
@@ -267,8 +267,8 @@ static ASN1_TYPE decode_dsa_key( const gnutls_datum* raw_key,
   * This function will convert the given DER or PEM encoded key
   * to the native gnutls_x509_privkey format. The output will be stored in 'key'.
   *
-  * If the Certificate is PEM encoded it should have a header of "X509 CERTIFICATE", or
-  * "CERTIFICATE".
+  * If the key is PEM encoded it should have a header of "RSA PRIVATE KEY", or
+  * "DSA PRIVATE KEY".
   *
   * Returns 0 on success.
   *
@@ -311,7 +311,7 @@ int gnutls_x509_privkey_import(gnutls_x509_privkey key, const gnutls_datum * dat
        }
 
        if (key->pk_algorithm == GNUTLS_PK_RSA) {
-               key->key = decode_pkcs1_rsa_key( &_data, key);
+               key->key = _gnutls_privkey_decode_pkcs1_rsa_key( &_data, key);
                if (key->key == NULL) {
                        gnutls_assert();
                        result = GNUTLS_E_ASN1_DER_ERROR;
@@ -333,7 +333,7 @@ int gnutls_x509_privkey_import(gnutls_x509_privkey key, const gnutls_datum * dat
 
                if (key->key == NULL) {
                        key->pk_algorithm = GNUTLS_PK_RSA;
-                       key->key = decode_pkcs1_rsa_key( &_data, key);
+                       key->key = _gnutls_privkey_decode_pkcs1_rsa_key( &_data, key);
                        if (key->key == NULL) {
                                gnutls_assert();
                                result = GNUTLS_E_ASN1_DER_ERROR;
diff --git a/lib/x509/privkey.h b/lib/x509/privkey.h
new file mode 100644 (file)
index 0000000..e1111c7
--- /dev/null
@@ -0,0 +1,4 @@
+int gnutls_x509_privkey_import(gnutls_x509_privkey key, const gnutls_datum * data,
+       gnutls_x509_crt_fmt format);
+ASN1_TYPE _gnutls_privkey_decode_pkcs1_rsa_key( const gnutls_datum *raw_key,
+        gnutls_x509_privkey pkey);
diff --git a/lib/x509/privkey_pkcs8.c b/lib/x509/privkey_pkcs8.c
new file mode 100644 (file)
index 0000000..45abbb0
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ *  Copyright (C) 2003 Nikos Mavroyanopoulos
+ *
+ *  This file is part of GNUTLS.
+ *
+ *  The GNUTLS library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public   
+ *  License as published by the Free Software Foundation; either 
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_datum.h>
+#include <gnutls_global.h>
+#include <gnutls_errors.h>
+#include <gnutls_rsa_export.h>
+#include <common.h>
+#include <gnutls_x509.h>
+#include <x509_b64.h>
+#include <x509.h>
+#include <dn.h>
+#include <pkcs5.h>
+#include <privkey.h>
+#include <extensions.h>
+#include <gnutls_algorithms.h>
+
+struct pbkdf2_params {
+       opaque salt[32];
+       int salt_size;
+       int iter_count;
+       int key_size;
+};
+
+struct pbe_enc_params {
+       opaque iv[8];
+       int iv_size;
+};
+
+static int read_pbkdf2_params( ASN1_TYPE pbes2_asn, const gnutls_datum* der, 
+       struct pbkdf2_params* params);
+static int read_pbe_enc_params( ASN1_TYPE pbes2_asn, const gnutls_datum* der, 
+       struct pbe_enc_params* params);
+static int decrypt_data( ASN1_TYPE pkcs8_asn, char* password,
+       const struct pbkdf2_params* kdf_params, const struct pbe_enc_params *enc_params, 
+       gnutls_datum* decrypted_data);
+static ASN1_TYPE decode_private_key_info( const gnutls_datum* der, gnutls_x509_privkey pkey);
+
+#define PEM_PKCS8 "ENCRYPTED PRIVATE KEY"
+
+/**
+  * gnutls_x509_privkey_export_pkcs8 - This function will export the private key to PKCS8 format
+  * @key: Holds the key
+  * @format: the format of output params. One of PEM or DER.
+  * @output_data: will contain a private key PEM or DER encoded
+  * @output_data_size: holds the size of output_data (and will be replaced by the actual size of parameters)
+  *
+  * This function will export the private key to a PKCS8 structure.
+  *
+  * If the buffer provided is not long enough to hold the output, then
+  * GNUTLS_E_SHORT_MEMORY_BUFFER will be returned.
+  *
+  * If the structure is PEM encoded, it will have a header
+  * of "BEGIN ENCRYPTED PRIVATE KEY".
+  *
+  * In case of failure a negative value will be returned, and
+  * 0 on success.
+  *
+  **/
+int gnutls_x509_privkey_export( gnutls_x509_privkey key,
+       gnutls_x509_crt_fmt format, unsigned char* output_data, int* output_data_size)
+{
+// NOT YET READY
+       return _gnutls_x509_export_int( key->key, format, PEM_PKCS8, *output_data_size,
+               output_data, output_data_size);
+}
+
+
+#define PBES2_OID "1.2.840.113549.1.5.13"
+#define PBKDF2_OID "1.2.840.113549.1.5.12"
+#define DES_EDE3_CBC_OID "1.2.840.113549.3.7"
+
+/* oid_pbeWithSHAAnd3_KeyTripleDES_CBC */
+#define PBE_3DES_SHA1_OID "1.2.840.113549.1.12.1.3"
+
+/* Converts a PKCS#8 key to
+ * an internal structure (gnutls_private_key)
+ */
+static ASN1_TYPE decode_pkcs8_key( const gnutls_datum *raw_key, 
+       char* password, gnutls_x509_privkey pkey)
+{
+       int result, len;
+       opaque enc_oid[64];
+       gnutls_datum tmp;
+       ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY, pkcs8_asn = ASN1_TYPE_EMPTY;
+       ASN1_TYPE ret_asn;
+       int params_start, params_end, params_len;
+       struct pbkdf2_params kdf_params;
+       struct pbe_enc_params enc_params;
+
+
+       if ((result =
+            asn1_create_element(_gnutls_get_pkix(),
+                                  "PKIX1.EncryptedPrivateKeyInfo", &pkcs8_asn
+                                  )) != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = asn1_der_decoding(&pkcs8_asn, raw_key->data, raw_key->size, NULL);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+
+       /* Check the encryption schema OID
+        */
+       len = sizeof(enc_oid);
+       result = asn1_read_value( pkcs8_asn, "encryptionAlgorithm.algorithm", enc_oid, &len);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+
+       /* we only support PBES2 
+        */
+       if (strcmp( enc_oid, PBES2_OID) != 0) {
+               gnutls_assert();
+               _gnutls_x509_log( "PKCS #8 encryption schema OID '%s' is unsupported.\n", enc_oid);
+               goto error;
+       }
+
+       /* Get the DER encoding of the parameters.
+        */
+       result = asn1_der_decoding_startEnd( pkcs8_asn, raw_key->data, raw_key->size,
+               "encryptionAlgorithm.parameters", &params_start, &params_end);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+       params_len = params_end - params_start + 1;
+
+       /* Now check the key derivation and the encryption
+        * functions.
+        */
+       if ((result =
+            asn1_create_element(_gnutls_get_pkix(),
+                                  "PKIX1.pkcs-5-PBES2-params", &pbes2_asn
+                                  )) != ASN1_SUCCESS) {
+               gnutls_assert();
+               return NULL;
+       }
+
+       /* Decode the parameters.
+        */
+       result = asn1_der_decoding(&pbes2_asn, &raw_key->data[params_start], params_len, NULL);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+
+       tmp.data = &raw_key->data[params_start];
+       tmp.size = params_len;
+
+       result = read_pbkdf2_params( pbes2_asn, &tmp, &kdf_params);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = read_pbe_enc_params( pbes2_asn, &tmp, &enc_params);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+       
+       asn1_delete_structure( &pbes2_asn);
+
+
+       /* Parameters have been decoded. Now
+        * decrypt the EncryptedData.
+        */
+       result = decrypt_data( pkcs8_asn, password, &kdf_params, &enc_params, &tmp);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       asn1_delete_structure(&pkcs8_asn);
+
+       ret_asn = decode_private_key_info( &tmp, pkey);
+       _gnutls_free_datum( &tmp);
+
+       return ret_asn;
+
+       error:
+               asn1_delete_structure(&pbes2_asn);
+               asn1_delete_structure(&pkcs8_asn);
+               return NULL;
+}
+
+static ASN1_TYPE decode_private_key_info( const gnutls_datum* der, gnutls_x509_privkey pkey)
+{
+       int result, len;
+       opaque oid[64], *data = NULL;
+       gnutls_datum tmp;
+       ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY;
+       ASN1_TYPE ret_asn;
+       int data_size;
+
+
+       if ((result =
+            asn1_create_element(_gnutls_get_pkix(),
+                                  "PKIX1.PrivateKeyInfo", &pkcs8_asn
+                                  )) != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = asn1_der_decoding(&pkcs8_asn, der->data, der->size, NULL);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+
+       /* Check the private key algorithm OID
+        */
+       len = sizeof(oid);
+       result = asn1_read_value( pkcs8_asn, "privateKeyAlgorithm.algorithm", oid, &len);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+
+       /* we only support RSA private keys.
+        */
+       if (strcmp( oid, PKIX1_RSA_OID) != 0) {
+               gnutls_assert();
+               _gnutls_x509_log( "PKCS #8 private key OID '%s' is unsupported.\n", oid);
+               goto error;
+       }
+
+       /* Get the DER encoding of the actual private key.
+        */
+       data_size = 0;
+       result = asn1_read_value( pkcs8_asn, "privateKey", NULL, &data_size);
+       if (result != ASN1_MEM_ERROR) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       data = gnutls_alloca( data_size);
+       if (data == NULL) {
+               gnutls_assert();
+               result = GNUTLS_E_MEMORY_ERROR;
+               goto error;
+       }
+
+       result = asn1_read_value( pkcs8_asn, "privateKey", data, &data_size);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       asn1_delete_structure( &pkcs8_asn);
+       
+       tmp.data = data;
+       tmp.size = data_size;
+       
+       pkey->pk_algorithm = GNUTLS_PK_RSA;
+
+       ret_asn = _gnutls_privkey_decode_pkcs1_rsa_key( &tmp, pkey);
+       if (ret_asn==NULL) {
+               gnutls_assert();
+       }
+       
+       return ret_asn;
+
+       error:
+               asn1_delete_structure( &pkcs8_asn);
+               if (data != NULL) { gnutls_afree(data); }
+               return NULL;
+
+}
+
+/**
+  * gnutls_x509_privkey_import_pkcs8 - This function will import a DER or PEM PKCS8 encoded key
+  * @key: The structure to store the parsed key
+  * @data: The DER or PEM encoded certificate.
+  * @format: One of DER or PEM
+  * @pass: the password to decode
+  *
+  * This function will convert the given DER or PEM encoded PKCS8 encrypted key
+  * to the native gnutls_x509_privkey format. The output will be stored in 'key'.
+  *
+  * If the Certificate is PEM encoded it should have a header of "ENCRYPTED PRIVATE KEY".
+  *
+  * Returns 0 on success.
+  *
+  **/
+int gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey key, const gnutls_datum * data,
+       gnutls_x509_crt_fmt format, char * pass)
+{
+       int result = 0, need_free = 0;
+       gnutls_datum _data = { data->data, data->size };
+
+       key->pk_algorithm = GNUTLS_PK_UNKNOWN;
+
+       /* If the Certificate is in PEM format then decode it
+        */
+       if (format == GNUTLS_X509_FMT_PEM) {
+               opaque *out;
+               
+               /* Try the first header */
+               result = _gnutls_fbase64_decode(PEM_PKCS8, data->data, data->size,
+                       &out);
+
+               if (result <= 0) {
+                       if (result==0) result = GNUTLS_E_INTERNAL_ERROR;
+                       gnutls_assert();
+                       return result;
+               }
+               
+               _data.data = out;
+               _data.size = result;
+               
+               need_free = 1;
+       }
+
+
+       key->key = decode_pkcs8_key( &_data, pass, key);
+       if (key->key == NULL) {
+               gnutls_assert();
+               result = GNUTLS_E_ASN1_DER_ERROR;
+               goto cleanup;
+       }
+
+       if (need_free) _gnutls_free_datum( &_data);
+
+       /* The key has now been decoded.
+        */
+
+       return 0;
+
+      cleanup:
+       key->pk_algorithm = GNUTLS_PK_UNKNOWN;
+       if (need_free) _gnutls_free_datum( &_data);
+       return result;
+}
+
+/* Reads the PBKDF2 parameters.
+ */
+static int read_pbkdf2_params( ASN1_TYPE pbes2_asn, const gnutls_datum* der, 
+       struct pbkdf2_params* params)
+{
+int params_start, params_end;
+int params_len, len, result;
+ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY;
+char oid[64];
+
+       memset( params, 0, sizeof(params));
+
+       /* Check the key derivation algorithm
+        */
+       len = sizeof(oid);
+       result = asn1_read_value( pbes2_asn, "keyDerivationFunc.algorithm", oid, &len);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+       
+       if (strcmp( oid, PBKDF2_OID) != 0) {
+               gnutls_assert();
+               _gnutls_x509_log( "PKCS #8 key derivation OID '%s' is unsupported.\n", oid);
+               return _gnutls_asn2err(result);
+       }
+
+       result = asn1_der_decoding_startEnd( pbes2_asn, der->data, der->size,
+               "keyDerivationFunc.parameters", &params_start, &params_end);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+       params_len = params_end - params_start + 1;
+
+       /* Now check the key derivation and the encryption
+        * functions.
+        */
+       if ((result =
+            asn1_create_element(_gnutls_get_pkix(),
+                                  "PKIX1.pkcs-5-PBKDF2-params", &pbkdf2_asn
+                                  )) != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+       
+       result = asn1_der_decoding(&pbkdf2_asn, &der->data[params_start], params_len, NULL);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       /* read the salt */
+       params->salt_size = sizeof(params->salt);
+       result = asn1_read_value( pbkdf2_asn, "salt.specified", params->salt, &params->salt_size);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       /* read the iteration count 
+        */
+       len = sizeof(oid);
+       result = _gnutls_x509_read_ui( pbkdf2_asn, "iterationCount", oid, len, &params->iter_count);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+
+
+       /* read the keylength, if it is set.
+        */
+       len = sizeof(oid);
+
+       result = _gnutls_x509_read_ui( pbkdf2_asn, "keyLength", oid, len, &params->key_size);
+       if (result < 0) {
+               params->key_size = 0;
+       }
+       
+       /* We don't read the PRF. We only use the default.
+        */
+
+       return 0;
+
+       error:
+               asn1_delete_structure( &pbkdf2_asn);
+               return result;
+
+}
+
+static int read_pbe_enc_params( ASN1_TYPE pbes2_asn, const gnutls_datum* der, 
+       struct pbe_enc_params* params)
+{
+int params_start, params_end;
+int params_len, len, result;
+ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY;
+char oid[64];
+
+       memset( params, 0, sizeof(params));
+
+       /* Check the encryption algorithm
+        */
+       len = sizeof(oid);
+       result = asn1_read_value( pbes2_asn, "encryptionScheme.algorithm", oid, &len);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+       
+       if (strcmp( oid, DES_EDE3_CBC_OID) != 0) {
+               gnutls_assert();
+               _gnutls_x509_log( "PKCS #8 encryption OID '%s' is unsupported.\n", oid);
+               goto error;
+       }
+
+       result = asn1_der_decoding_startEnd( pbes2_asn, der->data, der->size,
+               "encryptionScheme.parameters", &params_start, &params_end);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+       params_len = params_end - params_start + 1;
+
+       /* Now check the encryption parameters.
+        */
+       if ((result =
+            asn1_create_element(_gnutls_get_pkix(),
+                                  "PKIX1.pkcs-5-des-EDE3-CBC-params", &pbe_asn
+                                  )) != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+       
+       result = asn1_der_decoding(&pbe_asn, &der->data[params_start], params_len, NULL);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       /* read the salt */
+       params->iv_size = sizeof(params->iv);
+       result = asn1_read_value( pbe_asn, "", params->iv, &params->iv_size);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       return 0;
+
+       error:
+               asn1_delete_structure( &pbe_asn);
+               return result;
+
+}
+
+static int decrypt_data( ASN1_TYPE pkcs8_asn,  char* password,
+       const struct pbkdf2_params *kdf_params, const struct pbe_enc_params *enc_params, 
+       gnutls_datum* decrypted_data)
+{
+int result;
+int data_size;
+opaque* data, *key;
+gnutls_datum dkey, div;
+GNUTLS_CIPHER_HANDLE ch = NULL;
+int key_size;
+
+       data_size = 0;
+       result = asn1_read_value( pkcs8_asn, "encryptedData", NULL, &data_size);
+       if (result != ASN1_MEM_ERROR) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+
+       data = gnutls_malloc( data_size);
+       if (data == NULL) {
+               gnutls_assert();
+               return GNUTLS_E_MEMORY_ERROR;
+       }
+
+       result = asn1_read_value( pkcs8_asn, "encryptedData", data, &data_size);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       if (kdf_params->key_size == 0) {
+               key_size = gnutls_cipher_get_key_size( GNUTLS_CIPHER_3DES_CBC);
+       } else key_size = kdf_params->key_size;
+
+       key = gnutls_alloca( key_size);
+       if ( key == NULL) {
+               gnutls_assert();
+               result = GNUTLS_E_MEMORY_ERROR;
+               goto error;
+       }
+
+       /* generate the key
+        */
+       result = _gnutls_pkcs5_pbkdf2( PKCS5_PRF_SHA1, password, strlen(password),
+               kdf_params->salt, kdf_params->salt_size, kdf_params->iter_count,
+               key_size, key);
+       
+       if (result != PKCS5_OK) {
+               gnutls_assert();
+               result = GNUTLS_E_DECRYPTION_FAILED;
+               goto error;
+       }
+
+       /* do the decryption.
+        */
+       dkey.data = key;
+       dkey.size = key_size;
+       
+       div.data = (opaque*) enc_params->iv;
+       div.size = enc_params->iv_size;
+       ch = _gnutls_cipher_init( GNUTLS_CIPHER_3DES_CBC, dkey, div);
+
+       gnutls_afree( key);
+
+       if (ch == NULL) {
+               gnutls_assert();
+               result = GNUTLS_E_DECRYPTION_FAILED;
+               goto error;
+       }
+       
+       result = _gnutls_cipher_decrypt( ch, data, data_size);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       decrypted_data->data = data;
+       decrypted_data->size = data_size - data[data_size-1];
+
+       _gnutls_cipher_deinit( ch);
+
+       return 0;
+
+       error:
+               gnutls_free(data);
+               if (ch!=NULL)
+                       _gnutls_cipher_deinit( ch);
+               return result;
+}