summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2003-03-21 16:46:01 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2003-03-21 16:46:01 +0000
commit90e79a4ffdcb4833c91bc9312c5b7f7ec9abc00b (patch)
tree56cf459d1b4099c4a19cfb3dc33c3618ed5f73da
parent25613dd4ca6985ef0a8a403cad311b5ec644ae14 (diff)
downloadgnutls-90e79a4ffdcb4833c91bc9312c5b7f7ec9abc00b.tar.gz
Added ability to import PKCS8 encrypted keys.
-rw-r--r--lib/gnutls_global.c2
-rw-r--r--lib/gnutls_mpi.c34
-rw-r--r--lib/gnutls_mpi.h3
-rw-r--r--lib/pkix.asn61
-rw-r--r--lib/pkix_asn1_tab.c53
-rw-r--r--lib/x509/Makefile.am7
-rw-r--r--lib/x509/common.c7
-rw-r--r--lib/x509/common.h7
-rw-r--r--lib/x509/pkcs5.c202
-rw-r--r--lib/x509/pkcs5.h60
-rw-r--r--lib/x509/privkey.c12
-rw-r--r--lib/x509/privkey.h4
-rw-r--r--lib/x509/privkey_pkcs8.c610
13 files changed, 1043 insertions, 19 deletions
diff --git a/lib/gnutls_global.c b/lib/gnutls_global.c
index f92b33ecd2..237d1b0df5 100644
--- a/lib/gnutls_global.c
+++ b/lib/gnutls_global.c
@@ -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;
diff --git a/lib/gnutls_mpi.c b/lib/gnutls_mpi.c
index 8e35a62636..8406a6fb21 100644
--- a/lib/gnutls_mpi.c
+++ b/lib/gnutls_mpi.c
@@ -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)
diff --git a/lib/gnutls_mpi.h b/lib/gnutls_mpi.h
index 4e0ab1ebf1..33f0836e44 100644
--- a/lib/gnutls_mpi.h
+++ b/lib/gnutls_mpi.h
@@ -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
diff --git a/lib/pkix.asn b/lib/pkix.asn
index a37f03edd1..626224ca3b 100644
--- a/lib/pkix.asn
+++ b/lib/pkix.asn
@@ -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
diff --git a/lib/pkix_asn1_tab.c b/lib/pkix_asn1_tab.c
index 9a010abca8..2316133a46 100644
--- a/lib/pkix_asn1_tab.c
+++ b/lib/pkix_asn1_tab.c
@@ -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}
};
diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am
index a128b33578..f8b48492e9 100644
--- a/lib/x509/Makefile.am
+++ b/lib/x509/Makefile.am
@@ -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)
diff --git a/lib/x509/common.c b/lib/x509/common.c
index 6eaded7e6a..b76fc49996 100644
--- a/lib/x509/common.c
+++ b/lib/x509/common.c
@@ -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},
diff --git a/lib/x509/common.h b/lib/x509/common.h
index 14dcdac7ef..6c484c7bd1 100644
--- a/lib/x509/common.h
+++ b/lib/x509/common.h
@@ -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
index 0000000000..ed819ae792
--- /dev/null
+++ b/lib/x509/pkcs5.c
@@ -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
index 0000000000..733ba34ac9
--- /dev/null
+++ b/lib/x509/pkcs5.h
@@ -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 */
diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c
index ad3971d236..4dc64b4ae8 100644
--- a/lib/x509/privkey.c
+++ b/lib/x509/privkey.c
@@ -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
index 0000000000..e1111c75f7
--- /dev/null
+++ b/lib/x509/privkey.h
@@ -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
index 0000000000..45abbb0e0d
--- /dev/null
+++ b/lib/x509/privkey_pkcs8.c
@@ -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;
+}