call_client_cert_callback() in google.auth.transport._mtls_helper calls get_client_ssl_credentials(generate_encrypted_key=True) and discards the returned passphrase, returning only (cert_bytes, key_bytes).
When using SecureConnect (context_aware_metadata.json), generate_encrypted_key=True passes --with_passphrase to the certificate provider command, producing an encrypted PEM private key. During 401 certificate rotation, AuthorizedSession.request() and AuthorizedHttp.urlopen() pass those credentials to configure_mtls_channel() without the passphrase. SSLContext.load_cert_chain() then receives the encrypted key with password=None, causing OpenSSL to prompt for a passphrase on /dev/tty or fail with OSError and raise MutualTLSChannelError.
By contrast, initial mTLS setup in get_client_cert_and_key() passes generate_encrypted_key=False, relying on secure_cert_key_paths() to keep private keys in memory (os.memfd_create on Linux) or encrypt them on the fly with an ephemeral passphrase when falling back to temporary files.
Proposed Fix
Update call_client_cert_callback() in packages/google-auth/google/auth/transport/_mtls_helper.py to pass generate_encrypted_key=False:
def call_client_cert_callback():
"""Calls the client cert callback and returns the certificate and key."""
_, cert_bytes, key_bytes, _ = get_client_ssl_credentials(
generate_encrypted_key=False
)
return cert_bytes, key_bytes
Update test_call_client_cert_callback in packages/google-auth/tests/transport/test__mtls_helper.py to expect generate_encrypted_key=False.
call_client_cert_callback()ingoogle.auth.transport._mtls_helpercallsget_client_ssl_credentials(generate_encrypted_key=True)and discards the returnedpassphrase, returning only(cert_bytes, key_bytes).When using SecureConnect (
context_aware_metadata.json),generate_encrypted_key=Truepasses--with_passphraseto the certificate provider command, producing an encrypted PEM private key. During 401 certificate rotation,AuthorizedSession.request()andAuthorizedHttp.urlopen()pass those credentials toconfigure_mtls_channel()without the passphrase.SSLContext.load_cert_chain()then receives the encrypted key withpassword=None, causing OpenSSL to prompt for a passphrase on/dev/ttyor fail withOSErrorand raiseMutualTLSChannelError.By contrast, initial mTLS setup in
get_client_cert_and_key()passesgenerate_encrypted_key=False, relying onsecure_cert_key_paths()to keep private keys in memory (os.memfd_createon Linux) or encrypt them on the fly with an ephemeral passphrase when falling back to temporary files.Proposed Fix
Update
call_client_cert_callback()inpackages/google-auth/google/auth/transport/_mtls_helper.pyto passgenerate_encrypted_key=False:Update
test_call_client_cert_callbackinpackages/google-auth/tests/transport/test__mtls_helper.pyto expectgenerate_encrypted_key=False.