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:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user