Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
f357187
added warning
daniel-sanche Jan 9, 2026
575113c
added posargs to unit test nox command
daniel-sanche Jan 9, 2026
bef613a
added test
daniel-sanche Jan 9, 2026
15e6e7b
add rsa extra
daniel-sanche Jan 9, 2026
7715f92
updated warning
daniel-sanche Jan 9, 2026
ddacaf2
added TODO
daniel-sanche Jan 9, 2026
5573db6
added docstring warnings
daniel-sanche Jan 9, 2026
07a4f22
remove extra dependency
daniel-sanche Jan 9, 2026
6890572
Merge branch 'main' into warn_rsa
daniel-sanche Jan 9, 2026
c2ceeb4
added default rsa classes
daniel-sanche Jan 9, 2026
d1b3fb6
added shared wrapper class for RSASigner and RSAVerifier
daniel-sanche Jan 9, 2026
ad651ed
changed warning type
daniel-sanche Jan 9, 2026
540f260
added deprecation notices to docstrings
daniel-sanche Jan 9, 2026
a4830fa
Merge branch 'warn_rsa' into remove_rsa_2
daniel-sanche Jan 9, 2026
68410f1
moved warning
daniel-sanche Jan 9, 2026
f89e444
fixed warning type in tests
daniel-sanche Jan 9, 2026
fd429f3
remove rsa as required dependency
daniel-sanche Jan 9, 2026
fd1ae50
fixed errors
daniel-sanche Jan 10, 2026
0895998
added custom exception
daniel-sanche Jan 10, 2026
69bae96
ran blacken
daniel-sanche Jan 10, 2026
a803198
added new test file
daniel-sanche Jan 10, 2026
716bec3
added test file
daniel-sanche Jan 10, 2026
6191d3b
fixed bugs in implementation
daniel-sanche Jan 10, 2026
b7c270b
InvalidValue -> ValueError
daniel-sanche Jan 10, 2026
a1e0389
clean up tests
daniel-sanche Jan 10, 2026
6b17faa
finished tests
daniel-sanche Jan 10, 2026
e88fc1c
added e2e tests
daniel-sanche Jan 10, 2026
d7a3b23
added warning tests
daniel-sanche Jan 10, 2026
d0f1747
added unit tests
daniel-sanche Jan 10, 2026
db7037c
fixed lint
daniel-sanche Jan 10, 2026
556aa32
Merge branch 'main' into remove_rsa_2
daniel-sanche Jan 10, 2026
a9027c4
fixed typos
daniel-sanche Jan 12, 2026
d6ec87f
made cryptography into required dependency
daniel-sanche Jan 12, 2026
6aa7f41
rewrote classes to assume cryptography and rsa are both present
daniel-sanche Jan 12, 2026
a8efb6b
simplified from_string
daniel-sanche Jan 12, 2026
02dace8
updated tests
daniel-sanche Jan 12, 2026
3dd119f
Update google/auth/crypt/rsa.py
daniel-sanche Jan 12, 2026
208299a
Update google/auth/crypt/rsa.py
daniel-sanche Jan 12, 2026
3898071
Update google/auth/crypt/rsa.py
daniel-sanche Jan 12, 2026
754a401
Merge branch 'main' into add_cryptography_dependency
daniel-sanche Jan 12, 2026
7fa0a84
improved init logic
daniel-sanche Jan 13, 2026
2958fe1
fixed lint
daniel-sanche Jan 13, 2026
1ac38ff
improved from_string
daniel-sanche Jan 13, 2026
a11cb0f
fixed test
daniel-sanche Jan 13, 2026
fb52796
removed import check in __init__
daniel-sanche Jan 13, 2026
b455d59
fixed lint
daniel-sanche Jan 13, 2026
2416c6f
addressed PR comments
daniel-sanche Jan 15, 2026
c20c1af
Merge branch 'main' into add_cryptography_dependency
daniel-sanche Jan 15, 2026
1edd592
fixed lint
daniel-sanche Jan 15, 2026
1ccbb53
Merge branch 'main' into add_cryptography_dependency
daniel-sanche Jan 15, 2026
ba8b25e
updated cryptography requirement in enterprise_cert_extra
daniel-sanche Jan 15, 2026
d959680
Merge branch 'main' into add_cryptography_dependency
daniel-sanche Jan 16, 2026
18612ed
Merge branch 'main' into add_cryptography_dependency
daniel-sanche Jan 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 18 additions & 27 deletions google/auth/crypt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,35 +38,14 @@
"""

from google.auth.crypt import base
from google.auth.crypt import es
from google.auth.crypt import es256
from google.auth.crypt import rsa

# google.auth.crypt.es depends on the crytpography module which may not be
# successfully imported depending on the system.
try:
from google.auth.crypt import es
from google.auth.crypt import es256
except ImportError: # pragma: NO COVER
es = None # type: ignore
es256 = None # type: ignore

if es is not None and es256 is not None: # pragma: NO COVER
__all__ = [
"EsSigner",
"EsVerifier",
"ES256Signer",
"ES256Verifier",
"RSASigner",
"RSAVerifier",
"Signer",
"Verifier",
]

EsSigner = es.EsSigner
EsVerifier = es.EsVerifier
ES256Signer = es256.ES256Signer
ES256Verifier = es256.ES256Verifier
else: # pragma: NO COVER
__all__ = ["RSASigner", "RSAVerifier", "Signer", "Verifier"]
EsSigner = es.EsSigner
EsVerifier = es.EsVerifier
ES256Signer = es256.ES256Signer
ES256Verifier = es256.ES256Verifier


# Aliases to maintain the v1.0.0 interface, as the crypt module was split
Expand Down Expand Up @@ -103,3 +82,15 @@ class to use for verification. This can be used to select different
if verifier.verify(message, signature):
return True
return False


__all__ = [
"EsSigner",
"EsVerifier",
"ES256Signer",
"ES256Verifier",
"RSASigner",
"RSAVerifier",
"Signer",
"Verifier",
]
20 changes: 13 additions & 7 deletions google/auth/crypt/_python_rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,9 @@
_PKCS8_MARKER = ("-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----")
_PKCS8_SPEC = PrivateKeyInfo()

warnings.warn(
(
"The 'rsa' library is deprecated and will be removed in a future release. "
"Please migrate to 'cryptography'."
),
category=DeprecationWarning,
stacklevel=2,
_warning_msg = (
"The 'rsa' library is deprecated and will be removed in a future release. "
"Please migrate to 'cryptography'."
)


Expand Down Expand Up @@ -84,6 +80,11 @@ class RSAVerifier(base.Verifier):
"""

