aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/libstrongswan/asn1/asn1.c10
-rw-r--r--src/libstrongswan/asn1/oid.txt3
-rw-r--r--src/libstrongswan/plugins/pkcs8/pkcs8_builder.c252
3 files changed, 261 insertions, 4 deletions
diff --git a/src/libstrongswan/asn1/asn1.c b/src/libstrongswan/asn1/asn1.c
index 64ee6d160..4fac0a49c 100644
--- a/src/libstrongswan/asn1/asn1.c
+++ b/src/libstrongswan/asn1/asn1.c
@@ -556,10 +556,13 @@ static const asn1Object_t algorithmIdentifierObjects[] = {
{ 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */
{ 1, "parameters", ASN1_OID, ASN1_RAW|ASN1_OPT }, /* 2 */
{ 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
+ { 1, "parameters", ASN1_SEQUENCE, ASN1_RAW|ASN1_OPT }, /* 4 */
+ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 5 */
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
};
-#define ALGORITHM_ID_ALG 1
-#define ALGORITHM_ID_PARAMETERS 2
+#define ALGORITHM_ID_ALG 1
+#define ALGORITHM_ID_PARAMETERS_OID 2
+#define ALGORITHM_ID_PARAMETERS_SEQ 4
/*
* Defined in header
@@ -581,7 +584,8 @@ int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters
case ALGORITHM_ID_ALG:
alg = asn1_known_oid(object);
break;
- case ALGORITHM_ID_PARAMETERS:
+ case ALGORITHM_ID_PARAMETERS_OID:
+ case ALGORITHM_ID_PARAMETERS_SEQ:
if (parameters != NULL)
{
*parameters = object;
diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt
index f16287cb2..a9a616301 100644
--- a/src/libstrongswan/asn1/oid.txt
+++ b/src/libstrongswan/asn1/oid.txt
@@ -97,6 +97,9 @@
0x0C "sha384WithRSAEncryption" OID_SHA384_WITH_RSA
0x0D "sha512WithRSAEncryption" OID_SHA512_WITH_RSA
0x0E "sha224WithRSAEncryption" OID_SHA224_WITH_RSA
+ 0x05 "PKCS-5"
+ 0x03 "pbeWithMD5AndDES-CBC" OID_PBE_MD5_DES_CBC
+ 0x0A "pbeWithSHA1AndDES-CBC" OID_PBE_SHA1_DES_CBC
0x07 "PKCS-7"
0x01 "data" OID_PKCS7_DATA
0x02 "signedData" OID_PKCS7_SIGNED_DATA
diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
index a83dc307d..184179b5d 100644
--- a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
+++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
@@ -101,11 +101,255 @@ end:
}
/**
+ * Verify padding of decrypted blob.
+ * Length of blob is adjusted accordingly.
+ */
+static bool verify_padding(chunk_t *blob)
+{
+ u_int8_t padding, count;
+
+ padding = count = blob->ptr[blob->len - 1];
+ if (padding > 8)
+ {
+ return FALSE;
+ }
+ for (; blob->len && count; --blob->len, --count)
+ {
+ if (blob->ptr[blob->len - 1] != padding)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * PBKDF1 key derivation function
+ */
+static void pbkdf1(hasher_t *hasher, chunk_t password, chunk_t salt,
+ u_int64_t iterations, chunk_t key)
+{
+ chunk_t hash;
+ u_int64_t i;
+
+ hash = chunk_alloca(hasher->get_hash_size(hasher));
+ hasher->get_hash(hasher, password, NULL);
+ hasher->get_hash(hasher, salt, hash.ptr);
+
+ for (i = 1; i < iterations; i++)
+ {
+ hasher->get_hash(hasher, hash, hash.ptr);
+ }
+
+ memcpy(key.ptr, hash.ptr, key.len);
+}
+
+/**
+ * Decrypt an encrypted PKCS#8 encoded private key
+ */
+static private_key_t *decrypt_private_key(chunk_t blob,
+ encryption_algorithm_t encr, size_t key_size,
+ hash_algorithm_t hash, chunk_t salt,
+ u_int64_t iterations)
+{
+ enumerator_t *enumerator;
+ shared_key_t *shared;
+ private_key_t *private_key = NULL;
+ crypter_t *crypter = NULL;
+ hasher_t *hasher = NULL;
+ chunk_t keymat, key, iv;
+
+ hasher = lib->crypto->create_hasher(lib->crypto, hash);
+ if (!hasher)
+ {
+ DBG1(DBG_ASN, " %N hash algorithm not available",
+ hash_algorithm_names, hash);
+ goto end;
+ }
+ if (hasher->get_hash_size(hasher) < key_size)
+ {
+ goto end;
+ }
+
+ crypter = lib->crypto->create_crypter(lib->crypto, encr, key_size);
+ if (!crypter)
+ {
+ DBG1(DBG_ASN, " %N encryption algorithm not available",
+ encryption_algorithm_names, encr);
+ goto end;
+ }
+ if (blob.len % crypter->get_block_size(crypter))
+ {
+ DBG1(DBG_ASN, " data size is not a multiple of block size");
+ goto end;
+ }
+
+ keymat = chunk_alloca(key_size * 2);
+ key.len = key_size;
+ key.ptr = keymat.ptr;
+ iv.len = key_size;
+ iv.ptr = keymat.ptr + key_size;
+
+ enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
+ SHARED_PRIVATE_KEY_PASS, NULL, NULL);
+ while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
+ {
+ chunk_t decrypted;
+
+ pbkdf1(hasher, shared->get_key(shared), salt, iterations, keymat);
+
+ crypter->set_key(crypter, key);
+ crypter->decrypt(crypter, blob, iv, &decrypted);
+ if (verify_padding(&decrypted))
+ {
+ private_key = parse_private_key(decrypted);
+ if (private_key)
+ {
+ chunk_clear(&decrypted);
+ break;
+ }
+ }
+ chunk_free(&decrypted);
+ }
+ enumerator->destroy(enumerator);
+
+end:
+ DESTROY_IF(crypter);
+ DESTROY_IF(hasher);
+ return private_key;
+}
+
+/**
+ * ASN.1 definition of a PBEParameter structure
+ */
+static const asn1Object_t pbeParameterObjects[] = {
+ { 0, "PBEParameter", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */
+ { 1, "iterationCount", ASN1_INTEGER, ASN1_BODY }, /* 2 */
+ { 0, "exit", ASN1_EOC, ASN1_EXIT }
+};
+#define PBEPARAM_SALT 1
+#define PBEPARAM_ITERATION_COUNT 2
+
+/**
+ * Parse a PBEParameter structure
+ */
+static void parse_pbe_parameters(chunk_t blob, chunk_t *salt,
+ u_int64_t *iterations)
+{
+ asn1_parser_t *parser;
+ chunk_t object;
+ int objectID;
+
+ parser = asn1_parser_create(pbeParameterObjects, blob);
+
+ while (parser->iterate(parser, &objectID, &object))
+ {
+ switch (objectID)
+ {
+ case PBEPARAM_SALT:
+ {
+ *salt = object;
+ break;
+ }
+ case PBEPARAM_ITERATION_COUNT:
+ {
+ u_int64_t val = 0;
+ int i;
+
+ for (i = 0; i < object.len; i++)
+ { /* if it is longer than 8 bytes, we just use the 8 LSBs */
+ val <<= 8;
+ val |= (u_int64_t)object.ptr[i];
+ }
+ *iterations = val;
+ break;
+ }
+ }
+ }
+
+ parser->destroy(parser);
+}
+
+/**
+ * ASN.1 definition of an encryptedPrivateKeyInfo structure
+ */
+static const asn1Object_t encryptedPKIObjects[] = {
+ { 0, "encryptedPrivateKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "encryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 1 */
+ { 1, "encryptedData", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */
+ { 0, "exit", ASN1_EOC, ASN1_EXIT }
+};
+#define EPKINFO_ENCRYPTION_ALGORITHM 1
+#define EPKINFO_ENCRYPTED_DATA 2
+
+/**
+ * Load an encrypted private key from an ASN.1 encoded blob
+ * Schemes per PKCS#5 (RFC 2898), currently only a subset of PBES1 is supported
+ */
+static private_key_t *parse_encrypted_private_key(chunk_t blob)
+{
+ asn1_parser_t *parser;
+ chunk_t object, params = chunk_empty, salt;
+ u_int64_t iterations;
+ int objectID;
+ encryption_algorithm_t encr = ENCR_UNDEFINED;
+ hash_algorithm_t hash = HASH_UNKNOWN;
+ private_key_t *key = NULL;
+ size_t key_size;
+
+ parser = asn1_parser_create(encryptedPKIObjects, blob);
+
+ while (parser->iterate(parser, &objectID, &object))
+ {
+ switch (objectID)
+ {
+ case EPKINFO_ENCRYPTION_ALGORITHM:
+ {
+ int oid = asn1_parse_algorithmIdentifier(object,
+ parser->get_level(parser) + 1, &params);
+
+ switch (oid)
+ {
+ case OID_PBE_MD5_DES_CBC:
+ encr = ENCR_DES;
+ hash = HASH_MD5;
+ key_size = 8;
+ parse_pbe_parameters(params, &salt, &iterations);
+ break;
+ case OID_PBE_SHA1_DES_CBC:
+ encr = ENCR_DES;
+ hash = HASH_SHA1;
+ key_size = 8;
+ parse_pbe_parameters(params, &salt, &iterations);
+ break;
+ default:
+ /* encryption scheme not supported */
+ goto end;
+ }
+ break;
+ }
+ case EPKINFO_ENCRYPTED_DATA:
+ {
+ key = decrypt_private_key(object, encr, key_size, hash, salt,
+ iterations);
+ break;
+ }
+ }
+ }
+
+end:
+ parser->destroy(parser);
+ return key;
+}
+
+/**
* See header.
*/
private_key_t *pkcs8_private_key_load(key_type_t type, va_list args)
{
chunk_t blob = chunk_empty;
+ private_key_t *key;
while (TRUE)
{
@@ -121,6 +365,12 @@ private_key_t *pkcs8_private_key_load(key_type_t type, va_list args)
}
break;
}
- return parse_private_key(blob);
+ /* we don't know whether it is encrypted or not, try both ways */
+ key = parse_encrypted_private_key(blob);
+ if (!key)
+ {
+ key = parse_private_key(blob);
+ }
+ return key;
}