From 7bb6aed5abd004af5cb98f35756a71cc065fe559 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 18 Oct 2023 17:01:52 +0200 Subject: [PATCH 1/6] openssl: Add support for unprotected PKCS#12 containers --- .../plugins/openssl/openssl_pkcs12.c | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs12.c b/src/libstrongswan/plugins/openssl/openssl_pkcs12.c index 3c74ac199..51745f524 100644 --- a/src/libstrongswan/plugins/openssl/openssl_pkcs12.c +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs12.c @@ -139,6 +139,29 @@ static bool add_key(private_pkcs12_t *this, EVP_PKEY *private) 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 */ @@ -146,13 +169,21 @@ static bool decrypt_and_unpack(private_pkcs12_t *this) { enumerator_t *enumerator; shared_key_t *shared; - STACK_OF(X509) *cas = NULL; - EVP_PKEY *private; - X509 *cert; chunk_t key; char *password; 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, SHARED_PRIVATE_KEY_PASS, 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); 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); - success &= add_cert(this, cert); - success &= add_cas(this, cas); - free(password); - break; + case PARSE_ERROR: + /* password was incorrect, try another */ + memwipe(password, strlen(password)); + free(password); + continue; + case SUCCESS: + success = TRUE; + break; + default: + /* password was correct but we were unable to unpack anything */ + break; } + memwipe(password, strlen(password)); free(password); + break; } enumerator->destroy(enumerator); return success; From ad804fa036a534fe315dd33563d9bba514bf9907 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 18 Oct 2023 17:11:51 +0200 Subject: [PATCH 2/6] pkcs12: Treat empty string and no password differently When deriving the PKCS#12 key, the empty string should result in a non-zero length Unicode string (two bytes for the 0 terminator). --- src/libstrongswan/credentials/containers/pkcs12.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstrongswan/credentials/containers/pkcs12.c b/src/libstrongswan/credentials/containers/pkcs12.c index 5e36b3cd4..d73891007 100644 --- a/src/libstrongswan/credentials/containers/pkcs12.c +++ b/src/libstrongswan/credentials/containers/pkcs12.c @@ -157,7 +157,7 @@ bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt, bool success; int i; - if (password.len) + if (password.ptr) { /* convert the password to UTF-16BE (without BOM) with 0 terminator */ unicode = chunk_alloca(password.len * 2 + 2); for (i = 0; i < password.len; i++) From dc704cf20644e3fd0d5bae2b2d6de89ad140fa91 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 18 Oct 2023 17:15:44 +0200 Subject: [PATCH 3/6] pkcs8: Add support for unprotected PKCS#8 containers --- .../plugins/pkcs8/pkcs8_builder.c | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c index 6f8499409..defaac19d 100644 --- a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c @@ -121,6 +121,32 @@ end: 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 * pkcs5 object. @@ -130,36 +156,26 @@ static private_key_t *decrypt_private_key(key_type_t type, pkcs5_t *pkcs5, { enumerator_t *enumerator; 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, SHARED_PRIVATE_KEY_PASS, NULL, NULL); while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) { - chunk_t decrypted; - - 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); + private_key = decrypt_private_key_pw(type, pkcs5, blob, + shared->get_key(shared)); if (private_key) { - chunk_clear(&decrypted); break; } - chunk_free(&decrypted); } enumerator->destroy(enumerator); - return private_key; } From bdd8f14354673631ebe82a783c7c7870c51d9e3a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 18 Oct 2023 17:18:49 +0200 Subject: [PATCH 4/6] pkcs7: Add supported for unprotected PKCS#7 encrypted-data --- src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c index 472e72e2a..cdcb0eb71 100644 --- a/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c @@ -54,6 +54,11 @@ static bool decrypt(pkcs5_t *pkcs5, chunk_t data, chunk_t *decrypted) shared_key_t *shared; bool success = FALSE; + if (pkcs5->decrypt(pkcs5, chunk_empty, data, decrypted)) + { + return TRUE; + } + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PRIVATE_KEY_PASS, NULL, NULL); while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) From 799511d90fa77f67cbb6ff328cb49e12ffd38314 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 18 Oct 2023 17:22:08 +0200 Subject: [PATCH 5/6] pkcs12: Add support for PKCS#12 containers with empty or no password --- .../plugins/pkcs12/pkcs12_decode.c | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c index bed02cc42..c214147ef 100644 --- a/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c @@ -321,43 +321,62 @@ end: 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. */ static bool verify_mac(hash_algorithm_t hash, chunk_t salt, uint64_t iterations, chunk_t data, chunk_t mac) { - integrity_algorithm_t integ; enumerator_t *enumerator; shared_key_t *shared; signer_t *signer; - chunk_t key, calculated; bool success = FALSE; - integ = hasher_algorithm_to_integrity(hash, mac.len); - signer = lib->crypto->create_signer(lib->crypto, integ); + signer = lib->crypto->create_signer(lib->crypto, + hasher_algorithm_to_integrity(hash, mac.len)); if (!signer) { 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, SHARED_PRIVATE_KEY_PASS, NULL, NULL); while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) { - if (!pkcs12_derive_key(hash, shared->get_key(shared), salt, iterations, - PKCS12_KEY_MAC, key)) - { - break; - } - if (!signer->set_key(signer, key) || - !signer->get_signature(signer, data, calculated.ptr)) - { - break; - } - if (chunk_equals_const(mac, calculated)) + if (verify_mac_pw(signer, hash, salt, iterations, data, mac, + shared->get_key(shared))) { success = TRUE; break; From 8581a19dd793eca6c0959242cd07f72f5d0a7f8d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 18 Oct 2023 17:29:25 +0200 Subject: [PATCH 6/6] charon-cmd: Add support for key types other than RSA --- src/charon-cmd/charon-cmd.8.in | 4 ++++ src/charon-cmd/cmd/cmd_connection.c | 1 + src/charon-cmd/cmd/cmd_creds.c | 3 +++ src/charon-cmd/cmd/cmd_options.c | 2 ++ src/charon-cmd/cmd/cmd_options.h | 1 + 5 files changed, 11 insertions(+) diff --git a/src/charon-cmd/charon-cmd.8.in b/src/charon-cmd/charon-cmd.8.in index a2d424e9a..c9b4d8625 100644 --- a/src/charon-cmd/charon-cmd.8.in +++ b/src/charon-cmd/charon-cmd.8.in @@ -95,6 +95,10 @@ options can be used. .TP .BI "\-\-rsa " path 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). .TP .BI "\-\-p12 " path diff --git a/src/charon-cmd/cmd/cmd_connection.c b/src/charon-cmd/cmd/cmd_connection.c index 2e2cb3ca2..8e8d8236e 100644 --- a/src/charon-cmd/cmd/cmd_connection.c +++ b/src/charon-cmd/cmd/cmd_connection.c @@ -499,6 +499,7 @@ METHOD(cmd_connection_t, handle, bool, this->xautheap = arg; break; case CMD_OPT_RSA: + case CMD_OPT_PRIV: case CMD_OPT_AGENT: case CMD_OPT_PKCS12: this->key_seen = TRUE; diff --git a/src/charon-cmd/cmd/cmd_creds.c b/src/charon-cmd/cmd/cmd_creds.c index 9fa039498..e2bc4b9c2 100644 --- a/src/charon-cmd/cmd/cmd_creds.c +++ b/src/charon-cmd/cmd/cmd_creds.c @@ -237,6 +237,9 @@ METHOD(cmd_creds_t, handle, bool, case CMD_OPT_RSA: load_key(this, KEY_RSA, arg); break; + case CMD_OPT_PRIV: + load_key(this, KEY_ANY, arg); + break; case CMD_OPT_PKCS12: load_pkcs12(this, arg); break; diff --git a/src/charon-cmd/cmd/cmd_options.c b/src/charon-cmd/cmd/cmd_options.c index f2fd3b178..c53b79a4f 100644 --- a/src/charon-cmd/cmd/cmd_options.c +++ b/src/charon-cmd/cmd/cmd_options.c @@ -43,6 +43,8 @@ cmd_option_t cmd_options[CMD_OPT_COUNT] = { "certificate for authentication or trust chain validation", {}}, { CMD_OPT_RSA, "rsa", required_argument, "path", "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", "PKCS#12 file with private key and certificates to use for ", { "authentication and trust chain validation" diff --git a/src/charon-cmd/cmd/cmd_options.h b/src/charon-cmd/cmd/cmd_options.h index a6a7c994e..0fe2f5698 100644 --- a/src/charon-cmd/cmd/cmd_options.h +++ b/src/charon-cmd/cmd/cmd_options.h @@ -40,6 +40,7 @@ enum cmd_option_type_t { CMD_OPT_REMOTE_IDENTITY, CMD_OPT_CERT, CMD_OPT_RSA, + CMD_OPT_PRIV, CMD_OPT_PKCS12, CMD_OPT_AGENT, CMD_OPT_LOCAL_TS,