From d96580d07bba58c3cff73f5f5eb1737a0388097f Mon Sep 17 00:00:00 2001 From: Yesitha Sathsara <60166952+yesitha@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:52:13 +0530 Subject: [PATCH] Signed Url Function Updated --- .../resources/application-docker.properties | 2 +- .../resources/application-kube.properties | 2 +- .../src/main/resources/application.properties | 2 +- .../itgura/util/ResourceManagementURI.java | 2 +- .../resources/application-docker.properties | 2 +- .../resources/application-kube.properties | 2 +- .../src/main/resources/application.properties | 2 +- .../resources/application-docker.properties | 2 +- .../resources/application-kube.properties | 2 +- .../src/main/resources/application.properties | 2 +- .../main/resources/application-dev.properties | 3 +- .../itgura/controller/MaterialController.java | 2 +- .../resources/application-docker.properties | 2 +- .../resources/application-kube.properties | 2 +- .../main/resources/application-dev.properties | 2 +- .../resource-management-service/pom.xml | 11 ++ .../service/impl/MaterialServiceImpl.java | 132 ++++++++++++++---- 17 files changed, 135 insertions(+), 39 deletions(-) diff --git a/auth-service/src/main/resources/application-docker.properties b/auth-service/src/main/resources/application-docker.properties index fcfcb84..a856893 100644 --- a/auth-service/src/main/resources/application-docker.properties +++ b/auth-service/src/main/resources/application-docker.properties @@ -22,4 +22,4 @@ eureka.client.fetch-registry=true eureka.instance.hostname=eureka-server eureka.client.serviceUrl.defaultZone=http://eureka-server:8761/eureka -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 \ No newline at end of file +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d \ No newline at end of file diff --git a/auth-service/src/main/resources/application-kube.properties b/auth-service/src/main/resources/application-kube.properties index a45455c..02775e6 100644 --- a/auth-service/src/main/resources/application-kube.properties +++ b/auth-service/src/main/resources/application-kube.properties @@ -27,4 +27,4 @@ eureka.client.fetch-registry=true eureka.instance.hostname=eureka-server eureka.client.serviceUrl.defaultZone=http://eureka-server:8761/eureka -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 \ No newline at end of file +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d \ No newline at end of file diff --git a/auth-service/src/main/resources/application.properties b/auth-service/src/main/resources/application.properties index f02958b..9c1ea17 100644 --- a/auth-service/src/main/resources/application.properties +++ b/auth-service/src/main/resources/application.properties @@ -24,4 +24,4 @@ spring.jpa.properties.hibernate.format_sql=true -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 \ No newline at end of file +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d \ No newline at end of file diff --git a/lib-global/src/main/java/com/itgura/util/ResourceManagementURI.java b/lib-global/src/main/java/com/itgura/util/ResourceManagementURI.java index d40420f..8259c86 100644 --- a/lib-global/src/main/java/com/itgura/util/ResourceManagementURI.java +++ b/lib-global/src/main/java/com/itgura/util/ResourceManagementURI.java @@ -10,7 +10,7 @@ public class ResourceManagementURI { public static final String test = "/test"; public static final String INMONTHANDCLASS = "/in-month-and-class"; public static final String MATERIAL_TYPE = "/material-type"; - public static final String MATERIAL = "/material-type"; + public static final String MATERIAL = "/material"; public static final String SESSION_ID = "/{sessionId}"; public static final String MATERIAL_ID = "/{materialId}"; public static final String CLASS_ID = "/{classId}"; diff --git a/lms-gateway/src/main/resources/application-docker.properties b/lms-gateway/src/main/resources/application-docker.properties index c08d3d5..6cacf1b 100644 --- a/lms-gateway/src/main/resources/application-docker.properties +++ b/lms-gateway/src/main/resources/application-docker.properties @@ -18,7 +18,7 @@ server.servlet.context-path=/ #springdoc.swagger-ui.urls[1].url=/dms-mediator/v3/api-docs #springdoc.swagger-ui.urls[1].display-name=dms-mediator -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d # eureka eureka.client.register-with-eureka=true diff --git a/lms-gateway/src/main/resources/application-kube.properties b/lms-gateway/src/main/resources/application-kube.properties index e59386a..fd6aa7b 100644 --- a/lms-gateway/src/main/resources/application-kube.properties +++ b/lms-gateway/src/main/resources/application-kube.properties @@ -18,7 +18,7 @@ server.servlet.context-path=/ #springdoc.swagger-ui.urls[1].url=/dms-mediator/v3/api-docs #springdoc.swagger-ui.urls[1].display-name=dms-mediator -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d #linkered spring.cloud.gateway.discovery.locator.enabled=true diff --git a/lms-gateway/src/main/resources/application.properties b/lms-gateway/src/main/resources/application.properties index 5de35e4..4a152b8 100644 --- a/lms-gateway/src/main/resources/application.properties +++ b/lms-gateway/src/main/resources/application.properties @@ -20,7 +20,7 @@ logging.level.org.springframework.cloud.gateway.handler.RoutePredicateHandlerMap #springdoc.swagger-ui.urls[1].url=/dms-mediator/v3/api-docs #springdoc.swagger-ui.urls[1].display-name=dms-mediator -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d # eureka eureka.client.register-with-eureka=true diff --git a/payment-service/src/main/resources/application-docker.properties b/payment-service/src/main/resources/application-docker.properties index 3d3908e..f811e53 100644 --- a/payment-service/src/main/resources/application-docker.properties +++ b/payment-service/src/main/resources/application-docker.properties @@ -20,7 +20,7 @@ eureka.client.fetch-registry=true eureka.instance.hostname=eureka-server eureka.client.serviceUrl.defaultZone=http://eureka-server:8761/eureka -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d # payhere payhere.merchentSecretCode=YOUR_MERCHANT_SECRET_KEY diff --git a/payment-service/src/main/resources/application-kube.properties b/payment-service/src/main/resources/application-kube.properties index 827f97e..c2d5b3f 100644 --- a/payment-service/src/main/resources/application-kube.properties +++ b/payment-service/src/main/resources/application-kube.properties @@ -24,7 +24,7 @@ eureka.client.fetch-registry=true eureka.instance.hostname=eureka-server eureka.client.serviceUrl.defaultZone=http://eureka-server:8761/eureka -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d # payhere payhere.merchentSecretCode=YOUR_MERCHANT_SECRET_KEY diff --git a/payment-service/src/main/resources/application.properties b/payment-service/src/main/resources/application.properties index 93810f7..c56f085 100644 --- a/payment-service/src/main/resources/application.properties +++ b/payment-service/src/main/resources/application.properties @@ -20,7 +20,7 @@ eureka.client.fetch-registry=true eureka.instance.hostname=localhost eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d # payhere payhere.merchentSecretCode=YOUR_MERCHANT_SECRET_KEY diff --git a/quiz-management/quiz-management-dao/src/main/resources/application-dev.properties b/quiz-management/quiz-management-dao/src/main/resources/application-dev.properties index cd35b15..832bed3 100644 --- a/quiz-management/quiz-management-dao/src/main/resources/application-dev.properties +++ b/quiz-management/quiz-management-dao/src/main/resources/application-dev.properties @@ -31,4 +31,5 @@ eureka.client.registerWithEureka=true eureka.client.fetchRegistry=true eureka.instance.hostname=localhost -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 \ No newline at end of file +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d + \ No newline at end of file diff --git a/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java b/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java index 7bb7dde..e3a4b18 100644 --- a/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java +++ b/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java @@ -50,7 +50,7 @@ public AppResponse deleteMaterial(@PathVariable UUID materialId) { } } - @GetMapping(ResourceManagementURI.MATERIAL + URIPrefix.GET_VIDEO_Signed_Url) + @PostMapping (ResourceManagementURI.MATERIAL + URIPrefix.GET_VIDEO_Signed_Url) public AppResponse getVideoMaterialSignedUrl(@RequestBody AppRequest request) { try { String s = materialService.getVideoMaterialSignedUrl(request.getData()); diff --git a/resource-management/resource-management-all/src/main/resources/application-docker.properties b/resource-management/resource-management-all/src/main/resources/application-docker.properties index fef4fb5..22d564a 100644 --- a/resource-management/resource-management-all/src/main/resources/application-docker.properties +++ b/resource-management/resource-management-all/src/main/resources/application-docker.properties @@ -37,4 +37,4 @@ eureka.client.registerWithEureka=true eureka.client.fetchRegistry=true eureka.instance.hostname=eureka-server -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 \ No newline at end of file +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d \ No newline at end of file diff --git a/resource-management/resource-management-all/src/main/resources/application-kube.properties b/resource-management/resource-management-all/src/main/resources/application-kube.properties index c4efa01..71b1f2c 100644 --- a/resource-management/resource-management-all/src/main/resources/application-kube.properties +++ b/resource-management/resource-management-all/src/main/resources/application-kube.properties @@ -42,4 +42,4 @@ eureka.client.registerWithEureka=true eureka.client.fetchRegistry=true eureka.instance.hostname=eureka-server -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 \ No newline at end of file +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d \ No newline at end of file diff --git a/resource-management/resource-management-dao/src/main/resources/application-dev.properties b/resource-management/resource-management-dao/src/main/resources/application-dev.properties index 1b0b737..cd88ec6 100644 --- a/resource-management/resource-management-dao/src/main/resources/application-dev.properties +++ b/resource-management/resource-management-dao/src/main/resources/application-dev.properties @@ -31,4 +31,4 @@ eureka.client.registerWithEureka=true eureka.client.fetchRegistry=true eureka.instance.hostname=localhost -jwt.secretKey = f2b21eeadc7f3693dbc373dca5f49400293d722eb955353c11250b9367cd1635 \ No newline at end of file +jwt.secretKey = 0dbe57fd2165cd78d6da1d118028a1fd3b60cb06e8d331f42f59d75be65a857d \ No newline at end of file diff --git a/resource-management/resource-management-service/pom.xml b/resource-management/resource-management-service/pom.xml index baf1330..ebfb0e6 100644 --- a/resource-management/resource-management-service/pom.xml +++ b/resource-management/resource-management-service/pom.xml @@ -18,6 +18,17 @@ 1.0.0 compile + + + org.bouncycastle + bcprov-jdk18on + 1.78.1 + + + software.amazon.awssdk + cloudfront + 2.29.15 + diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java index 5e70df9..bbd1f8e 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java @@ -12,29 +12,33 @@ import com.itgura.request.MaterialRequest; import com.itgura.request.SignedUrlRequest; import com.itgura.request.dto.UserResponseDto; -import com.itgura.response.dto.MaterialResponseDto; -import com.itgura.response.dto.SessionResponseDto; -import com.itgura.response.dto.mapper.MaterialMapper; -import com.itgura.response.dto.mapper.SessionMapper; import com.itgura.service.MaterialService; import com.itgura.service.UserDetailService; import com.itgura.util.UserUtil; import jakarta.transaction.Transactional; import jakarta.ws.rs.ForbiddenException; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; -import org.springframework.util.ResourceUtils; +import software.amazon.awssdk.services.cloudfront.CloudFrontUtilities; +import software.amazon.awssdk.services.cloudfront.internal.utils.SigningUtils; +import software.amazon.awssdk.services.cloudfront.model.CannedSignerRequest; +import software.amazon.awssdk.services.cloudfront.model.CustomSignerRequest; +import software.amazon.awssdk.services.cloudfront.url.SignedUrl; import javax.security.auth.login.CredentialNotFoundException; -import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; +import java.security.KeyFactory; import java.security.PrivateKey; +import java.security.Security; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; @@ -157,34 +161,76 @@ public String deleteMaterial(UUID materialId) throws ApplicationException, Crede @Override public String getVideoMaterialSignedUrl(SignedUrlRequest signedUrlRequest) throws Exception { - long expirationTime = Instant.now().getEpochSecond() + (signedUrlRequest.getExpiresInHours() * 3600L); + CloudFrontUtilities cloudFrontUtilities = CloudFrontUtilities.create(); + Instant expirationDate = Instant.ofEpochSecond(Instant.now().getEpochSecond() + (signedUrlRequest.getExpiresInHours() * 3600L)); - // Define policy with IP restriction and expiration time - String policy = String.format( - "{\"Statement\":[{\"Resource\":\"%s/%s\",\"Condition\":{\"DateLessThan\":{\"AWS:EpochTime\":%d},\"IpAddress\":{\"AWS:SourceIp\":\"%s\"}}}]}", - cloudFrontDomain, signedUrlRequest.getFilePath(), expirationTime, signedUrlRequest.getUserIpAddress()); - // Base64 encode the policy - String base64EncodedPolicy = Base64.getEncoder().encodeToString(policy.getBytes()); - // Sign the policy with the private key - String signature = signPolicyWithPrivateKey(base64EncodedPolicy); + CustomSignerRequest customSignerRequest = CustomSignerRequest.builder() + .resourceUrl(cloudFrontDomain + "/" + signedUrlRequest.getFilePath()) + .expirationDate(expirationDate) + .ipRange(signedUrlRequest.getUserIpAddress()) + .keyPairId(keyPairId) + .privateKey(new ClassPathResource(privateKeyPath).getFile().toPath()) + .build(); + SignedUrl signedUrl = cloudFrontUtilities.getSignedUrlWithCustomPolicy(customSignerRequest); + return signedUrl.url(); - // Construct the signed URL - return String.format("%s/%s?Policy=%s&Signature=%s&Key-Pair-Id=%s", - cloudFrontDomain, signedUrlRequest.getFilePath(), - urlEncode(base64EncodedPolicy), - urlEncode(signature), - keyPairId); + +// long expirationTime = Instant.now().getEpochSecond() + (signedUrlRequest.getExpiresInHours() * 3600L); +// +// // Define policy with IP restriction and expiration time +// String policy = String.format( +// "{\"Statement\":[{\"Resource\":\"%s/%s\",\"Condition\":{\"DateLessThan\":{\"AWS:EpochTime\":%d},\"IpAddress\":{\"AWS:SourceIp\":\"%s\"}}}]}", +// cloudFrontDomain, signedUrlRequest.getFilePath(), expirationTime, signedUrlRequest.getUserIpAddress()); +// +//// String policy = String.format( +//// "{\"Statement\":[{\"Resource\":\"%s/%s\"}]}", +//// cloudFrontDomain, signedUrlRequest.getFilePath()); +// System.out.println("policy: "+policy); +// // Base64 encode the policy +// String base64EncodedPolicy = Base64.getEncoder().encodeToString(policy.getBytes()); +// +// String decodedPolicy = new String(Base64.getDecoder().decode(base64EncodedPolicy)); +// System.out.println("Decoded Policy: " + decodedPolicy); +// +// // Sign the policy with the private key +// String signature = signPolicyWithPrivateKey(base64EncodedPolicy); +// System.out.println("Signature: "+signature); +// +// +// // Construct the signed URL +// return String.format("%s/%s?Policy=%s&Signature=%s&Key-Pair-Id=%s", +// cloudFrontDomain, signedUrlRequest.getFilePath(), +// urlEncode(base64EncodedPolicy), +// urlEncode(signature), +// keyPairId); } private String signPolicyWithPrivateKey(String base64EncodedPolicy) throws Exception { - byte[] privateKeyBytes = Files.readAllBytes(Paths.get(privateKeyPath)); + // Add the Bouncy Castle provider + Security.addProvider(new BouncyCastleProvider()); + + ClassPathResource resource = new ClassPathResource(privateKeyPath); + byte[] privateKeyBytes = Files.readAllBytes(resource.getFile().toPath()); + +// // Convert to string and strip PEM markers +// String privateKeyContent = new String(privateKeyBytes); +// privateKeyContent = privateKeyContent +// .replace("-----BEGIN PRIVATE KEY-----", "") +// .replace("-----END PRIVATE KEY-----", "") +// .replaceAll("\\s+", ""); // Remove all whitespace +// +// // Decode Base64 content +// byte[] decodedKey = Base64.getDecoder().decode(privateKeyContent); + + + // Generate the private key PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); - PrivateKey privateKey = java.security.KeyFactory.getInstance("RSA").generatePrivate(keySpec); + PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec); - Signature signature = Signature.getInstance("SHA1withRSA"); + Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(base64EncodedPolicy.getBytes()); byte[] signedBytes = signature.sign(); @@ -194,6 +240,7 @@ private String signPolicyWithPrivateKey(String base64EncodedPolicy) throws Excep private String urlEncode(String value) { + return value.replace("+", "-").replace("=", "_").replace("/", "~"); } @@ -227,4 +274,41 @@ private String urlEncode(String value) { // // } // } + + + + public String generateCustomSignedUrl(String resourcePath, Instant activeDate, Instant expirationDate, String ipAddress) throws Exception { + // Load private key + PrivateKey privateKey = loadPrivateKey(); + + // Build custom policy + String resourceUrl = cloudFrontDomain + "/" + resourcePath; + String customPolicy = SigningUtils.buildCustomPolicyForSignedUrl(resourceUrl, activeDate, expirationDate, ipAddress); + System.out.println("Custom Policy: " + customPolicy); + + // Sign the policy + byte[] signatureBytes = SigningUtils.signWithSha1Rsa(customPolicy.getBytes(), privateKey); + String urlSafeSignature = SigningUtils.makeBytesUrlSafe(signatureBytes); + + // Base64 encode the policy + String base64EncodedPolicy = Base64.getEncoder().encodeToString(customPolicy.getBytes()); + + // Construct the signed URL + return String.format("%s/%s?Policy=%s&Signature=%s&Key-Pair-Id=%s", + cloudFrontDomain, + resourcePath, + urlSafeEncode(base64EncodedPolicy), + urlSafeSignature, + keyPairId); + } + + private PrivateKey loadPrivateKey() throws Exception { + byte[] privateKeyBytes = Files.readAllBytes(Paths.get(privateKeyPath)); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + return KeyFactory.getInstance("RSA").generatePrivate(keySpec); + } + + private String urlSafeEncode(String value) { + return value.replace("+", "-").replace("=", "_").replace("/", "~"); + } }