Merge branch 'pkcs12-no-pw'

This adds support for password-less PKCS#12 containers and PKCS#8 files.

A new option for charon-cmd also allows loading private keys of any
type (previously only RSA keys were supported).

References strongswan/strongswan#1955
This commit is contained in:
Tobias Brunner
2023-11-13 12:27:31 +01:00
10 changed files with 137 additions and 47 deletions
+4
View File
@@ -95,6 +95,10 @@ options can be used.
.TP .TP
.BI "\-\-rsa " path .BI "\-\-rsa " path
RSA private key to use for authentication (if a password is required, it will RSA private key to use for authentication (if a password is required, it will
be requested on demand). For other key types use \fI\-\-priv\fR.
.TP
.BI "\-\-priv " path
Pivate key to use for authentication (if a password is required, it will
be requested on demand). be requested on demand).
.TP .TP
.BI "\-\-p12 " path .BI "\-\-p12 " path
+1
View File
@@ -499,6 +499,7 @@ METHOD(cmd_connection_t, handle, bool,
this->xautheap = arg; this->xautheap = arg;
break; break;
case CMD_OPT_RSA: case CMD_OPT_RSA:
case CMD_OPT_PRIV:
case CMD_OPT_AGENT: case CMD_OPT_AGENT:
case CMD_OPT_PKCS12: case CMD_OPT_PKCS12:
this->key_seen = TRUE; this->key_seen = TRUE;
+3
View File
@@ -237,6 +237,9 @@ METHOD(cmd_creds_t, handle, bool,
case CMD_OPT_RSA: case CMD_OPT_RSA:
load_key(this, KEY_RSA, arg); load_key(this, KEY_RSA, arg);
break; break;
case CMD_OPT_PRIV:
load_key(this, KEY_ANY, arg);
break;
case CMD_OPT_PKCS12: case CMD_OPT_PKCS12:
load_pkcs12(this, arg); load_pkcs12(this, arg);
break; break;
+2
View File
@@ -43,6 +43,8 @@ cmd_option_t cmd_options[CMD_OPT_COUNT] = {
"certificate for authentication or trust chain validation", {}}, "certificate for authentication or trust chain validation", {}},
{ CMD_OPT_RSA, "rsa", required_argument, "path", { CMD_OPT_RSA, "rsa", required_argument, "path",
"RSA private key to use for authentication", {}}, "RSA private key to use for authentication", {}},
{ CMD_OPT_PRIV, "priv", required_argument, "path",
"Private key to use for authentication", {}},
{ CMD_OPT_PKCS12, "p12", required_argument, "path", { CMD_OPT_PKCS12, "p12", required_argument, "path",
"PKCS#12 file with private key and certificates to use for ", { "PKCS#12 file with private key and certificates to use for ", {
"authentication and trust chain validation" "authentication and trust chain validation"
+1
View File
@@ -40,6 +40,7 @@ enum cmd_option_type_t {
CMD_OPT_REMOTE_IDENTITY, CMD_OPT_REMOTE_IDENTITY,
CMD_OPT_CERT, CMD_OPT_CERT,
CMD_OPT_RSA, CMD_OPT_RSA,
CMD_OPT_PRIV,
CMD_OPT_PKCS12, CMD_OPT_PKCS12,
CMD_OPT_AGENT, CMD_OPT_AGENT,
CMD_OPT_LOCAL_TS, CMD_OPT_LOCAL_TS,
@@ -157,7 +157,7 @@ bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt,
bool success; bool success;
int i; int i;
if (password.len) if (password.ptr)
{ /* convert the password to UTF-16BE (without BOM) with 0 terminator */ { /* convert the password to UTF-16BE (without BOM) with 0 terminator */
unicode = chunk_alloca(password.len * 2 + 2); unicode = chunk_alloca(password.len * 2 + 2);
for (i = 0; i < password.len; i++) for (i = 0; i < password.len; i++)
@@ -139,6 +139,29 @@ static bool add_key(private_pkcs12_t *this, EVP_PKEY *private)
return key != NULL; return key != NULL;
} }
/**
* Decrypt PKCS#12 file using the given password and unpack credentials
*/
static status_t decrypt_and_unpack_pw(private_pkcs12_t *this, char *password)
{
STACK_OF(X509) *cas = NULL;
EVP_PKEY *private;
X509 *cert;
if (PKCS12_parse(this->p12, password, &private, &cert, &cas))
{
/* if at least one is successful, we accept it */
if ((int)add_key(this, private) |
(int)add_cert(this, cert) |
(int)add_cas(this, cas))
{
return SUCCESS;
}
return FAILED;
}
return PARSE_ERROR;
}
/** /**
* Decrypt PKCS#12 file and unpack credentials * Decrypt PKCS#12 file and unpack credentials
*/ */
@@ -146,13 +169,21 @@ static bool decrypt_and_unpack(private_pkcs12_t *this)
{ {
enumerator_t *enumerator; enumerator_t *enumerator;
shared_key_t *shared; shared_key_t *shared;
STACK_OF(X509) *cas = NULL;
EVP_PKEY *private;
X509 *cert;
chunk_t key; chunk_t key;
char *password; char *password;
bool success = FALSE; bool success = FALSE;
/* try without password first */
switch (decrypt_and_unpack_pw(this, NULL))
{
case PARSE_ERROR:
break;
case SUCCESS:
return TRUE;
default:
return FALSE;
}
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
SHARED_PRIVATE_KEY_PASS, NULL, NULL); SHARED_PRIVATE_KEY_PASS, NULL, NULL);
while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
@@ -160,17 +191,25 @@ static bool decrypt_and_unpack(private_pkcs12_t *this)
key = shared->get_key(shared); key = shared->get_key(shared);
if (!key.ptr || asprintf(&password, "%.*s", (int)key.len, key.ptr) < 0) if (!key.ptr || asprintf(&password, "%.*s", (int)key.len, key.ptr) < 0)
{ {
password = NULL; password = strdup("");
} }
if (PKCS12_parse(this->p12, password, &private, &cert, &cas)) switch (decrypt_and_unpack_pw(this, password))
{ {
success = add_key(this, private); case PARSE_ERROR:
success &= add_cert(this, cert); /* password was incorrect, try another */
success &= add_cas(this, cas); memwipe(password, strlen(password));
free(password); free(password);
continue;
case SUCCESS:
success = TRUE;
break;
default:
/* password was correct but we were unable to unpack anything */
break; break;
} }
memwipe(password, strlen(password));
free(password); free(password);
break;
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
return success; return success;
@@ -321,43 +321,62 @@ end:
return success; return success;
} }
/**
* Verify the given MAC using the given password.
*/
static bool verify_mac_pw(signer_t *signer, hash_algorithm_t hash, chunk_t salt,
uint64_t iterations, chunk_t data, chunk_t mac,
chunk_t pw)
{
chunk_t key, calculated;
bool success = FALSE;
key = chunk_alloca(signer->get_key_size(signer));
calculated = chunk_alloca(signer->get_block_size(signer));
if (pkcs12_derive_key(hash, pw, salt, iterations, PKCS12_KEY_MAC, key) &&
signer->set_key(signer, key) &&
signer->get_signature(signer, data, calculated.ptr) &&
chunk_equals_const(mac, calculated))
{
success = TRUE;
}
memwipe(key.ptr, key.len);
return success;
}
/** /**
* Verify the given MAC with available passwords. * Verify the given MAC with available passwords.
*/ */
static bool verify_mac(hash_algorithm_t hash, chunk_t salt, static bool verify_mac(hash_algorithm_t hash, chunk_t salt,
uint64_t iterations, chunk_t data, chunk_t mac) uint64_t iterations, chunk_t data, chunk_t mac)
{ {
integrity_algorithm_t integ;
enumerator_t *enumerator; enumerator_t *enumerator;
shared_key_t *shared; shared_key_t *shared;
signer_t *signer; signer_t *signer;
chunk_t key, calculated;
bool success = FALSE; bool success = FALSE;
integ = hasher_algorithm_to_integrity(hash, mac.len); signer = lib->crypto->create_signer(lib->crypto,
signer = lib->crypto->create_signer(lib->crypto, integ); hasher_algorithm_to_integrity(hash, mac.len));
if (!signer) if (!signer)
{ {
return FALSE; return FALSE;
} }
key = chunk_alloca(signer->get_key_size(signer));
calculated = chunk_alloca(signer->get_block_size(signer)); /* try without and with an empty password, which is not the same thing */
if (verify_mac_pw(signer, hash, salt, iterations, data, mac, chunk_empty) ||
verify_mac_pw(signer, hash, salt, iterations, data, mac, chunk_from_str("")))
{
signer->destroy(signer);
return TRUE;
}
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
SHARED_PRIVATE_KEY_PASS, NULL, NULL); SHARED_PRIVATE_KEY_PASS, NULL, NULL);
while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
{ {
if (!pkcs12_derive_key(hash, shared->get_key(shared), salt, iterations, if (verify_mac_pw(signer, hash, salt, iterations, data, mac,
PKCS12_KEY_MAC, key)) shared->get_key(shared)))
{
break;
}
if (!signer->set_key(signer, key) ||
!signer->get_signature(signer, data, calculated.ptr))
{
break;
}
if (chunk_equals_const(mac, calculated))
{ {
success = TRUE; success = TRUE;
break; break;
@@ -54,6 +54,11 @@ static bool decrypt(pkcs5_t *pkcs5, chunk_t data, chunk_t *decrypted)
shared_key_t *shared; shared_key_t *shared;
bool success = FALSE; bool success = FALSE;
if (pkcs5->decrypt(pkcs5, chunk_empty, data, decrypted))
{
return TRUE;
}
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
SHARED_PRIVATE_KEY_PASS, NULL, NULL); SHARED_PRIVATE_KEY_PASS, NULL, NULL);
while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
+35 -19
View File
@@ -121,6 +121,32 @@ end:
return key; return key;
} }
/**
* Try to decrypt the given blob using the given password and pkcs5 object.
*/
static private_key_t *decrypt_private_key_pw(key_type_t type, pkcs5_t *pkcs5,
chunk_t blob, chunk_t password)
{
private_key_t *private_key;
chunk_t decrypted;
if (!pkcs5->decrypt(pkcs5, password, blob, &decrypted))
{
return NULL;
}
/* do a quick check to validate whether the password was correct */
if (!is_asn1(decrypted))
{
chunk_clear(&decrypted);
return NULL;
}
private_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
type, BUILD_BLOB_ASN1_DER,
decrypted, BUILD_END);
chunk_clear(&decrypted);
return private_key;
}
/** /**
* Try to decrypt the given blob with multiple passwords using the given * Try to decrypt the given blob with multiple passwords using the given
* pkcs5 object. * pkcs5 object.
@@ -130,36 +156,26 @@ static private_key_t *decrypt_private_key(key_type_t type, pkcs5_t *pkcs5,
{ {
enumerator_t *enumerator; enumerator_t *enumerator;
shared_key_t *shared; shared_key_t *shared;
private_key_t *private_key = NULL; private_key_t *private_key;
private_key = decrypt_private_key_pw(type, pkcs5, blob, chunk_empty);
if (private_key)
{
return private_key;
}
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
SHARED_PRIVATE_KEY_PASS, NULL, NULL); SHARED_PRIVATE_KEY_PASS, NULL, NULL);
while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
{ {
chunk_t decrypted; private_key = decrypt_private_key_pw(type, pkcs5, blob,
shared->get_key(shared));
if (!pkcs5->decrypt(pkcs5, shared->get_key(shared), blob, &decrypted))
{
continue;
}
/* do a quick check to validate whether the password was correct */
if (!is_asn1(decrypted))
{
chunk_clear(&decrypted);
continue;
}
private_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
type, BUILD_BLOB_ASN1_DER,
decrypted, BUILD_END);
if (private_key) if (private_key)
{ {
chunk_clear(&decrypted);
break; break;
} }
chunk_free(&decrypted);
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
return private_key; return private_key;
} }