async def verify_signature(data, signature, key_name, iam_client): key_data = await iam_client.get_public_key(key_name) cert = x509.load_pem_x509_certificate(decode(key_data['publicKeyData']), backend=default_backend()) pubkey = cert.public_key() # raises on failure pubkey.verify(decode(signature), data.encode(), padding.PKCS1v15(), hashes.SHA256())
async def get_signed_url( # pylint: disable=too-many-locals self, expiration: int, headers: Optional[dict] = None, query_params: Optional[dict] = None, http_method: str = 'GET', iam_client: Optional[IamClient] = None, service_account_email: Optional[str] = None, service_file: Optional[str] = None, token: Optional[Token] = None, session: Optional[aiohttp.ClientSession] = None) -> str: """ Create a temporary access URL for Storage Blob accessible by anyone with the link. Adapted from Google Documentation: https://cloud.google.com/storage/docs/access-control/signing-urls-manually#python-sample """ if expiration > 604800: raise ValueError("expiration time can't be longer than 604800 " 'seconds (7 days)') iam_client = iam_client or IamClient( service_file=service_file, token=token, session=session) quoted_name = quote(self.name, safe='') canonical_uri = f'/{self.bucket.name}/{quoted_name}' datetime_now = datetime.datetime.utcnow() request_timestamp = datetime_now.strftime('%Y%m%dT%H%M%SZ') datestamp = datetime_now.strftime('%Y%m%d') service_account_email = (service_account_email or iam_client.service_account_email) credential_scope = f'{datestamp}/auto/storage/goog4_request' credential = f'{service_account_email}/{credential_scope}' headers = headers or {} headers['host'] = HOST ordered_headers = collections.OrderedDict(sorted(headers.items())) canonical_headers = ''.join(f'{str(k).lower()}:{str(v).lower()}\n' for k, v in ordered_headers.items()) signed_headers = ';'.join(f'{str(k).lower()}' for k in ordered_headers.keys()) query_params = query_params or {} query_params['X-Goog-Algorithm'] = 'GOOG4-RSA-SHA256' query_params['X-Goog-Credential'] = credential query_params['X-Goog-Date'] = request_timestamp query_params['X-Goog-Expires'] = expiration query_params['X-Goog-SignedHeaders'] = signed_headers ordered_query_params = collections.OrderedDict( sorted(query_params.items())) canonical_query_str = '&'.join( f'{quote(str(k), safe="")}={quote(str(v), safe="")}' for k, v in ordered_query_params.items()) canonical_req = '\n'.join([ http_method, canonical_uri, canonical_query_str, canonical_headers, signed_headers, 'UNSIGNED-PAYLOAD' ]) canonical_req_hash = hashlib.sha256(canonical_req.encode()).hexdigest() str_to_sign = '\n'.join([ 'GOOG4-RSA-SHA256', request_timestamp, credential_scope, canonical_req_hash ]) signed_resp = await iam_client.sign_blob( str_to_sign, service_account_email=service_account_email, session=session) signature = binascii.hexlify(decode( signed_resp['signedBlob'])).decode() return (f'https://{HOST}{canonical_uri}?{canonical_query_str}' f'&X-Goog-Signature={signature}')