Skip to content
This repository was archived by the owner on Jan 18, 2025. It is now read-only.

Commit b59ac6e

Browse files
committed
Addressing review comments.
1 parent 8627411 commit b59ac6e

File tree

4 files changed

+64
-41
lines changed

4 files changed

+64
-41
lines changed

oauth2client/_pure_python_crypt.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,12 @@
3939
> openssl rsa > key.pem
4040
"""
4141

42-
4342
_POW2 = (128, 64, 32, 16, 8, 4, 2, 1)
43+
_PKCS1_MARKER = ('-----BEGIN RSA PRIVATE KEY-----',
44+
'-----END RSA PRIVATE KEY-----')
45+
_PKCS8_MARKER = ('-----BEGIN PRIVATE KEY-----',
46+
'-----END PRIVATE KEY-----')
47+
_PKCS8_SPEC = PrivateKeyInfo()
4448

4549

4650
def _bit_list_to_bytes(bit_list):
@@ -88,8 +92,8 @@ def verify(self, message, signature):
8892
except (ValueError, rsa.pkcs1.VerificationError):
8993
return False
9094

91-
@staticmethod
92-
def from_string(key_pem, is_x509_cert):
95+
@classmethod
96+
def from_string(cls, key_pem, is_x509_cert):
9397
"""Construct a Verified instance from a string.
9498
9599
Args:
@@ -119,7 +123,7 @@ def from_string(key_pem, is_x509_cert):
119123
pubkey = rsa.PublicKey.load_pkcs1(key_bytes, 'DER')
120124
else:
121125
pubkey = rsa.PublicKey.load_pkcs1(key_pem, 'PEM')
122-
return RsaVerifier(pubkey)
126+
return cls(pubkey)
123127

124128

125129
class RsaSigner(object):
@@ -129,12 +133,6 @@ class RsaSigner(object):
129133
pkey: rsa.key.PrivateKey (or equiv), The private key to sign with.
130134
"""
131135

132-
_PKCS1_MARKER = ('-----BEGIN RSA PRIVATE KEY-----',
133-
'-----END RSA PRIVATE KEY-----')
134-
_PKCS8_MARKER = ('-----BEGIN PRIVATE KEY-----',
135-
'-----END PRIVATE KEY-----')
136-
_PKCS8_SPEC = PrivateKeyInfo()
137-
138136
def __init__(self, pkey):
139137
self._key = pkey
140138

@@ -167,14 +165,14 @@ def from_string(cls, key, password='notasecret'):
167165
"""
168166
key = _from_bytes(key) # pem expects str in Py3
169167
marker_id, key_bytes = pem.readPemBlocksFromFile(
170-
six.StringIO(key), cls._PKCS1_MARKER, cls._PKCS8_MARKER)
168+
six.StringIO(key), _PKCS1_MARKER, _PKCS8_MARKER)
171169

172170
if marker_id == 0:
173171
pkey = rsa.key.PrivateKey.load_pkcs1(key_bytes,
174172
format='DER')
175173
elif marker_id == 1:
176174
key_info, remaining = decoder.decode(
177-
key_bytes, asn1Spec=cls._PKCS8_SPEC)
175+
key_bytes, asn1Spec=_PKCS8_SPEC)
178176
if remaining != b'':
179177
raise ValueError('Unused bytes', remaining)
180178
pkey_info = key_info.getComponentByName('privateKey')

oauth2client/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ def from_json(cls, json_str):
12431243
# possible return type of GoogleCredentials.get_application_default()
12441244
if (data['_module'] == 'oauth2client.service_account' and
12451245
data['_class'] == 'ServiceAccountCredentials'):
1246-
return ServiceAccountCredentials.from_json(s)
1246+
return ServiceAccountCredentials.from_json(json_str)
12471247

