【アップデート】CloudFrontの署名付きURLでECDSA鍵を利用可能に!──実測で91%高速化、55%のURL短縮効果
Contents
はじめに
2025 年 9 月 9 日、Amazon CloudFront の署名付き URL に ECDSA(Elliptic Curve Digital Signature Algorithm: 楕円曲線デジタル署名アルゴリズム)がサポートされました。これまでの RSA-2048 に加えて、ECDSA P-256(prime256v1)が利用可能になったことで、より高速な署名生成と短い URL 生成が実現できます。
本記事では、実際に ECDSA キーを使用した署名付き URL 生成から性能比較までを紹介します。
アップデート概要
主な変更点
- CloudFront の署名付き URL で ECDSA(P-256)がサポートされた
- これまで RSA ベースの暗号化アルゴリズムのみだったが、RSA と ECDSA の両方から選択可能になった
- より高速な署名生成と短い URL 生成が可能になった
期待されるメリット
- 署名生成速度の向上: ECDSA は RSA より高速な署名処理が可能なため
- URL 長の短縮: 署名サイズが小さいため URL 全体が短くなるため
- 計算効率の改善: より少ない CPU リソースで同等のセキュリティを実現可能なため
検証準備
1. キーペアを生成
ECDSA キーペアを生成
# ECDSA P-256 秘密鍵を生成
openssl ecparam -name prime256v1 -genkey -noout -out ecdsa-private.pem
# 公開鍵を生成
openssl ec -in ecdsa-private.pem -pubout -out ecdsa-public.pem
RSA キーペアを生成
# RSA 2048ビット鍵
openssl genrsa -out rsa-private.pem 2048
openssl rsa -pubout -in rsa-private.pem -out rsa-public.pem
生成されたキーを確認
# ECDSA鍵の詳細確認
openssl ec -in ecdsa-private.pem -text -noout
#read EC key
#Private-Key: (256 bit)
#priv:
# 05:70:ee:e3:9a:43:a3:a9:43:0c:03:c6:c5:51:92:
# 98:9b:bb:aa:f9:e8:04:c9:fc:f3:70:e6:1c:ec:5e:
# fb:7f
#pub:
# 04:3f:a0:64:f0:48:dc:37:fc:6a:9d:f9:f3:c2:ad:
# ca:e9:a4:fb:56:ee:be:30:1c:fc:24:09:07:d8:92:
# 92:e7:7e:aa:21:ab:e4:17:da:3c:9d:f1:35:1e:04:
# ca:a1:87:3f:9c:f6:c7:78:9c:03:84:23:4f:28:da:
# b1:fc:d1:13:4d
#ASN1 OID: prime256v1
#NIST CURVE: P-256
# RSA鍵の詳細確認
openssl rsa -in rsa-private.pem -text -noout
#RSA Private-Key: (2048 bit, 2 primes)
#modulus:
# 00:b9:65:c9:7f:5d:83:39:b9:41:9f:20:c0:ba:82:
# a6:33:c5:ca:aa:d0:10:33:87:04:9d:dd:34:46:1a:
# 27:71:aa:69:3e:60:ea:7a:51:c5:df:37:f9:3b:bb:
# 74:64:ef:9a:ce:82:14:df:cc:9c:9d:1a:2b:72:e9:
# 76:22:c9:86:b1:e9:1b:bd:69:24:ce:5e:54:60:0b:
# 0f:ed:24:ba:02:af:e1:84:17:d5:aa:18:bd:a5:a0:
# 4a:32:64:8e:df:89:97:31:49:40:05:fb:36:ba:aa:
# a5:55:da:98:a6:d1:c8:8e:54:b5:a9:82:08:d9:38:
# e6:77:c8:86:16:6c:15:0a:0e:49:ce:c7:c1:b5:6b:
# ee:fd:f5:39:ba:49:99:07:08:3f:99:ec:24:e8:84:
# 50:b7:cc:00:d7:54:aa:69:47:54:5c:9c:e9:f0:01:
# 80:f9:a0:ee:81:a9:b9:96:26:d3:d2:0e:cf:fc:fe:
# a3:ca:35:de:1e:15:6a:4d:4e:bb:55:40:01:cd:37:
# 0d:bf:0a:fb:ac:88:5d:ec:06:94:9a:71:66:1d:0c:
# 8d:5f:03:da:bb:b6:3f:4d:00:b9:f7:73:c3:a2:4e:
# c6:1c:ad:59:3c:da:de:d7:2c:e7:14:75:f3:76:eb:
# a3:ba:65:31:4a:82:d7:a0:44:0b:92:e8:20:fd:16:
# 77:6b
#publicExponent: 65537 (0x10001)
#privateExponent:
# 7b:08:d5:a3:b4:87:35:73:7f:dc:fe:7b:68:4a:f3:
# c9:3f:e5:b3:5d:c8:a2:a9:63:a2:ef:fd:94:d8:1e:
# 7d:0c:3e:33:ab:55:70:3f:7c:35:1a:8c:47:db:aa:
# ee:a1:23:f7:0f:d4:92:80:d3:1e:5f:13:9e:7a:d8:
# 6f:55:96:11:d3:45:19:7a:01:77:02:9a:b3:f2:23:
# 6b:99:07:e8:68:98:1c:75:44:d2:51:00:ea:3d:69:
# 7f:ef:c3:f1:0c:fa:4d:36:f3:4a:04:e0:d9:18:0e:
# 08:df:bc:96:58:79:e4:1a:cb:43:e6:8b:c0:bc:46:
# 16:0f:f9:0c:40:45:e6:19:5c:23:16:a4:65:86:2e:
# dd:f2:cd:84:02:05:55:18:b0:4c:02:a0:f3:05:76:
# 0d:74:80:db:a0:9a:e8:cc:85:72:06:82:79:32:a2:
# 30:fc:34:19:78:cd:ea:27:10:76:4e:57:d3:67:9b:
#:(略)
2. CloudFront への公開鍵登録
ECDSA 公開鍵の登録
#!/bin/bash
ECDSA_KEY_NAME="my-ecdsa-signing-key"
ECDSA_CALLER_REFERENCE=$(date +%s)
# パブリックキーの内容を取得
ECDSA_KEY_CONTENT=$(cat ecdsa-public.pem)
# パブリックキーを作成
ECDSA_KEY_RESPONSE=$(aws cloudfront create-public-key \
--public-key-config \
Name="$ECDSA_KEY_NAME",CallerReference="$ECDSA_CALLER_REFERENCE",EncodedKey="$ECDSA_KEY_CONTENT",Comment="ECDSA P-256 key for signed URLs")
# パブリックキーIDを取得
export ECDSA_KEY_ID=$(echo $ECDSA_KEY_RESPONSE | jq -r '.PublicKey.Id')
echo "Created Public Key ID: $ECDSA_KEY_ID"
RSA 公開鍵の登録
RSA_KEY_NAME="my-rsa-signing-key"
RSA_CALLER_REFERENCE=$(date +%s)
# パブリックキーの内容を取得
RSA_KEY_CONTENT=$(cat rsa-public.pem)
# パブリックキーを作成
RSA_KEY_RESPONSE=$(aws cloudfront create-public-key \
--public-key-config \
Name="$RSA_KEY_NAME",CallerReference="$RSA_CALLER_REFERENCE",EncodedKey="$RSA_KEY_CONTENT",Comment="RSA 2048 key for signed URLs")
# パブリックキーIDを取得
export RSA_KEY_ID=$(echo $RSA_KEY_RESPONSE | jq -r '.PublicKey.Id')
echo "Created Public Key ID: $RSA_KEY_ID"
3. CloudFront へのキーグループ作成
ECDSA キーグループ作成
# ECDSAキーグループ作成
ECDSA_KEY_GROUP_RESPONSE=$(aws cloudfront create-key-group \
--key-group-config \
Name="ecdsa-key-group",Items="$ECDSA_KEY_ID",Comment="ECDSA key group for signed URLs")
# キーグループIDを取得
ECDSA_KEY_GROUP_ID=$(echo $ECDSA_KEY_GROUP_RESPONSE | jq -r '.KeyGroup.Id')
echo "Created Key Group ID: $ECDSA_KEY_GROUP_ID"
RSA キーグループ作成
# RSAキーグループ作成
RSA_KEY_GROUP_RESPONSE=$(aws cloudfront create-key-group \
--key-group-config \
Name="rsa-key-group",Items="$RSA_KEY_ID",Comment="RSA key group for signed URLs")
# キーグループIDを取得
RSA_KEY_GROUP_ID=$(echo $RSA_KEY_GROUP_RESPONSE | jq -r '.KeyGroup.Id')
echo "Created Key Group ID: $RSA_KEY_GROUP_ID"
4. CloudFront ディストリビューション設定
⚠️ これを忘れると署名付き URL を生成しても表示できません!!
AWS Management Console で以下を設定します。
- ディストリビューションの「Behaviors」タブ
- 対象のビヘイビアで「Edit」を選択
- 「Restrict viewer access」を「Yes」に設定
- 「Trusted authorization type」を「Trusted key groups」に設定
- 作成したキーグループを選択
5. 署名付き URL 生成プログラムの実装
AWS CLI の制限事項
⚠️ 重要: 2025 年 9 月 9 日時点で、AWS CLI のcloudfront sign
コマンドは ECDSA キーをサポートしていないためか、RSA ではないというエラーがでます。
#aws --version
#aws-cli/2.29.0 Python/3.13.7 Linux/5.15.167....
# AWS CLIでECDSA鍵を使用するとエラーが発生
aws cloudfront sign \
--url https://example.com/file.jpg \
--key-pair-id $ECDSA_KEY_ID \
--private-key file://ecdsa-private.pem \
--date-less-than "2025-09-10T12:09:03Z"
# エラー: RSA private key not found in PEM.
Python 実装
ここは生成 AI にお願いしました。
ECDSA 署名付き URL 生成
# ecdsa_signer.py
import sys
import time
from botocore.signers import CloudFrontSigner
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.backends import default_backend
import os
from datetime import datetime
def ecdsa_signer(message):
with open("ecdsa-private.pem", "rb") as key_file:
private_key = load_pem_private_key(
key_file.read(), password=None, backend=default_backend()
)
signature = private_key.sign(message, ec.ECDSA(hashes.SHA1()))
return signature
def generate_signed_url(url, key_pair_id, date_less_than):
start_time = time.time()
cf_signer = CloudFrontSigner(key_pair_id, ecdsa_signer)
signed_url = cf_signer.generate_presigned_url(
url,
date_less_than=datetime.fromisoformat(date_less_than.replace("Z", "+00:00")),
)
processing_time_ms = (time.time() - start_time) * 1000
print(f"# ECDSA processing time: {processing_time_ms:.2f}ms", file=sys.stderr)
print(f"# ECDSA URL length: {len(signed_url)} characters", file=sys.stderr)
return signed_url
if __name__ == "__main__":
url = sys.argv[1] if len(sys.argv) > 1 else "https://example.com/file.jpg"
expiry_time = sys.argv[2] if len(sys.argv) > 2 else "2025-09-10T12:09:03Z"
key_pair_id = os.environ.get("ECDSA_KEY_ID")
signed_url = generate_signed_url(url, key_pair_id, expiry_time)
print(signed_url)
RSA 署名付き URL 生成
# rsa_signer.py
import sys
import time
from botocore.signers import CloudFrontSigner
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.backends import default_backend
import os
from datetime import datetime
def rsa_signer(message):
with open("rsa-private.pem", "rb") as key_file:
private_key = load_pem_private_key(
key_file.read(), password=None, backend=default_backend()
)
signature = private_key.sign(message, padding.PKCS1v15(), hashes.SHA1())
return signature
def generate_signed_url(url, key_pair_id, date_less_than):
start_time = time.time()
cf_signer = CloudFrontSigner(key_pair_id, rsa_signer)
signed_url = cf_signer.generate_presigned_url(
url,
date_less_than=datetime.fromisoformat(date_less_than.replace("Z", "+00:00")),
)
processing_time_ms = (time.time() - start_time) * 1000
print(f"# RSA processing time: {processing_time_ms:.2f}ms", file=sys.stderr)
print(f"# RSA URL length: {len(signed_url)} characters", file=sys.stderr)
return signed_url
if __name__ == "__main__":
url = sys.argv[1] if len(sys.argv) > 1 else "https://example.com/file.jpg"
expiry_time = sys.argv[2] if len(sys.argv) > 2 else "2025-09-10T12:09:03Z"
key_pair_id = os.environ.get("RSA_KEY_ID")
signed_url = generate_signed_url(url, key_pair_id, expiry_time)
print(signed_url)
動作確認
それぞれの Python コードを実行して、署名付き URL が生成できれば、問題なく設定ができています。
expiry_time=$(date -u -d '+1 minute' '+%Y-%m-%dT%H:%M:%SZ')
echo "# Generating RSA signed URL for CloudFront..."
rsa_signedurl=$(python3 rsa_signer.py "https://example.com/private-files/test.jpg" "$expiry_time")
echo "# RSA Signed URL: $rsa_signedurl"
echo "# Generating ECDSA signed URL for CloudFront..."
ecdsa_signedurl=$(python3 ecdsa_signer.py "https://example.com/private-files/test.jpg" "$expiry_time")
echo "# ECDSA Signed URL: $ecdsa_signedurl"
性能測定の実施
署名付き URL 生成の動作確認ができたら、性能測定を行ないます。
環境設定
途中で取得した以下の変数が未設定であれば、設定します。
export RSA_KEY_ID="ZZZZZZZZZZZZZ"
export ECDSA_KEY_ID="WWWWWWWWWWWWWW"
性能測定スクリプト
性能測定を行なうために、別途 Python コードを用意して、以下のように実行しました。実行回数を1回、10 回、20 回と変えて実行します。
# 使用方法
expiry_time=$(date -u -d '+1 minute' '+%Y-%m-%dT%H:%M:%SZ')
python3 benchmark_signers.py \
"https://example.com/file1.jpg" \
"https://example.com/file2.jpg" \
"$expiry_time" \
100 # 実行回数
性能測定用のコード例
:
def benchmark_ecdsa(
self, url: str, expiry_time: str, iterations: int = 10
) -> Tuple[List[float], str]:
"""ECDSA署名付きURL生成のベンチマーク"""
times = []
signed_url = None
cf_signer = CloudFrontSigner(self.ecdsa_key_pair_id, self.ecdsa_signer)
date_less_than = datetime.fromisoformat(expiry_time.replace("Z", "+00:00"))
for _ in range(iterations):
start_time = time.perf_counter()
signed_url = cf_signer.generate_presigned_url(
url, date_less_than=date_less_than
)
end_time = time.perf_counter()
times.append((end_time - start_time) * 1000) # ミリ秒に変換
return times, signed_url
:
RSA と ECDSA の平均処理速度比較
測定回数 | RSA 平均 | ECDSA 平均 | 改善率 | RSA 標準偏差 | ECDSA 標準偏差 |
---|---|---|---|---|---|
1 回 | 36.47ms | 3.04ms | 91.7% | 0.00ms | 0.00ms |
10 回 | 35.93ms | 3.00ms | 91.6% | 2.54ms | 0.31ms |
20 回 | 33.54ms | 2.60ms | 92.2% | 0.98ms | 0.21ms |
50 回 | 34.82ms | 2.82ms | 91.9% | 1.73ms | 0.58ms |
100 回 | 34.39ms | 2.91ms | 91.5% | 1.33ms | 0.58ms |
結果サマリー
- ECDSA を使用した署名付き URL 生成は RSA に比べて約 12 倍高速(平均 91.6%の改善)
- URL 長は約 55.1%短縮(RSA: 448 文字 → ECDSA: 201 文字)
- 安定した性能(標準偏差が小さい)
この結果から、ECDSA が特に有効なケースは次のような場合だと考えられます。
- 高頻度な URL 生成: API で大量の署名付き URL を生成する場合
- モバイル/IoT デバイス: 計算リソースが限られた環境
- URL 長制限: SMS、QR コード等で URL 長に制約がある場合
- レスポンス時間重視: リアルタイム性が要求される用途
実際の署名付き URL 例
RSA 署名 URL(448 文字)
https://example.com/private-files/uploadtest.jpg?Expires=1757511063&Signature=TNGmM2jrc5GRTalKgYhdZla1uaR~gOiX4HevKDMBbe5JkecwxGe0VoCqXB5KnCPgetRbbBE6JCtOdCYiTwGYxKpByOBJTMaS5Z3H9rqhln~wqQ0KZ-q5-lqvEPsxXlbBXbzJ0MDwNgzcCcu3GavsYGiceFr0veGw5RuYNLSs49HdFUKtdk~7wpI~bjdHzLDSaxoTKtIz0HDgii~VTPP769X~aOmG7~W0hm23nBP4WcTDe6Z8YvDRr61TqjixkNYrh4FiuUFFVuA2Tn0FUuaxcU5qVhtcl0ng8QqoyHUwWFju65h6g0TUM2iueJudQtV-CcoYf-UHlVOGQ39pnG9HxQ__&Key-Pair-Id=ZZZZZZZZZZZZZ
ECDSA 署名 URL(201 文字)
RSA で生成した署名付き URL に比べて、247
文字も短い結果となりました。
https://example.com/private-files/uploadtest.jpg?Expires=1757511063&Signature=MEQCIBO~LD6qEaVjiwGFFF233dOB~XJvi7SPhubaN0FjFsigAiBlLZAYz9sCp6aY93bE9z0SuZDhEH3Pw8uL-9rPODVbQw__&Key-Pair-Id=WWWWWWWWWWWWWW
まとめ
Amazon CloudFront の署名付き URL での ECDSA サポートにより、大幅な性能向上と URL 短縮が実現されました。特に高負荷なシステムや計算リソースに制約がある環境の場合、大きな恩恵を受けるのではないでしょうか。
Discussion