Skip to content

Instantly share code, notes, and snippets.

@mekza
Last active January 16, 2025 21:41
Show Gist options
  • Save mekza/b7cdc0858aa1dd22b016 to your computer and use it in GitHub Desktop.
Save mekza/b7cdc0858aa1dd22b016 to your computer and use it in GitHub Desktop.
Signed URLs and Signed Cookies for CloudFront in Python with boto
from boto.cloudfront.distribution import Distribution
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
import base64
class BetterThanBoto(Distribution):
def sign_rsa(self, message):
private_key = serialization.load_pem_private_key(self.keyfile, password=None,
backend=default_backend())
signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
message = message.encode('utf-8')
signer.update(message)
return signer.finalize()
def _sign_string(self, message, private_key_file=None, private_key_string=None):
if private_key_file:
self.keyfile = open(private_key_file, 'rb').read()
elif private_key_string:
self.keyfile = private_key_string.encode('utf-8')
return self.sign_rsa(message)
@staticmethod
def _url_base64_encode(msg):
"""
Base64 encodes a string using the URL-safe characters specified by
Amazon.
"""
msg_base64 = base64.b64encode(msg).decode('utf-8')
msg_base64 = msg_base64.replace('+', '-')
msg_base64 = msg_base64.replace('=', '_')
msg_base64 = msg_base64.replace('/', '~')
return msg_base64
def generate_signature(self, policy, private_key_file=None):
"""
:param policy: no-whitespace json str (NOT encoded yet)
:param private_key_file: your .pem file with which to sign the policy
:return: encoded signature for use in cookie
"""
# Distribution._create_signing_params()
signature = self._sign_string(policy, private_key_file)
# now base64 encode the signature & make URL safe
encoded_signature = self._url_base64_encode(signature)
return encoded_signature
def create_signed_cookies(self, url, private_key_file=None, keypair_id=None,
expires_at=20, secure=True):
"""
generate the Cloudfront download distirbution signed cookies
:param resource: The object or path of resource.
Examples: 'dir/object.mp4', 'dir/*', '*'
:param private_key_file: Path to the private key file (pem encoded)
:param key_pair_id: ID of the keypair used to sign the cookie
:param expire_minutes: The number of minutes until expiration
:param secure: use https or http protocol for Cloudfront URL - update
to match your distribution settings.
:return: Cookies to be set
"""
# generate no-whitespace json policy,
# then base64 encode & make url safe
policy = self._custom_policy(
url,
expires_at
)
encoded_policy = self._url_base64_encode(policy.encode('utf-8'))
# assemble the 3 Cloudfront cookies
signature = self.generate_signature(
policy, private_key_file=private_key_file
)
cookies = {
"CloudFront-Policy": encoded_policy,
"CloudFront-Signature": signature,
"CloudFront-Key-Pair-Id": keypair_id
}
return cookies
def sign_to_cloudfront(object_url, expires_at):
""" Sign URL to distribute file"""
cf = BetterThanBoto()
url = cf.create_signed_url(url=object_url,
keypair_id="XXXXXXXXXXX",
expire_time=expires_at,
private_key_file="ssl/key.pem")
return url
def create_signed_cookies(object_url, expires_at):
"""
Create a signed cookie
"""
cf = BetterThanBoto()
cookies = cf.create_signed_cookies(url=object_url,
keypair_id=keypair_id="XXXXXXXXXXX",
expires_at=expires_at,
private_key_file="ssl/key.pem")
return cookies
@zh4n7wm
Copy link

zh4n7wm commented Jan 13, 2022

@fitcwill
Copy link

Latest link appears to be https://gist.github.com/zh4n7wm/f532475f394c1b14f0e33ef047f78e05#python-code. Seems to be a username change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment