Selvakumar Ponnusamy
Selvakumar Ponnusamy

Reputation: 5563

Replacement for CloudFrontUrlSigner in AWS Java SDK2

I use Java 11. As AWS Java SDK2 supports for Java 11 I use SDK2. How to create cloud front url for s3 key. I'm able to get many examples for SDK 1.x version but not for SDK2. This is how the url is been generated in 1.x

CloudFrontUrlSigner.getSignedURLWithCannedPolicy(url, keyPairId, privateKey, expires)

Is there any alternative way or replacement in SDK 2.x version

Upvotes: 3

Views: 1489

Answers (2)

Jared Bates
Jared Bates

Reputation: 141

This is available now in AWS Java SDK v2. You can use CloudFrontUtilities and CannedSignerRequest.

CloudFrontUtilities cloudFrontUtilities = CloudFrontUtilities.create();
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();

Refer to https://aws.amazon.com/blogs/developer/amazon-cloudfront-signed-urls-and-cookies-are-now-supported-in-aws-sdk-for-java-2-x/ for more information.

Upvotes: 1

CaptRespect
CaptRespect

Reputation: 2055

I don't think it's been implemented yet. In the meantime, it was fairly easy to rip code from the old version to do the same thing.

This is from https://github.com/dashpradeep99/aws-sdk-java-code/blob/master/aws-java-sdk-cloudfront/src/main/java/com/amazonaws/services/cloudfront/util/SignerUtils.java

and

https://github.com/dashpradeep99/aws-sdk-java-code/blob/master/aws-java-sdk-cloudfront/src/main/java/com/amazonaws/services/cloudfront/CloudFrontUrlSigner.java

import software.amazon.awssdk.core.exception.SdkException;

import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Date;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

public class AwsUtils {

  private static final SecureRandom srand = new SecureRandom();

  /**
   * Generates a signed url that expires after given date.
   * @param resourceUrlOrPath The url.
   * @param keyPairId The keypair id used to sign.
   * @param privateKey The private key.
   * @param dateLessThan The expire date/time.
   * @return A valid cloudwatch url.
   * @throws SdkException If any errors occur during the signing process.
   */
  public static String getSignedUrlWithCannedPolicy(String resourceUrlOrPath,
                                                    String keyPairId,
                                                    PrivateKey privateKey,
                                                    Date dateLessThan) throws SdkException {
    try {
      String cannedPolicy = buildCannedPolicy(resourceUrlOrPath, dateLessThan);
      byte[] signatureBytes = signWithSha1Rsa(cannedPolicy.getBytes(StandardCharsets.UTF_8), privateKey);
      String urlSafeSignature = makeBytesUrlSafe(signatureBytes);
      return resourceUrlOrPath
          + (resourceUrlOrPath.indexOf('?') >= 0 ? "&" : "?")
          + "Expires=" + MILLISECONDS.toSeconds(dateLessThan.getTime())
          + "&Signature=" + urlSafeSignature
          + "&Key-Pair-Id=" + keyPairId;
    } catch (InvalidKeyException e) {
      throw SdkException.create("Couldn't sign url", e);
    }
  }

  /**
   * Returns a "canned" policy for the given parameters.
   * For more information, see <a href=
   * "http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls-overview.html"
   * >Overview of Signed URLs</a>.
   * @param resourceUrlOrPath The resource to grant access.
   * @param dateLessThan The expiration time.
   * @return the aws policy as a string.
   */
  public static String buildCannedPolicy(String resourceUrlOrPath,
                                         Date dateLessThan) {
    return "{\"Statement\":[{\"Resource\":\""
        + resourceUrlOrPath
        + "\",\"Condition\":{\"DateLessThan\":{\"AWS:EpochTime\":"
        + MILLISECONDS.toSeconds(dateLessThan.getTime())
        + "}}}]}";
  }

  /**
   * Signs the data given with the private key given, using the SHA1withRSA
   * algorithm provided by bouncy castle.
   * @param dataToSign The data to sign.
   * @param privateKey The private key.
   * @return A signature.
   * @throws InvalidKeyException if an invalid key was provided.
   */
  public static byte[] signWithSha1Rsa(byte[] dataToSign,
                                       PrivateKey privateKey) throws InvalidKeyException {
    Signature signature;
    try {
      signature = Signature.getInstance("SHA1withRSA");
      signature.initSign(privateKey, srand);
      signature.update(dataToSign);
      return signature.sign();
    } catch (NoSuchAlgorithmException | SignatureException e) {
      throw new IllegalStateException(e);
    }
  }

  /**
   * Converts the given data to be safe for use in signed URLs for a private
   * distribution by using specialized Base64 encoding.
   * @param bytes The bytes
   */
  public static String makeBytesUrlSafe(byte[] bytes) {
    byte[] encoded = java.util.Base64.getEncoder().encode(bytes);

    for (int i = 0; i < encoded.length; i++) {
      switch (encoded[i]) {
        case '+':
          encoded[i] = '-';
          continue;
        case '=':
          encoded[i] = '_';
          continue;
        case '/':
          encoded[i] = '~';
          continue;
        default:
          continue;
      }
    }
    return new String(encoded, StandardCharsets.UTF_8);
  }
}

Upvotes: 1

Related Questions