diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2003-03-21 16:46:01 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2003-03-21 16:46:01 +0000 |
commit | 90e79a4ffdcb4833c91bc9312c5b7f7ec9abc00b (patch) | |
tree | 56cf459d1b4099c4a19cfb3dc33c3618ed5f73da | |
parent | 25613dd4ca6985ef0a8a403cad311b5ec644ae14 (diff) | |
download | gnutls-90e79a4ffdcb4833c91bc9312c5b7f7ec9abc00b.tar.gz |
Added ability to import PKCS8 encrypted keys.
-rw-r--r-- | lib/gnutls_global.c | 2 | ||||
-rw-r--r-- | lib/gnutls_mpi.c | 34 | ||||
-rw-r--r-- | lib/gnutls_mpi.h | 3 | ||||
-rw-r--r-- | lib/pkix.asn | 61 | ||||
-rw-r--r-- | lib/pkix_asn1_tab.c | 53 | ||||
-rw-r--r-- | lib/x509/Makefile.am | 7 | ||||
-rw-r--r-- | lib/x509/common.c | 7 | ||||
-rw-r--r-- | lib/x509/common.h | 7 | ||||
-rw-r--r-- | lib/x509/pkcs5.c | 202 | ||||
-rw-r--r-- | lib/x509/pkcs5.h | 60 | ||||
-rw-r--r-- | lib/x509/privkey.c | 12 | ||||
-rw-r--r-- | lib/x509/privkey.h | 4 | ||||
-rw-r--r-- | lib/x509/privkey_pkcs8.c | 610 |
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", ¶ms_start, ¶ms_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", ¶ms_start, ¶ms_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, ¶ms->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, ¶ms->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, ¶ms->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", ¶ms_start, ¶ms_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, ¶ms->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; +} |