Python で Amazon CloudFront の Signed URL を発行する

CloudFront x S3 => 期限付き URL
------------------------------

facebook や flickr などのように、一部ユーザにだけ画像を見せたいというケース、ありますよね。こういうサービスでは、画像に対して期限付きの URL を発行するのが一般的のようです。Amazon CloudFront と Amazon S3 を使うと、こんな形の期限付き URL を発行できます。

> http://d3iwlid5dsmdb3.cloudfront.net/_default_san.png?Expires=1354332636&Signature=EWVDLqfSRDJFlOazy9ruQV96xPravIFbiUq9GOsTVaL0Uhj3u6LEWXoM3042rWddnc4+CLop/mBCY5P/WcMyDcR5LGFfxEvCltsLYXtjCR+Djnh0WEsp+ogWlGjYAn6eZNtjUfl9FlMbiC6tn7Ehs9oMJu+EaYgpGM6aSlyQ88A=&Key-Pair-Id=APKAJ4YNPASFVCNBXK3A

CloudFront と S3 のつなぎ込みや、鍵ペアの発行などの手順は公式ドキュメントの [Serving Private Content through CloudFront](http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html) に書いてあります。そして最終的な URL の生成についてのコードが Perl、PHP、C#(.NET Framework)、Java で書かれているのですが、Python がありません。これでは仕事になりませんね。

tlslite を使って Signed URL を発行する
------------------------------------

Python で AWS といえば boto……なのですが、今回は違った方法を使います。boto が駄目だった理由は後述します。

"python sign private key" で検索したところ、[Signing a string with RSA private key on Google App Engine Python SDK](
http://stackoverflow.com/questions/2364084/signing-a-string-with-rsa-private-key-on-google-app-engine-python-sdk) がヒットしました。そこにはこう書いてありました。

> The library tlslite included in the gdata python library is a good option. 

ふむふむ、tlslite がいいらしい。さっそくインストールしてみましょう。

```
(venv)rch850mba:Desktop rch850$ pip install tlslite
Downloading/unpacking tlslite
  Downloading tlslite-0.4.3.tar.gz (562Kb): 562Kb downloaded
  Running setup.py egg_info for package tlslite
    
Installing collected packages: tlslite
  Running setup.py install for tlslite
    changing mode of build/scripts-2.7/tls.py from 644 to 755
    changing mode of build/scripts-2.7/tlsdb.py from 644 to 755
    
    changing mode of /Users/rch850/Desktop/cloudfront/venv/bin/tls.py to 755
    changing mode of /Users/rch850/Desktop/cloudfront/venv/bin/tlsdb.py to 755
Successfully installed tlslite
Cleaning up...
```

そして例を見ながらコーディング。

```python
from tlslite.utils import keyfactory
from base64 import b64encode
import time

url = "http://d3iwlid5dsmdb3.cloudfront.net/_default_san.png"
expires = int(time.time() + 60)
privkey_file = "pk-APKAJ4YNPASFVCNBXK3A-cloudfront.pem"
key_pair_id = "APKAJ4YNPASFVCNBXK3A"

policy = '{"Statement":[{"Resource":"%s","Condition":{"DateLessThan":{"AWS:EpochTime":%d}}}]}' % (url, expires)
privkey = keyfactory.parsePrivateKey(open(privkey_file).read())            
print '%s?Expires=%d&Signature=%s&Key-Pair-Id=%s' % (url, expires, b64encode(privkey.hashAndSign(policy)), key_pair_id)
```

これでできた！！！はずです！！！！このあたりの調査に数日かけたので、感嘆符が多くなるのは仕方ないです！！！！！


boto が駄目だった理由
-------------------

boto で一応 CloudFront を使えそうだったので、適当に書いてみたところ……

```python
import boto

c = boto.connect_cloudfront()
dist = c.get_all_distributions()[0].get_distribution()
dist.create_signed_url("http://d3iwlid5dsmdb3.cloudfront.net/apple-touch-icon.png", "APKAJ4YNPASFVCNBXK3A", expire_time=time.time() + 60, private_key_file="./pk-APKAJ4YNPASFVCNBXK3A-cloudfront.pem")
```

すると、以下のエラーが。

> NotImplementedError: Boto depends on the python M2Crypto library to generate signed URLs for CloudFront

M2Crypto が要るとのこと。インストールしてみる。

```
pip install M2crypto
（略）
error: command 'swig' failed with exit status 1
```

swig コマンドが無くてインストールできないとのことです。そこまでして M2Crypto に頼るのは嫌だなぁ、ということで、紆余曲折を経て tlslite にたどり着きました。boto が M2Crypto を使うことになった経緯は[こちら](http://stackoverflow.com/questions/6549787/getting-started-with-secure-aws-cloudfront-streaming-with-python/6590986#6590986)。

インストールのしやすさ以外だと M2Crypto と tlslite どっちが優れてるんだろう？


調査にあたって参考にしたサイト
-------------------------

* [Amazon CloudFront でプライベートコンテンツにアクセス その1 ｜ クラスメソッド開発ブログ](http://dev.classmethod.jp/etc/amazon-cloudfront-private-contents-access-1/)
* [CloudFrontでプライベートコンテンツの公開：Restricting End User Access - Develop with pleasure!](http://d.hatena.ne.jp/techmedia-think/20110522/1306072815)