Added support for PKCS#5 v2 schemes when decrypting PKCS#8 files.
This commit is contained in:
parent
1f2e036b3e
commit
fd1ff46f61
|
@ -552,17 +552,20 @@ bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const c
|
|||
* ASN.1 definition of an algorithmIdentifier
|
||||
*/
|
||||
static const asn1Object_t algorithmIdentifierObjects[] = {
|
||||
{ 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
|
||||
{ 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 }
|
||||
{ 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
|
||||
{ 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 */
|
||||
{ 1, "parameters", ASN1_OCTET_STRING, ASN1_RAW|ASN1_OPT }, /* 6 */
|
||||
{ 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */
|
||||
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
|
||||
};
|
||||
#define ALGORITHM_ID_ALG 1
|
||||
#define ALGORITHM_ID_PARAMETERS_OID 2
|
||||
#define ALGORITHM_ID_PARAMETERS_SEQ 4
|
||||
#define ALGORITHM_ID_PARAMETERS_OCT 6
|
||||
|
||||
/*
|
||||
* Defined in header
|
||||
|
@ -586,6 +589,7 @@ int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters
|
|||
break;
|
||||
case ALGORITHM_ID_PARAMETERS_OID:
|
||||
case ALGORITHM_ID_PARAMETERS_SEQ:
|
||||
case ALGORITHM_ID_PARAMETERS_OCT:
|
||||
if (parameters != NULL)
|
||||
{
|
||||
*parameters = object;
|
||||
|
|
|
@ -100,6 +100,8 @@
|
|||
0x05 "PKCS-5"
|
||||
0x03 "pbeWithMD5AndDES-CBC" OID_PBE_MD5_DES_CBC
|
||||
0x0A "pbeWithSHA1AndDES-CBC" OID_PBE_SHA1_DES_CBC
|
||||
0x0C "id-PBKDF2" OID_PBKDF2
|
||||
0x0D "id-PBES2" OID_PBES2
|
||||
0x07 "PKCS-7"
|
||||
0x01 "data" OID_PKCS7_DATA
|
||||
0x02 "signedData" OID_PKCS7_SIGNED_DATA
|
||||
|
|
|
@ -123,6 +123,146 @@ static bool verify_padding(chunk_t *blob)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prototype for key derivation functions.
|
||||
*/
|
||||
typedef void (*kdf_t)(void *generator, chunk_t password, chunk_t salt,
|
||||
u_int64_t iterations, chunk_t key);
|
||||
|
||||
/**
|
||||
* Try to decrypt the given blob with multiple passwords using the given
|
||||
* key derivation function. keymat is where the kdf function writes the key
|
||||
* to, key and iv point to the actual keys and initialization vectors resp.
|
||||
*/
|
||||
static private_key_t *decrypt_private_key(chunk_t blob,
|
||||
encryption_algorithm_t encr, size_t key_len, kdf_t kdf,
|
||||
void *generator, chunk_t salt, u_int64_t iterations,
|
||||
chunk_t keymat, chunk_t key, chunk_t iv)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
shared_key_t *shared;
|
||||
crypter_t *crypter;
|
||||
private_key_t *private_key = NULL;
|
||||
|
||||
crypter = lib->crypto->create_crypter(lib->crypto, encr, key_len);
|
||||
if (!crypter)
|
||||
{
|
||||
DBG1(DBG_ASN, " %N encryption algorithm not available",
|
||||
encryption_algorithm_names, encr);
|
||||
return NULL;
|
||||
}
|
||||
if (blob.len % crypter->get_block_size(crypter))
|
||||
{
|
||||
DBG1(DBG_ASN, " data size is not a multiple of block size");
|
||||
crypter->destroy(crypter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
|
||||
SHARED_PRIVATE_KEY_PASS, NULL, NULL);
|
||||
while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
|
||||
{
|
||||
chunk_t decrypted;
|
||||
|
||||
kdf(generator, 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);
|
||||
crypter->destroy(crypter);
|
||||
|
||||
return private_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function F of PBKDF2
|
||||
*/
|
||||
static void pbkdf2_f(chunk_t block, prf_t *prf, chunk_t seed,
|
||||
u_int64_t iterations)
|
||||
{
|
||||
chunk_t u;
|
||||
u_int64_t i;
|
||||
|
||||
u = chunk_alloca(prf->get_block_size(prf));
|
||||
prf->get_bytes(prf, seed, u.ptr);
|
||||
memcpy(block.ptr, u.ptr, block.len);
|
||||
|
||||
for (i = 1; i < iterations; i++)
|
||||
{
|
||||
prf->get_bytes(prf, u, u.ptr);
|
||||
memxor(block.ptr, u.ptr, block.len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PBKDF2 key derivation function
|
||||
*/
|
||||
static void pbkdf2(prf_t *prf, chunk_t password, chunk_t salt,
|
||||
u_int64_t iterations, chunk_t key)
|
||||
{
|
||||
chunk_t keymat, block, seed;
|
||||
size_t blocks;
|
||||
u_int32_t i = 0, *ni;
|
||||
|
||||
prf->set_key(prf, password);
|
||||
|
||||
block.len = prf->get_block_size(prf);
|
||||
blocks = (key.len - 1) / block.len + 1;
|
||||
keymat = chunk_alloca(blocks * block.len);
|
||||
|
||||
seed = chunk_cata("cc", salt, chunk_from_thing(i));
|
||||
ni = (u_int32_t*)(seed.ptr + salt.len);
|
||||
|
||||
for (; i < blocks; i++)
|
||||
{
|
||||
*ni = htonl(i + 1);
|
||||
block.ptr = keymat.ptr + (i * block.len);
|
||||
pbkdf2_f(block, prf, seed, iterations);
|
||||
}
|
||||
|
||||
memcpy(key.ptr, keymat.ptr, key.len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt an encrypted PKCS#8 encoded private key according to PBES2
|
||||
*/
|
||||
static private_key_t *decrypt_private_key_pbes2(chunk_t blob,
|
||||
encryption_algorithm_t encr, size_t key_len,
|
||||
chunk_t iv, pseudo_random_function_t prf_func,
|
||||
chunk_t salt, u_int64_t iterations)
|
||||
{
|
||||
private_key_t *private_key;
|
||||
prf_t *prf;
|
||||
chunk_t key;
|
||||
|
||||
prf = lib->crypto->create_prf(lib->crypto, prf_func);
|
||||
if (!prf)
|
||||
{
|
||||
DBG1(DBG_ASN, " %N prf algorithm not available",
|
||||
pseudo_random_function_names, prf_func);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
key = chunk_alloca(key_len);
|
||||
|
||||
private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf2, prf,
|
||||
salt, iterations, key, key, iv);
|
||||
|
||||
prf->destroy(prf);
|
||||
return private_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* PBKDF1 key derivation function
|
||||
*/
|
||||
|
@ -145,17 +285,14 @@ static void pbkdf1(hasher_t *hasher, chunk_t password, chunk_t salt,
|
|||
}
|
||||
|
||||
/**
|
||||
* Decrypt an encrypted PKCS#8 encoded private key
|
||||
* Decrypt an encrypted PKCS#8 encoded private key according to PBES1
|
||||
*/
|
||||
static private_key_t *decrypt_private_key(chunk_t blob,
|
||||
encryption_algorithm_t encr, size_t key_size,
|
||||
static private_key_t *decrypt_private_key_pbes1(chunk_t blob,
|
||||
encryption_algorithm_t encr, size_t key_len,
|
||||
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;
|
||||
|
||||
|
@ -166,57 +303,173 @@ static private_key_t *decrypt_private_key(chunk_t blob,
|
|||
hash_algorithm_names, hash);
|
||||
goto end;
|
||||
}
|
||||
if (hasher->get_hash_size(hasher) < key_size)
|
||||
if (hasher->get_hash_size(hasher) < key_len)
|
||||
{
|
||||
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;
|
||||
keymat = chunk_alloca(key_len * 2);
|
||||
key.len = key_len;
|
||||
key.ptr = keymat.ptr;
|
||||
iv.len = key_size;
|
||||
iv.ptr = keymat.ptr + key_size;
|
||||
iv.len = key_len;
|
||||
iv.ptr = keymat.ptr + key_len;
|
||||
|
||||
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
|
||||
SHARED_PRIVATE_KEY_PASS, NULL, NULL);
|
||||
while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
|
||||
private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf1,
|
||||
hasher, salt, iterations, keymat,
|
||||
key, iv);
|
||||
|
||||
end:
|
||||
DESTROY_IF(hasher);
|
||||
return private_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN1_INTEGER to a u_int64_t.
|
||||
*/
|
||||
static u_int64_t parse_asn1_integer_uint64(chunk_t blob)
|
||||
{
|
||||
u_int64_t val = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < blob.len; i++)
|
||||
{ /* if it is longer than 8 bytes, we just use the 8 LSBs */
|
||||
val <<= 8;
|
||||
val |= (u_int64_t)blob.ptr[i];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 definition of a PBKDF2-params structure
|
||||
* The salt is actually a CHOICE and could be an AlgorithmIdentifier from
|
||||
* PBKDF2-SaltSources (but as per RFC 2898 that's for future versions).
|
||||
*/
|
||||
static const asn1Object_t pbkdf2ParamsObjects[] = {
|
||||
{ 0, "PBKDF2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
|
||||
{ 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */
|
||||
{ 1, "iterationCount",ASN1_INTEGER, ASN1_BODY }, /* 2 */
|
||||
{ 1, "keyLength", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 3 */
|
||||
{ 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */
|
||||
{ 1, "prf", ASN1_EOC, ASN1_DEF|ASN1_RAW }, /* 5 */
|
||||
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
|
||||
};
|
||||
#define PBKDF2_SALT 1
|
||||
#define PBKDF2_ITERATION_COUNT 2
|
||||
#define PBKDF2_KEY_LENGTH 3
|
||||
#define PBKDF2_PRF 5
|
||||
|
||||
/**
|
||||
* Parse a PBKDF2-params structure
|
||||
*/
|
||||
static void parse_pbkdf2_params(chunk_t blob, chunk_t *salt,
|
||||
u_int64_t *iterations, size_t *key_len,
|
||||
pseudo_random_function_t *prf)
|
||||
{
|
||||
asn1_parser_t *parser;
|
||||
chunk_t object;
|
||||
int objectID;
|
||||
|
||||
parser = asn1_parser_create(pbkdf2ParamsObjects, blob);
|
||||
|
||||
*key_len = 0; /* key_len is optional */
|
||||
|
||||
while (parser->iterate(parser, &objectID, &object))
|
||||
{
|
||||
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))
|
||||
switch (objectID)
|
||||
{
|
||||
private_key = parse_private_key(decrypted);
|
||||
if (private_key)
|
||||
case PBKDF2_SALT:
|
||||
{
|
||||
chunk_clear(&decrypted);
|
||||
*salt = object;
|
||||
break;
|
||||
}
|
||||
case PBKDF2_ITERATION_COUNT:
|
||||
{
|
||||
*iterations = parse_asn1_integer_uint64(object);
|
||||
break;
|
||||
}
|
||||
case PBKDF2_KEY_LENGTH:
|
||||
{
|
||||
*key_len = (size_t)parse_asn1_integer_uint64(object);
|
||||
break;
|
||||
}
|
||||
case PBKDF2_PRF:
|
||||
{ /* defaults to id-hmacWithSHA1 */
|
||||
*prf = PRF_HMAC_SHA1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parser->destroy(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 definition of a PBES2-params structure
|
||||
*/
|
||||
static const asn1Object_t pbes2ParamsObjects[] = {
|
||||
{ 0, "PBES2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
|
||||
{ 1, "keyDerivationFunc", ASN1_EOC, ASN1_RAW }, /* 1 */
|
||||
{ 1, "encryptionScheme", ASN1_EOC, ASN1_RAW }, /* 2 */
|
||||
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
|
||||
};
|
||||
#define PBES2PARAMS_KEY_DERIVATION_FUNC 1
|
||||
#define PBES2PARAMS_ENCRYPTION_SCHEME 2
|
||||
|
||||
/**
|
||||
* Parse a PBES2-params structure
|
||||
*/
|
||||
static void parse_pbes2_params(chunk_t blob, chunk_t *salt,
|
||||
u_int64_t *iterations, size_t *key_len,
|
||||
pseudo_random_function_t *prf,
|
||||
encryption_algorithm_t *encr, chunk_t *iv)
|
||||
{
|
||||
asn1_parser_t *parser;
|
||||
chunk_t object, params;
|
||||
int objectID;
|
||||
|
||||
parser = asn1_parser_create(pbes2ParamsObjects, blob);
|
||||
|
||||
while (parser->iterate(parser, &objectID, &object))
|
||||
{
|
||||
switch (objectID)
|
||||
{
|
||||
case PBES2PARAMS_KEY_DERIVATION_FUNC:
|
||||
{
|
||||
int oid = asn1_parse_algorithmIdentifier(object,
|
||||
parser->get_level(parser) + 1, ¶ms);
|
||||
if (oid != OID_PBKDF2)
|
||||
{ /* unsupported key derivation function */
|
||||
goto end;
|
||||
}
|
||||
parse_pbkdf2_params(params, salt, iterations, key_len, prf);
|
||||
break;
|
||||
}
|
||||
case PBES2PARAMS_ENCRYPTION_SCHEME:
|
||||
{
|
||||
int oid = asn1_parse_algorithmIdentifier(object,
|
||||
parser->get_level(parser) + 1, ¶ms);
|
||||
if (oid != OID_3DES_EDE_CBC)
|
||||
{ /* unsupported encryption scheme */
|
||||
goto end;
|
||||
}
|
||||
if (*key_len <= 0)
|
||||
{ /* default key len for DES-EDE3-CBC-Pad */
|
||||
*key_len = 24;
|
||||
}
|
||||
if (!asn1_parse_simple_object(¶ms, ASN1_OCTET_STRING,
|
||||
parser->get_level(parser) + 1, "IV"))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
*encr = ENCR_3DES;
|
||||
*iv = params;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chunk_free(&decrypted);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
end:
|
||||
DESTROY_IF(crypter);
|
||||
DESTROY_IF(hasher);
|
||||
return private_key;
|
||||
parser->destroy(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -254,15 +507,7 @@ static void parse_pbe_parameters(chunk_t blob, chunk_t *salt,
|
|||
}
|
||||
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;
|
||||
*iterations = parse_asn1_integer_uint64(object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -285,18 +530,19 @@ static const asn1Object_t encryptedPKIObjects[] = {
|
|||
|
||||
/**
|
||||
* 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
|
||||
* Schemes per PKCS#5 (RFC 2898)
|
||||
*/
|
||||
static private_key_t *parse_encrypted_private_key(chunk_t blob)
|
||||
{
|
||||
asn1_parser_t *parser;
|
||||
chunk_t object, params = chunk_empty, salt;
|
||||
chunk_t object, params, salt, iv;
|
||||
u_int64_t iterations;
|
||||
int objectID;
|
||||
encryption_algorithm_t encr = ENCR_UNDEFINED;
|
||||
hash_algorithm_t hash = HASH_UNKNOWN;
|
||||
pseudo_random_function_t prf = PRF_UNDEFINED;
|
||||
private_key_t *key = NULL;
|
||||
size_t key_size;
|
||||
size_t key_len = 8;
|
||||
|
||||
parser = asn1_parser_create(encryptedPKIObjects, blob);
|
||||
|
||||
|
@ -314,15 +560,17 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob)
|
|||
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;
|
||||
case OID_PBES2:
|
||||
parse_pbes2_params(params, &salt, &iterations,
|
||||
&key_len, &prf, &encr, &iv);
|
||||
break;
|
||||
default:
|
||||
/* encryption scheme not supported */
|
||||
goto end;
|
||||
|
@ -331,8 +579,16 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob)
|
|||
}
|
||||
case EPKINFO_ENCRYPTED_DATA:
|
||||
{
|
||||
key = decrypt_private_key(object, encr, key_size, hash, salt,
|
||||
iterations);
|
||||
if (prf != PRF_UNDEFINED)
|
||||
{
|
||||
key = decrypt_private_key_pbes2(object, encr, key_len, iv,
|
||||
prf, salt, iterations);
|
||||
}
|
||||
else
|
||||
{
|
||||
key = decrypt_private_key_pbes1(object, encr, key_len, hash,
|
||||
salt, iterations);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue