Skip to content

Commit

Permalink
document/add cloudfront signed url
Browse files Browse the repository at this point in the history
  • Loading branch information
shubhamv108 committed Jan 31, 2024
1 parent 0c5ad41 commit 391a8fb
Show file tree
Hide file tree
Showing 29 changed files with 684 additions and 139 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ dependencies {

'org.springframework.kafka:spring-kafka',

'code.shubham:crypto-utils:0.0.1',

'software.amazon.awssdk:s3:2.22.1',
'com.amazonaws:aws-java-sdk-cloudfront:1.12.646',
'org.bouncycastle:bcprov-jdk18on:1.77'
)
runtimeOnly (
'com.mysql:mysql-connector-j',
Expand Down
6 changes: 6 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
rootProject.name = 'template-service-java-springboot'

sourceControl {
gitRepository ("https://github.com/shubhamv108/crypto-utils.git") {
producesModule ("code.shubham:crypto-utils")
}
}


//include "shared", "api", "services:webservice"
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
package code.shubham;

import code.shubham.commons.annotations.SpringBootApp;
import code.shubham.commons.aws.CloudFrontUtils;
import code.shubham.commons.keystore.dao.entities.KeyStore;
import code.shubham.commons.keystore.dao.repositories.KeyRepository;
import code.shubham.encryption.keys.asymmetric.RSAUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

@Slf4j
@SpringBootApp
public class TemplateServiceJavaSpringBootApplication {
public class TemplateServiceJavaSpringBootApplication implements CommandLineRunner {

public static void main(String[] args) {
SpringApplication.run(TemplateServiceJavaSpringBootApplication.class, args);
}

@Autowired
private KeyRepository keyRepository;

@Override
public void run(String... args) throws Exception {
// final PrivateKey privateKey = CloudFrontUtils.readPrivateKeyFromFile(args[0]);
// final KeyStore entity =
// this.keyRepository.findByPurpose("DOCUMENTS-CDN-CLOUD_FRONT-PRIVATE_KEY");
// if (entity == null)
// this.keyRepository.save(
// KeyStore.builder()
// .key(privateKey.getEncoded())
// .purpose("DOCUMENTS-CDN-CLOUD_FRONT-PRIVATE_KEY")
// .build());
// System.out.println ("Key algorithm: " + privateKey.getAlgorithm());
}

}
83 changes: 83 additions & 0 deletions src/main/java/code/shubham/commons/aws/CloudFrontUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package code.shubham.commons.aws;

import com.amazonaws.services.cloudfront.CloudFrontUrlSigner;
import com.amazonaws.services.cloudfront.model.KeyPairIds;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;

@Slf4j
public class CloudFrontUtils {

public static String signedURL(final String distributionDomain, final String s3ObjectKey, final String keyPairId,
final PrivateKey privateKey, final Date dateLessThan) {
return CloudFrontUrlSigner.getSignedURLWithCannedPolicy("https://" + distributionDomain + "/" + s3ObjectKey,
keyPairId, privateKey, dateLessThan);
}

public static String keyPair(final String distributionDomain, final String s3ObjectKey, final String keyPairId,
final PrivateKey privateKey, final Date dateLessThan) {
return CloudFrontUrlSigner.getSignedURLWithCannedPolicy("https://" + distributionDomain + "/" + s3ObjectKey,
keyPairId, privateKey, dateLessThan);
}

public static PrivateKey readPrivateKeyFromFile(final String filePath)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Security.addProvider(new BouncyCastleProvider());
File file = new File(filePath);
FileInputStream fileStream = new FileInputStream(file);

PrivateKey var10;
try {
DataInputStream dataStream = new DataInputStream(fileStream);

try {
byte[] keyBytes = new byte[(int) file.length()];
dataStream.readFully(keyBytes);
String temp = new String(keyBytes, "UTF-8");
String header = temp.replace("-----BEGIN PRIVATE KEY-----", "");
header = header.replace("-----END PRIVATE KEY-----", "");
header.replace("\n", "");
byte[] decoded = java.util.Base64.getMimeDecoder().decode(header);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
var10 = kf.generatePrivate(spec);
}
catch (Throwable var13) {
try {
dataStream.close();
}
catch (Throwable var12) {
var13.addSuppressed(var12);
}
log.error("", var13);
throw var13;
}

dataStream.close();
}
catch (Throwable var14) {
try {
fileStream.close();
}
catch (Throwable var11) {
var14.addSuppressed(var11);
}
log.error("", var14);
throw var14;
}
return var10;
}

}
38 changes: 11 additions & 27 deletions src/main/java/code/shubham/commons/aws/S3Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.UploadPartPresignRequest;

import java.time.Duration;
import java.util.Map;
Expand All @@ -30,8 +31,7 @@ public static String createPresignedUrl(final String region, final String bucket
.build();

final PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(10)) // The URL expires in 10
// minutes.
.signatureDuration(Duration.ofMinutes(10))
.putObjectRequest(objectRequest)
.build();

Expand All @@ -44,15 +44,14 @@ public static String createPresignedUrl(final String region, final String bucket
}
}

