Repository navigation
fix(auth): use an unencrypted key for mTLS certificate rotation - #18709
Open
Thanatos9404 wants to merge 1 commit into
Open
Thanatos9404 wants to merge 1 commit into
Thanatos9404 wants to merge 1 commit into
Conversation
call_client_cert_callback() asked get_client_ssl_credentials() for an encrypted key (generate_encrypted_key=True) and then dropped the passphrase. With SecureConnect this adds --with_passphrase to the cert provider command, so the returned key is encrypted. The 401 certificate rotation path in AuthorizedSession.request() and AuthorizedHttp.urlopen() passes that key to configure_mtls_channel() without a passphrase, so load_cert_chain() receives an encrypted key with password=None. OpenSSL then prompts for a pass phrase on the terminal, blocking if one is attached, or fails, which surfaces as MutualTLSChannelError. Request an unencrypted key instead, as get_client_cert_and_key() already does for the initial mTLS setup. Fixes googleapis#18467
Contributor
There was a problem hiding this comment.
Code Review
This pull request modifies call_client_cert_callback in _mtls_helper.py to request an unencrypted key by setting generate_encrypted_key=False when calling get_client_ssl_credentials, as callers load the key without a passphrase. It also updates existing tests and adds a new test test_call_client_cert_callback_secure_connect_unencrypted_key to verify this behavior. No review comments were provided, and I have no additional feedback.
nbayati
self-requested a review
October 15, 2026 22:16
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
call_client_cert_callback()ingoogle/auth/transport/_mtls_helper.pycalledget_client_ssl_credentials(generate_encrypted_key=True)and then dropped the passphrase. With SecureConnect (context_aware_metadata.json) that adds--with_passphraseto the cert provider command, so the key it returns is encrypted.Its only caller is
check_parameters_for_unauthorized_response(), which the 401 certificate rotation inAuthorizedSession.request()andAuthorizedHttp.urlopen()uses. Both pass the key toconfigure_mtls_channel()without a passphrase, soload_cert_chain()gets an encrypted key withpassword=None.This passes
generate_encrypted_key=False, asget_client_cert_and_key()already does for the initial mTLS setup, and updatestest_call_client_cert_callbackto expect it. A new test drives the SecureConnect path through a fake cert provider that returns an encrypted key only for--with_passphrase, and checks that the returned key loads without a passphrase. Workload certificates fromcertificate_config.jsonare returned before the flag is read, so they are not affected, and the async rotation path does not use this function.The change does not touch
requests.pyorurllib3.py, so it is independent of #18381. The rotation paths added there also go throughcheck_parameters_for_unauthorized_response()and get the unencrypted key too.Testing (Python 3.13 on Windows, Python 3.12 on Linux):
The updated
test_call_client_cert_callbackand the newtest_call_client_cert_callback_secure_connect_unencrypted_keyfail against_mtls_helper.pyfrommain(the first withExpected: get_client_ssl_credentials(generate_encrypted_key=False),Actual: ...(generate_encrypted_key=True); the second gets the encrypted key back) and pass with the change.pytest tests/transport/test__mtls_helper.py tests/transport/test_requests.py tests/transport/test_urllib3.py tests/transport/test_mtls.py: 371 passed.pytest tests tests_asyncon Linux: 2263 passed, 7 skipped.ruff check --select Iandruff format --check(ruff 0.14.14 with the flags from thelintsession) andflake8on the two changed files: no findings.On Linux, passing an encrypted key with no passphrase to
_MutualTlsAdapterwith no terminal attached printedEnter PEM pass phrase:and raisedMutualTLSChannelError(caused byOSError(22, 'Invalid argument')); with a terminal it waits for input. The same key unencrypted loads fine.Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
Ensure the tests and linter pass
Code coverage does not decrease (if any source code was changed)
Appropriate docs were updated (if necessary)
Fixes #18467 🦕.