

# 서명된 URL과 서명된 쿠키를 사용하여 프라이빗 콘텐츠 제공
<a name="PrivateContent"></a>

인터넷을 통해 콘텐츠를 배포하는 많은 기업에서는 유료 사용자 등 일부 사용자용으로 제작된 각종 문서, 비즈니스 데이터, 미디어 스트림 또는 콘텐츠에 대한 액세스를 제한하고자 합니다. CloudFront를 통해 이러한 프라이빗 콘텐츠를 안전하게 제공하려면 다음과 같이 하세요.
+ 사용자가 특별한 CloudFront 서명된 URL 또는 서명된 쿠키를 사용하여 프라이빗 콘텐츠에 액세스하도록 합니다.
+ 사용자가 오리진 서버(예: Amazon S3 또는 프라이빗 HTTP 서버)에서 직접 콘텐츠에 액세스하는 URL이 아닌 CloudFront URL을 사용하여 콘텐츠에 액세스해야 합니다. 반드시 CloudFront URL을 사용해야 하는 것은 아니지만, 서명된 URL 또는 서명된 쿠키에 지정한 제약 조건을 사용자가 우회하지 못하도록 하려면 사용하는 것이 좋습니다.

자세한 내용은 [파일에 대한 액세스 제한](private-content-overview.md) 섹션을 참조하세요.

## 프라이빗 콘텐츠를 제공하는 방법
<a name="private-content-task-list"></a>

프라이빗 콘텐츠를 제공하도록 CloudFront를 구성하려면 다음 작업을 수행하세요.