12481248
token_expiry = _parse_expiry(data.get('token_expiry'))
12491249
google_credentials = cls(

oauth2client/service_account.py

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,47 @@
5454
_PKCS12_KEY = '_private_key_pkcs12'
5555

5656

57+
def _get_signer(pkcs8_pem, pkcs12, password):
58+
"""Get a signer from the ``crypt`` module.
59+
60+
Will be used by :class:`ServiceAccountCredentials` to sign the
61+
header and payload in a JWT.
62+
63+
Args:
64+
pkcs8_pem: bytes or ``NoneType``, The content of a PKCS#8 key
65+
in PEM format.
66+
pkcs12: bytes or ``NoneType``, The content of a PKCS#12 key.
67+
password: string or ``NoneType``, Password for PKCS#12 private
68+
key. Defaults to ``notasecret``.
69+
70+
Returns:
71+
tuple, A pair of :class:`crypt.Signer` and the potentially updated
72+
value of ``password``.
73+
74+
Raises:
75+
ValueError: If both ``pkcs8_pem`` and ``pkcs12`` are set.
76+
ValueError: If ``pkcs8_pem`` is set and ``password`` is passed in.
77+
ValueError: If ``pkcs12`` is set and ``password`` is not passed in.
78+
, """
79+
if pkcs8_pem is not None:
80+
if pkcs12 is not None:
81+
raise ValueError(_AT_LEAST_ONE_KEY, 'Both were passed.')
82+
if password is not None:
83+
raise ValueError('Private key password can only be used with '
84+
'a PKCS#12 key.')
85+
return crypt.Signer.from_string(pkcs8_pem), None
86+
87+
if pkcs12 is None:
88+
raise ValueError(_AT_LEAST_ONE_KEY, 'Neither were passed.')
89+
# From here we assume a PKCS#12 key, which is only
90+
# supported by pyOpenSSL.
91+
if crypt.Signer is not crypt.OpenSSLSigner:
92+
raise EnvironmentError(_PKCS12_ERROR)
93+
if password is None:
94+
password = _PASSWORD_DEFAULT
95+
return crypt.Signer.from_string(pkcs12, password), password
96+
97+
5798
class ServiceAccountCredentials(AssertionCredentials):
5899
"""Service Account credential for OAuth 2.0 signed JWT grants.
59100
@@ -76,8 +117,8 @@ class ServiceAccountCredentials(AssertionCredentials):
76117
Args:
77118
service_account_email: string, The email associated with the
78119
service account.
79-
private_key_pkcs8_pem: bytes, The content of a PKCS#8 key in PEM
80-
format.
120+
private_key_pkcs8_pem: bytes, (Optional) The content of a PKCS#8 key
121+
in PEM format.
81122
private_key_pkcs12: bytes, (Optional) The content of a PKCS#12 key.
82123
private_key_password: string, (Optional) Password for PKCS#12 private
83124
key. Defaults to ``notasecret``.
@@ -97,15 +138,15 @@ class ServiceAccountCredentials(AssertionCredentials):
97138
access token.
98139
_revoke_uri: string, (Optional) The URI to use when revoking an
99140
access token.
100-
kwargs: dict, Extra key-value pairs to send in the payload body
101-
when making an assertion.
141+
kwargs: dict, Extra key-value pairs (both strings) to send in the
142+
payload body when making an assertion.
102143
103144
Raises:
104145
ValueError: If both ``private_key_pkcs8_pem`` and
105146
``private_key_pkcs12`` are set.
106147
ValueError: If ``private_key_pkcs8_pem`` is set and
107148
``private_key_password`` is passed in.
108-
ValueError: If ``private_key_pkcs11`` is set and
149+
ValueError: If ``private_key_pkcs12`` is set and
109150
``private_key_password`` is not passed in.
110151
"""
111152

@@ -138,33 +179,15 @@ def __init__(self,
138179
self._private_key_pkcs8_pem = private_key_pkcs8_pem
139180
self._private_key_pkcs12 = private_key_pkcs12
140181
self._private_key_password = private_key_password
141-
self._signer = self._get_signer()
182+
self._signer, self._private_key_password = _get_signer(
183+
self._private_key_pkcs8_pem, self._private_key_pkcs12,
184+
self._private_key_password)
142185
self._scopes = util.scopes_to_string(scopes)
143186
self._private_key_id = private_key_id
144187
self._service_account_id = service_account_id
145188
self._user_agent = user_agent
146189
self._kwargs = kwargs
147190

148-
def _get_signer(self):
149-
if self._private_key_pkcs8_pem is not None:
150-
if self._private_key_pkcs12 is not None:
151-
raise ValueError(_AT_LEAST_ONE_KEY, 'Both were passed.')
152-
if self._private_key_password is not None:
153-
raise ValueError('Private key password can only be used with '
154-
'a PKCS#12 key.')
155-
return crypt.Signer.from_string(self._private_key_pkcs8_pem)
156-
157-
if self._private_key_pkcs12 is None:
158-
raise ValueError(_AT_LEAST_ONE_KEY, 'Neither were passed.')
159-
# From here we assume a PKCS#12 key, which is only
160-
# supported by pyOpenSSL.
161-
if crypt.Signer is not crypt.OpenSSLSigner:
162-
raise EnvironmentError(_PKCS12_ERROR)
163-
if self._private_key_password is None:
164-
self._private_key_password = _PASSWORD_DEFAULT
165-
return crypt.Signer.from_string(self._private_key_pkcs12,
166-
self._private_key_password)
167-
168191
def _to_json(self, strip, to_serialize=None):
169192
"""Utility function that creates JSON repr. of a Credentials object.
170193

tests/test__pure_python_crypt.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import unittest2
2424

2525
from oauth2client._helpers import _from_bytes
26+
from oauth2client import _pure_python_crypt
2627
from oauth2client.crypt import RsaSigner
2728
from oauth2client.crypt import RsaVerifier
2829

@@ -150,7 +151,8 @@ def test_from_string_pkcs8(self):
150151
def test_from_string_pkcs8_extra_bytes(self):
151152
key_bytes = self._load_pkcs8_key_bytes()
152153
_, pem_bytes = pem.readPemBlocksFromFile(
153-
six.StringIO(_from_bytes(key_bytes)), RsaSigner._PKCS8_MARKER)
154+
six.StringIO(_from_bytes(key_bytes)),
155+
_pure_python_crypt._PKCS8_MARKER)
154156

155157
with mock.patch('pyasn1.codec.der.decoder.decode') as mock_decode:
156158
key_info, remaining = None, 'extra'
@@ -159,7 +161,7 @@ def test_from_string_pkcs8_extra_bytes(self):
159161
RsaSigner.from_string(key_bytes)
160162
# Verify mock was called.
161163
mock_decode.assert_called_once_with(
162-
pem_bytes, asn1Spec=RsaSigner._PKCS8_SPEC)
164+
pem_bytes, asn1Spec=_pure_python_crypt._PKCS8_SPEC)
163165

164166
def test_from_string_pkcs8_unicode(self):
165167
key_bytes = _from_bytes(self._load_pkcs8_key_bytes())

0 commit comments

Comments
 (0)