/* Create a pre-signed URL to download an object in a subsequent GET request. */
public static String createPresignedGetUrl(final String region, final String bucketName, String keyName) {
public static String createPresignedGetUrl(final String region, final String bucketName, final String keyName,
long durationInMilliSeconds) {
try (final S3Presigner presigner = S3Presigner.builder().region(Region.of(region)).build()) {

GetObjectRequest objectRequest = GetObjectRequest.builder().bucket(bucketName).key(keyName).build();

GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(10)) // The URL will expire in 10
// minutes.
.signatureDuration(Duration.ofMillis(durationInMilliSeconds))
.getObjectRequest(objectRequest)
.build();

Expand All @@ -68,9 +67,12 @@ public static boolean doesObjectExist(final String region, final String bucket,
try {
final S3Client s3 = S3Client.builder().region(Region.of(region)).build();

final GetObjectAttributesResponse response = s3
.getObjectAttributes(GetObjectAttributesRequest.builder().bucket(bucket).key(key).build());
return !response.deleteMarker() && Optional.ofNullable(checksum)
final GetObjectAttributesResponse response = s3.getObjectAttributes(GetObjectAttributesRequest.builder()
.bucket(bucket)
.key(key)
.objectAttributes(ObjectAttributes.CHECKSUM, ObjectAttributes.OBJECT_SIZE)
.build());
return Optional.ofNullable(response.deleteMarker()).orElse(true) && Optional.ofNullable(checksum)
.map(sum -> sum.equals(response.checksum().checksumCRC32C()))
.orElse(true);
}
Expand All @@ -84,22 +86,4 @@ public static boolean doesObjectExist(final String region, final String bucket,
}
}

public static boolean getMetadata(final String region, final String bucket, final String key) {
try {
final S3Client s3 = S3Client.builder().region(Region.of(region)).build();

final GetObjectAttributesResponse response = s3
.getObjectAttributes(GetObjectAttributesRequest.builder().bucket(bucket).key(key).build());
}
catch (NoSuchKeyException exception) {
log.error("No such key!!!", exception);
return false;
}
catch (SdkException exception) {
log.error("Exception while invoking S3::getObjectAttributes", exception);
throw new InternalServerException("Something went wrong");
}
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package code.shubham.commons.enums;

public enum DownloadURLSource {

S3, CLOUD_FRONT

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import code.shubham.commons.Constants;
import code.shubham.commons.utils.MetricsLogger;
import code.shubham.commons.utils.StringUtils;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -48,7 +49,6 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
final String responseBody = new String(responseArray, responseCacheWrapperObject.getCharacterEncoding());
responseCacheWrapperObject.copyBodyToResponse();

final Long requestStartTimestamp = (Long) request.getAttribute(Constants.RequestKey.REQUEST_START_TIMESTAMP);
log.info("Response:{StatusCode: {}, Headers: {}, Body: {}}", response.getStatus(), responseHeaders,
responseBody);
this.metricsLogger.log(request);
Expand All @@ -64,6 +64,10 @@ private Map<String, Object> getHeaders(final Iterator<String> headerNames,
headers.put(headerName, headersValue.apply(headerName));
}
}
final String location = (String) headersValue.apply("Location");
if (StringUtils.isNotEmpty(location))
headers.put("Location", location);

return headers;
}

Expand Down
Loading

0 comments on commit 391a8fb

Please sign in to comment.