-
-
Save mekza/b7cdc0858aa1dd22b016 to your computer and use it in GitHub Desktop.
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 |
got this working https://stackoverflow.com/a/49972685/1118576
How do I generate the pem file for this?
@DataGreed note that my snippet might be very outdated since it uses boto and not botocore nor boto3. Better way to do it: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudfront.html#generate-a-signed-url-for-amazon-cloudfront
@mekza thank you very much!
That was helpful! Thanks
more simple and clean code: https://gist.github.com/ox0spy/f532475f394c1b14f0e33ef047f78e05#python-code
Hi there, I was searching for a python version for signing cloudfront cookies and found yours.
@ox0spy I gave it a try with your implementation but I keep getting:
Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
.
I followed the steps you mentioned:
- created the keys, and upload the public to cloudfront
- configured a key group associated to the public key, which then is the one used for restricting access to the s3 bucket.
-the bucket policy is correctly configured to only accept access from the cloudfront distribution (if I disable the restricted access based on the cookies/urls it works fine.)
Do have any idea that might help? Maybe if you could share the values of the variables you are using could give an insight on what I might be doing wrong.
private_key_path=''; key_id = ''; base_url =''; obj_key ='';
Thanks
Hi abaptista, please check my latest code and make sure you choose the correct Trusted Key Group
.
# example of variables
private_key_path = 'the-path-of-your-private-key' # e.g.: './private_key.pem'
key_id = 'KXXXXXXXXXXXQ'
base_url = 'https://xxxx.cloudfront.net'
obj_key = 'the-object-path-of-your-s3' # e.g.: 'images/avatar.png'
new code remove `base_url`, just using the file's cdn url, e.g.: `url = 'https://xxxx.cloudfront.net/images/avatar.png'`
@zhangwm404
Do you still have your updated code? I'm getting a 404 on the link you sent earlier. Any help would be much appreciated.
I tried using this script for generating signed cookies but im getting access denied in response. Is there any thing im missing.
below is the issue i posted in stack overflow
http://stackoverflow.com/questions/43540243/unable-to-use-signed-cookies-for-cloudfront