Added support for encrypted PKCS#8 files (for some PKCS#5 v1.5 schemes).
This commit is contained in:
@@ -556,10 +556,13 @@ static const asn1Object_t algorithmIdentifierObjects[] = {
|
|||||||
{ 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */
|
{ 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */
|
||||||
{ 1, "parameters", ASN1_OID, ASN1_RAW|ASN1_OPT }, /* 2 */
|
{ 1, "parameters", ASN1_OID, ASN1_RAW|ASN1_OPT }, /* 2 */
|
||||||
{ 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
|
{ 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, "exit", ASN1_EOC, ASN1_EXIT }
|
||||||
};
|
};
|
||||||
#define ALGORITHM_ID_ALG 1
|
#define ALGORITHM_ID_ALG 1
|
||||||
#define ALGORITHM_ID_PARAMETERS 2
|
#define ALGORITHM_ID_PARAMETERS_OID 2
|
||||||
|
#define ALGORITHM_ID_PARAMETERS_SEQ 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defined in header
|
* Defined in header
|
||||||
@@ -581,7 +584,8 @@ int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters
|
|||||||
case ALGORITHM_ID_ALG:
|
case ALGORITHM_ID_ALG:
|
||||||
alg = asn1_known_oid(object);
|
alg = asn1_known_oid(object);
|
||||||
break;
|
break;
|
||||||
case ALGORITHM_ID_PARAMETERS:
|
case ALGORITHM_ID_PARAMETERS_OID:
|
||||||
|
case ALGORITHM_ID_PARAMETERS_SEQ:
|
||||||
if (parameters != NULL)
|
if (parameters != NULL)
|
||||||
{
|
{
|
||||||
*parameters = object;
|
*parameters = object;
|
||||||
|
|||||||
@@ -97,6 +97,9 @@
|
|||||||
0x0C "sha384WithRSAEncryption" OID_SHA384_WITH_RSA
|
0x0C "sha384WithRSAEncryption" OID_SHA384_WITH_RSA
|
||||||
0x0D "sha512WithRSAEncryption" OID_SHA512_WITH_RSA
|
0x0D "sha512WithRSAEncryption" OID_SHA512_WITH_RSA
|
||||||
0x0E "sha224WithRSAEncryption" OID_SHA224_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"
|
0x07 "PKCS-7"
|
||||||
0x01 "data" OID_PKCS7_DATA
|
0x01 "data" OID_PKCS7_DATA
|
||||||
0x02 "signedData" OID_PKCS7_SIGNED_DATA
|
0x02 "signedData" OID_PKCS7_SIGNED_DATA
|
||||||
|
|||||||
@@ -100,12 +100,256 @@ end:
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, ¶ms);
|
||||||
|
|
||||||
|
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.
|
* See header.
|
||||||
*/
|
*/
|
||||||
private_key_t *pkcs8_private_key_load(key_type_t type, va_list args)
|
private_key_t *pkcs8_private_key_load(key_type_t type, va_list args)
|
||||||
{
|
{
|
||||||
chunk_t blob = chunk_empty;
|
chunk_t blob = chunk_empty;
|
||||||
|
private_key_t *key;
|
||||||
|
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
{
|
{
|
||||||
@@ -121,6 +365,12 @@ private_key_t *pkcs8_private_key_load(key_type_t type, va_list args)
|
|||||||
}
|
}
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user