1. (권장 선택 사항) 사용자가 CloudFront를 통해서만 콘텐츠에 액세스하도록 요구합니다. 사용하는 방식은 Amazon S3 또는 사용자 지정 오리진 중 어떤 것을 사용하는지에 따라 다릅니다.
   + **Amazon S3** – [Amazon S3 오리진에 대한 액세스 제한](private-content-restricting-access-to-s3.md) 단원을 참조하세요.
   + **사용자 지정 오리진** – [사용자 지정 오리진의 파일에 대한 액세스 제한](private-content-overview.md#forward-custom-headers-restrict-access) 단원을 참조하세요.

   사용자 지정 오리진은 Amazon EC2, 웹 사이트 엔드포인트로 구성된 Amazon S3 버킷, Elastic Load Balancing, 자체 HTTP 웹 서버를 포함합니다.

1. 서명된 URL 또는 서명된 쿠키를 생성할 때 사용할 *신뢰할 수 있는 키 그룹* 또는 *신뢰할 수 있는 서명자*를 지정합니다. 신뢰할 수 있는 키 그룹을 사용하는 것이 좋습니다. 자세한 내용은 [서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정](private-content-trusted-signers.md) 단원을 참조하세요.

1. 서명된 쿠키를 설정하는 `Set-Cookie` 헤더 또는 서명된 URL을 보유한 권한 있는 사용자의 요청에 응답하는 애플리케이션을 작성합니다. 다음 주제 중 하나에 있는 단계를 따릅니다.
   + [서명된 URL 사용](private-content-signed-urls.md)
   + [서명된 쿠키 사용](private-content-signed-cookies.md)

   어떤 방법을 사용할지 확실하지 않은 경우 [서명된 URL 또는 서명된 쿠키 사용 결정](private-content-choosing-signed-urls-cookies.md) 단원을 참조하세요.

**Topics**
+ [프라이빗 콘텐츠를 제공하는 방법](#private-content-task-list)
+ [파일에 대한 액세스 제한](private-content-overview.md)
+ [서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정](private-content-trusted-signers.md)
+ [서명된 URL 또는 서명된 쿠키 사용 결정](private-content-choosing-signed-urls-cookies.md)
+ [서명된 URL 사용](private-content-signed-urls.md)
+ [서명된 쿠키 사용](private-content-signed-cookies.md)
+ [base64 인코딩 및 암호화를 위한 Linux 명령 및 OpenSSL](private-content-linux-openssl.md)
+ [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)

# 파일에 대한 액세스 제한
<a name="private-content-overview"></a>

프라이빗 콘텐츠에 대한 사용자 액세스를 두 가지 방법으로 제어할 수 있습니다.
+ [CloudFront 캐시에 있는 파일에 대한 액세스를 제한합니다](#private-content-overview-edge-caches).
+ 다음 중 하나를 수행하여 오리진의 파일에 대한 액세스를 제한합니다.
  + [Amazon S3 버킷의 오리진 액세스 제어(OAC)를 설정합니다](private-content-restricting-access-to-s3.md).
  + [프라이빗 HTTP 서버(사용자 지정 오리진)에 대한 사용자 지정 헤더를 구성합니다.](#forward-custom-headers-restrict-access)

## CloudFront 캐시에 있는 파일에 대한 액세스 제한
<a name="private-content-overview-edge-caches"></a>

사용자가 *서명된 URL* 또는 *서명된 쿠키*를 사용하여 파일에 액세스할 것을 요구하도록 CloudFront를 구성할 수 있습니다. 그런 다음 서명된 URL을 만들어 인증된 사용자에게 배포하거나 인증된 사용자를 위해 `Set-Cookie` 헤더를 보내 서명된 쿠키를 설정하도록 하는 애플리케이션을 개발합니다. (사용자 몇 명에게 적은 수의 파일에 대한 장기 액세스 권한을 부여하려면 서명된 URL을 수동으로 만드는 방법도 있습니다.) 

서명된 URL 또는 서명된 쿠키를 만들어 파일 액세스를 제어할 때 다음과 같은 제약 조건을 지정할 수 있습니다.
+ URL의 효력이 사라지는 종료 날짜 및 시간 
+ (선택 사항) URL의 효력이 발생하는 날짜 및 시간
+ (선택 사항) 콘텐츠에 액세스할 때 사용할 수 있는 컴퓨터의 IP 주소 또는 주소 범위 

퍼블릭–프라이빗 키 페어의 프라이빗 키를 사용하여 서명된 URL 또는 서명된 쿠키의 일부분을 해시 및 서명합니다. 누군가 서명된 URL 또는 서명된 쿠키를 사용하여 파일에 액세스하면 CloudFront는 URL 또는 쿠키의 서명된 부분과 서명되지 않은 부분을 비교합니다. 양쪽이 일치하지 않으면 CloudFront는 파일을 제공하지 않습니다.

URL 또는 쿠키에 서명하려면 RSA 2048 또는 ECDSA 256 프라이빗 키를 사용해야 합니다.

## Amazon S3 버킷에 있는 파일에 대한 액세스 제한
<a name="private-content-overview-s3"></a>

사용자가 지정된 CloudFront 배포를 통해 액세스할 수는 있지만 Amazon S3 URL을 사용하여 직접 액세스할 수는 없도록 Amazon S3 버킷의 콘텐츠를 선택적으로 보호할 수 있습니다. 이렇게 하면 CloudFront를 우회하고 Amazon S3 URL을 사용하여 액세스 제한 콘텐츠에 접근하는 사람을 막을 수 있습니다. 서명된 URL을 사용하기 위해 꼭 필요한 단계는 아니지만 이렇게 하는 것이 좋습니다.

사용자가 CloudFront URL을 통해 콘텐츠에 액세스하도록 요구하려면 다음 작업을 수행합니다.
+ CloudFront에 S3 버킷의 파일을 읽을 수 있는 *오리진 액세스 제어* 권한을 부여합니다.
+ 오리진 액세스 제어를 생성하고 CloudFront 배포와 연결합니다.
+ 나머지 모든 사람으로부터 Amazon S3 URL을 사용하여 파일을 읽을 수 있는 권한을 제거합니다.

자세한 내용은 [Amazon S3 오리진에 대한 액세스 제한](private-content-restricting-access-to-s3.md) 섹션을 참조하세요.

## 사용자 지정 오리진의 파일에 대한 액세스 제한
<a name="forward-custom-headers-restrict-access"></a>

사용자 지정 오리진을 사용하는 경우 선택적으로 사용자 지정 헤더를 설정하여 액세스를 제한할 수 있습니다. CloudFront가 사용자 지정 오리진에서 파일을 가져오려면 표준 HTTP(또는 HTTPS) 요청을 사용하여 CloudFront에서 해당 파일에 액세스할 수 있어야 합니다. 그러나 사용자 지정 헤더를 사용하면 사용자가 직접 액세스할 수 없고 CloudFront를 통해서만 액세스할 수 있도록 콘텐츠에 대한 액세스를 추가로 제한할 수 있습니다. 서명된 URL을 사용하기 위해 꼭 필요한 단계는 아니지만 이렇게 하는 것이 좋습니다.

사용자가 CloudFront를 통해 콘텐츠에 액세스하도록 하려면 CloudFront 배포의 다음 설정을 변경합니다.

**오리진 사용자 지정 헤더**  
오리진에 사용자 지정 헤더를 전달하도록 CloudFront를 구성합니다. [사용자 지정 헤더를 오리진 요청에 추가하도록 CloudFront 구성](add-origin-custom-headers.md#add-origin-custom-headers-configure)을(를) 참조하세요.

**Viewer Protocol Policy**  
최종 사용자가 HTTPS를 사용하여 CloudFront에 액세스하도록 배포를 구성합니다. [뷰어 프로토콜 정책](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy)을(를) 참조하세요.

**오리진 프로토콜 정책**  
CloudFront가 최종 사용자와 같은 프로토콜을 사용하여 오리진에 요청을 전달하도록 배포를 구성합니다. [프로토콜(사용자 지정 오리진만 해당)](DownloadDistValuesOrigin.md#DownloadDistValuesOriginProtocolPolicy)을(를) 참조하세요.

이러한 변경을 수행한 후에는 CloudFront에서 전송하도록 구성한 사용자 지정 헤더를 포함하는 요청만 수락하도록 사용자 지정 오리진에서 애플리케이션을 업데이트합니다.

**최종 사용자 프로토콜 정책**(Viewer Protocol Policy)과 **오리진 프로토콜 정책**(Origin Protocol Policy)의 조합을 통해 사용자 지정 헤더가 전송 중에 암호화되도록 합니다. 하지만 주기적으로 다음을 수행하여 CloudFront가 오리진에 전달하는 사용자 지정 헤더를 교체하는 것이 좋습니다.

1. CloudFront 배포를 업데이트하여 사용자 지정 오리진에 새 헤더 전달을 시작합니다.

1. 요청이 CloudFront에서 온다는 확인으로 애플리케이션을 업데이트하여 새 헤더를 허용합니다.

1. 요청에 더 이상 교체하는 헤더가 포함되지 않는 경우, 요청이 CloudFront에서 온다는 확인으로 애플리케이션을 업데이트하여 새 헤더를 허용합니다.

# 서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정
<a name="private-content-trusted-signers"></a>

**Topics**
+ [신뢰할 수 있는 키 그룹(권장)과 AWS 계정 사이에서 선택](#choosing-key-groups-or-AWS-accounts)
+ [서명자에 대해 키 페어 생성](#private-content-creating-cloudfront-key-pairs)
+ [프라이빗 키 재포맷(.NET 및 Java만 해당)](#private-content-reformatting-private-key)
+ [배포에 서명자 추가](#private-content-adding-trusted-signers)
+ [키 페어 교체](#private-content-rotating-key-pairs)

서명된 URL 또는 서명된 쿠키를 만들려면 *서명자*가 필요합니다. 서명자는 CloudFront에 생성하는 신뢰할 수 있는 키 그룹이거나, CloudFront 키 페어가 포함된 AWS 계정입니다. 서명된 URL 및 서명된 쿠키와 함께 신뢰할 수 있는 키 그룹을 사용하는 것이 좋습니다. 자세한 내용은 [신뢰할 수 있는 키 그룹(권장)과 AWS 계정 사이에서 선택](#choosing-key-groups-or-AWS-accounts) 단원을 참조하세요.

서명자에는 두 가지 목적이 있습니다.
+ 배포에 서명자를 추가하는 즉시 CloudFront에서는 파일에 액세스할 때 서명된 URL 또는 서명된 쿠키를 사용할 것을 최종 사용자에게 요구하기 시작합니다.
+ 서명된 URL 또는 서명된 쿠키를 만들 때는 서명자의 키 페어에서 프라이빗 키를 사용하여 URL 또는 쿠키의 일부분에 서명합니다. 제한된 객체에 대한 요청이 들어오면 CloudFront는 URL 또는 쿠키의 서명을 서명되지 않은 URL 또는 쿠키와 비교하여 URL 또는 쿠키가 변조되지 않았는지 확인합니다. 또한 CloudFront는 URL 또는 쿠키가 유효한지, 의미가 있는지를 확인합니다(예: 만료 날짜 및 시간 이전인지 확인).

서명자를 지정할 때 서명자를 캐시 동작에 추가하여 서명된 URL 또는 서명된 쿠키가 필요한 파일을 간접적으로 지정합니다. 배포에 캐시 동작이 한 개뿐이라면 최종 사용자는 반드시 서명된 URL 또는 서명된 쿠키를 사용하여 해당 배포의 파일에 액세스해야 합니다. 여러 가지 캐시 동작을 만들고 일부 캐시 동작에만 서명자를 추가하는 경우, 일부 파일에 액세스할 때만 서명된 URL 또는 서명된 쿠키를 사용하도록 최종 사용자에게 요구할 수 있습니다.

서명된 URL 또는 서명된 쿠키를 만들고 CloudFront 배포에 서명자를 추가할 수 있는 서명자(프라이빗 키)를 지정하려면 다음 작업을 수행하세요.

1. 신뢰할 수 있는 키 그룹 또는 AWS 계정을 서명자로 사용할지 결정합니다. 신뢰할 수 있는 키 그룹을 사용하는 것이 좋습니다. 자세한 내용은 [신뢰할 수 있는 키 그룹(권장)과 AWS 계정 사이에서 선택](#choosing-key-groups-or-AWS-accounts) 단원을 참조하세요.

1. 1단계에서 선택한 서명자에 대해 퍼블릭–프라이빗 키 페어를 만듭니다. 자세한 내용은 [서명자에 대해 키 페어 생성](#private-content-creating-cloudfront-key-pairs) 단원을 참조하세요.

1. .NET 또는 Java를 사용하여 서명된 URL 또는 서명된 쿠키를 만드는 경우, 프라이빗 키를 다시 포맷해야 합니다. 자세한 내용은 [프라이빗 키 재포맷(.NET 및 Java만 해당)](#private-content-reformatting-private-key) 단원을 참조하세요.

1. 서명된 URL 또는 서명된 쿠키를 만들려는 배포에서 서명자를 지정합니다. 자세한 내용은 [배포에 서명자 추가](#private-content-adding-trusted-signers) 섹션을 참조하세요.

## 신뢰할 수 있는 키 그룹(권장)과 AWS 계정 사이에서 선택
<a name="choosing-key-groups-or-AWS-accounts"></a>

서명된 URL 또는 서명된 쿠키를 사용하려면 *서명자*가 필요합니다. 서명자는 CloudFront에 생성하는 신뢰할 수 있는 키 그룹이거나, CloudFront 키 페어가 포함된 AWS 계정입니다. 다음과 같은 이유로 신뢰할 수 있는 키 그룹을 사용하는 것이 좋습니다.
+ CloudFront 키 그룹을 사용하면 CloudFront 서명된 URL 및 서명된 쿠키에 대한 퍼블릭 키를 관리하기 위해 AWS 계정 루트 사용자를 사용할 필요가 없습니다. [AWS 모범 사례](https://docs.aws.amazon.com/general/latest/gr/root-vs-iam.html#aws_tasks-that-require-root)에서는 필요하지 않은 경우 루트 사용자를 사용하지 않는 것을 권장합니다.
+ CloudFront 키 그룹을 사용하면 CloudFront API를 사용하여 퍼블릭 키, 키 그룹 및 신뢰할 수 있는 서명자를 관리할 수 있습니다. API를 사용하여 키 생성 및 키 회전을 자동화할 수 있습니다. AWS 루트 사용자를 사용할 때는 AWS Management Console을 사용하여 CloudFront 키 페어를 관리해야 하므로 프로세스를 자동화할 수 없습니다.
+ CloudFront API를 사용하여 키 그룹을 관리할 수 있으므로 AWS Identity and Access Management(IAM) 권한 정책을 사용하여 다른 사용자가 수행할 수 있는 작업을 제한할 수도 있습니다. 예를 들어 사용자가 퍼블릭 키를 업로드하되 삭제할 수는 없도록 할 수 있습니다. 또는 멀티 팩터 인증을 사용하거나 특정 네트워크에서 요청을 보내거나 특정 날짜 및 시간 범위 내에서 요청을 보내는 등 특정 조건을 충족하는 경우에만 사용자가 퍼블릭 키를 삭제하도록 허용할 수 있습니다.
+ CloudFront 키 그룹을 사용하면 더 많은 수의 퍼블릭 키를 CloudFront 배포와 연결할 수 있으므로 퍼블릭 키를 더 유연하게 사용하고 관리할 수 있습니다. 기본적으로 단일 배포에 최대 4개의 키 그룹을 연결할 수 있으며 키 그룹에 최대 5개의 퍼블릭 키를 포함할 수 있습니다.

  AWS 계정 루트 사용자를 사용하여 CloudFront 키 페어를 관리하는 경우 AWS 계정당 최대 두 개의 활성 CloudFront 키 페어만 가질 수 있습니다.

## 서명자에 대해 키 페어 생성
<a name="private-content-creating-cloudfront-key-pairs"></a>

CloudFront 서명된 URL 또는 서명된 쿠키를 만드는 데 사용하는 각 서명자에게는 퍼블릭–프라이빗 키 페어가 있어야 합니다. 서명자는 프라이빗 키를 사용하여 URL 또는 쿠키에 서명하고 CloudFront는 퍼블릭 키를 사용하여 서명을 확인합니다.

키 페어를 생성하는 방법은 신뢰할 수 있는 키 그룹을 서명자(권장)로 사용하는지 또는 CloudFront 키 페어로 사용하는지에 따라 다릅니다. 자세한 내용은 다음 단원을 참조하세요. 생성하는 키 페어는 다음 요구 사항을 충족해야 합니다.
+ SSH-2 RSA 2048 또는 ECDSA 256 키 페어여야 합니다.
+ base64로 인코딩된 PEM 형식이어야 합니다.

애플리케이션 보안을 위해 키 페어를 주기적으로 교체하는 것이 좋습니다. 자세한 내용은 [키 페어 교체](#private-content-rotating-key-pairs) 단원을 참조하세요.

### 신뢰할 수 있는 키 그룹에 대한 키 페어 생성(권장)
<a name="create-key-pair-and-key-group"></a>

신뢰할 수 있는 키 그룹에 대한 키 페어를 생성하려면 다음 단계를 수행합니다.

1. 퍼블릭–프라이빗 키 페어를 생성합니다.

1. CloudFront에 퍼블릭 키를 업로드합니다.

1. CloudFront 키 그룹에 퍼블릭 키를 추가합니다.

자세한 내용은 다음 절차를 참조하세요.<a name="private-content-uploading-cloudfront-public-key-procedure"></a>

**키 페어를 생성하려면**
**참고**  
다음 단계에서는 키 페어를 생성하는 방법의 예시로 OpenSSL을 사용합니다. RSA 또는 ECDSA 키 페어를 생성하는 방법에는 여러 가지가 있습니다.

1. 다음 예제 명령 중 하나를 실행합니다.
   + 다음 예제 명령은 OpenSSL을 사용하여 길이가 2048비트인 RSA 키 페어를 생성하고 `private_key.pem`이라는 파일에 저장합니다.

     ```
     openssl genrsa -out private_key.pem 2048
     ```
   + 다음 예제 명령어는 OpenSSL을 사용하여 `prime256v1` 곡선을 가진 ECDSA 키 쌍을 생성하고 `private_key.pem`이라는 이름의 파일에 저장합니다.

     ```
     openssl ecparam -name prime256v1 -genkey -noout -out privatekey.pem
     ```

1. 이렇게 만들어진 파일은 퍼블릭 키와 프라이빗 키를 모두 포함합니다. 다음 예제 명령은 `private_key.pem`이라는 파일에서 퍼블릭 키를 추출합니다.

   ```
   openssl rsa -pubout -in private_key.pem -out public_key.pem
   ```

   나중에 다음 절차에서 `public_key.pem` 파일의 퍼블릭 키를 업로드합니다.

**퍼블릭 키를 CloudFront에 업로드하려면**

1. AWS Management Console에 로그인한 다음 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)에서 CloudFront 콘솔을 엽니다.

1. 탐색 메뉴에서 **퍼블릭 키**를 선택합니다.

1. **퍼블릭 키 생성**을 선택합니다.

1. **퍼블릭 키 생성** 창에서 다음을 수행합니다.

   1. **키 이름**에 퍼블릭 키를 식별할 이름을 입력합니다.

   1. **키 값**에 퍼블릭 키를 붙여 넣습니다. 이전 절차의 단계를 수행한 경우 퍼블릭 키는 `public_key.pem`이라는 파일에 있습니다. 퍼블릭 키의 내용을 복사하여 붙여 넣으려면 다음과 같이 하면 됩니다.
      + 다음과 같이 macOS 또는 Linux 명령줄에서 **cat** 명령을 사용합니다.

        ```
        cat public_key.pem
        ```

        해당 명령의 출력을 복사한 다음 **키 값**(Key value) 필드에 붙여 넣습니다.
      + 메모장(Windows) 또는 TextEdit(macOS)와 같은 일반 텍스트 편집기로 `public_key.pem` 파일을 엽니다. 파일의 내용을 복사한 다음 **키 값**(Key value) 필드에 붙여 넣습니다.

   1. (선택 사항) **설명**(Comment)에 퍼블릭 키에 대한 설명을 추가합니다.

   완료되면 **추가**(Add)를 선택합니다.

1. 퍼블릭 키 ID를 적어 둡니다. 나중에 서명된 URL 또는 서명된 쿠키를 만들 때 `Key-Pair-Id` 필드 값으로 이를 사용합니다.

**키 그룹에 퍼블릭 키를 추가하려면**

1. 에서 CloudFront 콘솔을 엽니다[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)

1. 탐색 메뉴에서 **키 그룹**(Key groups)을 선택합니다.

1. **키 그룹 추가**(Add key group)를 선택합니다.

1. **키 그룹 만들기**(Create key group) 페이지에서 다음을 수행합니다.

   1. **키 그룹 이름**(Key group name)에 키 그룹을 식별할 이름을 입력합니다.

   1. (선택 사항) **설명**(Comment)에 키 그룹에 대한 설명을 입력합니다.

   1. **퍼블릭 키**(Public keys)에서 키 그룹에 추가할 퍼블릭 키를 선택한 다음 **추가**(Add)를 선택합니다. 키 그룹에 추가할 각 퍼블릭 키에 대해 이 단계를 반복합니다.

1. **키 그룹 만들기**(Create Key Pair)를 선택합니다.

1. 키 그룹 이름을 적어 둡니다. 나중에 키 그룹을 CloudFront 배포의 캐시 동작과 연결하는 데 이를 사용합니다. CloudFront API에서 키 그룹 ID를 사용하여 키 그룹을 캐시 동작과 연결합니다.

### CloudFront 키 페어 생성(권장되지 않음, AWS 계정 루트 사용자 필요)
<a name="create-key-pair-aws-account"></a>

**중요**  
다음 단계를 수행하는 대신 신뢰할 수 있는 키 그룹에 대한 퍼블릭 키를 만드는 것이 좋습니다. 서명된 URL 및 서명된 쿠키에 대한 퍼블릭 키를 만드는 권장 방법은 [신뢰할 수 있는 키 그룹에 대한 키 페어 생성(권장)](#create-key-pair-and-key-group) 단원을 참조하세요.

CloudFront 키 페어를 만드는 방법은 다음과 같습니다.
+ AWS Management Console에서 키 페어를 만들고 프라이빗 키를 다운로드합니다. 다음 절차를 참조하세요.
+ OpenSSL과 같은 애플리케이션을 사용하여 RSA 키 페어를 만든 후 퍼블릭 키를 에 업로드합니다AWS Management Console RSA 키 페어 생성에 대한 자세한 내용은 [신뢰할 수 있는 키 그룹에 대한 키 페어 생성(권장)](#create-key-pair-and-key-group) 단원을 참조하세요.<a name="private-content-creating-cloudfront-key-pairs-procedure"></a>

**에서 CloudFront 키 페어 생성AWS Management Console**

1. AWS 계정 루트 사용자의 자격 증명을 사용하여 AWS Management Console에 로그인합니다.
**중요**  
IAM 사용자는 CloudFront 키 페어를 만들 수 없습니다. 키 페어를 만들려면 루트 사용자 자격 증명을 사용하여 로그인해야 합니다.

1. 계정 이름을 선택한 다음 **내 보안 자격 증명**을 선택합니다.

1. **CloudFront 키 페어(CloudFront key pairs)**를 선택합니다.

1. 활성 키 페어가 하나만 있는지 확인합니다. 활성 키 페어가 이미 두 개라면 키 페어를 만들 수 없습니다.

1. **새 키 페어 생성**을 선택합니다.
**참고**  
사용자 고유의 키 페어를 만들고 퍼블릭 키를 업로드하도록 선택할 수도 있습니다. CloudFront 키 페어는 1024, 2048 또는 4096비트 키를 지원합니다.

1. **키 페어 생성** 대화 상자에서 **프라이빗 키 파일 다운로드**를 선택한 다음 컴퓨터에 파일을 저장합니다.
**중요**  
CloudFront 키 페어의 프라이빗 키를 안전한 위치에 저장하고 파일에 대한 권한을 설정하여 원하는 관리자만 읽을 수 있도록 합니다. 다른 사람이 프라이빗 키를 가지게 되면 이들이 유효한 서명된 URL과 서명된 쿠키를 만들어 콘텐츠를 다운로드할 수 있게 됩니다. 프라이빗 키는 다시 가져올 수 없기 때문에 이를 잃어버리거나 삭제한 경우 새 CloudFront 키 페어를 만들어야 합니다.

1. 키 페어의 키 페어 ID를 기록합니다. (AWS Management Console에서는 **액세스 키 ID**라고 합니다.) 서명된 URL 또는 서명된 쿠키를 만들 때 이것을 사용합니다.

## 프라이빗 키 재포맷(.NET 및 Java만 해당)
<a name="private-content-reformatting-private-key"></a>

.NET 또는 Java를 사용하여 서명된 URL 또는 서명된 쿠키를 만드는 경우, 기본 PEM 형식의 키 페어에서 프라이빗 키를 사용하여 서명을 만들 수 없습니다. 대신 다음을 수행합니다.
+ **.NET 프레임워크** – 프라이빗 키를 .NET 프레임워크에서 사용하는 XML 형식으로 변환합니다. 몇 가지 도구를 사용할 수 있습니다.
+ **Java** – 프라이빗 키를 DER 형식으로 변환합니다. 이 작업을 수행하는 한 가지 방법은 다음 OpenSSL 명령을 사용하는 것입니다. 다음 명령에서 `private_key.pem`은 PEM 형식의 프라이빗 키가 들어 있는 파일의 이름이며, `private_key.der`은 명령을 실행한 후 DER 형식의 프라이빗 키가 들어 있는 파일의 이름입니다.

  ```
  openssl pkcs8 -topk8 -nocrypt -in private_key.pem -inform PEM -out private_key.der -outform DER
  ```

  인코더가 정확하게 작동하려면 Bouncy Castle Java 암호화 API에 대한 JAR을 프로젝트에 추가한 다음 Bouncy Castle 공급자를 추가합니다.

## 배포에 서명자 추가
<a name="private-content-adding-trusted-signers"></a>

서명자는 배포에 대해 서명된 URL 및 서명된 쿠키를 만들 수 있는 신뢰할 수 있는 키 그룹(권장) 또는 CloudFront 키 페어입니다. CloudFront 배포와 함께 서명된 URL 또는 서명된 쿠키를 사용하려면 서명자를 지정해야 합니다.

서명자는 캐시 동작과 연결됩니다. 이렇게 하면 같은 배포의 일부 파일에 대해서만 서명된 URL 또는 서명된 쿠키를 요구할 수 있습니다. 배포에는 해당 캐시 동작과 연결된 파일에 대해서만 서명된 URL 또는 쿠키가 필요합니다.

마찬가지로 서명자는 해당 캐시 동작과 연결된 파일에 대해서만 URL 또는 쿠키에 서명할 수 있습니다. 예를 들어, 캐시 동작 하나당 서명자가 하나씩 있고 다른 캐시 동작에는 다른 서명자가 있다면 양쪽의 서명자 모두 상대편의 캐시 동작과 연결된 파일에 대해 서명된 URL 또는 쿠키를 만들 수 없습니다.

**중요**  
배포에 서명자를 추가하기 전에 다음을 수행합니다.  
캐시 동작의 경로 패턴과 캐시 동작 순서를 신중하게 정의하여 콘텐츠 액세스 권한을 실수로 사용자에게 부여하거나 일부 공개 콘텐츠에 사용자가 액세스하는 일이 없도록 해야 합니다.  
예를 들어, 하나의 요청이 두 캐시 동작의 경로 패턴과 일치한다고 가정합니다. 첫 번째 캐시 동작에는 서명된 URL 또는 서명된 쿠키가 필요하지 않고, 두 번째 캐시 동작에는 필요합니다. CloudFront에서는 첫 번째와 연결된 캐시 동작을 처리하므로 사용자는 서명된 URL 또는 서명된 쿠키 없이도 파일에 액세스할 수 있습니다.  
경로 패턴에 대한 자세한 내용은 [경로 패턴](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)을 참조하세요.
콘텐츠를 배포하는 데 이미 사용하고 있는 배포의 경우 서명자를 추가하기 전에 서명된 URL 및 서명된 쿠키를 생성할 준비가 되어 있어야 합니다. 서명자를 추가하면 CloudFront는 유효한 서명된 URL 또는 서명된 쿠키를 포함하지 않는 요청을 거부합니다.

CloudFront 콘솔 또는 CloudFront API를 사용하여 배포에 서명자를 추가할 수 있습니다.

------
#### [ Console ]

다음 단계에서는 신뢰할 수 있는 키 그룹을 서명자로 추가하는 방법을 보여 줍니다. AWS 계정을 신뢰할 수 있는 서명자로 추가할 수도 있지만 권장하지 않습니다.<a name="private-content-adding-trusted-signers-console-procedure"></a>

**콘솔을 사용하여 배포에 서명자를 추가하려면**

1. 신뢰할 수 있는 서명자로 사용할 키 그룹의 키 그룹 ID를 적어 둡니다. 자세한 내용은 [신뢰할 수 있는 키 그룹에 대한 키 페어 생성(권장)](#create-key-pair-and-key-group) 섹션을 참조하세요.

1. [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)에서 CloudFront 콘솔을 엽니다

1. 서명된 URL 또는 서명된 쿠키로 보호하려는 파일의 배포를 선택합니다.
**참고**  
새 배포에 서명자를 추가하려면 배포를 만들 때 6단계에서 설명하는 것과 동일한 설정을 지정합니다.

1. **동작** 탭을 선택합니다.

1. 경로 패턴이 서명된 URL 또는 서명된 쿠키로 보호하려는 파일과 일치하는 캐시 동작을 선택한 다음 **편집**을 선택합니다.

1. **동작 편집**(Edit Behavior) 페이지에서 다음을 수행합니다.

   1. **최종 사용자 액세스 제한(서명된 URL 또는 서명된 쿠키 사용)**(Restrict Viewer Access (Use Signed URLs or Signed Cookies)에서 **예**를 클릭합니다.

   1. **신뢰할 수 있는 키 그룹 또는 신뢰할 수 있는 서명자**(Trusted Key Groups or Trusted Signer)에서 **신뢰할 수 있는 키 그룹**(Trusted Key Groups)을 선택합니다.

   1. **신뢰할 수 있는 키 그룹**(Trusted Key Groups)에서 추가할 키 그룹을 선택한 다음 **추가**를 선택합니다. 두 개 이상의 키 그룹을 추가하려면 이 과정을 반복합니다.

1. **예, 편집합니다**를 선택하여 캐시 동작을 업데이트합니다.

------
#### [ API ]

CloudFront API를 사용하여 신뢰할 수 있는 키 그룹을 서명자로 추가할 수 있습니다. 기존 배포 또는 새 배포에 서명자를 추가할 수 있습니다. 두 경우 모두 `TrustedKeyGroups` 요소에 값을 지정합니다.

AWS 계정을 신뢰할 수 있는 서명자로 추가할 수도 있지만 권장하지 않습니다.

*Amazon CloudFront API 참조*에서 다음 주제를 참조하세요.
+ **기존 배포 업데이트** – [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)
+ **새로운 배포 생성** – [CreateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateDistribution.html)

------

## 키 페어 교체
<a name="private-content-rotating-key-pairs"></a>

서명된 URL 및 서명된 쿠키에 대한 키 페어를 주기적으로 교체(변경)하는 것이 좋습니다. 아직 만료되지 않은 서명된 URL 또는 서명된 쿠키를 무효화하지 않고 URL 또는 쿠키를 만들기 위해 사용 중인 키 페어를 교체하려면 다음 작업을 수행하세요.

1. 새 키 페어를 만들고 키 그룹에 퍼블릭 키를 추가합니다. 자세한 내용은 [신뢰할 수 있는 키 그룹에 대한 키 페어 생성(권장)](#create-key-pair-and-key-group) 단원을 참조하세요.

1. 이전 단계에서 새 키 그룹을 만든 경우 [키 그룹을 배포에 서명자로 추가](#private-content-adding-trusted-signers)합니다.
**중요**  
키 그룹에서 기존 퍼블릭 키를 제거하거나 배포에서 키 그룹을 제거하지 마세요. 새것만 추가하세요.

1. 새로운 키 페어의 프라이빗 키를 사용하여 서명을 만들도록 애플리케이션을 업데이트합니다. 새 프라이빗 키로 서명된 URL 또는 서명된 쿠키가 작동하는지 확인합니다.

1. 이전 프라이빗 키로 서명한 URL 또는 쿠키의 만료 날짜가 지날 때까지 기다립니다. 그런 다음 키 그룹에서 이전 프라이빗 키를 제거합니다. 2단계에서 새 키 그룹을 만든 경우 배포에서 이전 키 그룹을 제거합니다.

# 서명된 URL 또는 서명된 쿠키 사용 결정
<a name="private-content-choosing-signed-urls-cookies"></a>

CloudFront 서명된 URL 및 서명된 쿠키는 기본 기능이 같습니다. 바로 콘텐츠에 액세스할 수 있는 대상을 제어하는 기능입니다. CloudFront를 통해 프라이빗 콘텐츠를 제공 중이며 서명된 URL 또는 서명된 쿠키 중 하나를 결정해야 하는 경우, 다음 사항을 고려하세요.

다음과 같은 경우 서명된 URL을 사용합니다.
+ 애플리케이션 설치 파일을 다운로드하는 등 개별 파일에 대한 액세스를 제한하고 싶은 경우
+ 사용자가 쿠키를 지원하지 않는 클라이언트(예: 사용자 지정 HTTP 클라이언트)를 사용하는 경우

다음과 같은 경우 서명된 쿠키를 사용합니다.
+ HLS 형식의 비디오 파일 전체 또는 웹 사이트의 구독자 영역에 있는 파일 전체 등 제한된 파일 여러 개에 대한 액세스 권한을 제공하려는 경우
+ 현재의 URL을 변경하고 싶지 않은 경우

현재 서명된 URL을 사용하지 않는데 (서명되지 않은) URL에 다음과 같은 쿼리 문자열 파라미터가 들어 있다면 서명된 URL 또는 서명된 쿠키를 사용할 수 없습니다.
+ `Expires`
+ `Policy`
+ `Signature`
+ `Key-Pair-Id`

CloudFront는 이러한 쿼리 문자열 파라미터가 포함된 URL을 서명된 URL로 간주하기 때문에 서명된 쿠키를 살펴보지 않을 것입니다.

## 서명된 URL과 서명된 쿠키 모두 사용
<a name="private-content-using-signed-urls-and-cookies"></a>

서명된 URL은 서명된 쿠키보다 우선합니다. 같은 파일에 대한 액세스를 제어할 때 서명된 URL과 서명된 쿠키를 모두 사용하고 최종 사용자는 서명된 URL로 파일을 요청하는 경우, CloudFront는 서명된 URL만을 기준으로 최종 사용자에게 파일을 반환할지 여부를 판단합니다.

# 서명된 URL 사용
<a name="private-content-signed-urls"></a>

서명된 URL에는 만료 날짜 및 시간 같은 추가 정보가 포함되므로 콘텐츠에 대한 액세스를 보다 세부적으로 제어할 수 있습니다. 이러한 추가 정보는 미리 준비된(canned) 정책 또는 사용자 지정 정책에 따라 정책 설명에 나타납니다. 미리 준비된(canned) 정책과 사용자 지정 정책 간의 차이점은 이어지는 두 단원에 설명되어 있습니다.

**참고**  
같은 배포에 대해 미리 준비된(canned) 정책과 사용자 지정 정책으로 각각 서명된 URL을 만들 수 있습니다.

**Topics**
+ [서명된 URL에 대해 미리 준비된 정책 또는 사용자 지정 정책 사용 결정](#private-content-choosing-canned-custom-policy)
+ [서명된 URL의 작동 방식](#private-content-how-signed-urls-work)
+ [서명된 URL의 유효 기간 결정](#private-content-overview-choosing-duration)
+ [CloudFront가 서명된 URL에서 만료 날짜 및 시간을 확인하는 시기](#private-content-check-expiration)
+ [예제 코드 및 타사 도구](#private-content-overview-sample-code)
+ [미리 준비된 정책을 사용하여 서명된 URL 생성](private-content-creating-signed-url-canned-policy.md)
+ [사용자 지정 정책을 사용하여 서명된 URL 생성](private-content-creating-signed-url-custom-policy.md)

## 서명된 URL에 대해 미리 준비된 정책 또는 사용자 지정 정책 사용 결정
<a name="private-content-choosing-canned-custom-policy"></a>

서명된 URL을 만들 때 JSON 형식의 정책 설명을 작성하여 서명된 URL의 제약 조건(예: URL의 유효 기간)을 지정합니다. 미리 준비된(canned) 정책 또는 사용자 지정 정책을 사용할 수 있습니다. 다음은 미리 준비된(canned) 정책과 사용자 지정 정책을 비교한 것입니다.


****  

| 설명 | 미리 준비된 정책 | 사용자 정의 정책 | 
| --- | --- | --- | 
| 정책 설명은 여러 파일에 재사용할 수 있습니다. 정책 설명을 재사용하려면 `Resource` 객체에 와일드카드 문자를 사용해야 합니다. 자세한 내용은 [사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명에서 지정한 값](private-content-creating-signed-url-custom-policy.md#private-content-custom-policy-statement-values) 섹션을 참조하세요.)  | 아니요 | 예 | 
| 날짜 및 시간을 지정하여 사용자가 콘텐츠에 액세스를 시작할 수 있습니다. | 아니요 | 예(선택 사항) | 
| 사용자의 콘텐츠 액세스를 중단할 날짜 및 시간을 지정할 수 있습니다. | 예 | 예 | 
| 콘텐츠에 액세스할 수 있는 사용자의 IP 주소 또는 IP 주소 범위를 지정할 수 있습니다. | 아니요 | 예(선택 사항) | 
| 서명된 URL에는 base64 인코딩 버전의 정책이 포함되므로 URL 길이가 더 깁니다. | 아니요 | 예 | 

*미리 준비된(canned)* 정책으로 서명된 URL을 만드는 방법에 대한 자세한 내용은 [미리 준비된 정책을 사용하여 서명된 URL 생성](private-content-creating-signed-url-canned-policy.md) 단원을 참조하세요.

*사용자 지정* 정책으로 서명된 URL을 만드는 방법에 대한 자세한 내용은 [사용자 지정 정책을 사용하여 서명된 URL 생성](private-content-creating-signed-url-custom-policy.md) 단원을 참조하세요.

## 서명된 URL의 작동 방식
<a name="private-content-how-signed-urls-work"></a>

여기서는 서명된 URL에 알맞게 CloudFront 및 Amazon S3를 구성하는 방법과 사용자가 서명된 URL로 파일을 요청할 때 CloudFront가 대응하는 방식을 살펴봅니다.

1. CloudFront 배포에서 CloudFront가 URL 서명을 확인하는 데 사용할 수 있는 퍼블릭 키가 포함된 신뢰할 수 있는 키 그룹을 하나 이상 지정합니다. 해당 프라이빗 키를 사용하여 URL에 서명합니다.

   CloudFront는 RSA 2048 및 ECDSA 256 키 서명을 사용하여 서명된 URL을 지원합니다.

   자세한 내용은 [서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정](private-content-trusted-signers.md) 섹션을 참조하세요.

1. 애플리케이션을 개발하여 사용자에게 콘텐츠 액세스를 허용할지 여부를 판단하고, 액세스를 제한할 애플리케이션 부분 또는 파일에 대해 서명된 URL을 만듭니다. 자세한 정보는 다음 주제를 참조하세요.
   + [미리 준비된 정책을 사용하여 서명된 URL 생성](private-content-creating-signed-url-canned-policy.md)
   + [사용자 지정 정책을 사용하여 서명된 URL 생성](private-content-creating-signed-url-custom-policy.md)

1. 사용자가 서명된 URL이 필요한 파일을 요청합니다.

1. 애플리케이션은 해당 사용자에게 파일 액세스 권한이 있는지 확인합니다. 사용자가 로그인했는지, 콘텐츠 액세스 비용을 지불했는지, 기타 액세스 요구 사항을 충족했는지 등을 확인합니다.

1. 애플리케이션은 서명된 URL을 만들어 사용자에게 반환합니다.

1. 사용자는 서명된 URL을 통해 콘텐츠를 다운로드하거나 스트리밍할 수 있습니다.

   이 단계는 자동으로 진행되기 때문에 사용자는 콘텐츠에 액세스하기 위해 아무것도 하지 않아도 됩니다. 예를 들어, 사용자가 웹 브라우저에서 콘텐츠에 액세스한다면 애플리케이션은 서명된 URL을 브라우저에 반환합니다. 브라우저는 즉시 서명된 URL을 사용하여 사용자 개입 없이 CloudFront 엣지 캐시의 파일에 액세스합니다.

1. CloudFront는 퍼블릭 키를 사용하여 서명을 확인하고 URL이 아직 변조되지 않았음을 확인합니다. 유효하지 않은 서명인 경우 요청을 거부합니다.

   서명이 유효한 경우, CloudFront는 URL의 정책 설명을 보고 아직 유효한 정책임을 확인합니다. 미리 준비된(canned) 정책을 사용하는 경우에는 정책 설명을 구성합니다. 예를 들어, URL의 시작 및 종료 날짜와 시간을 지정했다면 CloudFront는 액세스를 허용하려는 기간 동안 사용자가 콘텐츠에 액세스를 시도하고 있음을 확인합니다.

   요청이 정책 설명의 요구 사항을 충족하는 경우, CloudFront는 스탠다드 작업을 수행합니다. 다시 말해 파일이 이미 엣지 캐시에 있는지 여부를 판단하고, 필요에 따라 요청을 오리진으로 전달하고, 사용자에게 파일을 반환합니다.

**참고**  
서명되지 않은 URL에 쿼리 문자열 파라미터가 포함된 경우 서명하는 URL 부분에 이를 포함시켜야 합니다. 서명된 URL에 서명한 후 서명된 URL에 쿼리 문자열을 추가하면 해당 URL이 HTTP 403 상태를 반환합니다.

## 서명된 URL의 유효 기간 결정
<a name="private-content-overview-choosing-duration"></a>

단시간(최소 몇 분) 동안만 유효한 서명된 URL을 사용하여 프라이빗 콘텐츠를 배포할 수 있습니다. 이렇게 단시간 동안 유효한 서명된 URL은 영화 대여 또는 음악 다운로드 등을 주문 방식으로 고객에게 제공하는 등 특정 목적으로 즉석에서 콘텐츠를 배포할 때 유용합니다. 서명된 URL의 효력을 단시간 동안만 유지하려는 경우, 이를 자동으로 만드는 애플리케이션을 개발할 수 있습니다. 사용자가 파일을 다운로드하거나 미디어 파일을 재생하기 시작하면 CloudFront는 URL의 만료 시간을 현재 시간과 비교하여 URL이 아직 유효한지 파악합니다.

또한 장시간(몇 년 정도) 동안 유효한 서명된 URL을 사용하여 프라이빗 콘텐츠를 배포할 수도 있습니다. 장시간 동안 유효한 서명된 URL은 알려진 사용자에게 프라이빗 콘텐츠를 배포할 때 유용합니다. 투자자에게 사업 계획을 배포하거나 직원에게 교육 자료를 배포하는 경우가 여기에 해당합니다. 이러한 장기 서명 URL을 생성하는 애플리케이션을 개발할 수 있습니다.

## CloudFront가 서명된 URL에서 만료 날짜 및 시간을 확인하는 시기
<a name="private-content-check-expiration"></a>

CloudFront는 HTTP 요청이 있을 때 서명된 URL의 만료 날짜 및 시간을 확인합니다. 클라이언트가 만료 시간 직전에 대용량 파일을 다운로드하기 시작한 경우, 다운로드 도중 만료 시간이 지나도 다운로드는 완료됩니다. TCP 연결이 끊어진 경우, 클라이언트가 만료 시간 이후에 다운로드를 다시 시작하는 것은 불가능합니다.

클라이언트가 범위 GET을 사용하여 파일을 작은 조각으로 가져오는 경우, 만료 시간 이후의 GET 요청은 실패합니다. 범위 GET에 대한 자세한 내용은 [CloudFront에서 객체에 대한 부분적인 요청을 처리하는 방법(범위 GET)](RangeGETs.md)을 참조하세요.

## 예제 코드 및 타사 도구
<a name="private-content-overview-sample-code"></a>

서명된 URL의 해시 및 서명된 부분을 만드는 예제 코드에 대해서는 다음 주제를 참조하세요.
+ [Perl을 사용한 URL 서명 생성](CreateURLPerl.md)
+ [PHP를 사용한 URL 서명 생성](CreateURL_PHP.md)
+ [C\$1 및 .NET Framework를 사용한 URL 서명 생성](CreateSignatureInCSharp.md)
+ [Java를 사용한 URL 서명 생성](CFPrivateDistJavaDevelopment.md)

# 미리 준비된 정책을 사용하여 서명된 URL 생성
<a name="private-content-creating-signed-url-canned-policy"></a>

미리 준비된 정책을 사용하여 서명된 URL을 만들려면 다음 단계를 수행합니다.<a name="private-content-creating-signed-url-canned-policy-procedure"></a>

**미리 준비된 정책을 사용하여 서명된 URL을 만들려면**

1. .NET 또는 Java를 사용하여 서명된 URL을 만드는 중인데 키 페어의 프라이빗 키를 기본 .pem 형식에서 .NET 또는 Java와 상환되는 형식으로 다시 포맷하지 않았다면 지금 포맷하세요. 자세한 내용은 [프라이빗 키 재포맷(.NET 및 Java만 해당)](private-content-trusted-signers.md#private-content-reformatting-private-key) 섹션을 참조하세요.

1. 다음 값을 연결합니다. 이 예제의 서명된 URL의 형식을 사용할 수 있습니다.

   ```
   https://d111111abcdef8.cloudfront.net/image.jpg?color=red&size=medium&Expires=1767290400&Signature=nitfHRCrtziwO2HwPfWw~yYDhUF5EwRunQA-j19DzZrvDh6hQ73lDx~-ar3UocvvRQVw6EkC~GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmatEXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6&Key-Pair-Id=K2JCJMDEHXQW5F
   ```

   모든 빈 공백(탭과 줄바꿈 문자 포함)을 제거합니다. 애플리케이션 코드의 문자열에 이스케이프 문자를 포함해야 할 수도 있습니다. 모든 값에는 일종의 `String`이 있습니다.  
**1. *파일의 기본 URL***  
기본 URL이란 서명된 URL을 사용하지 않고 파일에 액세스할 때 사용할 CloudFront URL을 말합니다. 자체 쿼리 문자열 파라미터가 있는 경우 여기에 포함됩니다. 이전 예제에서 기본 URL은 `https://d111111abcdef8.cloudfront.net/image.jpg`입니다. 웹 배포의 URL 형식에 대한 자세한 내용은 [CloudFront에서 파일에 대한 URL 형식 사용자 지정](LinkFormat.md)을 참조하세요.  
   + 다음 CloudFront URL은 배포의 이미지 파일을 위한 것입니다(CloudFront 도메인 이름 사용). `image.jpg`는 `images` 디렉터리에 있습니다. URL에 있는 파일의 경로는 HTTP 서버 또는 Amazon S3 버킷의 파일 경로와 일치해야 합니다.

     `https://d111111abcdef8.cloudfront.net/images/image.jpg`
   + 다음 CloudFront URL은 쿼리 문자열을 포함합니다.

     `https://d111111abcdef8.cloudfront.net/images/image.jpg?size=large`
   + 다음 CloudFront URL은 배포의 이미지 파일을 위한 것입니다. 모두 대체 도메인 이름을 사용합니다. 두 번째는 쿼리 문자열을 포함합니다.

     `https://www.example.com/images/image.jpg`

     `https://www.example.com/images/image.jpg?color=red`
   + 다음 CloudFront URL은 대체 도메인 이름과 HTTPS 프로토콜을 사용하는 배포의 이미지 파일을 위한 것입니다.

     `https://www.example.com/images/image.jpg`  
** 2. `?` **  
`?`는 기본 URL 뒤에 쿼리 파라미터가 있음을 나타냅니다. 쿼리 파라미터를 지정하지 않은 경우에도 `?`를 포함합니다.  
순서에 관계없이 다음 쿼리 파라미터를 지정할 수 있습니다.  
**3. *자체 쿼리 문자열 파라미터(있는 경우*`&`**  
(선택 사항) 자체 쿼리 문자열 파라미터를 입력할 수 있습니다. 이렇게 하려면 `color=red&size=medium`처럼 각 문자열 파라미터 간에 앰퍼샌드(`&`)를 추가합니다. 쿼리 문자열 파라미터는 URL 내에서 원하는 순서로 지정할 수 있습니다.  
쿼리 문자열 파라미터의 이름은 `Expires`, `Signature`, `Key-Pair-Id`로 지정할 수 없습니다.  
** 4. `Expires=`*Unix 시간 형식(초) 및 협정 세계시(UTC) 기준의 날짜 및 시간***  
URL에서 파일에 대한 액세스 허용을 중지하게 할 날짜 및 시간입니다.  
Unix 시간 형식(초) 및 협정 세계시(UTC) 기준의 URL 만료 날짜 및 시간을 지정합니다. 예를 들면 2026년 1월 1일 오전 10시(UTC)를 이 주제의 시작 부분에 있는 예와 같이 Unix 시간 형식의 `1767290400`으로 변환합니다.  
에포크 시간을 사용하려면 `9223372036854775807`(금요일, 2262년 4월 11일 23:47:16.854 UTC) 이전 날짜에 대한 64비트 정수를 지정합니다.  
  
UTC에 대한 자세한 내용은 [RFC 3339, 인터넷의 날짜 및 시간: 타임스탬프](https://tools.ietf.org/html/rfc3339)를 참조하세요.  
** 5. `&Signature=`*정책 설명의 해시 및 서명된 버전***  
JSON 정책 설명을 해시, 서명 및 base64로 인코딩한 버전입니다. 자세한 내용은 [미리 준비된 정책을 사용하는 서명된 URL에 대한 서명 생성](#private-content-canned-policy-creating-signature) 섹션을 참조하세요.  
** 6. `&Key-Pair-Id=`*서명을 생성하는 데 사용 중인 해당 프라이빗 키가 있는 CloudFront 퍼블릭 키의 퍼블릭 키 ID***  
CloudFront 퍼블릭 키의 ID입니다(예: `K2JCJMDEHXQW5F`). CloudFront는 서명된 URL을 확인할 때 사용할 퍼블릭 키를 퍼블릭 키 ID로 판단합니다. CloudFront는 서명의 정보를 정책 설명의 정보와 비교하고 URL이 변조되지 않았음을 확인합니다.  
이 퍼블릭 키는 배포에서 신뢰할 수 있는 서명자인 키 그룹에 속해야 합니다. 자세한 내용은 [서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정](private-content-trusted-signers.md) 섹션을 참조하세요.

## 미리 준비된 정책을 사용하는 서명된 URL에 대한 서명 생성
<a name="private-content-canned-policy-creating-signature"></a>

미리 준비된 정책을 사용하는 서명된 URL의 서명을 만들려면 다음 절차를 완료하세요.

**Topics**
+ [미리 준비된 정책을 사용하는 서명된 URL의 정책 설명 생성](#private-content-canned-policy-creating-policy-statement)
+ [미리 준비된 정책을 사용하는 서명된 URL에 대한 서명 생성](#private-content-canned-policy-signing-policy-statement)

### 미리 준비된 정책을 사용하는 서명된 URL의 정책 설명 생성
<a name="private-content-canned-policy-creating-policy-statement"></a>

미리 준비된 정책을 사용하여 서명된 URL을 만드는 경우, `Signature` 파라미터는 정책 설명의 해시 및 서명된 버전입니다. 미리 준비된 정책을 사용하는 서명된 URL의 경우, 사용자 지정 정책을 사용하는 서명된 URL과 마찬가지로 URL에 정책 설명을 포함하지 않습니다. 정책 설명을 만들려면 다음 절차를 수행하세요.<a name="private-content-canned-policy-creating-policy-statement-procedure"></a>

**미리 준비된 정책을 사용하는 서명된 URL에 대한 정책 설명을 만들려면**

1. 다음 JSON 형식과 UTF-8 문자 인코딩을 사용하여 정책 설명을 구성합니다. 지정된 모든 문장 부호 및 기타 리터럴 값을 정확히 포함해야 합니다. `Resource` 및 `DateLessThan` 파라미터에 대한 내용은 [미리 준비된 정책을 사용하는 서명된 URL에 대한 정책 설명에서 지정한 값](#private-content-canned-policy-statement-values)를 참조하세요.

   ```
   {
       "Statement": [
           {
               "Resource": "base URL or stream name",
               "Condition": {
                   "DateLessThan": {
                       "AWS:EpochTime": ending date and time in Unix time format and UTC
                   }
               }
           }
       ]
   }
   ```

1. 정책 설명에서 모든 공백(탭과 줄바꿈 문자 포함)을 제거합니다. 애플리케이션 코드의 문자열에 이스케이프 문자를 포함해야 할 수도 있습니다.

#### 미리 준비된 정책을 사용하는 서명된 URL에 대한 정책 설명에서 지정한 값
<a name="private-content-canned-policy-statement-values"></a>

미리 준비된 정책에 대한 정책 설명을 만들 때 다음 값을 지정합니다.

**리소스**  
`Resource`에 대해 값을 하나만 지정할 수 있습니다.
쿼리 문자열(있는 경우)을 포함하지만 CloudFront `Expires`, `Signature` 및 `Key-Pair-Id` 파라미터를 제외한 기본 URL입니다. 예를 들면 다음과 같습니다.  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`  
다음을 참조하세요.  
+ **프로토콜(Protocol)** – 값은 `http://` 또는 `https://`로 시작해야 합니다.
+ **쿼리 문자열 파라미터(Query string parameters)** – 쿼리 문자열 파라미터가 없는 경우, 물음표를 생략합니다.
+ **대체 도메인 이름(Alternate domain names)** – URL에 대체 도메인 이름(CNAME)을 지정하는 경우, 웹 페이지 또는 애플리케이션의 파일을 참조할 때 대체 도메인 이름을 지정해야 합니다. 객체에 대한 Amazon S3 URL을 지정하지 마세요.

**DateLessThan**  
Unix 시간 형식(초) 및 협정 세계시(UTC) 기준의 URL 만료 날짜 및 시간. 예를 들면 2026년 1월 1일 오전 10시(UTC)를 Unix 시간 형식의 1767290400으로 변환합니다.  
이 값은 서명된 URL의 `Expires` 쿼리 문자열 파라미터 값과 일치해야 합니다. 값을 인용 부호로 묶지 마세요.  
자세한 내용은 [CloudFront가 서명된 URL에서 만료 날짜 및 시간을 확인하는 시기](private-content-signed-urls.md#private-content-check-expiration) 단원을 참조하세요.

#### 미리 준비된 정책을 사용하는 서명된 URL에 대한 정책 설명 예제
<a name="private-content-canned-policy-creating-policy-statement-example"></a>

서명된 URL에 다음 예제의 정책 설명을 사용할 경우, 사용자는 UTC 기준 2026년 1월 1일 오전 10시까지 `https://d111111abcdef8.cloudfront.net/horizon.jpg` 파일에 액세스할 수 있습니다.

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/horizon.jpg?size=large&license=yes",
            "Condition": {
                "DateLessThan": {
                    "AWS:EpochTime": 1767290400
                }
            }
        }
    ]
}
```

### 미리 준비된 정책을 사용하는 서명된 URL에 대한 서명 생성
<a name="private-content-canned-policy-signing-policy-statement"></a>

서명된 URL의 `Signature` 파라미터 값을 만들려면 [미리 준비된 정책을 사용하는 서명된 URL의 정책 설명 생성](#private-content-canned-policy-creating-policy-statement)에서 만든 정책 설명을 해시하고 서명합니다.

다음을 참조하여 정책 설명을 해시, 서명 및 인코딩하는 방법에 대한 추가적인 내용과 예제를 확인하세요.
+ [base64 인코딩 및 암호화를 위한 Linux 명령 및 OpenSSL](private-content-linux-openssl.md)
+ [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)<a name="private-content-canned-policy-creating-signature-download-procedure"></a>

**옵션 1: 미리 준비된 정책을 사용하여 서명을 만들려면**

1. SHA-1 해시 함수와 생성된 RSA 또는 ECDSA 프라이빗 키를 사용하여 [미리 준비된 정책을 사용하는 서명된 URL에 대한 정책 설명을 만들려면](#private-content-canned-policy-creating-policy-statement-procedure) 절차에서 만든 정책 설명을 해시하고 서명합니다. 공백이 삭제된 버전의 정책 설명을 사용합니다.

   해시 함수에 필요한 프라이빗 키의 경우 배포에 대해 신뢰할 수 있는 활성 키 그룹에 퍼블릭 키가 있는 프라이빗 키를 사용합니다.
**참고**  
정책 설명을 해시 및 서명하는 방법은 프로그래밍 언어와 플랫폼에 따라 달라집니다. 샘플 코드에 대한 내용은 [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)를 참조하세요.

1. 해시 및 서명된 문자열에서 공백(탭과 줄바꿈 문자 포함)을 제거합니다.

1. MIME base64 인코딩 기준으로 문자열을 base64로 인코딩합니다. 자세한 내용은 *RFC 2045, MIME(Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*의 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)을 참조하세요.

1. URL 쿼리 문자열에서 사용할 수 없는 문자를 유효한 문자로 교체합니다. 아래 표에 사용할 수 없는 문자와 유효한 문자가 나열되어 있습니다.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html)

1. 서명된 URL의 `&Signature=` 뒤에 결과 값을 추가하고 [미리 준비된 정책을 사용하여 서명된 URL을 만들려면](#private-content-creating-signed-url-canned-policy-procedure)로 돌아가서 서명된 URL 부분을 마저 연결합니다.

# 사용자 지정 정책을 사용하여 서명된 URL 생성
<a name="private-content-creating-signed-url-custom-policy"></a>

사용자 지정 정책을 사용하여 서명된 URL을 만들려면 다음 절차를 완료합니다.<a name="private-content-creating-signed-url-custom-policy-procedure"></a>

**사용자 지정 정책을 사용하여 서명된 URL을 만들려면**

1. .NET 또는 Java를 사용하여 서명된 URL을 만드는 중인데 키 페어의 프라이빗 키를 기본 .pem 형식에서 .NET 또는 Java와 상환되는 형식으로 다시 포맷하지 않았다면 지금 포맷하세요. 자세한 내용은 [프라이빗 키 재포맷(.NET 및 Java만 해당)](private-content-trusted-signers.md#private-content-reformatting-private-key) 섹션을 참조하세요.

1. 다음 값을 연결합니다. 이 예제의 서명된 URL의 형식을 사용할 수 있습니다.

   

   ```
   https://d111111abcdef8.cloudfront.net/image.jpg?color=red&size=medium&Policy=eyANCiAgICEXAMPLEW1lbnQiOiBbeyANCiAgICAgICJSZXNvdXJjZSI6Imh0dHA6Ly9kemJlc3FtN3VuMW0wLmNsb3VkZnJvbnQubmV0L2RlbW8ucGhwIiwgDQogICAgICAiQ29uZGl0aW9uIjp7IA0KICAgICAgICAgIklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIyMDcuMTcxLjE4MC4xMDEvMzIifSwNCiAgICAgICAgICJEYXRlR3JlYXRlclRoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTI5Njg2MDE3Nn0sDQogICAgICAgICAiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjEyOTY4NjAyMjZ9DQogICAgICB9IA0KICAgfV0gDQp9DQo&Signature=nitfHRCrtziwO2HwPfWw~yYDhUF5EwRunQA-j19DzZrvDh6hQ73lDx~-ar3UocvvRQVw6EkC~GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmatEXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6&Key-Pair-Id=K2JCJMDEHXQW5F
   ```

   모든 빈 공백(탭과 줄바꿈 문자 포함)을 제거합니다. 애플리케이션 코드의 문자열에 이스케이프 문자를 포함해야 할 수도 있습니다. 모든 값에는 일종의 `String`이 있습니다.  
**1. *파일의 기본 URL***  
기본 URL이란 서명된 URL을 사용하지 않고 파일에 액세스할 때 사용할 CloudFront URL을 말합니다. 자체 쿼리 문자열 파라미터가 있는 경우 여기에 포함됩니다. 이전 예제에서 기본 URL은 `https://d111111abcdef8.cloudfront.net/image.jpg`입니다. 웹 배포의 URL 형식에 대한 자세한 내용은 [CloudFront에서 파일에 대한 URL 형식 사용자 지정](LinkFormat.md)을 참조하세요.  
다음 예는 배포에 대해 지정하는 값을 보여줍니다.  
   + 다음 CloudFront URL은 배포의 이미지 파일을 위한 것입니다(CloudFront 도메인 이름 사용). `image.jpg`는 `images` 디렉터리에 있습니다. URL에 있는 파일의 경로는 HTTP 서버 또는 Amazon S3 버킷의 파일 경로와 일치해야 합니다.

     `https://d111111abcdef8.cloudfront.net/images/image.jpg`
   + 다음 CloudFront URL은 쿼리 문자열을 포함합니다.

     `https://d111111abcdef8.cloudfront.net/images/image.jpg?size=large`
   + 다음 CloudFront URL은 배포의 이미지 파일을 위한 것입니다. 둘 다 대체 도메인 이름을 사용하며 두 번째는 쿼리 문자열을 포함합니다.

     `https://www.example.com/images/image.jpg`

     `https://www.example.com/images/image.jpg?color=red`
   + 다음 CloudFront URL은 대체 도메인 이름과 HTTPS 프로토콜을 사용하는 배포의 이미지 파일을 위한 것입니다.

     `https://www.example.com/images/image.jpg`  
**2. `?` **  
`?`는 기본 URL 뒤에 쿼리 문자열 파라미터가 있음을 나타냅니다. 쿼리 파라미터를 지정하지 않은 경우에도 `?`를 포함합니다.  
순서에 관계없이 다음 쿼리 파라미터를 지정할 수 있습니다.  
**3. *자체 쿼리 문자열 파라미터(있는 경우*`&`**  
(선택 사항) 자체 쿼리 문자열 파라미터를 입력할 수 있습니다. 이렇게 하려면 `color=red&size=medium`처럼 각 문자열 파라미터 간에 앰퍼샌드(&)를 추가합니다. 쿼리 문자열 파라미터는 URL 내에서 원하는 순서로 지정할 수 있습니다.  
쿼리 문자열 파라미터의 이름은 `Policy`, `Signature`, `Key-Pair-Id`로 지정할 수 없습니다.
자체 파라미터를 추가하는 경우, 마지막 파라미터를 포함하여 각 파라미터 뒤에 `&`를 추가합니다.  
**4. `Policy=`*정책 설명의 base64 인코딩 버전***  
공백이 제거된 JSON 형식의 정책 설명은 base64로 인코딩합니다. 자세한 내용은 [사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명 생성](#private-content-custom-policy-statement) 섹션을 참조하세요.  
정책 설명은 서명된 URL이 사용자에게 부여하는 액세스를 제어합니다. 여기에는 파일의 URL, 만료 날짜 및 시간, URL의 효력이 발생하는 날짜 및 시간(선택 사항), 파일에 액세스할 수 있는 IP 주소 또는 IP 주소 범위(선택 사항)가 포함됩니다.  
**5. `&Signature=`*정책 설명의 해시 및 서명된 버전***  
JSON 정책 설명을 해시, 서명 및 base64로 인코딩한 버전입니다. 자세한 내용은 [사용자 지정 정책을 사용하는 서명된 URL에 대한 서명 생성](#private-content-custom-policy-creating-signature) 섹션을 참조하세요.  
**6. `&Key-Pair-Id=`*서명을 생성하는 데 사용 중인 해당 프라이빗 키가 있는 CloudFront 퍼블릭 키의 퍼블릭 키 ID***  
CloudFront 퍼블릭 키의 ID입니다(예: `K2JCJMDEHXQW5F`). CloudFront는 서명된 URL을 확인할 때 사용할 퍼블릭 키를 퍼블릭 키 ID로 판단합니다. CloudFront는 서명의 정보를 정책 설명의 정보와 비교하고 URL이 변조되지 않았음을 확인합니다.  
이 퍼블릭 키는 배포에서 신뢰할 수 있는 서명자인 키 그룹에 속해야 합니다. 자세한 내용은 [서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정](private-content-trusted-signers.md) 섹션을 참조하세요.

## 사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명 생성
<a name="private-content-custom-policy-statement"></a>

다음 단계를 완료하여 사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 명령문을 만듭니다.

다양한 방법으로 파일에 대한 액세스를 제어하는 예시 정책 명령문에 대한 내용은 [사용자 지정 정책을 사용하는 서명된 URL에 대한 예제 정책 설명](#private-content-custom-policy-statement-examples) 섹션을 참조하세요.<a name="private-content-custom-policy-creating-policy-procedure"></a>

**사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명을 만들려면**

1. 다음 JSON 형식을 사용하여 정책 설명을 구성하세요. 다음 보다 작음(`<`) 및 다음보다 큼(`>`) 기호 및 해당 기호 내의 설명을 사용자 지정 값으로 바꿉니다. 자세한 내용은 [사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명에서 지정한 값](#private-content-custom-policy-statement-values) 섹션을 참조하세요.

   ```
   {
       "Statement": [
           {
               "Resource": "<Optional but recommended: URL of the file>",
               "Condition": {
                   "DateLessThan": {
   	                "AWS:EpochTime": <Required: ending date and time in Unix time format and UTC>
                   },
                   "DateGreaterThan": {
   	                "AWS:EpochTime": <Optional: beginning date and time in Unix time format and UTC>
                   },
                   "IpAddress": {
   	                "AWS:SourceIp": "<Optional: IP address>"
                   }
               }
           }
       ]
   }
   ```

   유의할 사항:
   + 정책에는 명령문을 하나만 포함할 수 있습니다.
   + UTF-8 문자 인코딩을 사용하세요.
   + 지정된 대로 정확하게 모든 문장 부호 및 파라미터 이름을 포함하세요. 파라미터 이름의 약어는 허용되지 않습니다.
   + `Condition` 섹션의 파라미터 순서는 중요하지 않습니다.
   + `Resource`, `DateLessThan`, `DateGreaterThan`, `IpAddress`에 대한 값 관련 내용은 [사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명에서 지정한 값](#private-content-custom-policy-statement-values)을 참조하세요.

1. 정책 설명에서 모든 공백(탭과 줄바꿈 문자 포함)을 제거합니다. 애플리케이션 코드의 문자열에 이스케이프 문자를 포함해야 할 수도 있습니다.

1. MIME base64 인코딩 기준으로 정책 설명을 Base64로 인코딩합니다. 자세한 내용은 *RFC 2045, MIME(Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*의 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)을 참조하세요.

1. URL 쿼리 문자열에서 사용할 수 없는 문자를 유효한 문자로 교체합니다. 아래 표에 사용할 수 없는 문자와 유효한 문자가 나열되어 있습니다.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html)

1. 서명된 URL의 `Policy=` 뒤에 결과 값을 추가하세요.

1. 정책 설명을 해시, 서명 및 base64로 인코딩하여 서명된 URL에 대한 서명을 만드세요. 자세한 내용은 [사용자 지정 정책을 사용하는 서명된 URL에 대한 서명 생성](#private-content-custom-policy-creating-signature) 섹션을 참조하세요.

### 사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명에서 지정한 값
<a name="private-content-custom-policy-statement-values"></a>

사용자 지정 정책에 대한 정책 설명을 생성할 때 다음 값을 지정합니다.

**리소스**  
있는 경우 쿼리 문자열은 포함하지만 CloudFront `Policy`, `Signature` 및 `Key-Pair-Id` 파라미터는 제외한 기본 URL입니다. 예제:  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg\?size=large&license=yes`  
`Resource`에 대해 값을 하나만 지정할 수 있습니다.  
정책에서 `Resource` 파라미터를 생략할 수 있지만 생략하는 경우 서명된 URL을 가진 사람은 누구나 서명된 URL을 만드는 데 사용한 키 페어와 연결된 어떠한 배포에서든 모든 파일에 액세스할 수 있다는 뜻이 됩니다.****
유의할 사항:  
+ **프로토콜(Protocol)** – 값은 `http://`, `https://` 또는 `*://`로 시작해야 합니다.
+ **쿼리 문자열 파라미터** - URL에 쿼리 문자열 파라미터가 있는 경우 백슬래시 문자(`\`)를 사용하지 않고 쿼리 문자열을 시작하는 물음표 문자(`?`)를 이스케이프 처리합니다. 예제:

  `https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`
+ **와일드카드 문자** - 정책의 URL에 와일드카드 문자를 사용할 수 있습니다. 다음 와일드카드 문자가 지원됩니다.
  + 별표(`*`). 0개 이상의 문자와 매칭됩니다.
  + 물음표(`?`). 정확히 한 문자와 매칭됩니다.

  CloudFront가 정책의 URL을 HTTP 요청의 URL과 매칭하면 정책의 URL은 네 개의 섹션, 즉 프로토콜, 도메인, 경로, 쿼리 문자열로 구분됩니다.

  `[protocol]://[domain]/[path]\?[query string]`

  정책의 URL에 와일드카드 문자를 사용하는 경우 와일드카드 매칭은 와일드카드가 포함된 섹션의 경계 내에서만 적용됩니다. 예를 들어, 다음 정책에서 이 URL을 고려해 보세요.

  `https://www.example.com/hello*world`

  이 예시에서 별표 와일드카드(`*`)는 경로 섹션 내에만 적용되므로 URL `https://www.example.com/helloworld` 및 `https://www.example.com/hello-world`와는 매칭되지만 URL `https://www.example.net/hello?world`와는 매칭되지 않습니다.

  와일드카드 매칭을 위한 섹션 경계에는 다음과 같은 예외가 적용됩니다.
  + 경로 섹션의 후행 별표는 쿼리 문자열 섹션의 별표를 의미합니다. 예를 들어, `http://example.com/hello*`은 `http://example.com/hello*\?*`과 같습니다.
  + 도메인 섹션의 후행 별표는 경로와 쿼리 문자열 섹션의 별표를 의미합니다. 예를 들어, `http://example.com*`은 `http://example.com*/*\?*`과 같습니다.
  + 정책의 URL은 프로토콜 섹션을 생략하고 도메인 섹션에서 별표로 시작할 수 있습니다. 이 경우 프로토콜 섹션은 암시적으로 별표로 설정됩니다. 예를 들어, 정책의 `*example.com` URL은 `*://*example.com/`에 해당합니다.
  + 별표(`"Resource": "*"`)는 그 자체로 모든 URL과 매칭됩니다.

  예를 들어, 정책의 `https://d111111abcdef8.cloudfront.net/*game_download.zip*` 값은 다음 URL 모두와 매칭됩니다.
  + `https://d111111abcdef8.cloudfront.net/game_download.zip`
  + `https://d111111abcdef8.cloudfront.net/example_game_download.zip?license=yes`
  + `https://d111111abcdef8.cloudfront.net/test_game_download.zip?license=temp`
+ **대체 도메인 이름** – 정책의 URL에 대체 도메인 이름(CNAME)을 지정하는 경우, 웹 페이지 또는 애플리케이션에서 HTTP 요청이 대체 도메인 이름을 사용해야 합니다. 정책의 파일에 Amazon S3 URL을 지정하지 마시기 바랍니다.

**DateLessThan**  
Unix 시간 형식(초) 및 협정 세계시(UTC) 기준의 URL 만료 날짜 및 시간. 정책에서 값을 인용 부호로 묶지 마세요. UTC에 대한 자세한 내용은 [인터넷의 날짜 및 시간: 타임스탬프](https://tools.ietf.org/html/rfc3339)를 참조하세요.  
예를 들면 2023년 1월 31일 오전 10시(UTC)를 Unix 시간 형식의 1675159200으로 변환합니다.  
`Condition` 섹션에서 유일한 필수 파라미터입니다. CloudFront는 이 값을 통해 프라이빗 콘텐츠에 대한 영구적인 액세스를 방지합니다.  
자세한 내용은 [CloudFront가 서명된 URL에서 만료 날짜 및 시간을 확인하는 시기](private-content-signed-urls.md#private-content-check-expiration) 섹션을 참조하세요.

**DateGreaterThan(선택 사항)**  
Unix 시간 형식(초)과 협정 세계시(UTC)의 URL에 대한 선택적 시작 날짜 및 시간. 사용자는 지정된 날짜 및 시간 이전에 파일에 액세스할 수 없습니다. 값을 인용 부호로 묶지 마세요.

**IpAddress(선택 사항)**  
HTTP 요청을 수행하는 클라이언트의 IP 주소입니다. 다음 사항에 유의하세요.  
+ 파일에 액세스하는 IP 주소를 허용하려면 `IpAddress` 파라미터를 생략합니다.
+ 하나의 IP 주소 또는 하나의 IP 주소 범위를 지정할 수 있습니다. 클라이언트의 IP 주소가 두 개의 다른 범위 중 하나에 있는 경우, 정책을 사용하여 액세스를 허용할 수 없습니다.
+ 단일 IP 주소에서 액세스를 허용하기 위해 다음을 지정합니다.

  `"`*IPv4 IP 주소*`/32"`
+ IP 주소 범위는 스탠다드 IPv4 CIDR 형식(예: `192.0.2.0/24`)을 지정해야 합니다. 자세한 내용은 [Classless Inter-domain Routing(CIDR): 인터넷 주소 할당 및 집계 계획](https://tools.ietf.org/html/rfc4632)을 참조하세요.
**중요**  
IPv6 형식의 IP 주소(예: 2001:0db8:85a3::8a2e:0370:7334)는 지원되지 않습니다.

  `IpAddress`를 포함하는 사용자 지정 정책을 사용할 경우 배포에 대해 IPv6를 사용하도록 설정하지 마세요. IP 주소로 일부 콘텐츠에 대한 액세스를 제한하고, 다른 콘텐츠에 대해서는 IPv6 요청을 지원하려면 두 가지 배포를 만들면 됩니다. 자세한 내용은 [IPv6 활성화(뷰어 요청)](DownloadDistValuesGeneral.md#DownloadDistValuesEnableIPv6) 주제에서 [모든 배포 설정 참조](distribution-web-values-specify.md) 단원을 참조하세요.

## 사용자 지정 정책을 사용하는 서명된 URL에 대한 예제 정책 설명
<a name="private-content-custom-policy-statement-examples"></a>

다음 예제 정책 설명은 특정 파일, 디렉터리의 모든 파일 또는 키 페어 ID에 연결된 모든 파일의 액세스 제어 방법을 보여줍니다. 예제는 또한 개별 IP 주소 또는 IP 주소 범위에서 액세스를 제어하는 방법과 지정된 날짜 및 시간 이후 사용자의 서명된 URL 사용 방지 방법을 보여줍니다.

이러한 예시를 복사하여 붙여 넣는 경우, 공백(탭과 줄바꿈 문자 포함)을 제거하고 값을 자체 값과 교체한 후 닫는 괄호(`}`)뒤에 줄바꿈 문자를 포함합니다.

자세한 내용은 [사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명에서 지정한 값](#private-content-custom-policy-statement-values) 섹션을 참조하세요.

**Topics**
+ [예제 정책 설명: IP 주소 범위에서 하나의 파일에 액세스](#private-content-custom-policy-statement-example-one-object)
+ [예제 정책 설명: IP 주소 범위에서 디렉터리의 모든 파일에 액세스](#private-content-custom-policy-statement-example-all-objects)
+ [예제 정책 설명: 하나의 IP 주소에서 키 페어 ID에 연결된 모든 파일에 액세스](#private-content-custom-policy-statement-example-one-ip)

### 예제 정책 설명: IP 주소 범위에서 하나의 파일에 액세스
<a name="private-content-custom-policy-statement-example-one-object"></a>

다음 서명된 URL의 예시 사용자 지정 정책은 UTC 기준 2023년 1월 31일 오전 10시까지 `192.0.2.0/24` 범위의 IP 주소에서 `https://d111111abcdef8.cloudfront.net/game_download.zip` 파일에 액세스할 수 있는 사용자를 지정합니다.

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/game_download.zip",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675159200
                }
            }
        }
    ]
}
```

### 예제 정책 설명: IP 주소 범위에서 디렉터리의 모든 파일에 액세스
<a name="private-content-custom-policy-statement-example-all-objects"></a>

다음 예시 사용자 지정 정책은 `Resource` 파라미터의 와일드카드 문자(`*`)로 표시된 바와 같이 `training` 디렉터리의 파일에 대한 서명된 URL을 만들 수 있도록 허용합니다. 사용자는 UTC 기준 2023년 1월 31일 오전 10시까지 `192.0.2.0/24` 범위의 IP 주소에서 파일에 액세스할 수 있습니다.

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/training/*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675159200
                }
            }
        }
    ]
}
```

이 정책과 함께 사용하는 서명된 URL에는 다음과 같이 특정 파일을 식별하는 URL이 있습니다.

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

### 예제 정책 설명: 하나의 IP 주소에서 키 페어 ID에 연결된 모든 파일에 액세스
<a name="private-content-custom-policy-statement-example-one-ip"></a>

다음 예시 사용자 지정 정책은 `Resource` 파라미터의 와일드카드 문자(`*`)로 표시된 대로 배포와 연결된 파일에 대한 서명된 URL을 만들 수 있도록 허용합니다. 서명된 URL은 `http://` 프로토콜이 아닌 `https://` 프로토콜을 사용해야 합니다. 사용자는 IP 주소 `192.0.2.10/32`를 사용해야 합니다. CIDR 표기법의 `192.0.2.10/32` 값은 단일 IP 주소 `192.0.2.10`을 가리킵니다. 파일은 UTC 기준 2023년 1월 31일 오전 10시에서 2023년 2월 2일 오전 10시까지만 사용할 수 있습니다.

```
{
    "Statement": [
       {
            "Resource": "https://*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.10/32"
                },
                "DateGreaterThan": {
                    "AWS:EpochTime": 1675159200
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675332000
                }
            }
        }
    ]
}
```

이 정책과 사용하는 서명된 URL에는 다음과 같이 특정 CloudFront 배포의 특정 파일을 식별하는 URL이 있습니다.

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

또한 서명된 URL은 키 페어 ID를 포함하고 이는 URL에서 지정한 배포(d111111abcdef8.cloudfront.net)의 신뢰할 수 있는 키 그룹과 연결되어야 합니다.

## 사용자 지정 정책을 사용하는 서명된 URL에 대한 서명 생성
<a name="private-content-custom-policy-creating-signature"></a>

서명된 URL에 대한 서명은 정책 설명의 해시, 서명 및 base64로 인코딩된 버전의 사용자 지정 정책을 사용합니다. 사용자 지정 정책에 대한 서명을 만들려면 다음 단계를 수행합니다.

다음을 참조하여 정책 설명을 해시, 서명 및 인코딩하는 방법에 대한 추가적인 내용과 예제를 확인하세요.
+ [base64 인코딩 및 암호화를 위한 Linux 명령 및 OpenSSL](private-content-linux-openssl.md)
+ [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)<a name="private-content-custom-policy-creating-signature-download-procedure"></a>

**옵션 1: 사용자 지정 정책을 사용하여 서명을 만들려면**

1. SHA-1 해시 함수와 생성된 RSA 또는 ECDSA 프라이빗 키를 사용하여 [사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명을 만들려면](#private-content-custom-policy-creating-policy-procedure) 절차에서 만든 JSON 정책 설명을 해시하고 서명합니다. 더 이상 공백을 포함하지 않지만 base64로 인코딩되지 않은 정책 설명 버전을 사용합니다.

   해시 함수에 필요한 프라이빗 키의 경우 배포에 대해 신뢰할 수 있는 활성 키 그룹에 퍼블릭 키가 있는 프라이빗 키를 사용합니다.
**참고**  
정책 설명을 해시 및 서명하는 방법은 프로그래밍 언어와 플랫폼에 따라 달라집니다. 샘플 코드에 대한 내용은 [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)를 참조하세요.

1. 해시 및 서명된 문자열에서 공백(탭과 줄바꿈 문자 포함)을 제거합니다.

1. MIME base64 인코딩 기준으로 문자열을 base64로 인코딩합니다. 자세한 내용은 *RFC 2045, MIME(Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*의 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)을 참조하세요.

1. URL 쿼리 문자열에서 사용할 수 없는 문자를 유효한 문자로 교체합니다. 아래 표에 사용할 수 없는 문자와 유효한 문자가 나열되어 있습니다.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html)

1. 서명된 URL의 `&Signature=` 뒤에 결과 값을 추가하고 [사용자 지정 정책을 사용하여 서명된 URL을 만들려면](#private-content-creating-signed-url-custom-policy-procedure)로 돌아가서 서명된 URL 부분을 마저 연결합니다.

# 서명된 쿠키 사용
<a name="private-content-signed-cookies"></a>

현재의 URL을 변경하지 않으려는 경우나 여러 제한된 파일(예: 웹 사이트의 구독자 영역에 있는 전체 파일)에 대한 액세스 권한을 제공하려는 경우, CloudFront 서명된 쿠키를 사용하여 콘텐츠 액세스를 제어할 수 있습니다. 이 주제에서는 서명된 쿠키를 사용할 때 고려해야 할 사항과 미리 준비된 정책 및 사용자 지정 정책을 사용하여 서명된 쿠키를 설정하는 방법을 다룹니다.

**Topics**
+ [서명된 쿠키에 대해 미리 준비된 정책 또는 사용자 지정 정책 사용 결정](#private-content-choosing-canned-custom-cookies)
+ [서명된 쿠키 작동 방식](#private-content-how-signed-cookies-work)
+ [서명된 쿠키 악용 방지](#private-content-signed-cookie-misuse)
+ [CloudFront가 서명된 쿠키의 만료 날짜 및 시간을 확인하는 시기](#private-content-check-expiration-cookie)
+ [샘플 코드 및 타사 도구](#private-content-overview-sample-code-cookies)
+ [미리 준비된 정책을 사용하여 서명된 쿠키 설정](private-content-setting-signed-cookie-canned-policy.md)
+ [사용자 지정 정책을 사용하여 서명된 쿠키 설정](private-content-setting-signed-cookie-custom-policy.md)
+ [PHP를 사용하여 서명된 쿠키 생성](signed-cookies-PHP.md)

## 서명된 쿠키에 대해 미리 준비된 정책 또는 사용자 지정 정책 사용 결정
<a name="private-content-choosing-canned-custom-cookies"></a>

서명된 쿠키를 만들 때 JSON 형식의 정책 설명을 작성하여 서명된 쿠키의 제약 조건(예: 쿠키의 유효 기간)을 지정합니다. 미리 준비된 정책 또는 사용자 지정 정책을 사용할 수 있습니다. 다음 테이블은 미리 준비된 정책과 사용자 지정 정책을 비교합니다.


****  

| 설명 | 미리 준비된 정책 | 사용자 정의 정책 | 
| --- | --- | --- | 
| 정책 설명은 여러 파일에 재사용할 수 있습니다. 정책 설명을 재사용하려면 `Resource` 객체에 와일드카드 문자를 사용해야 합니다. 자세한 내용은 [서명된 쿠키에서 사용자 지정 정책을 위한 정책 설명에서 지정한 값](private-content-setting-signed-cookie-custom-policy.md#private-content-custom-policy-statement-cookies-values) 섹션을 참조하세요.)  | 아니요 | 예 | 
| 날짜 및 시간을 지정하여 사용자가 콘텐츠에 액세스를 시작할 수 있습니다. | 아니요 | 예(선택 사항) | 
| 사용자의 콘텐츠 액세스를 중단할 날짜 및 시간을 지정할 수 있습니다. | 예 | 예 | 
| 콘텐츠에 액세스할 수 있는 사용자의 IP 주소 또는 IP 주소 범위를 지정할 수 있습니다. | 아니요 | 예(선택 사항) | 

미리 준비된(canned) 정책으로 서명된 쿠키를 만드는 방법에 대한 자세한 내용은 [미리 준비된 정책을 사용하여 서명된 쿠키 설정](private-content-setting-signed-cookie-canned-policy.md)을 참조하세요.

사용자 지정 정책으로 서명된 쿠키를 만드는 방법에 대한 자세한 내용은 [사용자 지정 정책을 사용하여 서명된 쿠키 설정](private-content-setting-signed-cookie-custom-policy.md)을 참조하세요.

## 서명된 쿠키 작동 방식
<a name="private-content-how-signed-cookies-work"></a>

서명된 쿠키에 대한 CloudFront 구성 방법과 사용자가 서명된 쿠키를 포함한 요청을 제출할 때 CloudFront 반응에 대한 개요가 있습니다.

1. CloudFront 배포에서 CloudFront가 URL 서명을 확인하는 데 사용할 수 있는 퍼블릭 키가 포함된 신뢰할 수 있는 키 그룹을 하나 이상 지정합니다. 해당 프라이빗 키를 사용하여 URL에 서명합니다.

   자세한 내용은 [서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정](private-content-trusted-signers.md) 단원을 참조하세요.

1. 사용자가 콘텐츠에 액세스해야 하는지 여부를 파악하고, 필요하면 최종 사용자에게 `Set-Cookie` 헤더 세 개를 보내도록 애플리케이션을 개발합니다. (각 `Set-Cookie` 헤더는 이름-값 페어를 하나만 포함할 수 있고 CloudFront 서명된 쿠키에는 이름-값 페어 3개가 필요합니다.) 최종 사용자가 프라이빗 콘텐츠를 요청하기 전에 `Set-Cookie` 헤더를 최종 사용자에게 보내야 합니다. 쿠키의 만료 시간을 짧게 설정한 경우, 사용자가 계속 액세스할 수 있도록 후속 요청에 대해 `Set-Cookie` 헤더 세 개를 더 보낼 수 있습니다.

   일반적으로 CloudFront 배포에는 캐시 동작이 2개 이상 있는데 하나는 인증이 필요 없고 다른 하나는 필요합니다. 사이트 보안에 대한 오류 페이지에는 로그인 페이지로 가는 리디렉터 또는 링크가 있습니다.

   쿠키를 기반으로 파일을 캐시하도록 배포를 구성하는 경우, CloudFront는 서명된 쿠키의 속성을 기반으로 별도의 파일을 캐시하지 않습니다.

1. 사용자가 웹 사이트에 로그인하여 콘텐츠에 대한 비용을 지불하거나 액세스에 필요한 자격 요건을 충족합니다.

1. 애플리케이션은 응답을 통해 `Set-Cookie` 헤더를 반환하고 최종 사용자는 이름-값 페어를 저장합니다.

1. 사용자가 파일을 요청합니다.

   사용자의 브라우저 또는 기타 최종 사용자는 4단계의 이름-값 페어를 불러와서 이를 `Cookie` 헤더의 요청에 추가합니다. 이것이 서명된 쿠키입니다.

1. CloudFront는 퍼블릭 키를 사용하여 서명된 쿠키의 서명을 확인하고 쿠키가 아직 변조되지 않았음을 확인합니다. 유효하지 않은 서명인 경우 요청을 거부합니다.

   쿠키의 서명이 유효한 경우, CloudFront는 쿠키의 정책 설명을 보거나 미리 준비된(canned) 정책을 사용하는 경우, 정책 설명을 구성하여 요청이 아직 유효함을 확인합니다. 예를 들어, 쿠키의 시작 및 종료 날짜와 시간을 지정하는 경우, CloudFront는 액세스가 허용된 시간 동안 콘텐츠 액세스를 시도한 사용자를 확인합니다.

   요청이 정책 설명의 요구 사항을 충족하는 경우, CloudFront는 제한되지 않은 콘텐츠와 같은 방식으로 콘텐츠를 제공합니다. 다시 말해 파일이 이미 엣지 캐시에 있는지 여부를 판단하고, 필요에 따라 요청을 오리진으로 전달하고, 사용자에게 파일을 반환합니다.

## 서명된 쿠키 악용 방지
<a name="private-content-signed-cookie-misuse"></a>

`Domain` 헤더에 `Set-Cookie` 파라미터를 지정할 때는 루트 도메인 이름을 가진 사용자의 액세스 가능성을 줄이도록 최대한 정확한 값을 지정하세요. 예를 들자면 example.com보다 app.example.com이 더 좋고, example.com을 제어하지 않을 때는 특히 그렇습니다. 이렇게 하면 www.example.com을 통한 콘텐츠 액세스를 방지할 수 있습니다.

이런 유형의 공격을 방지하려면 다음을 수행하세요.
+ `Expires` 및 `Max-Age` 쿠키 속성을 제외하여 `Set-Cookie` 헤더에서 세션 쿠키를 만들도록 합니다. 세션 쿠키는 사용자가 브라우저를 닫을 때 자동으로 삭제되며, 따라서 권한 없는 제3자가 콘텐츠에 액세스할 가능성을 줄여 줍니다.
+ `Secure` 속성을 포함하여 최종 사용자가 이를 요청할 때 쿠키가 암호화되도록 하세요.
+ 가능하면 사용자 지정 정책을 사용하고 최종 사용자의 IP 주소를 포함합니다.
+ `CloudFront-Expires` 속성에서 사용자의 콘텐츠 액세스를 허용할 기간을 기준으로 하여 가장 짧고 합리적인 만료 시간을 지정하세요.

## CloudFront가 서명된 쿠키의 만료 날짜 및 시간을 확인하는 시기
<a name="private-content-check-expiration-cookie"></a>

CloudFront는 서명된 쿠키가 아직 유효한지 파악하기 위해 HTTP 요청 시 쿠키의 만료 날짜 및 시간을 확인합니다. 클라이언트가 만료 시간 직전에 대용량 파일을 다운로드하기 시작한 경우, 다운로드 도중 만료 시간이 지나도 다운로드는 완료됩니다. TCP 연결이 끊어진 경우, 클라이언트가 만료 시간 이후에 다운로드를 다시 시작하는 것은 불가능합니다.

클라이언트가 범위 GET을 사용하여 파일을 작은 조각으로 가져오는 경우, 만료 시간 이후의 GET 요청은 실패합니다. 범위 GET에 대한 자세한 내용은 [CloudFront에서 객체에 대한 부분적인 요청을 처리하는 방법(범위 GET)](RangeGETs.md)을 참조하세요.

## 샘플 코드 및 타사 도구
<a name="private-content-overview-sample-code-cookies"></a>

프라이빗 콘텐츠에 대한 샘플 코드는 서명된 URL에 대한 서명을 만드는 방법만 보여줍니다. 그러나 서명된 쿠키에 대한 서명 생성 프로세스도 이와 유사하기 때문에 대부분의 샘플 코드는 여전히 관계가 있습니다. 자세한 내용은 다음 항목을 참조하세요.
+ [Perl을 사용한 URL 서명 생성](CreateURLPerl.md)
+ [PHP를 사용한 URL 서명 생성](CreateURL_PHP.md)
+ [C\$1 및 .NET Framework를 사용한 URL 서명 생성](CreateSignatureInCSharp.md)
+ [Java를 사용한 URL 서명 생성](CFPrivateDistJavaDevelopment.md)

# 미리 준비된 정책을 사용하여 서명된 쿠키 설정
<a name="private-content-setting-signed-cookie-canned-policy"></a>

미리 준비된 정책을 사용하여 서명된 쿠키를 설정하려면 다음 단계를 완료하세요. 서명을 만들려면 [미리 준비된 정책을 사용하는 서명된 쿠키에 대한 서명 생성](#private-content-canned-policy-signature-cookies) 단원을 참조하세요.<a name="private-content-setting-signed-cookie-canned-policy-procedure"></a>

**미리 준비된 정책을 사용하여 서명된 쿠키를 설정하려면**

1. .NET 또는 Java를 사용하여 서명된 쿠키를 생성하는 중인데 키 페어의 프라이빗 키를 기본 .pem 형식에서 .NET 또는 Java와 상환되는 형식으로 다시 포맷하지 않았다면 지금 포맷하세요. 자세한 내용은 [프라이빗 키 재포맷(.NET 및 Java만 해당)](private-content-trusted-signers.md#private-content-reformatting-private-key) 단원을 참조하세요.

1. `Set-Cookie` 헤더 세 개를 승인된 최종 사용자에게 보내도록 애플리케이션을 프로그래밍합니다. 각 `Set-Cookie` 헤더는 이름-값 페어를 하나만 포함할 수 있고 CloudFront 서명된 쿠키에는 이름-값 페어 3개가 필요하기 때문에 `Set-Cookie` 헤더가 3개 있어야 합니다. 이름-값 페어는 `CloudFront-Expires`, `CloudFront-Signature`, `CloudFront-Key-Pair-Id`입니다. 액세스가 제한되는 파일에 대해 사용자가 첫 번째 요청을 하려면 최종 사용자에게 값이 있어야 합니다.
**참고**  
일반적으로 `Expires` 및 `Max-Age` 속성을 제외하는 것이 좋습니다. 이러한 속성을 제외하면 사용자가 브라우저를 닫을 때 쿠키가 삭제되므로 권한 없는 사람이 콘텐츠에 액세스할 가능성이 줄어듭니다. 자세한 내용은 [서명된 쿠키 악용 방지](private-content-signed-cookies.md#private-content-signed-cookie-misuse) 단원을 참조하세요.

   **쿠키 속성의 이름은 대소문자를 구분합니다**.

   줄바꿈은 속성의 가독성을 높이기 위해서만 사용됩니다.

   ```
   Set-Cookie: 
   CloudFront-Expires=date and time in Unix time format (in seconds) and Coordinated Universal Time (UTC); 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   
   Set-Cookie: 
   CloudFront-Signature=hashed and signed version of the policy statement; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   
   Set-Cookie: 
   CloudFront-Key-Pair-Id=public key ID for the CloudFront public key whose corresponding private key you're using to generate the signature; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   ```  
**(선택 사항) `Domain`**  
요청된 파일의 도메인 이름입니다. `Domain` 속성을 지정하지 않는 경우, 기본값은 URL의 도메인 이름이고 이는 하위 도메인이 아닌 지정된 도메인 이름에만 적용됩니다. `Domain` 속성을 지정하는 경우, 하위 도메인에도 적용됩니다. 도메인 이름 앞의 점(예: `Domain=.example.com`)은 선택 사항입니다. 또한 `Domain` 속성을 지정하는 경우, URL의 도메인 이름과 `Domain` 속성의 값이 일치해야 합니다.  
CloudFront가 배포에 할당하는 도메인 이름을 d111111abcdef8.cloudfront.net과 같이 지정할 수 있으나, \$1.cloudfront.net을 도메인 이름으로 지정할 수는 없습니다.  
URL에 example.com과 같은 대체 도메인 이름을 사용하려면 `Domain` 속성 지정 여부와 상관없이 대체 도메인 이름을 배포에 추가해야 합니다. 자세한 내용은 [대체 도메인 이름(CNAME)](DownloadDistValuesGeneral.md#DownloadDistValuesCNAME) 주제에서 [모든 배포 설정 참조](distribution-web-values-specify.md) 단원을 참조하세요.  
**(선택 사항) `Path`**  
요청된 파일의 경로입니다. `Path` 속성을 지정하지 않는 경우, 기본값은 URL의 경로입니다.  
**`Secure`**  
요청을 보내기 전 최종 사용자에게 쿠키의 암호화를 요청하세요. 쿠키 속성이 MITM(중간자 공격)을 당하지 않도록 HTTPS 연결을 통해 `Set-Cookie` 헤더를 보내는 것이 좋습니다.  
**`HttpOnly`**  
브라우저(지원되는 경우)가 쿠키 값과 상호 작용하는 방식을 정의합니다. `HttpOnly`를 사용하면 JavaScript에서 쿠키 값에 액세스할 수 없습니다. 이 예방 조치는 크로스 사이트 스크립팅(XSS) 공격을 완화하는 데 도움이 될 수 있습니다. 자세한 내용은 [HTTP 쿠키 사용](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)을 참조하세요.  
**`CloudFront-Expires`**  
Unix 시간 형식(초) 및 협정 세계시(UTC) 기준의 URL 만료 날짜 및 시간을 지정합니다. 예를 들면 2026년 1월 1일 오전 10시(UTC)를 Unix 시간 형식의 1767290400으로 변환합니다.  
에포크 시간을 사용하려면 `9223372036854775807`(금요일, 2262년 4월 11일 23:47:16.854 UTC) 이전 날짜에 대한 64비트 정수를 지정합니다.  
UTC에 대한 자세한 내용은 *RFC 3339, 인터넷의 날짜 및 시간: 타임스탬프*, [https://tools.ietf.org/html/rfc3339](https://tools.ietf.org/html/rfc3339)를 참조하세요.  
**`CloudFront-Signature`**  
JSON 정책 설명의 해시, 서명 및 base64 인코딩 버전입니다. 자세한 내용은 [미리 준비된 정책을 사용하는 서명된 쿠키에 대한 서명 생성](#private-content-canned-policy-signature-cookies) 단원을 참조하세요.  
**`CloudFront-Key-Pair-Id`**  
CloudFront 퍼블릭 키의 ID입니다(예: `K2JCJMDEHXQW5F`). CloudFront는 서명된 URL을 확인할 때 사용할 퍼블릭 키를 퍼블릭 키 ID로 판단합니다. CloudFront는 서명의 정보를 정책 설명의 정보와 비교하고 URL이 변조되지 않았음을 확인합니다.  
이 퍼블릭 키는 배포에서 신뢰할 수 있는 서명자인 키 그룹에 속해야 합니다. 자세한 내용은 [서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정](private-content-trusted-signers.md) 단원을 참조하세요.

다음 예제는 배포와 연결된 도메인 이름을 파일의 URL에 사용할 때 서명된 쿠키 한 개의 `Set-Cookie` 헤더를 보여줍니다.

```
Set-Cookie: CloudFront-Expires=1426500000; Domain=d111111abcdef8.cloudfront.net; Path=/images/*; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=yXrSIgyQoeE4FBI4eMKF6ho~CA8_; Domain=d111111abcdef8.cloudfront.net; Path=/images/*; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=d111111abcdef8.cloudfront.net; Path=/images/*; Secure; HttpOnly
```

다음 예제는 파일에 대한 URL의 example.org 대체 도메인 이름을 사용할 때 하나의 서명된 쿠키에 대한 예제 `Set-Cookie` 헤더를 보여줍니다.

```
Set-Cookie: CloudFront-Expires=1426500000; Domain=example.org; Path=/images/*; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=yXrSIgyQoeE4FBI4eMKF6ho~CA8_; Domain=example.org; Path=/images/*; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=example.org; Path=/images/*; Secure; HttpOnly
```

URL에 example.com과 같은 대체 도메인 이름을 사용하려면 `Domain` 속성 지정 여부와 상관없이 대체 도메인 이름을 배포에 추가해야 합니다. 자세한 내용은 [대체 도메인 이름(CNAME)](DownloadDistValuesGeneral.md#DownloadDistValuesCNAME) 주제에서 [모든 배포 설정 참조](distribution-web-values-specify.md) 단원을 참조하세요.

## 미리 준비된 정책을 사용하는 서명된 쿠키에 대한 서명 생성
<a name="private-content-canned-policy-signature-cookies"></a>

미리 준비된 정책을 사용하는 서명된 쿠키의 서명을 만들려면 다음 절차를 완료하세요.

**Topics**
+ [미리 준비된 정책을 사용하는 서명된 쿠키에 대한 정책 설명 생성](#private-content-canned-policy-statement-cookies)
+ [정책 설명에 서명하여 미리 준비된 정책을 사용하는 서명된 쿠키에 대한 서명 생성](#private-content-canned-policy-cookies-signing-policy-statement)

### 미리 준비된 정책을 사용하는 서명된 쿠키에 대한 정책 설명 생성
<a name="private-content-canned-policy-statement-cookies"></a>

미리 준비된 정책을 사용하는 서명된 쿠키를 설정할 때 `CloudFront-Signature` 속성은 정책 설명의 해시 및 서명된 버전입니다. 미리 준비된 정책을 사용하는 서명된 쿠키의 경우, 사용자 지정 정책을 사용하는 서명된 쿠키와 마찬가지로 `Set-Cookie` 헤더에 정책 설명을 포함하지 않습니다. 정책 설명을 만들려면 다음 단계를 수행합니다.<a name="private-content-canned-policy-statement-cookies-procedure"></a>

**미리 준비된 정책을 사용하는 서명된 쿠키에 대한 정책 설명을 만들려면**

1. 다음 JSON 형식과 UTF-8 문자 인코딩을 사용하여 정책 설명을 구성합니다. 지정된 모든 문장 부호 및 기타 리터럴 값을 정확히 포함해야 합니다. `Resource` 및 `DateLessThan` 파라미터에 대한 내용은 [서명된 쿠키에서 미리 준비된 정책을 위한 정책 설명에서 지정한 값](#private-content-canned-policy-statement-cookies-values)를 참조하세요.

   ```
   {
       "Statement": [
           {
               "Resource": "base URL or stream name",
               "Condition": {
                   "DateLessThan": {
                       "AWS:EpochTime": ending date and time in Unix time format and UTC
                   }
               }
           }
       ]
   }
   ```

1. 정책 설명에서 모든 공백(탭과 줄바꿈 문자 포함)을 제거합니다. 애플리케이션 코드의 문자열에 이스케이프 문자를 포함해야 할 수도 있습니다.

#### 서명된 쿠키에서 미리 준비된 정책을 위한 정책 설명에서 지정한 값
<a name="private-content-canned-policy-statement-cookies-values"></a>

미리 준비된 정책에 대한 정책 설명을 만들 때 다음 값을 지정합니다.

**리소스**  
쿼리 문자열을 포함하는 기본 URL(있는 경우)은 다음과 같습니다.  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`  
`Resource`에 대해 값을 하나만 지정할 수 있습니다.  
다음을 참조하세요.  
+ **프로토콜(Protocol)** – 값은 `http://` 또는 `https://`로 시작해야 합니다.
+ **쿼리 문자열 파라미터(Query string parameters)** – 쿼리 문자열 파라미터가 없는 경우, 물음표를 생략합니다.
+ **대체 도메인 이름(Alternate domain names)** – URL에 대체 도메인 이름(CNAME)을 지정하는 경우, 웹 페이지 또는 애플리케이션의 파일을 참조할 때 대체 도메인 이름을 지정해야 합니다. 파일에 대한 Amazon S3 URL을 지정하지 마시기 바랍니다.

**DateLessThan**  
Unix 시간 형식(초) 및 협정 세계시(UTC) 기준의 URL 만료 날짜 및 시간. 값을 인용 부호로 묶지 마세요.  
예를 들면 2015년 3월 16일 오전 10시(UTC)를 Unix 시간 형식인 1426500000으로 변환합니다.  
이 값은 `CloudFront-Expires` 헤더의 `Set-Cookie` 속성 값과 일치해야 합니다. 값을 인용 부호로 묶지 마세요.  
자세한 내용은 [CloudFront가 서명된 쿠키의 만료 날짜 및 시간을 확인하는 시기](private-content-signed-cookies.md#private-content-check-expiration-cookie) 단원을 참조하세요.

#### 미리 준비된 정책에 대한 정책 설명 예제
<a name="private-content-canned-policy-cookies-sample-policy-statement"></a>

서명된 쿠키에 다음 예제의 정책 설명을 사용할 경우, 사용자는 UTC 기준 2015년 3월 16일 오전 10시까지 `https://d111111abcdef8.cloudfront.net/horizon.jpg` 파일에 액세스할 수 있습니다.

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/horizon.jpg?size=large&license=yes",
            "Condition": {
                "DateLessThan": {
                    "AWS:EpochTime": 1426500000
                }
            }
        }
    ]
}
```

### 정책 설명에 서명하여 미리 준비된 정책을 사용하는 서명된 쿠키에 대한 서명 생성
<a name="private-content-canned-policy-cookies-signing-policy-statement"></a>

`CloudFront-Signature` 헤더의 `Set-Cookie` 속성의 값을 만들려면 [미리 준비된 정책을 사용하는 서명된 쿠키에 대한 정책 설명을 만들려면](#private-content-canned-policy-statement-cookies-procedure)에서 만든 정책 설명을 해시하고 서명합니다.

다음 주제를 참조하여 정책 설명을 해시, 서명 및 인코딩하는 방법에 대한 추가적인 내용과 예제를 확인하세요.
+ [base64 인코딩 및 암호화를 위한 Linux 명령 및 OpenSSL](private-content-linux-openssl.md)
+ [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)<a name="private-content-canned-policy-cookie-creating-signature-procedure"></a>

**미리 준비된 정책을 사용하는 서명된 쿠키에 대한 서명을 만들려면**

1. SHA-1 해시 함수 및 RSA를 사용하여 [미리 준비된 정책을 사용하는 서명된 쿠키에 대한 정책 설명을 만들려면](#private-content-canned-policy-statement-cookies-procedure) 절차에서 만든 RSA 정책 설명을 해시하고 서명합니다. 공백이 삭제된 버전의 정책 설명을 사용합니다.

   해시 함수에 필요한 프라이빗 키의 경우 배포에 대해 신뢰할 수 있는 활성 키 그룹에 퍼블릭 키가 있는 프라이빗 키를 사용합니다.
**참고**  
정책 설명을 해시 및 서명하는 방법은 프로그래밍 언어와 플랫폼에 따라 달라집니다. 샘플 코드에 대한 내용은 [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)를 참조하세요.

1. 해시 및 서명된 문자열에서 공백(탭과 줄바꿈 문자 포함)을 제거합니다.

1. MIME base64 인코딩 기준으로 문자열을 base64로 인코딩합니다. 자세한 내용은 *RFC 2045, MIME(Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*의 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)을 참조하세요.

1. URL 쿼리 문자열에서 사용할 수 없는 문자를 유효한 문자로 교체합니다. 아래 표에 사용할 수 없는 문자와 유효한 문자가 나열되어 있습니다.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-canned-policy.html)

1. 결과로 얻은 값을 `Set-Cookie` 이름-값 페어의 `CloudFront-Signature` 헤더에 포함하세요. 그런 다음, [미리 준비된 정책을 사용하여 서명된 쿠키를 설정하려면](#private-content-setting-signed-cookie-canned-policy-procedure) 절차로 돌아가서 `Set-Cookie`에 대한 `CloudFront-Key-Pair-Id` 헤더를 추가합니다.

# 사용자 지정 정책을 사용하여 서명된 쿠키 설정
<a name="private-content-setting-signed-cookie-custom-policy"></a>

사용자 지정 정책을 사용하는 서명된 쿠키를 설정하려면 다음 단계를 수행합니다.<a name="private-content-setting-signed-cookie-custom-policy-procedure"></a>

**사용자 지정 정책을 사용하여 서명된 쿠키를 설정하려면**

1. .NET 또는 Java를 사용하여 서명된 URL을 만드는 중인데 키 페어의 프라이빗 키를 기본 .pem 형식에서 .NET 또는 Java와 상환되는 형식으로 다시 포맷하지 않았다면 지금 포맷하세요. 자세한 내용은 [프라이빗 키 재포맷(.NET 및 Java만 해당)](private-content-trusted-signers.md#private-content-reformatting-private-key) 단원을 참조하세요.

1. `Set-Cookie` 헤더 세 개를 승인된 최종 사용자에게 보내도록 애플리케이션을 프로그래밍합니다. 각 `Set-Cookie` 헤더는 이름-값 페어를 하나만 포함할 수 있고 CloudFront 서명된 쿠키에는 이름-값 페어 3개가 필요하기 때문에 `Set-Cookie` 헤더가 3개 있어야 합니다. 이름-값 페어는 `CloudFront-Policy`, `CloudFront-Signature`, `CloudFront-Key-Pair-Id`입니다. 액세스가 제한되는 파일에 대해 사용자가 첫 번째 요청을 하려면 최종 사용자에게 값이 있어야 합니다.
**참고**  
일반적으로 `Expires` 및 `Max-Age` 속성을 제외하는 것이 좋습니다. 이렇게 하면 사용자가 브라우저를 닫을 때 쿠키가 삭제되므로 권한 없는 사람이 콘텐츠에 액세스할 가능성이 줄어듭니다. 자세한 내용은 [서명된 쿠키 악용 방지](private-content-signed-cookies.md#private-content-signed-cookie-misuse) 단원을 참조하세요.

   **쿠키 속성의 이름은 대소문자를 구분합니다**.

   줄바꿈은 속성의 가독성을 높이기 위해서만 사용됩니다.

   ```
   Set-Cookie: 
   CloudFront-Policy=base64 encoded version of the policy statement; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   
   
   Set-Cookie: 
   CloudFront-Signature=hashed and signed version of the policy statement; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   
   Set-Cookie: 
   CloudFront-Key-Pair-Id=public key ID for the CloudFront public key whose corresponding private key you're using to generate the signature; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   ```  
**(선택 사항) `Domain`**  
요청된 파일의 도메인 이름입니다. `Domain` 속성을 지정하지 않는 경우, 기본값은 URL의 도메인 이름이고 이는 하위 도메인이 아닌 지정된 도메인 이름에만 적용됩니다. `Domain` 속성을 지정하는 경우, 하위 도메인에도 적용됩니다. 도메인 이름 앞의 점(예: `Domain=.example.com`)은 선택 사항입니다. 또한 `Domain` 속성을 지정하는 경우, URL의 도메인 이름과 `Domain` 속성의 값이 일치해야 합니다.  
CloudFront가 배포에 할당하는 도메인 이름을 d111111abcdef8.cloudfront.net과 같이 지정할 수 있으나, \$1.cloudfront.net을 도메인 이름으로 지정할 수는 없습니다.  
URL에 example.com과 같은 대체 도메인 이름을 사용하려면 `Domain` 속성 지정 여부와 상관없이 대체 도메인 이름을 배포에 추가해야 합니다. 자세한 내용은 [대체 도메인 이름(CNAME)](DownloadDistValuesGeneral.md#DownloadDistValuesCNAME) 주제에서 [모든 배포 설정 참조](distribution-web-values-specify.md) 단원을 참조하세요.  
**(선택 사항) `Path`**  
요청된 파일의 경로입니다. `Path` 속성을 지정하지 않는 경우, 기본값은 URL의 경로입니다.  
**`Secure`**  
요청을 보내기 전 최종 사용자에게 쿠키의 암호화를 요청하세요. 쿠키 속성이 MITM(중간자 공격)을 당하지 않도록 HTTPS 연결을 통해 `Set-Cookie` 헤더를 보내는 것이 좋습니다.  
**`HttpOnly`**  
HTTP 또는 HTTPS 요청에 한해 최종 사용자에게 쿠키를 요구하세요.  
**`CloudFront-Policy`**  
공백이 제거된 JSON 형식의 정책 설명은 base64로 인코딩합니다. 자세한 내용은 [사용자 지정 정책을 사용하는 서명된 쿠키에 대한 서명 생성](#private-content-custom-policy-signature-cookies) 섹션을 참조하세요.  
정책 설명은 서명된 쿠키가 사용자에게 부여하는 액세스를 제어합니다. 여기에는 사용자가 액세스할 수 있는 파일, 만료 날짜 및 시간, URL의 효력이 발생하는 날짜 및 시간(선택 사항), 파일에 액세스할 수 있는 IP 주소 또는 IP 주소 범위(선택 사항)가 포함됩니다.  
**`CloudFront-Signature`**  
JSON 정책 설명을 해시, 서명 및 base64로 인코딩한 버전입니다. 자세한 내용은 [사용자 지정 정책을 사용하는 서명된 쿠키에 대한 서명 생성](#private-content-custom-policy-signature-cookies) 단원을 참조하세요.  
**`CloudFront-Key-Pair-Id`**  
CloudFront 퍼블릭 키의 ID입니다(예: `K2JCJMDEHXQW5F`). CloudFront는 서명된 URL을 확인할 때 사용할 퍼블릭 키를 퍼블릭 키 ID로 판단합니다. CloudFront는 서명의 정보를 정책 설명의 정보와 비교하고 URL이 변조되지 않았음을 확인합니다.  
이 퍼블릭 키는 배포에서 신뢰할 수 있는 서명자인 키 그룹에 속해야 합니다. 자세한 내용은 [서명된 URL 및 서명된 쿠키를 생성할 수 있는 서명자 지정](private-content-trusted-signers.md) 섹션을 참조하세요.

## 사용자 지정 정책의 예제 헤더 `Set-Cookie`
<a name="example-set-cookie-headers-custom-policy"></a>

`Set-Cookie` 헤더 쌍의 다음 예를 참조하십시오.

URL에 example.org와 같은 대체 도메인 이름을 사용하려면 `Domain` 속성 지정 여부와 상관없이 대체 도메인 이름을 배포에 추가해야 합니다. 자세한 내용은 [대체 도메인 이름(CNAME)](DownloadDistValuesGeneral.md#DownloadDistValuesCNAME) 주제에서 [모든 배포 설정 참조](distribution-web-values-specify.md) 섹션을 참조하세요.

**Example 예 1**  
파일 URL에 배포와 연결된 도메인 이름을 사용하는 경우 서명된 쿠키 하나에 `Set-Cookie` 헤더를 사용할 수 있습니다.  

```
Set-Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=dtKhpJ3aUYxqDIwepczPiDb9NXQ_; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
```

**Example 예제 2**  
파일의 URL에 대체 도메인 이름(example.org)을 사용하는 경우 서명된 쿠키 하나에 `Set-Cookie` 헤더를 사용할 수 있습니다.  

```
Set-Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__; Domain=example.org; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=dtKhpJ3aUYxqDIwepczPiDb9NXQ_; Domain=example.org; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=example.org; Path=/; Secure; HttpOnly
```

**Example 예 3**  
파일의 URL에 배포와 연결된 도메인 이름을 사용하는 경우 서명된 요청에 `Set-Cookie` 헤더 쌍을 사용할 수 있습니다.  

```
Set-Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=dtKhpJ3aUYxqDIwepczPiDb9NXQ_; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=dd111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
```

**Example 예 4**  
파일 URL에 배포와 연결된 대체 도메인 이름(example.org)을 사용하는 경우 서명된 요청 하나에 `Set-Cookie` 헤더 쌍을 사용할 수 있습니다.  

```
Set-Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__; Domain=example.org; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=dtKhpJ3aUYxqDIwepczPiDb9NXQ_; Domain=example.org; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=example.org; Path=/; Secure; HttpOnly
```

## 사용자 지정 정책을 사용하는 서명된 쿠키에 대한 정책 설명 생성
<a name="private-content-custom-policy-statement-cookies"></a>

사용자 지정 정책에 대한 정책 설명을 만들려면 다음 단계를 수행합니다. 다양한 방법으로 파일에 대한 액세스를 제어하는 몇 가지 예제 정책 설명에 대한 내용은 [사용자 지정 정책을 사용하는 서명된 쿠키에 대한 예제 정책 설명](#private-content-custom-policy-statement-signed-cookies-examples) 단원을 참조하세요.<a name="private-content-custom-policy-statement-cookies-procedure"></a>

**사용자 지정 정책을 사용하는 서명된 쿠키에 대한 정책 설명을 만들려면**

1. 다음 JSON 형식을 사용하여 정책 설명을 구성하세요.

   ```
   {
       "Statement": [
           {
               "Resource": "URL of the file",
               "Condition": {
                   "DateLessThan": {
                       "AWS:EpochTime":required ending date and time in Unix time format and UTC
                   },
                   "DateGreaterThan": {
                       "AWS:EpochTime":optional beginning date and time in Unix time format and UTC
                   },
                   "IpAddress": {
                       "AWS:SourceIp": "optional IP address"
                   }
               }
           }
       ]
   }
   ```

   다음을 참조하세요.
   + 단 한 개의 명령문만 포함시킬 수 있습니다.
   + UTF-8 문자 인코딩을 사용하세요.
   + 지정된 대로 정확하게 모든 문장 부호 및 파라미터 이름을 포함하세요. 파라미터 이름의 약어는 허용되지 않습니다.
   + `Condition` 섹션의 파라미터 순서는 중요하지 않습니다.
   + `Resource`, `DateLessThan`, `DateGreaterThan`, `IpAddress`에 대한 값 관련 내용은 [서명된 쿠키에서 사용자 지정 정책을 위한 정책 설명에서 지정한 값](#private-content-custom-policy-statement-cookies-values)을 참조하세요.

1. 정책 설명에서 모든 공백(탭과 줄바꿈 문자 포함)을 제거합니다. 애플리케이션 코드의 문자열에 이스케이프 문자를 포함해야 할 수도 있습니다.

1. MIME base64 인코딩 기준으로 정책 설명을 Base64로 인코딩합니다. 자세한 내용은 *RFC 2045, MIME(Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*의 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)을 참조하세요.

1. URL 쿼리 문자열에서 사용할 수 없는 문자를 유효한 문자로 교체합니다. 아래 표에 사용할 수 없는 문자와 유효한 문자가 나열되어 있습니다.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html)

1. `Set-Cookie` 뒤 `CloudFront-Policy=` 헤더의 결과 값을 포함하세요.

1. 정책 설명을 해시, 서명 및 base64로 인코딩하여 `Set-Cookie`의 `CloudFront-Signature` 헤더에 대한 서명을 만드세요. 자세한 내용은 [사용자 지정 정책을 사용하는 서명된 쿠키에 대한 서명 생성](#private-content-custom-policy-signature-cookies) 단원을 참조하세요.

### 서명된 쿠키에서 사용자 지정 정책을 위한 정책 설명에서 지정한 값
<a name="private-content-custom-policy-statement-cookies-values"></a>

사용자 지정 정책에 대한 정책 설명을 생성할 때 다음 값을 지정합니다.

**리소스**  
쿼리 문자열을 포함하는 기본 URL(있는 경우):  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`  
`Resource` 파라미터를 생략하는 경우, 사용자는 서명된 URL을 만들 때 사용하는 키 페어에 연결된 배포 관련 모든 파일에 액세스할 수 있습니다.
`Resource`에 대해 값을 하나만 지정할 수 있습니다.  
다음을 참조하세요.  
+ **프로토콜(Protocol)** – 값은 `http://` 또는 `https://`로 시작해야 합니다.
+ **쿼리 문자열 파라미터(Query string parameters)** – 쿼리 문자열 파라미터가 없는 경우, 물음표를 생략합니다.
+ **와일드카드(Wildcards)** – 0개 이상의 문자(\$1)에 해당하거나 문자열 내 정확히 1문자(?)에 해당하는 와일드카드를 문자열 내 모든 곳에 사용할 수 있습니다. 예를 들어 값은

  `https://d111111abcdef8.cloudfront.net/*game_download.zip*`

  다음 파일을 포함합니다.
  + `https://d111111abcdef8.cloudfront.net/game_download.zip`
  + `https://d111111abcdef8.cloudfront.net/example_game_download.zip?license=yes`
  + `https://d111111abcdef8.cloudfront.net/test_game_download.zip?license=temp`
+ **대체 도메인 이름** – URL에 대체 도메인 이름(CNAME)을 지정하는 경우, 웹 페이지 또는 애플리케이션의 파일을 참조할 때 대체 도메인 이름을 지정해야 합니다. 파일에 대한 Amazon S3 URL을 지정하지 마시기 바랍니다.

**DateLessThan**  
Unix 시간 형식(초) 및 협정 세계시(UTC) 기준의 URL 만료 날짜 및 시간. 값을 인용 부호로 묶지 마세요.  
예를 들면 2015년 3월 16일 오전 10시(UTC)를 Unix 시간 형식인 1426500000으로 변환합니다.  
자세한 내용은 [CloudFront가 서명된 쿠키의 만료 날짜 및 시간을 확인하는 시기](private-content-signed-cookies.md#private-content-check-expiration-cookie) 단원을 참조하세요.

**DateGreaterThan(선택 사항)**  
Unix 시간 형식(초)과 협정 세계시(UTC)의 URL에 대한 선택적 시작 날짜 및 시간. 사용자는 지정된 날짜 및 시간 이전에 파일에 액세스할 수 없습니다. 값을 인용 부호로 묶지 마세요.

**IpAddress(선택 사항)**  
클라이언트의 IP 주소는 GET 요청을 합니다. 다음을 참조하세요.  
+ 파일에 액세스하는 IP 주소를 허용하려면 `IpAddress` 파라미터를 생략합니다.
+ 하나의 IP 주소 또는 하나의 IP 주소 범위를 지정할 수 있습니다. 예를 들면, 클라이언트의 IP 주소가 두 개의 다른 범위 중 하나에 있는 경우, 정책을 설정하여 액세스를 허용할 수 없습니다.
+ 단일 IP 주소에서 액세스를 허용하기 위해 다음을 지정합니다.

  `"`*IPv4 IP 주소*`/32"`
+ IP 주소 범위는 스탠다드 IPv4 CIDR 형식(예: `192.0.2.0/24`)을 지정해야 합니다. 자세한 내용은 *RFC 4632, Classless Inter-domain Routing (CIDR): The Internet Address Assignment and Aggregation Plan*, [https://tools.ietf.org/html/rfc4632](https://tools.ietf.org/html/rfc4632)를 참조하세요.
**중요**  
IPv6 형식의 IP 주소(예: 2001:0db8:85a3::8a2e:0370:7334)는 지원되지 않습니다.

  `IpAddress`를 포함하는 사용자 지정 정책을 사용할 경우 배포에 대해 IPv6를 사용하도록 설정하지 마세요. IP 주소로 일부 콘텐츠에 대한 액세스를 제한하고, 다른 콘텐츠에 대해서는 IPv6 요청을 지원하려면 두 가지 배포를 만들면 됩니다. 자세한 내용은 [IPv6 활성화(뷰어 요청)](DownloadDistValuesGeneral.md#DownloadDistValuesEnableIPv6) 주제에서 [모든 배포 설정 참조](distribution-web-values-specify.md) 단원을 참조하세요.

## 사용자 지정 정책을 사용하는 서명된 쿠키에 대한 예제 정책 설명
<a name="private-content-custom-policy-statement-signed-cookies-examples"></a>

다음 예제 정책 설명은 특정 파일, 디렉터리의 모든 파일 또는 키 페어 ID에 연결된 모든 파일의 액세스 제어 방법을 보여줍니다. 예제는 또한 개별 IP 주소 또는 IP 주소 범위에서 액세스를 제어하는 방법과 지정된 날짜 및 시간 이후 사용자의 서명된 쿠키 사용 방지 방법을 보여줍니다.

이러한 예제를 복사하여 붙여 넣는 경우, 공백(탭과 줄바꿈 문자 포함)을 제거하고 값을 자체 값과 교체한 후 닫는 괄호(\$1)뒤에 줄바꿈 문자를 포함합니다.

자세한 내용은 [서명된 쿠키에서 사용자 지정 정책을 위한 정책 설명에서 지정한 값](#private-content-custom-policy-statement-cookies-values) 섹션을 참조하세요.

**Topics**
+ [예제 정책 설명: IP 주소 범위에서 하나의 파일에 액세스](#private-content-custom-policy-statement-signed-cookies-example-one-object)
+ [예제 정책 설명: IP 주소 범위에서 디렉터리의 모든 파일에 액세스](#private-content-custom-policy-statement-signed-cookies-example-all-objects)
+ [예제 정책 설명: 하나의 IP 주소에서 키 페어 ID에 연결된 모든 파일에 액세스](#private-content-custom-policy-statement-signed-cookies-example-one-ip)

### 예제 정책 설명: IP 주소 범위에서 하나의 파일에 액세스
<a name="private-content-custom-policy-statement-signed-cookies-example-one-object"></a>

다음 서명된 쿠키의 예제 사용자 지정 정책은 UTC 기준 2023년 1월 1일 오전 10시까지 `https://d111111abcdef8.cloudfront.net/game_download.zip` 범위의 IP 주소에서 `192.0.2.0/24` 파일에 액세스할 수 있는 사용자를 지정합니다.

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/game_download.zip",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1767290400
                }
            }
        }
    ]
}
```

### 예제 정책 설명: IP 주소 범위에서 디렉터리의 모든 파일에 액세스
<a name="private-content-custom-policy-statement-signed-cookies-example-all-objects"></a>

다음 예제 사용자 지정 정책은 `training` 파라미터의 \$1 와일드카드 문자에 표시된 바와 같이 `Resource` 디렉터리의 파일에 대한 서명된 쿠키를 만들 수 있도록 허용합니다. 사용자는 UTC 기준 2013년 1월 1일 오전 10시까지 `192.0.2.0/24` 범위의 IP 주소에서 파일에 액세스할 수 있습니다.

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/training/*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1767290400
                }
            }
        }
    ]
}
```

이 정책을 사용하는 서명된 쿠키는 각자 특정 파일을 식별하는 다음과 같은 기본 URL을 포함합니다.

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

### 예제 정책 설명: 하나의 IP 주소에서 키 페어 ID에 연결된 모든 파일에 액세스
<a name="private-content-custom-policy-statement-signed-cookies-example-one-ip"></a>

다음 샘플 사용자 지정 정책은 `Resource` 파라미터의 \$1 와일드카드 문자 표시된 바와 같이 배포와 연결된 파일에 대한 서명된 쿠키를 설정할 수 있도록 허용합니다. 사용자는 IP 주소 `192.0.2.10/32`를 사용해야 합니다. CIDR 표기법의 `192.0.2.10/32` 값은 단일 IP 주소 `192.0.2.10`을(를) 가리킵니다. 파일은 UTC 기준 2013년 1월 1일 오전 10시에서 2013년 1월 2일 오전 10시까지만 사용할 수 있습니다.

```
{
    "Statement": [
        {
            "Resource": "https://*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.10/32"
                },
                "DateGreaterThan": {
                    "AWS:EpochTime": 1767290400
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1767376800
                }
            }
        }
    ]
}
```

이 정책을 사용하는 서명된 쿠키는 각각 특정 CloudFront 배포의 특정 파일을 식별하는 다음과 같은 기본 URL을 포함합니다.

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

또한 서명된 쿠키는 키 페어 ID를 포함하고 이는 기본 URL에서 지정한 배포(d111111abcdef8.cloudfront.net)의 신뢰할 수 있는 키 그룹과 연결되어야 합니다.

## 사용자 지정 정책을 사용하는 서명된 쿠키에 대한 서명 생성
<a name="private-content-custom-policy-signature-cookies"></a>

서명된 쿠키에 대한 서명은 정책 설명의 해시, 서명 및 base64로 인코딩된 버전의 사용자 지정 정책을 사용합니다.

다음을 참조하여 정책 설명을 해시, 서명 및 인코딩하는 방법에 대한 추가적인 내용과 예제를 확인하세요.
+ [base64 인코딩 및 암호화를 위한 Linux 명령 및 OpenSSL](private-content-linux-openssl.md)
+ [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)<a name="private-content-custom-policy-signature-cookies-procedure"></a>

**사용자 지정 정책으로 서명된 쿠키에 대한 서명을 만들려면**

1. SHA-1 해시 함수 및 RSA를 사용하여 [사용자 지정 정책을 사용하는 서명된 URL에 대한 정책 설명을 만들려면](private-content-creating-signed-url-custom-policy.md#private-content-custom-policy-creating-policy-procedure) 절차에서 만든 JSON 정책 설명을 해시하고 서명합니다. 더 이상 공백을 포함하지 않지만 base64로 인코딩되지 않은 정책 설명 버전을 사용합니다.

   해시 함수에 필요한 프라이빗 키의 경우 배포에 대해 신뢰할 수 있는 활성 키 그룹에 퍼블릭 키가 있는 프라이빗 키를 사용합니다.
**참고**  
정책 설명을 해시 및 서명하는 방법은 프로그래밍 언어와 플랫폼에 따라 달라집니다. 샘플 코드에 대한 내용은 [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)를 참조하세요.

1. 해시 및 서명된 문자열에서 공백(탭과 줄바꿈 문자 포함)을 제거합니다.

1. MIME base64 인코딩 기준으로 문자열을 base64로 인코딩합니다. 자세한 내용은 *RFC 2045, MIME(Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*의 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)을 참조하세요.

1. URL 쿼리 문자열에서 사용할 수 없는 문자를 유효한 문자로 교체합니다. 아래 표에 사용할 수 없는 문자와 유효한 문자가 나열되어 있습니다.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html)

1. `Set-Cookie` 이름-값 페어에 대한 `CloudFront-Signature=` 헤더의 결과 값을 포함하고 [사용자 지정 정책을 사용하여 서명된 쿠키를 설정하려면](#private-content-setting-signed-cookie-custom-policy-procedure)으로 돌아가서 `Set-Cookie`에 대한 `CloudFront-Key-Pair-Id` 헤더를 추가하세요.

# PHP를 사용하여 서명된 쿠키 생성
<a name="signed-cookies-PHP"></a>

다음 코드 예제는 동영상에 대한 링크를 만드는 [PHP를 사용한 URL 서명 생성](CreateURL_PHP.md)의 예제와 유사합니다. 그러나 이 예제에서는 코드의 URL에 서명하는 대신 `create_signed_cookies()` 함수를 사용하여 쿠키에 서명합니다. 클라이언트 측 플레이어는 쿠키를 사용하여 CloudFront 배포에 대한 각 요청을 인증합니다.

이 접근 방식은 클라이언트가 매니페스트, 세그먼트 및 관련 재생 자산을 검색하기 위해 여러 요청을 해야 하는 HLS(HTTP Live Streaming) 또는 DASH(Dynamic Adaptive Streaming over HTTP)와 같은 콘텐츠를 스트리밍하는 데 유용합니다. 클라이언트는 서명된 쿠키를 사용하여 각 세그먼트에 대해 서명된 새 URL을 만들 필요 없이 각 요청을 인증할 수 있습니다.

**참고**  
URL 서명을 만드는 것은 서명된 쿠키를 사용해 프라이빗 콘텐츠를 제공하는 프로세스의 한 부분에 불과합니다. 자세한 내용은 [서명된 쿠키 사용](private-content-signed-cookies.md) 섹션을 참조하세요.



**Topics**
+ [RSA SHA-1 서명 생성](#create-rsa-sha-1signature-cookies)
+ [서명된 쿠키 생성](#create-the-signed-cookie)
+ [전체 코드](#full-code-signed-cookies)

다음 섹션에서는 코드 예제를 각 부분으로 나눕니다. 아래에서 전체 [코드 샘플](#full-code-signed-cookies)을 확인할 수 있습니다.

## RSA SHA-1 서명 생성
<a name="create-rsa-sha-1signature-cookies"></a>

이 코드 예제에서는 다음을 수행합니다.

1. `rsa_sha1_sign` 함수는 정책 문을 해시하고 서명합니다. 필요한 인수는 배포의 신뢰할 수 있는 키 그룹에 있는 퍼블릭 키에 해당하는 프라이빗 키와 정책 설명입니다.

1. 다음으로 `url_safe_base64_encode` 함수는 서명의 안전한 URL 버전을 만듭니다.

   ```
   function rsa_sha1_sign($policy, $private_key_filename) {
       $signature = "";
       $fp = fopen($private_key_filename, "r");
       $priv_key = fread($fp, 8192);
       fclose($fp);
       $pkeyid = openssl_get_privatekey($priv_key);
       openssl_sign($policy, $signature, $pkeyid);
       openssl_free_key($pkeyid);
       return $signature;
   }
   
   function url_safe_base64_encode($value) {
       $encoded = base64_encode($value);
       return str_replace(
           array('+', '=', '/'),
           array('-', '_', '~'),
           $encoded);
   }
   ```

## 서명된 쿠키 생성
<a name="create-the-signed-cookie"></a>

다음 코드는 다음 쿠키 속성(예: `CloudFront-Expires`, `CloudFront-Signature`, `CloudFront-Key-Pair-Id`)을 사용하여 서명된 쿠키를 만듭니다. 코드는 사용자 지정 정책을 사용합니다.

```
function create_signed_cookies($resource, $private_key_filename, $key_pair_id, $expires, $client_ip = null) {
    $policy = array(
        'Statement' => array(
            array(
                'Resource' => $resource,
                'Condition' => array(
                    'DateLessThan' => array('AWS:EpochTime' => $expires)
                )
            )
        )
    );

    if ($client_ip) {
        $policy['Statement'][0]['Condition']['IpAddress'] = array('AWS:SourceIp' => $client_ip . '/32');
    }

    $policy = json_encode($policy);
    $encoded_policy = url_safe_base64_encode($policy);
    $signature = rsa_sha1_sign($policy, $private_key_filename);
    $encoded_signature = url_safe_base64_encode($signature);

    return array(
        'CloudFront-Policy' => $encoded_policy,
        'CloudFront-Signature' => $encoded_signature,
        'CloudFront-Key-Pair-Id' => $key_pair_id
    );
}
```

자세한 내용은 [사용자 지정 정책을 사용하여 서명된 쿠키 설정](private-content-setting-signed-cookie-custom-policy.md) 섹션을 참조하세요.

## 전체 코드
<a name="full-code-signed-cookies"></a>

다음 예제 코드는 PHP로 CloudFront 서명 쿠키를 만드는 전체 데모를 제공합니다. [demo-php.zip](samples/demo-php.zip) 파일에서 전체 예제를 다운로드할 수 있습니다.

다음 예제에서는 IPv4 및 IPv6 주소 범위를 모두 허용하도록 `$policy Condition` 요소를 수정할 수 있습니다. 예제는 Amazon Simple Storage Service 사용 설명서의 [IAM 정책에서 IPv6 주소 사용](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ipv6-access.html#ipv6-access-iam)을 참조합니다.**

```
<?php

function rsa_sha1_sign($policy, $private_key_filename) {
    $signature = "";
    $fp = fopen($private_key_filename, "r");
    $priv_key = fread($fp, 8192);
    fclose($fp);
    $pkeyid = openssl_get_privatekey($priv_key);
    openssl_sign($policy, $signature, $pkeyid);
    openssl_free_key($pkeyid);
    return $signature;
}

function url_safe_base64_encode($value) {
    $encoded = base64_encode($value);
    return str_replace(
        array('+', '=', '/'),
        array('-', '_', '~'),
        $encoded);
}

function create_signed_cookies($resource, $private_key_filename, $key_pair_id, $expires, $client_ip = null) {
    $policy = array(
        'Statement' => array(
            array(
                'Resource' => $resource,
                'Condition' => array(
                    'DateLessThan' => array('AWS:EpochTime' => $expires)
                )
            )
        )
    );

    if ($client_ip) {
        $policy['Statement'][0]['Condition']['IpAddress'] = array('AWS:SourceIp' => $client_ip . '/32');
    }

    $policy = json_encode($policy);
    $encoded_policy = url_safe_base64_encode($policy);
    $signature = rsa_sha1_sign($policy, $private_key_filename);
    $encoded_signature = url_safe_base64_encode($signature);

    return array(
        'CloudFront-Policy' => $encoded_policy,
        'CloudFront-Signature' => $encoded_signature,
        'CloudFront-Key-Pair-Id' => $key_pair_id
    );
}



$private_key_filename = '/home/test/secure/example-priv-key.pem';
$key_pair_id = 'K2JCJMDEHXQW5F';
$base_url = 'https://d1234.cloudfront.net';

$expires = time() + 3600; // 1 hour from now

// Get the viewer real IP from the x-forward-for header as $_SERVER['REMOTE_ADDR'] will return viewer facing IP. An alternative option is to use CloudFront-Viewer-Address header. Note that this header is a trusted CloudFront immutable header. Example format: IP:PORT ("CloudFront-Viewer-Address": "1.2.3.4:12345")
$client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];


// For HLS manifest and segments (using wildcard)
$hls_resource = $base_url . '/sign/*';
$signed_cookies = create_signed_cookies($hls_resource, $private_key_filename, $key_pair_id, $expires, $client_ip);

// Set the cookies
$cookie_domain = parse_url($base_url, PHP_URL_HOST);
foreach ($signed_cookies as $name => $value) {
    setcookie($name, $value, $expires, '/', $cookie_domain, true, true);
}

?>

<!DOCTYPE html>
<html>
<head>
    <title>CloudFront Signed HLS Stream with Cookies</title>
</head>
<body>
    <h1>Amazon CloudFront Signed HLS Stream with Cookies</h1>
    <h2>Expires at <?php echo gmdate('Y-m-d H:i:s T', $expires); ?> only viewable by IP <?php echo $client_ip; ?></h2>
    
    <div id='hls-video'>
        <video id="video" width="640" height="360" controls></video>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
    <script>
        var video = document.getElementById('video');
        var manifestUrl = '<?php echo $base_url; ?>/sign/manifest.m3u8';
        
        if (Hls.isSupported()) {
            var hls = new Hls();
            hls.loadSource(manifestUrl);
            hls.attachMedia(video);
        }
        else if (video.canPlayType('application/vnd.apple.mpegurl')) {
            video.src = manifestUrl;
        }
    </script>
</body>
</html>
```

서명된 쿠키를 사용하는 대신 서명된 URL을 사용할 수 있습니다. 자세한 내용은 [PHP를 사용한 URL 서명 생성](CreateURL_PHP.md) 섹션을 참조하세요.

# base64 인코딩 및 암호화를 위한 Linux 명령 및 OpenSSL
<a name="private-content-linux-openssl"></a>

다음 Linux 명령줄 명령과 OpenSSL을 사용하여 정책 설명을 해시 및 서명한 후, 서명을 base64로 인코딩하여 URL 쿼리 문자열 파라미터에서 사용할 수 없는 문자를 유효한 문자와 교체하세요.

OpenSSL에 대한 자세한 내용은 를 참조하세요[https://www.openssl.org](https://www.openssl.org)

```
cat policy | tr -d "\n" | tr -d " \t\n\r" | openssl sha1 -sign private_key.pem | openssl base64 -A | tr -- '+=/' '-_~'
```

앞의 명령에서:
+ `cat` 은 `policy` 파일을 읽습니다.
+ `tr -d "\n" | tr -d " \t\n\r"`은 `cat`에 의해 추가된 공백과 줄 바꿈 문자를 제거합니다.
+ OpenSSL은 SHA-1을 사용하여 파일을 해시하고 프라이빗 키 파일 `private_key.pem`을 사용하여 서명합니다. 프라이빗 키 서명은 RSA 2048 또는 ECDSA 256일 수 있습니다.
+ OpenSSL은 해시, 서명된 정책 문을 base64로 인코딩합니다.
+ `tr` 은 URL 쿼리 문자열 파라미터에 사용할 수 없는 문자를 유효한 문자로 교체합니다.

서명을 만드는 방법을 보여주는 코드 예시는 [서명 URL에 대한 서명을 만드는 코드 예제](PrivateCFSignatureCodeAndExamples.md)을 참조하세요.

# 서명 URL에 대한 서명을 만드는 코드 예제
<a name="PrivateCFSignatureCodeAndExamples"></a>

이 단원에는 서명된 URL에 대한 서명을 만드는 방법을 담은 애플리케이션 예제가 수록되어 있으며 이를 다운로드할 수 있습니다. 예제는 Perl, PHP, C\$1, Java로 제공됩니다. 이러한 예제를 사용하여 서명된 URL을 만들 수 있습니다. Perl 스크립트는 Linux 및 masOS 플랫폼에서 실행됩니다. PHP 예제는 PHP를 실행하는 모든 서버에서 작동합니다. C\$1 예제는 NET Framework를 사용합니다.

JavaScript(Node.js)의 예제 코드는 AWS 개발자 블로그의 [Node.js에서 Amazon CloudFront 서명 URL 생성](https://aws.amazon.com/blogs/developer/creating-amazon-cloudfront-signed-urls-in-node-js/)을 참조하세요.

Python의 예제 코드는 *AWS SDK for Python(Boto3) API 참조*의 [Amazon CloudFront에 대한 서명된 URL 생성](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudfront.html#examples)과 Boto3 GitHub 리포지토리의 [이 예제 코드](https://github.com/boto/boto3/blob/develop/boto3/examples/cloudfront.rst)를 참조하세요.

**Topics**
+ [Perl을 사용한 URL 서명 생성](CreateURLPerl.md)
+ [PHP를 사용한 URL 서명 생성](CreateURL_PHP.md)
+ [C\$1 및 .NET Framework를 사용한 URL 서명 생성](CreateSignatureInCSharp.md)
+ [Java를 사용한 URL 서명 생성](CFPrivateDistJavaDevelopment.md)

# Perl을 사용한 URL 서명 생성
<a name="CreateURLPerl"></a>

이 단원에는 프라이빗 콘텐츠에 대한 서명을 생성하는 데 사용할 수 있는 Linux/Mac 플랫폼용 Perl 스크립트가 포함되어 있습니다. 서명을 생성하려면, CloudFront URL, 서명자의 프라이빗 키 경로, 키 ID와 URL에 대한 만료 날짜를 지정하는 명령줄 인수를 포함한 스크립트를 실행합니다. 또한 이 도구는 서명된 URL을 디코딩할 수 있습니다.

**참고**  
URL 서명 생성은 서명된 URL을 통해 프라이빗 콘텐츠를 제공하는 프로세스의 한 부분에 불과합니다. 종단 간 프로세스에 대한 자세한 내용은 [서명된 URL 사용](private-content-signed-urls.md) 단원을 참조합니다.

**Topics**
+ [서명된 URL을 생성하기 위한 Perl 스크립트의 소스](#CreateURLPerlScriptSource)

## 서명된 URL을 생성하기 위한 Perl 스크립트의 소스
<a name="CreateURLPerlScriptSource"></a>

서명된 CloudFront URL을 만들기 위해 다음과 같은 Perl 소스 코드를 사용할 수 있습니다. 이 코드의 명령에는 명령줄 전환 및 이 도구의 기능에 대한 자세한 내용이 포함되어 있습니다.

```
#!/usr/bin/perl -w

# Copyright 2008 Amazon Technologies, Inc.  Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. You may obtain a copy of the License at:
#
# https://aws.amazon.com/apache2.0
#
# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.

=head1 cfsign.pl

cfsign.pl - A tool to generate and verify Amazon CloudFront signed URLs

=head1 SYNOPSIS

This script uses an existing RSA key pair to sign and verify Amazon CloudFront signed URLs

View the script source for details as to which CPAN packages are required beforehand. 

For help, try:

cfsign.pl --help

URL signing examples:

cfsign.pl --action encode --url https://images.my-website.com/gallery1.zip --policy sample_policy.json --private-key privkey.pem --key-pair-id mykey

cfsign.pl --action encode --url https://images.my-website.com/gallery1.zip --expires 1257439868 --private-key privkey.pem --key-pair-id mykey

URL decode example:

cfsign.pl --action decode --url "http//mydist.cloudfront.net/?Signature=AGO-PgxkYo99MkJFHvjfGXjG1QDEXeaDb4Qtzmy85wqyJjK7eKojQWa4BCRcow__&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovLypicmFkbS5qcGciLCJDb25kaXRpb24iOnsiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI6IjEwLjUyLjE3LjkvMCJ9LCJEYXRlR3JlYXRlclRoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTI1MjUyMDgzMH19fV19Cg__&Key-Pair-Id=mykey"


To generate an RSA key pair, you can use openssl and the following commands:

# Generate a 2048 bit key pair
openssl genrsa -out private-key.pem 2048
openssl rsa -in private-key.pem -pubout -out public-key.pem


=head1 OPTIONS

=over 8

=item B<--help>

Print a help message and exits.

=item B<--action> [action]

The action to execute.  action can be one of:

  encode - Generate a signed URL (using a canned policy or a user policy)
  decode - Decode a signed URL

=item B<--url>

The URL to en/decode

=item B<--stream>

The stream to en/decode

=item B<--private-key>

The path to your private key.

=item B<--key-pair-id>

The key pair identifier.

=item B<--policy>

The CloudFront policy document.

=item B<--expires>

The Unix epoch time when the URL is to expire. If both this option and
the --policy option are specified, --policy will be used. Otherwise, this 
option alone will use a canned policy.

=back

=cut

use strict;
use warnings;

# you might need to use CPAN to get these modules.
# run perl -MCPAN -e "install <module>" to get them.
# The openssl command line will also need to be in your $PATH.
use File::Temp qw/tempfile/;
use File::Slurp;
use Getopt::Long;
use IPC::Open2;
use MIME::Base64 qw(encode_base64 decode_base64);
use Pod::Usage;
use URI;

my $CANNED_POLICY 
    = '{"Statement":[{"Resource":"<RESOURCE>","Condition":{"DateLessThan":{"AWS:EpochTime":<EXPIRES>}}}]}';

my $POLICY_PARAM      = "Policy";
my $EXPIRES_PARAM     = "Expires";
my $SIGNATURE_PARAM   = "Signature";
my $KEY_PAIR_ID_PARAM = "Key-Pair-Id";

my $verbose = 0;
my $policy_filename = "";
my $expires_epoch = 0;
my $action = "";
my $help = 0;
my $key_pair_id = "";
my $url = "";
my $stream = "";
my $private_key_filename = "";

my $result = GetOptions("action=s"      => \$action,
                        "policy=s"      => \$policy_filename,
                        "expires=i"     => \$expires_epoch,
                        "private-key=s" => \$private_key_filename,
                        "key-pair-id=s" => \$key_pair_id,
                        "verbose"       => \$verbose,
                        "help"          => \$help,
                        "url=s"         => \$url,
                        "stream=s"      => \$stream,
                    );

if ($help or !$result) {
    pod2usage(1);
    exit;
}

if ($url eq "" and $stream eq "") {
    print STDERR "Must include a stream or a URL to encode or decode with the --stream or --url option\n";
    exit;
}

if ($url ne "" and $stream ne "") {
    print STDERR "Only one of --url and --stream may be specified\n";
    exit;
}

if ($url ne "" and !is_url_valid($url)) {
    exit;
}

if ($stream ne "") {
    exit unless is_stream_valid($stream);

    # The signing mechanism is identical, so from here on just pretend we're
    # dealing with a URL
    $url = $stream;
} 

if ($action eq "encode") {
    # The encode action will generate a private content URL given a base URL, 
    # a policy file (or an expires timestamp) and a key pair id parameter
    my $private_key;
    my $public_key;
    my $public_key_file;
    
    my $policy;
    if ($policy_filename eq "") {
        if ($expires_epoch == 0) {
            print STDERR "Must include policy filename with --policy argument or an expires" . 
                          "time using --expires\n";            
        }
        
        $policy = $CANNED_POLICY;
        $policy =~ s/<EXPIRES>/$expires_epoch/g;
        $policy =~ s/<RESOURCE>/$url/g;
    } else {
        if (! -e $policy_filename) {
            print STDERR "Policy file $policy_filename does not exist\n";
            exit;
        }
        $expires_epoch = 0; # ignore if set
        $policy = read_file($policy_filename);
    }

    if ($private_key_filename eq "") {
        print STDERR "You must specific the path to your private key file with --private-key\n";
        exit;
    }

    if (! -e $private_key_filename) {
        print STDERR "Private key file $private_key_filename does not exist\n";
        exit;
    }

    if ($key_pair_id eq "") {
        print STDERR "You must specify a key pair id with --key-pair-id\n";
        exit;
    }

    my $encoded_policy = url_safe_base64_encode($policy);
    my $signature = rsa_sha1_sign($policy, $private_key_filename);
    my $encoded_signature = url_safe_base64_encode($signature);

    my $generated_url = create_url($url, $encoded_policy, $encoded_signature, $key_pair_id, $expires_epoch);


    if ($stream ne "") {
        print "Encoded stream (for use within a swf):\n" . $generated_url . "\n";
        print "Encoded and escaped stream (for use on a webpage):\n" .  escape_url_for_webpage($generated_url) . "\n"; 
    } else {
        print "Encoded URL:\n" . $generated_url . "\n";
    }
} elsif ($action eq "decode") {
    my $decoded = decode_url($url);
    if (!$decoded) {
        print STDERR "Improperly formed URL\n";
        exit;
    }

    print_decoded_url($decoded);
} else {
    # No action specified, print help.  But only if this is run as a program (caller will be empty)
    pod2usage(1) unless caller();
}

# Decode a private content URL into its component parts
sub decode_url {
    my $url = shift;

    if ($url =~ /(.*)\?(.*)/) {
        my $base_url = $1;
        my $params = $2;

        my @unparsed_params = split(/&/, $params);
        my %params = ();
        foreach my $param (@unparsed_params) {
            my ($key, $val) = split(/=/, $param);
            $params{$key} = $val;
        }

        my $encoded_signature = "";
        if (exists $params{$SIGNATURE_PARAM}) {
            $encoded_signature = $params{"Signature"};
        } else {
            print STDERR "Missing Signature URL parameter\n";
            return 0;
        }

        my $encoded_policy = "";
        if (exists $params{$POLICY_PARAM}) {
            $encoded_policy = $params{$POLICY_PARAM};
        } else {
            if (!exists $params{$EXPIRES_PARAM}) {
                print STDERR "Either the Policy or Expires URL parameter needs to be specified\n";
                return 0;    
            }
            
            my $expires = $params{$EXPIRES_PARAM};
            
            my $policy = $CANNED_POLICY;
            $policy =~ s/<EXPIRES>/$expires/g;
            
            my $url_without_cf_params = $url;
            $url_without_cf_params =~ s/$SIGNATURE_PARAM=[^&]*&?//g;
            $url_without_cf_params =~ s/$POLICY_PARAM=[^&]*&?//g;
            $url_without_cf_params =~ s/$EXPIRES_PARAM=[^&]*&?//g;
            $url_without_cf_params =~ s/$KEY_PAIR_ID_PARAM=[^&]*&?//g;
            
            if ($url_without_cf_params =~ /(.*)\?$/) {
                $url_without_cf_params = $1;
            }
            
            $policy =~ s/<RESOURCE>/$url_without_cf_params/g;
            
            $encoded_policy = url_safe_base64_encode($policy);
        }

        my $key = "";
        if (exists $params{$KEY_PAIR_ID_PARAM}) {
            $key = $params{$KEY_PAIR_ID_PARAM};
        } else {
            print STDERR "Missing $KEY_PAIR_ID_PARAM parameter\n";
            return 0;
        }

        my $policy = url_safe_base64_decode($encoded_policy);

        my %ret = ();
        $ret{"base_url"} = $base_url;
        $ret{"policy"} = $policy;
        $ret{"key"} = $key;

        return \%ret;
    } else {
        return 0;
    }
}

# Print a decoded URL out
sub print_decoded_url {
    my $decoded = shift;

    print "Base URL: \n" . $decoded->{"base_url"} . "\n";
    print "Policy: \n" . $decoded->{"policy"} . "\n";
    print "Key: \n" . $decoded->{"key"} . "\n";
}

# Encode a string with base 64 encoding and replace some invalid URL characters
sub url_safe_base64_encode {
    my ($value) = @_;

    my $result = encode_base64($value);
    $result =~ tr|+=/|-_~|;

    return $result;
}

# Decode a string with base 64 encoding.  URL-decode the string first
# followed by reversing any special character ("+=/") translation.
sub url_safe_base64_decode {
    my ($value) = @_;

    $value =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
    $value =~ tr|-_~|+=/|;

    my $result = decode_base64($value);

    return $result;
}

# Create a private content URL
sub create_url {
    my ($path, $policy, $signature, $key_pair_id, $expires) = @_;
    
    my $result;
    my $separator = $path =~ /\?/ ? '&' : '?';
    if ($expires) {
        $result = "$path$separator$EXPIRES_PARAM=$expires&$SIGNATURE_PARAM=$signature&$KEY_PAIR_ID_PARAM=$key_pair_id";
    } else {
        $result = "$path$separator$POLICY_PARAM=$policy&$SIGNATURE_PARAM=$signature&$KEY_PAIR_ID_PARAM=$key_pair_id";
    }
    $result =~ s/\n//g;

    return $result;
}

# Sign a document with given private key file.
# The first argument is the document to sign
# The second argument is the name of the private key file
sub rsa_sha1_sign {
    my ($to_sign, $pvkFile) = @_;
    print "openssl sha1 -sign $pvkFile $to_sign\n";

    return write_to_program($pvkFile, $to_sign);
}

# Helper function to write data to a program
sub write_to_program {
my ($keyfile, $data) = @_;
unlink "temp_policy.dat" if (-e "temp_policy.dat");
unlink "temp_sign.dat" if (-e "temp_sign.dat");

write_file("temp_policy.dat", $data);

system("openssl dgst -sha1 -sign \"$keyfile\" -out temp_sign.dat temp_policy.dat");

my $output = read_file("temp_sign.dat");

    return $output;
}

# Read a file into a string and return the string
sub read_file {
    my ($file) = @_;

    open(INFILE, "<$file") or die("Failed to open $file: $!");
    my $str = join('', <INFILE>);
    close INFILE;

    return $str;
}

sub is_url_valid {
    my ($url) = @_;

    # HTTP distributions start with http[s]:// and are the correct thing to sign
    if ($url =~ /^https?:\/\//) {
        return 1;
    } else {
        print STDERR "CloudFront requires absolute URLs for HTTP distributions\n";
        return 0;
    }
}

sub is_stream_valid {
    my ($stream) = @_;

    if ($stream =~ /^rtmp:\/\// or $stream =~ /^\/?cfx\/st/) {
        print STDERR "Streaming distributions require that only the stream name is signed.\n";
        print STDERR "The stream name is everything after, but not including, cfx/st/\n";
        return 0;
    } else {
        return 1;
    }
}

# flash requires that the query parameters in the stream name are url
# encoded when passed in through javascript, etc.  This sub handles the minimal
# required url encoding.
sub escape_url_for_webpage {
    my ($url) = @_;

    $url =~ s/\?/%3F/g;
    $url =~ s/=/%3D/g;
    $url =~ s/&/%26/g;

    return $url;
}

1;
```

# PHP를 사용한 URL 서명 생성
<a name="CreateURL_PHP"></a>

PHP를 실행하는 모든 웹 서버는 이 PHP 예제 코드를 사용하여 프라이빗 CloudFront 배포에 대한 정책 설명 및 서명을 만듭니다. 예제 전체에서는 CloudFront 스트리밍을 사용하여 비디오 스트림을 재생하는 서명된 URL 링크가 있는 정상 웹 페이지를 만듭니다. [demo-php.zip](samples/demo-php.zip) 파일에서 전체 예제를 다운로드할 수 있습니다.

**참고**  
URL 서명 생성은 서명된 URL을 통해 프라이빗 콘텐츠를 제공하는 프로세스의 한 부분에 불과합니다. 전체 프로세스에 대한 자세한 내용은 [서명된 URL 사용](private-content-signed-urls.md)을 참조합니다.
AWS SDK for PHP에서 `UrlSigner` 클래스를 사용하여 서명된 URL을 생성할 수도 있습니다. 자세한 내용은 *AWS SDK for PHP API 참조*의 [Class UrlSigner](https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.CloudFront.UrlSigner.html)를 참조하세요.

**Topics**
+ [RSA SHA-1 서명 생성](#sample-rsa-sign)
+ [준비된 정책 생성](#sample-canned-policy)
+ [사용자 지정 정책 생성](#sample-custom-policy)
+ [전체 코드 예제](#full-example)

다음 섹션에서는 코드 예제를 각 부분으로 나눕니다. 아래 [전체 코드 예제](#full-example)에서 확인할 수 있습니다.

## RSA SHA-1 서명 생성
<a name="sample-rsa-sign"></a>

이 코드 예제에서는 다음을 수행합니다.
+ `rsa_sha1_sign` 함수는 정책 문을 해시하고 서명합니다. 필요한 인수는 배포의 신뢰할 수 있는 키 그룹에 있는 퍼블릭 키에 해당하는 프라이빗 키와 정책 설명입니다.
+ 다음으로 `url_safe_base64_encode` 함수는 서명의 안전한 URL 버전을 만듭니다.

```
function rsa_sha1_sign($policy, $private_key_filename) {
    $signature = "";

    // load the private key
    $fp = fopen($private_key_filename, "r");
    $priv_key = fread($fp, 8192);
    fclose($fp);
    $pkeyid = openssl_get_privatekey($priv_key);

    // compute signature
    openssl_sign($policy, $signature, $pkeyid);

    // free the key from memory
    openssl_free_key($pkeyid);

    return $signature;
}

function url_safe_base64_encode($value) {
    $encoded = base64_encode($value);
    // replace unsafe characters +, = and / with 
    // the safe characters -, _ and ~
    return str_replace(
        array('+', '=', '/'),
        array('-', '_', '~'),
        $encoded);
}
```

다음 코드 조각은 `get_canned_policy_stream_name()` 함수 및 `get_custom_policy_stream_name()`을 사용하여 준비된 사용자 지정 정책을 만듭니다. CloudFront는 정책을 사용하여 만료 시간 지정을 비롯해 동영상 스트리밍용 URL을 만듭니다.

그런 다음 준비된 정책 또는 사용자 지정 정책을 사용하여 콘텐츠에 대한 액세스를 관리하는 방법을 결정할 수 있습니다. 선택할 항목에 대한 자세한 내용은 [서명된 URL에 대해 미리 준비된 정책 또는 사용자 지정 정책 사용 결정](private-content-signed-urls.md#private-content-choosing-canned-custom-policy) 섹션을 참조하시기 바랍니다.

## 준비된 정책 생성
<a name="sample-canned-policy"></a>

다음 예제 코드는 서명에 대한 *미리 준비된* 정책 설명을 구성합니다.

**참고**  
`$expires` 변수는 문자열이 아닌 정수여야 하는 날짜/타임 스탬프입니다.

```
function get_canned_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $expires) {
    // this policy is well known by CloudFront, but you still need to sign it, since it contains your parameters
    $canned_policy = '{"Statement":[{"Resource":"' . $video_path . '","Condition":{"DateLessThan":{"AWS:EpochTime":'. $expires . '}}}]}';
    // the policy contains characters that cannot be part of a URL, so we base64 encode it
    $encoded_policy = url_safe_base64_encode($canned_policy);
    // sign the original policy, not the encoded version
    $signature = rsa_sha1_sign($canned_policy, $private_key_filename);
    // make the signature safe to be included in a URL
    $encoded_signature = url_safe_base64_encode($signature);

    // combine the above into a stream name
    $stream_name = create_stream_name($video_path, null, $encoded_signature, $key_pair_id, $expires);
    // URL-encode the query string characters
    return $stream_name;
}
```

미리 준비된 정책에 대한 자세한 내용은 [미리 준비된 정책을 사용하여 서명된 URL 생성](private-content-creating-signed-url-canned-policy.md)을 참조하세요.

## 사용자 지정 정책 생성
<a name="sample-custom-policy"></a>

다음 예제 코드는 서명에 대한 *사용자 지정* 정책 설명을 구성합니다.

```
function get_custom_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $policy) {
    // the policy contains characters that cannot be part of a URL, so we base64 encode it
    $encoded_policy = url_safe_base64_encode($policy);
    // sign the original policy, not the encoded version
    $signature = rsa_sha1_sign($policy, $private_key_filename);
    // make the signature safe to be included in a URL
    $encoded_signature = url_safe_base64_encode($signature);

    // combine the above into a stream name
    $stream_name = create_stream_name($video_path, $encoded_policy, $encoded_signature, $key_pair_id, null);
    // URL-encode the query string characters
    return $stream_name;
}
```

사용자 지정 정책에 대한 내용은 [사용자 지정 정책을 사용하여 서명된 URL 생성](private-content-creating-signed-url-custom-policy.md)을 참조하세요.

## 전체 코드 예제
<a name="full-example"></a>

다음 예제 코드는 PHP로 CloudFront 서명 URL을 만드는 전체 예제를 제공합니다. [demo-php.zip](samples/demo-php.zip) 파일에서 전체 예제를 다운로드할 수 있습니다.

다음 예제에서는 IPv4 및 IPv6 주소 범위를 모두 허용하도록 `$policy` `Condition` 요소를 수정할 수 있습니다. 예제는 Amazon Simple Storage Service 사용 설명서의 [IAM 정책에서 IPv6 주소 사용](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ipv6-access.html#ipv6-access-iam)을 참조합니다.**

```
<?php

function rsa_sha1_sign($policy, $private_key_filename) {
    $signature = "";

    // load the private key
    $fp = fopen($private_key_filename, "r");
    $priv_key = fread($fp, 8192);
    fclose($fp);
    $pkeyid = openssl_get_privatekey($priv_key);

    // compute signature
    openssl_sign($policy, $signature, $pkeyid);

    // free the key from memory
    openssl_free_key($pkeyid);

    return $signature;
}

function url_safe_base64_encode($value) {
    $encoded = base64_encode($value);
    // replace unsafe characters +, = and / with the safe characters -, _ and ~
    return str_replace(
        array('+', '=', '/'),
        array('-', '_', '~'),
        $encoded);
}

function create_stream_name($stream, $policy, $signature, $key_pair_id, $expires) {
    $result = $stream;
    // if the stream already contains query parameters, attach the new query parameters to the end
    // otherwise, add the query parameters
    $separator = strpos($stream, '?') == FALSE ? '?' : '&';
    // the presence of an expires time means we're using a canned policy
    if($expires) {
        $result .= $separator . "Expires=" . $expires . "&Signature=" . $signature . "&Key-Pair-Id=" . $key_pair_id;
    }
    // not using a canned policy, include the policy itself in the stream name
    else {
        $result .= $separator . "Policy=" . $policy . "&Signature=" . $signature . "&Key-Pair-Id=" . $key_pair_id;
    }

    // new lines would break us, so remove them
    return str_replace('\n', '', $result);
}


function get_canned_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $expires) {
    // this policy is well known by CloudFront, but you still need to sign it, since it contains your parameters
    $canned_policy = '{"Statement":[{"Resource":"' . $video_path . '","Condition":{"DateLessThan":{"AWS:EpochTime":'. $expires . '}}}]}';
    // the policy contains characters that cannot be part of a URL, so we base64 encode it
    $encoded_policy = url_safe_base64_encode($canned_policy);
    // sign the original policy, not the encoded version
    $signature = rsa_sha1_sign($canned_policy, $private_key_filename);
    // make the signature safe to be included in a URL
    $encoded_signature = url_safe_base64_encode($signature);

    // combine the above into a stream name
    $stream_name = create_stream_name($video_path, null, $encoded_signature, $key_pair_id, $expires);
    // URL-encode the query string characters
    return $stream_name;
}

function get_custom_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $policy) {
    // the policy contains characters that cannot be part of a URL, so we base64 encode it
    $encoded_policy = url_safe_base64_encode($policy);
    // sign the original policy, not the encoded version
    $signature = rsa_sha1_sign($policy, $private_key_filename);
    // make the signature safe to be included in a URL
    $encoded_signature = url_safe_base64_encode($signature);

    // combine the above into a stream name
    $stream_name = create_stream_name($video_path, $encoded_policy, $encoded_signature, $key_pair_id, null);
    // URL-encode the query string characters
    return $stream_name;
}


// Path to your private key.  Be very careful that this file is not accessible
// from the web!

$private_key_filename = '/home/test/secure/example-priv-key.pem';
$key_pair_id = 'K2JCJMDEHXQW5F';

// Make sure you have "Restrict viewer access" enabled on this path behaviour and using the above Trusted key groups (recommended).
$video_path = 'https://example.com/secure/example.mp4';

$expires = time() + 300; // 5 min from now
$canned_policy_stream_name = get_canned_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $expires);

// Get the viewer real IP from the x-forward-for header as $_SERVER['REMOTE_ADDR'] will return viewer facing IP. An alternative option is to use CloudFront-Viewer-Address header. Note that this header is a trusted CloudFront immutable header. Example format: IP:PORT ("CloudFront-Viewer-Address": "1.2.3.4:12345")
$client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$policy =
'{'.
    '"Statement":['.
        '{'.
            '"Resource":"'. $video_path . '",'.
            '"Condition":{'.
                '"IpAddress":{"AWS:SourceIp":"' . $client_ip . '/32"},'.
                '"DateLessThan":{"AWS:EpochTime":' . $expires . '}'.
            '}'.
        '}'.
    ']' .
    '}';
$custom_policy_stream_name = get_custom_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $policy);

?>

<html>

<head>
    <title>CloudFront</title>
</head>

<body>
    <h1>Amazon CloudFront</h1>
    <h2>Canned Policy</h2>
    <h3>Expires at <?php echo gmdate('Y-m-d H:i:s T', $expires); ?></h3>
    <br />

    <div id='canned'>The canned policy video will be here: <br>
    
        <video width="640" height="360" autoplay muted controls>
        <source src="<?php echo $canned_policy_stream_name; ?>" type="video/mp4">
        Your browser does not support the video tag.
        </video>
    </div>

    <h2>Custom Policy</h2>
    <h3>Expires at <?php echo gmdate('Y-m-d H:i:s T', $expires); ?> only viewable by IP <?php echo $client_ip; ?></h3>
    <div id='custom'>The custom policy video will be here: <br>

         <video width="640" height="360" autoplay muted controls>
         <source src="<?php echo $custom_policy_stream_name; ?>" type="video/mp4">
         Your browser does not support the video tag.
        </video>
    </div> 

</body>

</html>
```

추가 URL 서명 예제를 알아보려면 다음 주제를 참조하시기 바랍니다.
+ [Perl을 사용한 URL 서명 생성](CreateURLPerl.md)
+ [C\$1 및 .NET Framework를 사용한 URL 서명 생성](CreateSignatureInCSharp.md)
+ [Java를 사용한 URL 서명 생성](CFPrivateDistJavaDevelopment.md)

서명된 URL을 사용하여 서명을 만드는 대신 서명된 쿠키를 사용할 수 있습니다. 자세한 내용은 [PHP를 사용하여 서명된 쿠키 생성](signed-cookies-PHP.md) 섹션을 참조하세요.

# C\$1 및 .NET Framework를 사용한 URL 서명 생성
<a name="CreateSignatureInCSharp"></a>

이 섹션의 C\$1 예제는 사용자 지정 및 미리 준비된 정책 설명을 사용하여 CloudFront 프라이빗 배포에 대한 서명을 만드는 방법을 보여주는 예제 애플리케이션을 구현합니다. 이 예제는 .NET 애플리케이션에서 유용할 수 있는 [AWS SDK for .NET](https://aws.amazon.com/sdkfornet)를 기반으로 한 유틸리티 함수를 포함합니다.

SDK for .NET를 사용하여 서명된 URL과 서명된 쿠키를 생성할 수도 있습니다. *SDK for .NET API 참조*에서 다음 주제를 참조하세요.
+ **서명된 URL** – [AmazonCloudFrontUrlSigner](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFront/TCloudFrontUrlSigner.html) 
+ **서명된 쿠키** – [AmazonCloudFrontCookieSigner](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFront/TCloudFrontCookieSigner.html) 

코드를 다운로드하려면 [C\$1의 서명 코드](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/samples/AWS_PrivateCF_Distributions.zip)를 참조하세요.

**참고**  
`AmazonCloudFrontUrlSigner` 및 `AmazonCloudFrontCookieSigner` 클래스가 별도의 패키지로 이전되었습니다. 해당 클래스 사용에 대한 자세한 내용은 *AWS SDK for .NET(V4) 개발자 안내서*의 [CookieSigner and UrlSigner](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html#net-dg-v4-CookieSigner-UrlSigner)를 참조하시기 바랍니다.
URL 서명 생성은 서명된 URL을 통해 프라이빗 콘텐츠를 제공하는 프로세스의 한 부분에 불과합니다. 자세한 내용은 [서명된 URL 사용](private-content-signed-urls.md) 섹션을 참조하세요. 서명된 쿠키 사용에 대한 자세한 내용은 [서명된 쿠키 사용](private-content-signed-cookies.md) 섹션을 참조하세요.

## .NET Framework에서 RSA 키 사용
<a name="rsa-key-sdk-net"></a>

.NET Framework에서 RSA 키를 사용하려면 AWS가 제공한 .pem 파일을 .NET Framework에서 사용하는 XML 형식으로 변환해야 합니다.

변환 후, RSA 프라이빗 키 파일의 형식은 다음과 같습니다.

**Example : XML .NET Framework 형식의 RSA 프라이빗 키**  <a name="RSAPrivateKeyXML.NETFrameworkFormat"></a>

```
<RSAKeyValue>
  <Modulus>
    wO5IvYCP5UcoCKDo1dcspoMehWBZcyfs9QEzGi6Oe5y+ewGr1oW+vB2GPB
    ANBiVPcUHTFWhwaIBd3oglmF0lGQljP/jOfmXHUK2kUUnLnJp+oOBL2NiuFtqcW6h/L5lIpD8Yq+NRHg
    Ty4zDsyr2880MvXv88yEFURCkqEXAMPLE=
  </Modulus>
  <Exponent>AQAB</Exponent>
  <P>
    5bmKDaTz
    npENGVqz4Cea8XPH+sxt+2VaAwYnsarVUoSBeVt8WLloVuZGG9IZYmH5KteXEu7fZveYd9UEXAMPLE==
  </P>
  <Q>
    1v9l/WN1a1N3rOK4VGoCokx7kR2SyTMSbZgF9IWJNOugR/WZw7HTnjipO3c9dy1Ms9pUKwUF4
    6d7049EXAMPLE==
  </Q>
  <DP>
    RgrSKuLWXMyBH+/l1Dx/I4tXuAJIrlPyo+VmiOc7b5NzHptkSHEPfR9s1
    OK0VqjknclqCJ3Ig86OMEtEXAMPLE==
  </DP>
  <DQ>
    pjPjvSFw+RoaTu0pgCA/jwW/FGyfN6iim1RFbkT4
    z49DZb2IM885f3vf35eLTaEYRYUHQgZtChNEV0TEXAMPLE==
  </DQ>
  <InverseQ>
    nkvOJTg5QtGNgWb9i
    cVtzrL/1pFEOHbJXwEJdU99N+7sMK+1066DL/HSBUCD63qD4USpnf0myc24in0EXAMPLE==</InverseQ>
  <D>
      Bc7mp7XYHynuPZxChjWNJZIq+A73gm0ASDv6At7F8Vi9r0xUlQe/v0AQS3ycN8QlyR4XMbzMLYk
      3yjxFDXo4ZKQtOGzLGteCU2srANiLv26/imXA8FVidZftTAtLviWQZBVPTeYIA69ATUYPEq0a5u5wjGy
      UOij9OWyuEXAMPLE=
   </D>
</RSAKeyValue>
```

## C\$1의 미리 준비된 정책 서명 메서드
<a name="canned-policy-signed-url-net"></a>

다음 C\$1 코드는 다음을 수행하여 미리 준비된 정책을 사용하는 서명된 URL을 만듭니다.
+ 정책 설명을 만듭니다.
+ SHA1을 사용하여 정책 설명을 해시하고, RSA와 해당 퍼블릭 키가 신뢰할 수 있는 키 그룹에 있는 프라이빗 키를 사용하여 결과에 서명합니다.
+ Base64 방식으로 해시 및 서명된 정책 설명을 인코딩하고 특수 문자를 교체하여 URL 요청 파라미터로 사용하기에 안전한 문자열을 만드세요.
+ 값을 연결합니다.

완전한 구현에 대한 내용은 [C\$1의 서명 코드](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/samples/AWS_PrivateCF_Distributions.zip)에서 예제를 참조하세요.

**참고**  
CloudFront에 퍼블릭 키를 업로드하면 `keyId`가 반환됩니다. 자세한 내용은 ![\[6\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/images/callouts/6.png)[ &Key-Pair-Id](private-content-creating-signed-url-canned-policy.md)를 참조하세요.

**Example : C\$1의 미리 준비된 정책 서명 메서드**  <a name="ExampleCannedPolicySigningMethod-CSharp"></a>

```
public static string ToUrlSafeBase64String(byte[] bytes)
{
    return System.Convert.ToBase64String(bytes)
        .Replace('+', '-')
        .Replace('=', '_')
        .Replace('/', '~');
}

public static string CreateCannedPrivateURL(string urlString, 
    string durationUnits, string durationNumber, string pathToPolicyStmnt, 
    string pathToPrivateKey, string keyId)
{
    // args[] 0-thisMethod, 1-resourceUrl, 2-seconds-minutes-hours-days 
    // to expiration, 3-numberOfPreviousUnits, 4-pathToPolicyStmnt, 
    // 5-pathToPrivateKey, 6-keyId

    TimeSpan timeSpanInterval = GetDuration(durationUnits, durationNumber);

    // Create the policy statement.
    string strPolicy = CreatePolicyStatement(pathToPolicyStmnt,
        urlString, 
        DateTime.Now, 
        DateTime.Now.Add(timeSpanInterval), 
        "0.0.0.0/0");
    if ("Error!" == strPolicy) return "Invalid time frame." + 
        "Start time cannot be greater than end time.";

    // Copy the expiration time defined by policy statement.
    string strExpiration = CopyExpirationTimeFromPolicy(strPolicy);

    // Read the policy into a byte buffer.
    byte[] bufferPolicy = Encoding.ASCII.GetBytes(strPolicy);

    // Initialize the SHA1CryptoServiceProvider object and hash the policy data.
    using (SHA1CryptoServiceProvider 
        cryptoSHA1 = new SHA1CryptoServiceProvider())
    {
        bufferPolicy = cryptoSHA1.ComputeHash(bufferPolicy);

        // Initialize the RSACryptoServiceProvider object.
        RSACryptoServiceProvider providerRSA = new RSACryptoServiceProvider();
        XmlDocument xmlPrivateKey = new XmlDocument();

        // Load your private key, which you created by converting your 
        // .pem file to the XML format that the .NET framework uses.  
        // Several tools are available. 
        xmlPrivateKey.Load(pathToPrivateKey);

        // Format the RSACryptoServiceProvider providerRSA and 
        // create the signature.
        providerRSA.FromXmlString(xmlPrivateKey.InnerXml);
        RSAPKCS1SignatureFormatter rsaFormatter = 
            new RSAPKCS1SignatureFormatter(providerRSA);
        rsaFormatter.SetHashAlgorithm("SHA1");
        byte[] signedPolicyHash = rsaFormatter.CreateSignature(bufferPolicy);

        // Convert the signed policy to URL-safe base64 encoding and 
        // replace unsafe characters + = / with the safe characters - _ ~
        string strSignedPolicy = ToUrlSafeBase64String(signedPolicyHash);

        // Concatenate the URL, the timestamp, the signature, 
        // and the key pair ID to form the signed URL.
        return urlString + 
            "?Expires=" + 
            strExpiration + 
            "&Signature=" + 
            strSignedPolicy + 
            "&Key-Pair-Id=" + 
            keyId;
    }
}
```

## C\$1의 사용자 지정 정책 서명 메서드
<a name="custom-policy-signed-url-net"></a>

다음 C\$1 코드는 다음을 수행하여 사용자 지정 정책을 사용하는 서명된 URL을 만듭니다.

1. 정책 설명을 만듭니다.

1. Base64 방식으로 정책 설명을 인코딩하고 특수 문자를 교체하여 URL 요청 파라미터로 사용하기에 안전한 문자열을 만드세요.

1. SHA1을 사용하여 정책 설명을 해시하고, RSA와 해당 퍼블릭 키가 신뢰할 수 있는 키 그룹에 있는 프라이빗 키를 사용하여 결과를 암호화합니다.

1. Base64 방식으로 해시된 정책 설명을 인코딩하고 특수 문자를 교체하여 URL 요청 파라미터로 사용하기에 안전한 문자열을 만드세요.

1. 값을 연결합니다.

완전한 구현에 대한 내용은 [C\$1의 서명 코드](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/samples/AWS_PrivateCF_Distributions.zip)에서 예제를 참조하세요.

**참고**  
CloudFront에 퍼블릭 키를 업로드하면 `keyId`가 반환됩니다. 자세한 내용은 ![\[6\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/images/callouts/6.png)[ &Key-Pair-Id](private-content-creating-signed-url-canned-policy.md)를 참조하세요.

**Example : C\$1의 사용자 지정 정책 서명 메서드**  <a name="ExampleCustomPolicySigningMethod-CSharp"></a>

```
public static string ToUrlSafeBase64String(byte[] bytes)
{
    return System.Convert.ToBase64String(bytes)
        .Replace('+', '-')
        .Replace('=', '_')
        .Replace('/', '~');
}

public static string CreateCustomPrivateURL(string urlString, 
    string durationUnits, string durationNumber, string startIntervalFromNow, 
    string ipaddress, string pathToPolicyStmnt, string pathToPrivateKey, 
    string keyId)
{
    // args[] 0-thisMethod, 1-resourceUrl, 2-seconds-minutes-hours-days 
    // to expiration, 3-numberOfPreviousUnits, 4-starttimeFromNow, 
    // 5-ip_address, 6-pathToPolicyStmt, 7-pathToPrivateKey, 8-keyId

    TimeSpan timeSpanInterval = GetDuration(durationUnits, durationNumber);
    TimeSpan timeSpanToStart = GetDurationByUnits(durationUnits, 
        startIntervalFromNow);
    if (null == timeSpanToStart) 
        return "Invalid duration units." + 
            "Valid options: seconds, minutes, hours, or days";
            
    string strPolicy = CreatePolicyStatement(
        pathToPolicyStmnt, urlString, DateTime.Now.Add(timeSpanToStart), 
        DateTime.Now.Add(timeSpanInterval), ipaddress);

    // Read the policy into a byte buffer.
    byte[] bufferPolicy = Encoding.ASCII.GetBytes(strPolicy);

    // Convert the policy statement to URL-safe base64 encoding and 
    // replace unsafe characters + = / with the safe characters - _ ~

    string urlSafePolicy = ToUrlSafeBase64String(bufferPolicy);

    // Initialize the SHA1CryptoServiceProvider object and hash the policy data.
    byte[] bufferPolicyHash;
    using (SHA1CryptoServiceProvider cryptoSHA1 = 
        new SHA1CryptoServiceProvider())
    {
        bufferPolicyHash = cryptoSHA1.ComputeHash(bufferPolicy);

        // Initialize the RSACryptoServiceProvider object.
        RSACryptoServiceProvider providerRSA = new RSACryptoServiceProvider();
        XmlDocument xmlPrivateKey = new XmlDocument();

        // Load your private key, which you created by converting your 
        // .pem file to the XML format that the .NET framework uses.  
        // Several tools are available. 
        xmlPrivateKey.Load(pathToPrivateKey);

        // Format the RSACryptoServiceProvider providerRSA 
        // and create the signature.
        providerRSA.FromXmlString(xmlPrivateKey.InnerXml);
        RSAPKCS1SignatureFormatter RSAFormatter = 
            new RSAPKCS1SignatureFormatter(providerRSA);
        RSAFormatter.SetHashAlgorithm("SHA1");
        byte[] signedHash = RSAFormatter.CreateSignature(bufferPolicyHash);

        // Convert the signed policy to URL-safe base64 encoding and 
        // replace unsafe characters + = / with the safe characters - _ ~
        string strSignedPolicy = ToUrlSafeBase64String(signedHash);

        return urlString + 
            "?Policy=" + 
            urlSafePolicy + 
            "&Signature=" + 
            strSignedPolicy + 
            "&Key-Pair-Id=" + 
            keyId;
    }
}
```

## 서명 생성을 위한 유틸리티 메서드
<a name="utility-methods-signed-url"></a>

다음 메서드는 파일에서 정책 설명을 가져오고 서명 생성을 위해 시간 간격을 구문 분석합니다.

**Example : 서명 생성을 위한 유틸리티 메서드**  <a name="UtilityMethodsForSignatureGeneration"></a>

```
public static string CreatePolicyStatement(string policyStmnt, 
   string resourceUrl, 
   DateTime startTime, 
   DateTime endTime, 
   string ipAddress)
   
{
   // Create the policy statement.
   FileStream streamPolicy = new FileStream(policyStmnt, FileMode.Open, FileAccess.Read);
   using (StreamReader reader = new StreamReader(streamPolicy))
   {
      string strPolicy = reader.ReadToEnd();

      TimeSpan startTimeSpanFromNow = (startTime - DateTime.Now);
      TimeSpan endTimeSpanFromNow = (endTime - DateTime.Now);
      TimeSpan intervalStart = 
         (DateTime.UtcNow.Add(startTimeSpanFromNow)) - 
         new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
      TimeSpan intervalEnd = 
         (DateTime.UtcNow.Add(endTimeSpanFromNow)) - 
         new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

      int startTimestamp = (int)intervalStart.TotalSeconds; // START_TIME
      int endTimestamp = (int)intervalEnd.TotalSeconds;  // END_TIME

      if (startTimestamp > endTimestamp)
         return "Error!";

      // Replace variables in the policy statement.
      strPolicy = strPolicy.Replace("RESOURCE", resourceUrl);
      strPolicy = strPolicy.Replace("START_TIME", startTimestamp.ToString());
      strPolicy = strPolicy.Replace("END_TIME", endTimestamp.ToString());
      strPolicy = strPolicy.Replace("IP_ADDRESS", ipAddress);
      strPolicy = strPolicy.Replace("EXPIRES", endTimestamp.ToString());
      return strPolicy;
   }   
}

public static TimeSpan GetDuration(string units, string numUnits)
{
   TimeSpan timeSpanInterval = new TimeSpan();
   switch (units)
   {
      case "seconds":
         timeSpanInterval = new TimeSpan(0, 0, 0, int.Parse(numUnits));
         break;
      case "minutes":
         timeSpanInterval = new TimeSpan(0, 0, int.Parse(numUnits), 0);
         break;
      case "hours":
         timeSpanInterval = new TimeSpan(0, int.Parse(numUnits), 0 ,0);
         break;
      case "days":
         timeSpanInterval = new TimeSpan(int.Parse(numUnits),0 ,0 ,0);
         break;
      default:
         Console.WriteLine("Invalid time units;" + 
            "use seconds, minutes, hours, or days");
         break;
   }
   return timeSpanInterval;
}

private static TimeSpan GetDurationByUnits(string durationUnits, 
   string startIntervalFromNow)
{
   switch (durationUnits)
   {
      case "seconds":
         return new TimeSpan(0, 0, int.Parse(startIntervalFromNow));
      case "minutes":
         return new TimeSpan(0, int.Parse(startIntervalFromNow), 0);
      case "hours":
         return new TimeSpan(int.Parse(startIntervalFromNow), 0, 0);
      case "days":
         return new TimeSpan(int.Parse(startIntervalFromNow), 0, 0, 0);
      default:
         return new TimeSpan(0, 0, 0, 0);
   }
}

public static string CopyExpirationTimeFromPolicy(string policyStatement)
{
   int startExpiration = policyStatement.IndexOf("EpochTime");
   string strExpirationRough = policyStatement.Substring(startExpiration + 
      "EpochTime".Length);
   char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
         
   List<char> listDigits = new List<char>(digits);
   StringBuilder buildExpiration = new StringBuilder(20);
         
   foreach (char c in strExpirationRough)
   {
      if (listDigits.Contains(c))
         buildExpiration.Append(c);
   }
   return buildExpiration.ToString();   
}
```

다음 사항도 참조하세요.
+ [Perl을 사용한 URL 서명 생성](CreateURLPerl.md)
+ [PHP를 사용한 URL 서명 생성](CreateURL_PHP.md)
+ [Java를 사용한 URL 서명 생성](CFPrivateDistJavaDevelopment.md)

# Java를 사용한 URL 서명 생성
<a name="CFPrivateDistJavaDevelopment"></a>

다음 코드 예제 외에도 [AWS SDK for Java(버전 1)의 `CloudFrontUrlSigner` 유틸리티 클래스](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/cloudfront/CloudFrontUrlSigner.html)를 사용하여 [CloudFront 서명 URL](private-content-signed-urls.md)을 만들 수 있습니다.

자세한 예제는 AWS SDK Code Examples Code Library에서 [AWS SDK를 사용하여 서명된 URL 및 쿠키 만들기](https://docs.aws.amazon.com/code-library/latest/ug/cloudfront_example_cloudfront_CloudFrontUtilities_section.html)를 참조합니다.**

**참고**  
서명된 URL 생성은 [CloudFront를 통해 프라이빗 콘텐츠를 제공](PrivateContent.md)하는 프로세스의 한 부분에 불과합니다. 전체 프로세스에 대한 자세한 내용은 [서명된 URL 사용](private-content-signed-urls.md)을 참조하세요.

다음 예제는 CloudFront 서명된 URL을 만드는 방법을 보여줍니다.

**Example Java 정책 및 서명 암호화 메서드**  <a name="ExampleJavaPolicyAndSignatureEncryptionMethods"></a>

```
package org.example;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import software.amazon.awssdk.services.cloudfront.CloudFrontUtilities;
import software.amazon.awssdk.services.cloudfront.model.CannedSignerRequest;
import software.amazon.awssdk.services.cloudfront.url.SignedUrl;

public class Main {

    public static void main(String[] args) throws Exception {
        CloudFrontUtilities cloudFrontUtilities = CloudFrontUtilities.create();
        Instant expirationDate = Instant.now().plus(7, ChronoUnit.DAYS);
        String resourceUrl = "https://a1b2c3d4e5f6g7.cloudfront.net";
        String keyPairId = "K1UA3WV15I7JSD";
        CannedSignerRequest cannedRequest = CannedSignerRequest.builder()
                .resourceUrl(resourceUrl)
                .privateKey(new java.io.File("/path/to/private_key.pem").toPath())
                .keyPairId(keyPairId)
                .expirationDate(expirationDate)
                .build();
        SignedUrl signedUrl = cloudFrontUtilities.getSignedUrlWithCannedPolicy(cannedRequest);
        String url = signedUrl.url();
        System.out.println(url);

    }
}
```

추가 참고:
+ [Perl을 사용한 URL 서명 생성](CreateURLPerl.md)
+ [PHP를 사용한 URL 서명 생성](CreateURL_PHP.md)
+ [C\$1 및 .NET Framework를 사용한 URL 서명 생성](CreateSignatureInCSharp.md)