AWS Developer Tools Blog

Amazon CloudFront Signed URLs and Cookies are now supported in AWS SDK for Java 2.x

We are pleased to announce the general availability of Amazon CloudFront signed URLs and signed cookies in the AWS SDK for Java 2.x. You can now securely serve private content through CloudFront, requiring that your users access your content by using special CloudFront signed URLs or signed cookies. To configure your CloudFront distribution to use this feature, you must specify a trusted key group (recommended) or AWS account as a trusted signer. For more information on setting up and configuring a CloudFront distribution, please see the Developer Guide.

The new SDK 2.x CloudFrontUtilities component provides the highly-requested signing features that many customers miss from the AWS SDK for Java 1.x. The new APIs in the SDK 2.x APIs have been redesigned with one point of entry for a streamlined usage experience.

Motivation

You can control access to your content with either signed URLs or signed cookies. Signed URLs allow you to create a new URL that has temporary access to one of your protected CloudFront resources. Signed cookies allow you to create a temporary set of credentials that can be used by your customers to directly access one or many protected CloudFront resources.

Using CloudFront Signing

1) Add a dependency for CloudFront

The first step to getting started is to add the dependency for CloudFront in your project.

<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>cloudfront</artifactId>
  <version>2.18.26</version> <!-- Update to use the latest version. -->
</dependency>

We recommend using the most recent version of the SDK.

2) Instantiate the CloudFrontUtilities Class

To instantiate the utility class, you just need to call create().

import software.amazon.awssdk.services.cloudfront.CloudFrontUtilities;

CloudFrontUtilities cloudFrontUtilities = CloudFrontUtilities.create();

3) Create an CloudFrontSignerRequest

Next, you create a CloudFrontSignerRequest using a builder. A request can be used for both URLs and cookies.

Example #1: Signed Request with Canned Policy

For a URL/cookie with a canned policy, create a CannedSignerRequest, specifying the resource URL, private key, public key ID, and expiration date.

import software.amazon.awssdk.services.cloudfront.model.CannedSignerRequest;
import java.security.PrivateKey;
import java.time.Instant;
import java.time.temporal.ChronoUnit;


Instant expirationDate = Instant.now().plus(7, ChronoUnit.DAYS);
String resourceUrl = "https://d1npcfkc2mojrf.cloudfront.net/s3ObjectKey";
String keyPairId = "myKeyPairId";
PrivateKey privateKey = myPrivateKey; // Either PrivateKey or Path can be passed in
CannedSignerRequest cannedRequest = CannedSignerRequest.builder()
                                                       .resourceUrl(resourceUrl)
                                                       .privateKey(privateKey)
                                                       .keyPairId(keyPairId)
                                                       .expirationDate(expirationDate)
                                                       .build();

Example #2: Signed Request with Custom Policy

For a URL/cookie with a custom policy, create a CustomSignerRequest, where you can additionally specify the active date and/or the IP range. In this example, we specify both the active date and IP range, though you can choose to specify only one of them.

import software.amazon.awssdk.services.cloudfront.model.CustomSignerRequest;
import java.nio.file.Path;
import java.time.Instant;
import java.time.temporal.ChronoUnit;


Instant expirationDate = Instant.now().plus(7, ChronoUnit.DAYS);
String resourceUrl = "https://d111111abcdef8.cloudfront.net/s3ObjectKey";
String keyPairId = "myKeyPairId";
Instant activeDate = Instant.now().plus(2, ChronoUnit.DAYS);
String ipRange = "192.168.0.1/24";
Path keyFile = myKeyFile; // Either PrivateKey or Path can be passed in

CustomSignerRequest customRequest = CustomSignerRequest.builder()
                                                       .resourceUrl(resourceUrl)
                                                       .privateKey(keyFile)
                                                       .keyPairId(keyPairId)
                                                       .expirationDate(expirationDate)
                                                       .activeDate(activeDate) //optional
                                                       .ipRange(ipRange) //optional
                                                       .build();

4) Create a Signed URL/Signed Cookie

After you generate the request, create the signed URL or signed cookie, from which you can optionally generate an SdkHttpRequest to be executed by an HTTP client.

Example #1: Signed URL with Canned Policy

To create a SignedUrl with a canned policy, you just need to call getSignedUrlWithCannedPolicy(), passing in the CannedSignerRequest instance. With the SignedUrl object, you can then call url() to return a signed URL String.

import software.amazon.awssdk.services.cloudfront.model.CannedSignerRequest;
import software.amazon.awssdk.services.cloudfront.url.SignedUrl;


SignedUrl signedUrl = cloudFrontUtilities.getSignedUrlWithCannedPolicy(cannedRequest);
// Returns a signed URL String that you can provide to users which will allow them to access your content
String url = signedUrl.url()

Example #2: Signed Cookie with Custom Policy

To create a CookiesForCustomPolicy, you just need to call getCookiesForCustomPolicy(), passing in the CustomSignerRequest instance. With the CookiesForCustomPolicy object, you can call the appropriate methods to return the policy, signature, and key-pair ID cookie header values, which you can then send to the viewer to grant access to your private content.

import software.amazon.awssdk.services.cloudfront.cookie.CookiesForCustomPolicy;
import software.amazon.awssdk.services.cloudfront.model.CustomSignerRequest;


CookiesForCustomPolicy cookies = cloudFrontUtilities.getCookiesForCustomPolicy(customRequest);
// Generates Set-Cookie header values to send to the viewer to allow access
String signatureHeaderValue = cookies.signatureHeaderValue();
String keyPairIdHeaderValue = cookies.keyPairIdHeaderValue();
String policyHeaderValue = cookies.policyHeaderValue();

Cleanup

The CloudFront signed URLs/cookies you generated will be valid until the specified expiration dates, after which users will not be able to access your private content. If you wish to revoke access before the expiration date, you just need to remove the trusted signer from the CloudFront distribution.

To completely tear-down and clean up all your resources, you must first disable your CloudFront distribution. Then, you can delete your distribution, key group, and public key in CloudFront. Finally, you can empty and delete your S3 bucket.

Conclusion

In this blog post we went over the basic functionalities of CloudFront signed URLs and signed cookies. We also showed code examples of a signed URL with a canned policy and a signed cookie with a custom policy. To learn more about how to set up and begin using the feature, visit our Developer Guide. If you are curious about how it is implemented, check out the source code on GitHub. As always, we welcome bug reports, feature requests, and pull requests on the aws-sdk-java-v2 GitHub repository.