Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
url = https://github.com/gsutil-mirrors/boto.git
[submodule "gslib/vendored/oauth2client"]
path = gslib/vendored/oauth2client
url = https://github.com/gsutil-mirrors/oauth2client.git
url = https://github.com/gurusai-voleti/oauth2client.git
[submodule "third_party/google-auth-library-python"]
path = third_party/google-auth-library-python
url = https://github.com/googleapis/google-auth-library-python
Expand Down
2 changes: 1 addition & 1 deletion gslib/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ def _WriteBotoConfigFile(self,
if not HAS_CRYPTO:
raise CommandException(
'Service account authentication via a .p12 file requires '
'either\nPyOpenSSL or PyCrypto 2.6 or later. Please install '
'either\ncryptography. Please install '
'either of these\nto proceed, use a JSON-format key file, or '
'configure a different type of credentials.')

Expand Down
52 changes: 17 additions & 35 deletions gslib/commands/signurl.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,9 @@
from gslib.utils.shim_util import GcloudStorageMap, GcloudStorageFlag
from gslib.utils.signurl_helper import CreatePayload, GetFinalUrl, to_bytes

try:
# Check for openssl.
# pylint: disable=C6204
from OpenSSL.crypto import FILETYPE_PEM
from OpenSSL.crypto import load_privatekey
from OpenSSL.crypto import sign
from OpenSSL.crypto import PKey
HAVE_OPENSSL = True
except ImportError:
load_privatekey = None
sign = None
HAVE_OPENSSL = False
FILETYPE_PEM = None

try:
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.serialization import pkcs12
Expand All @@ -76,6 +63,7 @@
HAVE_CRYPTO = False
_CRYPTO_IMPORT_ERROR = "pyca/cryptography is not available. Either install it, or please consider using the .json keyfile"

HAVE_OPENSSL = HAVE_CRYPTO
_AUTO_DETECT_REGION = 'auto'
_MAX_EXPIRATION_TIME = timedelta(days=7)
_MAX_EXPIRATION_TIME_WITH_MINUS_U = timedelta(hours=12)
Expand Down Expand Up @@ -307,12 +295,6 @@ def _GenSignedUrl(key,
signed_headers=signed_headers,
string_to_sign_debug=string_to_sign_debug)
else:
if six.PY2:
digest = b'RSA-SHA256'
else:
# Your IDE may complain about this due to a bad docstring in pyOpenSsl:
# https://github.com/pyca/pyopenssl/issues/741
digest = 'RSA-SHA256'
string_to_sign, canonical_query_string = CreatePayload(
client_id=client_id,
method=method,
Expand All @@ -324,12 +306,10 @@ def _GenSignedUrl(key,
signed_headers=signed_headers,
billing_project=billing_project,
string_to_sign_debug=string_to_sign_debug)
if isinstance(key, PKey):
raw_signature = sign(key, string_to_sign, digest)
else:
if not HAVE_CRYPTO:
raise CommandException(_CRYPTO_IMPORT_ERROR)
raw_signature = key.sign(to_bytes(string_to_sign), padding.PKCS1v15(), hashes.SHA256())
if not HAVE_CRYPTO:
raise CommandException(_CRYPTO_IMPORT_ERROR)
string_to_sign = string_to_sign.replace('\r\n', '\n')
raw_signature = key.sign(to_bytes(string_to_sign), padding.PKCS1v15(), hashes.SHA256())
final_url = GetFinalUrl(raw_signature, gs_host, gcs_path,
canonical_query_string)
return final_url
Expand All @@ -353,8 +333,7 @@ def _ReadJSONKeystore(ks_contents, passwd=None):

Assumes this keystore was downloaded from the Cloud Platform Console.
By default, JSON keystore private keys from the Cloud Platform Console
aren't encrypted so the passwd is optional as load_privatekey will
prompt for the PEM passphrase if the key is encrypted.
aren't encrypted so the passwd is optional.

Arguments:
ks_contents: JSON formatted string representing the keystore contents. Must
Expand All @@ -378,10 +357,13 @@ def _ReadJSONKeystore(ks_contents, passwd=None):
raise ValueError('JSON keystore doesn\'t contain required fields')

client_email = ks['client_email']
if passwd:
key = load_privatekey(FILETYPE_PEM, ks['private_key'], passwd)
else:
key = load_privatekey(FILETYPE_PEM, ks['private_key'])
if not HAVE_CRYPTO:
raise CommandException(_CRYPTO_IMPORT_ERROR)

key = serialization.load_pem_private_key(
ks['private_key'].encode('utf-8'),
password=passwd.encode('utf-8') if passwd else None
)

return key, client_email

Expand Down Expand Up @@ -617,8 +599,8 @@ def RunCommand(self):
"""Command entry point for signurl command."""
if not HAVE_OPENSSL:
raise CommandException(
'The signurl command requires the pyopenssl library (try pip '
'install pyopenssl or easy_install pyopenssl)')
'The signurl command requires the cryptography library (try pip '
'install cryptography)')

method, delta, content_type, passwd, region, use_service_account, billing_project = (
self._ParseAndCheckSubOpts())
Expand All @@ -631,7 +613,7 @@ def RunCommand(self):
try:
key, client_email = _ReadJSONKeystore(
open(self.args[0], 'rb').read(), passwd)
except ValueError:
except:
# Ignore and try parsing as a pkcs12.
if not passwd:
passwd = getpass.getpass('Keystore password:')
Expand Down
6 changes: 0 additions & 6 deletions gslib/tests/test_gcs_json_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@
add_move(MovedModule("mock", "mock", "unittest.mock"))
from six.moves import mock

try:
from OpenSSL.crypto import load_pkcs12
HAS_OPENSSL = True
except ImportError:
HAS_OPENSSL = False

try:
import cryptography
HAS_CRYPTO = True
Expand Down
17 changes: 12 additions & 5 deletions gslib/tests/test_signurl.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,15 @@ def testSignUrlWithURLEncodeRequiredChars(self):
lines = lines[1:]

for obj, line, partial_url in zip(objs, lines, expected_partial_urls):
self.assertIn(obj, line)
self.assertIn(partial_url, line)
self.assertIn('x-goog-credential='+TEST_EMAIL, line)
# If we are on Windows, the output might be escaped to avoid crashes
try:
self.assertIn(obj, line)
except AssertionError:
# Escape the object string to match the 'backslashreplace' output
escaped_obj = obj.encode('ascii', 'backslashreplace').decode('ascii')
self.assertIn(escaped_obj, line)
self.assertIn(partial_url, line)
self.assertIn('x-goog-credential='+TEST_EMAIL, line)
self.assertIn('%2Fus%2F', stdout)

def testSignUrlWithWildcard(self):
Expand Down Expand Up @@ -239,8 +245,9 @@ def testSignUrlOfNonObjectUrl(self):
"""Tests the signurl output of a non-existent file."""
self.RunGsUtil(['signurl', self._GetJSONKsFile(), 'gs://'],
expected_status=1)
self.RunGsUtil(['signurl', 'file://tmp/abc', 'gs://bucket'],
expected_status=1)
self.RunGsUtil(['signurl', 'file:///tmp/abc', 'gs://bucket'],
expected_status=1,
return_stderr=True)


@unittest.skipUnless(HAVE_OPENSSL, 'signurl requires pyopenssl.')
Expand Down
4 changes: 2 additions & 2 deletions gslib/utils/boto_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def ConfigureNoOpAuthIfNeeded():
raise CommandException('\n'.join(
textwrap.wrap(
'Your gsutil is configured with an OAuth2 service account, but '
'you do not have PyOpenSSL or PyCrypto 2.6 or later installed. '
'you do not have cryptography installed. '
'Service account authentication requires one of these libraries; '
'please reactivate your service account via the gcloud auth '
'command and ensure any gcloud packages necessary for '
Expand All @@ -123,7 +123,7 @@ def ConfigureNoOpAuthIfNeeded():
raise CommandException('\n'.join(
textwrap.wrap(
'Your gsutil is configured with an OAuth2 service account, but '
'you do not have PyOpenSSL or PyCrypto 2.6 or later installed. '
'you do not have cryptography installed. '
'Service account authentication requires one of these libraries; '
'please install either of them to proceed, or configure a '
'different type of credentials with "gsutil config".')))
Expand Down
2 changes: 1 addition & 1 deletion gslib/utils/signurl_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def CreatePayload(client_id,

def GetFinalUrl(raw_signature, host, path, canonical_query_string):
"""Get the final signed url."""
signature = base64.b16encode(raw_signature).lower().decode()
signature = base64.b16encode(raw_signature).lower().decode(UTF8)
return _SIGNED_URL_FORMAT.format(host=host,
path=path,
sig=signature,
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
# 3.0.5 is the last version that supports Python 3.3 or lower.
'mock>=2.0.0, <=3.0.5; python_version < "3.3"',
'monotonic>=1.4',
'pyOpenSSL>=0.13, <=24.2.1',
'pyOpenSSL>=26.0.0',
'cryptography>=46.0.0',
'retry_decorator>=1.0.0',
'six>=1.17.0',
# aiohttp is the extra dependency that contains requests lib.
Expand Down
Loading