def __init__(self, public_key):
warnings.warn(
_warning_msg,
category=DeprecationWarning,
stacklevel=2,
)
self._pubkey = public_key

@_helpers.copy_docstring(base.Verifier)
Expand Down Expand Up @@ -142,6 +143,11 @@ class RSASigner(base.Signer, base.FromServiceAccountMixin):
"""

def __init__(self, private_key, key_id=None):
warnings.warn(
_warning_msg,
category=DeprecationWarning,
stacklevel=2,
)
self._key = private_key
self._key_id = key_id

Expand Down
4 changes: 2 additions & 2 deletions google/auth/crypt/es.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,15 @@ def verify(self, message: bytes, signature: bytes) -> bool:

@classmethod
def from_string(cls, public_key: Union[str, bytes]) -> "EsVerifier":
"""Construct an Verifier instance from a public key or public
"""Construct a Verifier instance from a public key or public
certificate string.

Args:
public_key (Union[str, bytes]): The public key in PEM format or the
x509 public key certificate.

Returns:
Verifier: The constructed verifier.
google.auth.crypt.Verifier: The constructed verifier.

Raises:
ValueError: If the public key can't be parsed.
Expand Down
121 changes: 109 additions & 12 deletions google/auth/crypt/rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,116 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""RSA cryptography signer and verifier."""
"""
RSA cryptography signer and verifier.

This file provides a shared wrapper, that defers to _python_rsa or _cryptography_rsa
for implmentations using different third party libraries
"""

try:
# Prefer cryptograph-based RSA implementation.
from google.auth.crypt import _cryptography_rsa
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey

RSASigner = _cryptography_rsa.RSASigner
RSAVerifier = _cryptography_rsa.RSAVerifier
except ImportError: # pragma: NO COVER
# Fallback to pure-python RSA implementation if cryptography is
# unavailable.
from google.auth.crypt import _python_rsa
from google.auth import _helpers
from google.auth.crypt import _cryptography_rsa
from google.auth.crypt import _python_rsa
from google.auth.crypt import base

RSASigner = _python_rsa.RSASigner # type: ignore
RSAVerifier = _python_rsa.RSAVerifier # type: ignore
RSA_KEY_MODULE_PREFIX = "rsa.key"


class RSAVerifier(base.Verifier):
"""Verifies RSA cryptographic signatures using public keys.

Args:
public_key (Union["rsa.key.PublicKey", cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey]):
The public key used to verify signatures.
Raises:
ValueError: if an unrecognized public key is provided
"""

def __init__(self, public_key):
module_str = public_key.__class__.__module__
if isinstance(public_key, RSAPublicKey):
impl_lib = _cryptography_rsa
elif module_str.startswith(RSA_KEY_MODULE_PREFIX):
impl_lib = _python_rsa
else:
raise ValueError(f"unrecognized public key type: {type(public_key)}")
self._impl = impl_lib.RSAVerifier(public_key)

@_helpers.copy_docstring(base.Verifier)
def verify(self, message, signature):
return self._impl.verify(message, signature)

