Set a signed cookie (for CloudFront) with a custom policy using the AWS SDK for Java

If you want to restrict access to content under CloudFront

Can be used.

[Reference: Link to documents in AWS] ** Use signed URL ** ** · [Create a signed URL using the default policy](http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned- policy.html) ** ** · [Create a signed URL using a custom policy](http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom- policy.html) ** ** Using signed cookies ** ** · [Setting Signed Cookies Using Default Policy](http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-canned-policy. html) ** ** · [Setting Signed Cookies Using Custom Policies](http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy. html) **

But,

So

In that case, the default policy or signed URL is fine,

In that case, it is not realistic to sign the links to the .css, image, and video files that are read with the signed URL, so this time ** Signed cookie using custom policy * I experimented with *.

0. Contents of this experiment

1. CloudFront + S3 side settings

Set according to the following flow.

1-1. Create a CloudFront key pair in the AWS Management Console

Below, IAM users cannot create it, so use ** a user with root authentication information ** to perform the operation.

01_sec_auth.jpg ① Select ** "Security Credentials" ** from the user menu on the upper right. ② Expand ** "CloudFront key pair" ** ③ Click ** [Create new key pair] ** ④ Click ** [Download Private Key File] ** to download the private key file. ⑤ Confirm and record the key pair ID on the screen with the pop-up closed.

openssl command


openssl pkcs8 -topk8 -nocrypt -in [Private key file name].pem -inform PEM -out [output file name].der -outform DER

1-2. Create a CloudFront distribution

02_cf_create_dist_01.jpg ① Click ** [Create Distribution] ** in CloudFront → Distribution on the console.

02_cf_create_dist_02.jpg ② Click ** [Get Started] ** on the Web

02_cf_create_dist_03.jpg ③ Set items related to Origin's S3 bucket ④ Select ** "Yes" ** in ** "Restrict Bucket Access" ** ⑤ Select ** "Create a New Identity" ** in ** "Origin Access Identity" ** ⑥ Select ** "Yes,…" ** in ** "Grant Read Permissions on Bucket" **

02_cf_create_dist_04.jpg ⑦ Select ** "Yes" ** in ** "Restrict Viewer Access" ** ⑧ Check ** "Self" ** in ** "Trusted Signers" ** ⑨ Specify the domain name (FQDN assigned to the CloudFront distribution) in ** "Alternate Domain Names" **

02_cf_create_dist_05.jpg ⑩ Set SSL / TLS related items (After selecting ** "Custom SSL Certificate" **, select the certificate to use or request a new SNI SSL certificate)

02_cf_create_dist_06.jpg ⑪ ** Click "Create Distribution" **

02_cf_oai.jpg ⑫ Click ** "Origin Access Identity" ** ⑬ ** Confirm "Amazon S3 Canonical User ID" ** ⑭ Wait for the distribution to be created

1-3. Set the access authority of S3 bucket

① In S3 of the console, select the S3 bucket to be published → ** "Access authority" tab **

03_s3_acl.jpg ② Click ** [Access Control List] ** ③ Click ** [Add user] ** ④ Enter the character string confirmed in ⑬ of CloudFront settings ⑤ Check ** "Read" ** in ** "Object Access" ** ⑥ Click ** [Save] **

03_s3_policy.jpg ⑦ Click ** [Bucket Policy] ** and check that the policy is set (as specified when creating the CloudFront distribution).

2. Implement code that sets a signed cookie in your application

This time, I implemented it in Spring Boot environment because it is an experiment in local development environment. Below, only the Controller class ** for ** "/ set-cookie" is described.

Create a ** "files" ** folder under ** "src / main / resources" ** and create a ** "private-" folder in the private key file that has been converted to .der format in advance. Save it as "key.der" **. This time, the file was saved in this location according to the Spring Boot environment, so it is a little troublesome to retrieve the contents of the private key file, but save the file in WEB-INF in the normal Servlet environment. If you keep it, you should be able to take it out more straightforwardly.

In addition, Amazon recommends updating the key pair (private key and public key) every 90 days, and if you update in such a short cycle, the key pair ID and private key file are hard-coded. It's better to keep it in a form that can be updated separately from the application, rather than having it fixedly included in the application package.

SetCookieController.java


package site.hmatsu47.springboot;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;

import com.amazonaws.services.cloudfront.CloudFrontCookieSigner;
import com.amazonaws.services.cloudfront.CloudFrontCookieSigner.CookiesForCustomPolicy;
import com.amazonaws.util.DateUtils;

import java.io.File;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Controller;

@Controller
public class SetCookieController {

	@Autowired
    ResourceLoader resourceLoader;
	