@classmethod
def from_string(cls, public_key):
"""Construct a Verifier instance from a public key or public
certificate string.

Args:
public_key (Union[str, bytes]): The public key in PEM format or the
x509 public key certificate.

Returns:
google.auth.crypt.Verifier: The constructed verifier.

Raises:
ValueError: If the public_key can't be parsed.
"""
instance = cls.__new__(cls)
instance._impl = _cryptography_rsa.RSAVerifier.from_string(public_key)
return instance


class RSASigner(base.Signer, base.FromServiceAccountMixin):
"""Signs messages with an RSA private key.

Args:
private_key (Union["rsa.key.PrivateKey", cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey]):
The private key to sign with.
key_id (str): Optional key ID used to identify this private key. This
can be useful to associate the private key with its associated
public key or certificate.

Raises:
ValueError: if an unrecognized public key is provided
"""

def __init__(self, private_key, key_id=None):
module_str = private_key.__class__.__module__
if isinstance(private_key, RSAPrivateKey):
impl_lib = _cryptography_rsa
elif module_str.startswith(RSA_KEY_MODULE_PREFIX):
impl_lib = _python_rsa
else:
raise ValueError(f"unrecognized private key type: {type(private_key)}")
self._impl = impl_lib.RSASigner(private_key, key_id=key_id)

@property # type: ignore
@_helpers.copy_docstring(base.Signer)
def key_id(self):
return self._impl.key_id

@_helpers.copy_docstring(base.Signer)
def sign(self, message):
return self._impl.sign(message)

@classmethod
def from_string(cls, key, key_id=None):
"""Construct a Signer instance from a private key in PEM format.

Args:
key (str): Private key in PEM format.
key_id (str): An optional key id used to identify the private key.

Returns:
google.auth.crypt.Signer: The constructed signer.

Raises:
ValueError: If the key cannot be parsed as PKCS#1 or PKCS#8 in
PEM format.
"""
instance = cls.__new__(cls)
instance._impl = _cryptography_rsa.RSASigner.from_string(key, key_id=key_id)
return instance
21 changes: 11 additions & 10 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,30 @@
from setuptools import find_namespace_packages
from setuptools import setup

cryptography_base_require = [
"cryptography >= 38.0.3",
]

DEPENDENCIES = (
"pyasn1-modules>=0.2.1",
# rsa==4.5 is the last version to support 2.7
# https://github.com/sybrenstuvel/python-rsa/issues/152#issuecomment-643470233
cryptography_base_require,
# TODO: remove rsa from dependencies in next release (replaced with cryptography)i
# https://github.com/googleapis/google-auth-library-python/issues/1810
"rsa>=3.1.4,<5",
)

cryptography_base_require = [
"cryptography >= 38.0.3",
]

requests_extra_require = ["requests >= 2.20.0, < 3.0.0"]

aiohttp_extra_require = ["aiohttp >= 3.6.2, < 4.0.0", *requests_extra_require]

pyjwt_extra_require = ["pyjwt>=2.0", *cryptography_base_require]
pyjwt_extra_require = ["pyjwt>=2.0"]

reauth_extra_require = ["pyu2f>=0.1.5"]

# TODO(https://github.com/googleapis/google-auth-library-python/issues/1738): Add bounds for cryptography and pyopenssl dependencies.
enterprise_cert_extra_require = ["cryptography", "pyopenssl"]
# TODO(https://github.com/googleapis/google-auth-library-python/issues/1738): Add bounds for pyopenssl dependency.
enterprise_cert_extra_require = ["pyopenssl"]

pyopenssl_extra_require = ["pyopenssl>=20.0.0", cryptography_base_require]
pyopenssl_extra_require = ["pyopenssl>=20.0.0"]

# TODO(https://github.com/googleapis/google-auth-library-python/issues/1739): Add bounds for urllib3 and packaging dependencies.
urllib3_extra_require = ["urllib3", "packaging"]
Expand Down Expand Up @@ -76,6 +76,7 @@
]

extras = {
# Note: cryptography was made into a required dependency. Extra is kept for backwards compatibility
"cryptography": cryptography_base_require,
"aiohttp": aiohttp_extra_require,
"enterprise_cert": enterprise_cert_extra_require,
Expand Down
5 changes: 3 additions & 2 deletions tests/crypt/test__python_rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,9 @@ def test_from_service_account_file(self):

class TestModule(object):
def test_import_warning(self):
import importlib
from google.auth.crypt import _python_rsa

with pytest.warns(DeprecationWarning, match="The 'rsa' library is deprecated"):
importlib.reload(_python_rsa)
_python_rsa.RSAVerifier(None)
with pytest.warns(DeprecationWarning, match="The 'rsa' library is deprecated"):
_python_rsa.RSASigner(None)
Loading
Loading