	@RequestMapping("/set-cookie")
	public void setCookie(HttpServletResponse res) throws Exception{
		String url = "https://www.hmatsu47.site/";		// URL
		String filepath = "files/private-key.der";		//Private key
		Resource resource = resourceLoader.getResource("classpath:" + filepath);
		File file = resource.getFile();
		byte[] privateKeyByteArray = Files.readAllBytes(file.toPath());
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyByteArray);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		RSAPrivateKey privateKey = (RSAPrivateKey) kf.generatePrivate(keySpec);
		String resourcePath = "*";						//Scope of permission for access
		String keyPairId = "[Key pair ID]";					//Key pair ID
		Date activeFrom = null;							//Validity period: Start (null = immediate)
		Date expiresOn = DateUtils.parseISO8601Date("2020-12-31T23:59:59.999Z"); //Validity period: End (in UTC)
		String ipRange = "0.0.0.0/0";					//IP address range of access source
		//Generating key / value for cookie
		CookiesForCustomPolicy cookies = CloudFrontCookieSigner.getCookiesForCustomPolicy(
				url + resourcePath, privateKey, keyPairId, expiresOn, activeFrom, ipRange);
		//Cookie settings
		Cookie cookiePolicy = new Cookie(
				cookies.getPolicy().getKey(), cookies.getPolicy().getValue());
		cookiePolicy.setDomain("hmatsu47.site");
		cookiePolicy.setHttpOnly(true);
		cookiePolicy.setSecure(true);
		res.addCookie(cookiePolicy);

		Cookie cookieSignature = new Cookie(
				cookies.getSignature().getKey(), cookies.getSignature().getValue());
		cookieSignature.setDomain("hmatsu47.site");
		cookieSignature.setHttpOnly(true);
		cookieSignature.setSecure(true);
		res.addCookie(cookieSignature);

		Cookie cookieKeyPairId = new Cookie(
				cookies.getKeyPairId().getKey(), cookies.getKeyPairId().getValue());
		cookieKeyPairId.setDomain("hmatsu47.site");
		cookieKeyPairId.setHttpOnly(true);
		cookieKeyPairId.setSecure(true);
		res.addCookie(cookieKeyPairId);
		//Transition by redirect
		res.sendRedirect(url + "index.html");
	}
}

3. Execute

In the S3 bucket to be published, save index.html with appropriate contents and images loaded by specifying index.html.

First, start your browser and open ** "https://www.hmatsu47.site/index.html" **. Then, since you do not have access authority, the target page will not open and an error will occur.

Next, open the ** "https://hmatsu47.site/set-cookie" ** implemented in 2. Then, after the signed cookie is set for the domain ** "hmatsu47.site" ** and its subdomain range, it will be redirected and this time the page will open correctly.

When you close the browser, the signed cookie disappears, so when you start the browser again, ** "https://www.hmatsu47.site/index.html" ** will not open and an error will occur.

hosts file (additional note)


127.0.0.1	hmatsu47.site

This worked fine in the development environment (STS 3.8.4.RELEASE on Windows 8.1) prepared this time, but there is no guarantee that it will work in other environments (local environment), so if it does not work, it is a normal public server environment. It seems better to try it with.

4. Use for video playback

See the article below. ** Play restricted access video (HLS format) under CloudFront with Video.js using signed cookie **

Recommended Posts

Set a signed cookie (for CloudFront) with a custom policy using the AWS SDK for Java
[AWS SDK for Java] Set a retry policy on the S3 client
Get a list of S3 files with ListObjectsV2Request (AWS SDK for Java)
Credentials referenced by the AWS SDK for Java by default
Try Spark Submit to EMR using AWS SDK for Java
AWS SDK for Java 1.11.x and 2.x
Encrypt data uploaded to S3 using AWS SDK for Java / SSE-KMS
Upload / download / bulk delete data to S3 using Amazon S3 Client Builder with AWS SDK for Java
AWS Elastic Beanstalk # 1 with Java starting from scratch-Building a Java web application environment using the EB CLI-
Using Java with AWS Lambda-Eclipse Preparation
Using Java with AWS Lambda-Implementation-Check CloudWatch Arguments
Using Java with AWS Lambda-Implementation-Stop / Launch EC2
Try using the Wii remote with Java
Validate the identity token of a user authenticated with AWS Cognito in Java
ChatWork4j for using the ChatWork API in Java
[Java] Set the time from the browser with jsoup
[Java] (for MacOS) How to set the classpath
I wrote a test code (Junit & mockit) for the code that calls the AWS API (Java)
Submit a job to AWS Batch with Java (Eclipse)
[Rails] How to create a signed URL for CloudFront
I tried to operate SQS using AWS Java SDK
[Java basics] Let's make a triangle with a for statement
A note for Initializing Fields in the Java tutorial
Using the database (SQL Server 2014) from a Java program 2018/01/04
Prepare the environment for java11 and javaFx with Ubuntu 18.4
Get S3 object size with AWS SDK for Ruby
The story of building a Java version of Minecraft server with GCP (and also set a whitelist)