From 49b8fe39a5ac5695c45567c4d05856193f0bbb66 Mon Sep 17 00:00:00 2001 From: Braydon Date: Sat, 2 Dec 2023 03:26:57 -0500 Subject: [PATCH] Add cryptography --- ...LicenseExample.java => LicenseClient.java} | 148 +++++++++++++++--- .../main/java/me/braydon/example/Main.java | 10 +- .../me/braydon/license/LicenseServer.java | 5 + .../license/common/CryptographyUtils.java | 81 ++++++++++ .../me/braydon/license/common/IPUtils.java | 5 + .../me/braydon/license/common/MiscUtils.java | 5 + .../braydon/license/common/RandomUtils.java | 5 + .../me/braydon/license/common/TimeUtils.java | 5 + .../controller/CryptographyController.java | 59 +++++++ .../license/controller/LicenseController.java | 46 ++++-- .../license/dto/LicenseCheckBodyDTO.java | 5 + .../me/braydon/license/dto/LicenseDTO.java | 5 + .../license/exception/APIException.java | 5 + .../exception/LicenseExpiredException.java | 5 + .../LicenseHwidLimitExceededException.java | 5 + .../LicenseIpLimitExceededException.java | 5 + .../exception/LicenseNotFoundException.java | 5 + .../me/braydon/license/model/License.java | 5 + .../license/repository/LicenseRepository.java | 5 + .../license/service/CryptographyService.java | 63 ++++++++ .../license/service/DiscordService.java | 12 +- .../license/service/LicenseService.java | 5 + 22 files changed, 457 insertions(+), 37 deletions(-) rename Example/src/main/java/me/braydon/example/{LicenseExample.java => LicenseClient.java} (61%) create mode 100644 src/main/java/me/braydon/license/common/CryptographyUtils.java create mode 100644 src/main/java/me/braydon/license/controller/CryptographyController.java create mode 100644 src/main/java/me/braydon/license/service/CryptographyService.java diff --git a/Example/src/main/java/me/braydon/example/LicenseExample.java b/Example/src/main/java/me/braydon/example/LicenseClient.java similarity index 61% rename from Example/src/main/java/me/braydon/example/LicenseExample.java rename to Example/src/main/java/me/braydon/example/LicenseClient.java index 580c0b2..6d120e8 100644 --- a/Example/src/main/java/me/braydon/example/LicenseExample.java +++ b/Example/src/main/java/me/braydon/example/LicenseClient.java @@ -1,13 +1,15 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.example; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; +import lombok.*; import okhttp3.*; import oshi.SystemInfo; import oshi.hardware.CentralProcessor; @@ -15,9 +17,18 @@ import oshi.hardware.ComputerSystem; import oshi.hardware.HardwareAbstractionLayer; import oshi.software.os.OperatingSystem; +import javax.crypto.Cipher; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; +import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -32,51 +43,93 @@ import java.util.Map; * @author Braydon * @see License Server */ -public final class LicenseExample { +public final class LicenseClient { + private static final String ALGORITHM = "RSA"; // The crypto algorithm to use + + /** + * The endpoint to use for downloading the {@link PublicKey}. + */ + private static final String PUBLIC_KEY_ENDPOINT = "/crypto/pub"; + /** * The endpoint to check licenses at. */ - private static final String CHECK_ENDPOINT = "http://localhost:7500/check"; + private static final String CHECK_ENDPOINT = "/check"; /** * The {@link Gson} instance to use. */ - private static final Gson GSON = new GsonBuilder() - .serializeNulls() - .create(); + private static final Gson GSON = new GsonBuilder().serializeNulls().create(); + + /** + * The URL of the license server to make requests to. + */ + @NonNull private final String appUrl; + + /** + * The product to use for client. + */ + @NonNull private final String product; + + /** + * The {@link OkHttpClient} to use for requests. + */ + @NonNull private final OkHttpClient httpClient; + + /** + * The {@link PublicKey} to use for encryption. + */ + @NonNull private final PublicKey publicKey; + + public LicenseClient(@NonNull String appUrl, @NonNull String product, @NonNull File publicKeyFile) { + this.appUrl = appUrl; + this.product = product; + httpClient = new OkHttpClient(); // Create a new http client + publicKey = fetchPublicKey(publicKeyFile); // Fetch our public key + } + + /** + * Read the public key from the given bytes. + * + * @param bytes the bytes of the public key + * @return the public key + * @see PrivateKey for public key + */ + @SneakyThrows + private static PublicKey readPublicKey(byte[] bytes) { + return KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(bytes)); + } /** * Check the license with the given * key for the given product. * * @param key the key to check - * @param product the product the key belongs to * @return the license response * @see LicenseResponse for response */ @NonNull - public static LicenseResponse check(@NonNull String key, @NonNull String product) { + public LicenseResponse check(@NonNull String key) { String hardwareId = getHardwareId(); // Get the hardware id of the machine // Build the json body Map body = new HashMap<>(); - body.put("key", key); + body.put("key", encrypt(key)); body.put("product", product); - body.put("hwid", hardwareId); + body.put("hwid", encrypt(hardwareId)); String bodyJson = GSON.toJson(body); // The json body - OkHttpClient client = new OkHttpClient(); // Create a new http client MediaType mediaType = MediaType.parse("application/json"); // Ensure the media type is json - RequestBody requestBody = RequestBody.create(bodyJson, mediaType); // Build the request body + RequestBody requestBody = RequestBody.create(mediaType, bodyJson); // Build the request body Request request = new Request.Builder() - .url(CHECK_ENDPOINT) + .url(appUrl + CHECK_ENDPOINT) .post(requestBody) .build(); // Build the POST request Response response = null; // The response of the request int responseCode = -1; // The response code of the request try { // Attempt to execute the request - response = client.newCall(request).execute(); + response = httpClient.newCall(request).execute(); responseCode = response.code(); // If the response is successful, we can parse the response @@ -127,6 +180,43 @@ public final class LicenseExample { return new LicenseResponse(responseCode, "An unknown error occurred"); } + /** + * Fetch the public key. + *

+ * If the public key is not already present, we + * fetch it from the server. Otherwise, the public + * key is loaded from the file. + *

+ * + * @param publicKeyFile the public key file + * @return the public key + * @see PublicKey for public key + */ + @SneakyThrows + private PublicKey fetchPublicKey(@NonNull File publicKeyFile) { + byte[] publicKey; + if (publicKeyFile.exists()) { // Public key exists, use it + publicKey = Files.readAllBytes(publicKeyFile.toPath()); + } else { + Request request = new Request.Builder() + .url(appUrl + PUBLIC_KEY_ENDPOINT) + .build(); // Build the GET request + @Cleanup Response response = httpClient.newCall(request).execute(); // Make the request + if (!response.isSuccessful()) { // Response wasn't successful + throw new IOException("Failed to download the public key, got response " + response.code()); + } + ResponseBody body = response.body(); // Get the response body + assert body != null; // We need a response body + publicKey = body.bytes(); // Read our public key + + // Write the response to the public key file + try (FileOutputStream outputStream = new FileOutputStream(publicKeyFile)) { + outputStream.write(publicKey); + } + } + return readPublicKey(publicKey); + } + /** * Get the unique hardware * identifier of this machine. @@ -134,7 +224,7 @@ public final class LicenseExample { * @return the hardware id */ @NonNull - private static String getHardwareId() { + private String getHardwareId() { SystemInfo systemInfo = new SystemInfo(); OperatingSystem operatingSystem = systemInfo.getOperatingSystem(); HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware(); @@ -155,9 +245,25 @@ public final class LicenseExample { + String.format("%08x", processorIdentifier.hashCode()) + "-" + processors; } - @AllArgsConstructor - @Getter - @ToString + /** + * Encrypt the given input. + * + * @param input the encrypted input + * @return the encrypted result + */ + @SneakyThrows @NonNull + private String encrypt(@NonNull String input) { + Cipher cipher = Cipher.getInstance(ALGORITHM); // Create our cipher + cipher.init(Cipher.ENCRYPT_MODE, publicKey); // Set our mode and public key + return Base64.getEncoder().encodeToString(cipher.doFinal(input.getBytes())); // Return our encrypted result + } + + /** + * The response of a license check. + * + * @see #check(String) + */ + @AllArgsConstructor @Getter @ToString public static class LicenseResponse { /** * The status code of the response. diff --git a/Example/src/main/java/me/braydon/example/Main.java b/Example/src/main/java/me/braydon/example/Main.java index 2454f11..bcf52b4 100644 --- a/Example/src/main/java/me/braydon/example/Main.java +++ b/Example/src/main/java/me/braydon/example/Main.java @@ -1,11 +1,19 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.example; +import java.io.File; + /** * @author Braydon */ public final class Main { public static void main(String[] args) { - LicenseExample.LicenseResponse response = LicenseExample.check("XXXX-XXXX-XXXX-XXXX", "Example"); + LicenseClient client = new LicenseClient("http://localhost:7500", "Example", new File("public.key")); // Create the client + LicenseClient.LicenseResponse response = client.check("XXXX-XXXX-XXXX-XXXX"); // Check our license if (!response.isValid()) { // License isn't valid System.err.println("Invalid license: " + response.getError()); return; diff --git a/src/main/java/me/braydon/license/LicenseServer.java b/src/main/java/me/braydon/license/LicenseServer.java index de974cb..81025f2 100644 --- a/src/main/java/me/braydon/license/LicenseServer.java +++ b/src/main/java/me/braydon/license/LicenseServer.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license; import com.google.gson.Gson; diff --git a/src/main/java/me/braydon/license/common/CryptographyUtils.java b/src/main/java/me/braydon/license/common/CryptographyUtils.java new file mode 100644 index 0000000..bd1cd61 --- /dev/null +++ b/src/main/java/me/braydon/license/common/CryptographyUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ +package me.braydon.license.common; + +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + +import javax.crypto.Cipher; +import java.io.File; +import java.nio.file.Files; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +/** + * @author Braydon + */ +@UtilityClass +public final class CryptographyUtils { + private static final String ALGORITHM = "RSA"; // The algorithm to use + + /** + * Generate a new key pair. + * + * @return the key pair + * @see KeyPair for key pair + */ + @NonNull @SneakyThrows + public static KeyPair generateKeyPair() { + KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM); // Create a generator + generator.initialize(2048); // Set the key size + return generator.generateKeyPair(); // Return our generated key pair + } + + /** + * Read the public key from the given file. + * + * @param keyFile the key file to read + * @return the public key + * @see PrivateKey for public key + */ + @SneakyThrows + public static PublicKey readPublicKey(@NonNull File keyFile) { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Files.readAllBytes(keyFile.toPath())); // Get the key spec + return KeyFactory.getInstance(ALGORITHM).generatePublic(keySpec); // Return the public key from the key spec + } + + /** + * Read the private key from the given file. + * + * @param keyFile the key file to read + * @return the private key + * @see PrivateKey for private key + */ + @SneakyThrows + public static PrivateKey readPrivateKey(@NonNull File keyFile) { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Files.readAllBytes(keyFile.toPath())); // Get the key spec + return KeyFactory.getInstance(ALGORITHM).generatePrivate(keySpec); // Return the private key from the key spec + } + + /** + * Decrypt the given input with + * the provided private key. + * + * @param input the encrypted input + * @param privateKey the private key + * @return the decrypted result + * @see PrivateKey for private key + */ + @SneakyThrows @NonNull + public static String decryptMessage(@NonNull String input, @NonNull PrivateKey privateKey) { + Cipher cipher = Cipher.getInstance(ALGORITHM); // Create the cipher + cipher.init(Cipher.DECRYPT_MODE, privateKey); // Set our mode and private key + return new String(cipher.doFinal(Base64.getDecoder().decode(input))); // Return our decrypted result + } +} \ No newline at end of file diff --git a/src/main/java/me/braydon/license/common/IPUtils.java b/src/main/java/me/braydon/license/common/IPUtils.java index e8990b6..610d7f7 100644 --- a/src/main/java/me/braydon/license/common/IPUtils.java +++ b/src/main/java/me/braydon/license/common/IPUtils.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.common; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/me/braydon/license/common/MiscUtils.java b/src/main/java/me/braydon/license/common/MiscUtils.java index 26e6569..bfd71b5 100644 --- a/src/main/java/me/braydon/license/common/MiscUtils.java +++ b/src/main/java/me/braydon/license/common/MiscUtils.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.common; import lombok.NonNull; diff --git a/src/main/java/me/braydon/license/common/RandomUtils.java b/src/main/java/me/braydon/license/common/RandomUtils.java index 9fca53a..807d9b6 100644 --- a/src/main/java/me/braydon/license/common/RandomUtils.java +++ b/src/main/java/me/braydon/license/common/RandomUtils.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.common; import lombok.NonNull; diff --git a/src/main/java/me/braydon/license/common/TimeUtils.java b/src/main/java/me/braydon/license/common/TimeUtils.java index 4908469..684bb00 100644 --- a/src/main/java/me/braydon/license/common/TimeUtils.java +++ b/src/main/java/me/braydon/license/common/TimeUtils.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.common; import lombok.NonNull; diff --git a/src/main/java/me/braydon/license/controller/CryptographyController.java b/src/main/java/me/braydon/license/controller/CryptographyController.java new file mode 100644 index 0000000..092a91e --- /dev/null +++ b/src/main/java/me/braydon/license/controller/CryptographyController.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ +package me.braydon.license.controller; + +import lombok.NonNull; +import me.braydon.license.model.License; +import me.braydon.license.service.CryptographyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.security.PublicKey; + +/** + * @author Braydon + */ +@RestController +@RequestMapping(value = "/crypto", produces = MediaType.APPLICATION_JSON_VALUE) +public final class CryptographyController { + /** + * The {@link CryptographyService} to use. + */ + @NonNull private final CryptographyService service; + + @Autowired + public CryptographyController(@NonNull CryptographyService service) { + this.service = service; + } + + /** + * Downloads the public key file. + * + * @return the response entity + * @see PublicKey for public key + * @see License for license + * @see ResponseEntity for response entity + */ + @GetMapping("/pub") + @ResponseBody + public ResponseEntity publicKey() { + byte[] publicKey = service.getKeyPair().getPublic().getEncoded(); // Get the public key + String fileName = "public.key"; // The name of the file to download + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") + .contentLength(publicKey.length) + .body(new ByteArrayResource(publicKey)); + } +} diff --git a/src/main/java/me/braydon/license/controller/LicenseController.java b/src/main/java/me/braydon/license/controller/LicenseController.java index bdd0837..3066237 100644 --- a/src/main/java/me/braydon/license/controller/LicenseController.java +++ b/src/main/java/me/braydon/license/controller/LicenseController.java @@ -1,12 +1,19 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.controller; import jakarta.servlet.http.HttpServletRequest; import lombok.NonNull; +import me.braydon.license.common.CryptographyUtils; import me.braydon.license.common.IPUtils; import me.braydon.license.dto.LicenseCheckBodyDTO; import me.braydon.license.dto.LicenseDTO; import me.braydon.license.exception.APIException; import me.braydon.license.model.License; +import me.braydon.license.service.CryptographyService; import me.braydon.license.service.LicenseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -14,6 +21,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.security.PrivateKey; import java.util.Map; /** @@ -22,14 +30,20 @@ import java.util.Map; @RestController @RequestMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE) public final class LicenseController { + /** + * The {@link CryptographyService} to use. + */ + @NonNull private final CryptographyService cryptographyService; + /** * The {@link LicenseService} to use. */ - @NonNull private final LicenseService service; + @NonNull private final LicenseService licenseService; @Autowired - public LicenseController(@NonNull LicenseService service) { - this.service = service; + public LicenseController(@NonNull CryptographyService cryptographyService, @NonNull LicenseService licenseService) { + this.cryptographyService = cryptographyService; + this.licenseService = licenseService; } /** @@ -53,24 +67,34 @@ public final class LicenseController { if (IPUtils.getIpType(ip) == -1) { throw new APIException(HttpStatus.BAD_REQUEST, "Invalid IP address"); } - // Ensure the HWID is valid - String hwidString = body.getHwid(); + String key; + String hwid; + try { + PrivateKey privateKey = cryptographyService.getKeyPair().getPrivate(); // Get our private key + key = CryptographyUtils.decryptMessage(body.getKey(), privateKey); // Decrypt our license key + hwid = CryptographyUtils.decryptMessage(body.getHwid(), privateKey); // Decrypt our hwid + } catch (IllegalArgumentException ex) { + throw new APIException(HttpStatus.BAD_REQUEST, "Signature Error"); + } + + // Validating that the UUID is in the correct format boolean invalidHwid = true; - if (hwidString.contains("-")) { - int segments = hwidString.substring(0, hwidString.lastIndexOf("-")).split("-").length; + if (hwid.contains("-")) { + int segments = hwid.substring(0, hwid.lastIndexOf("-")).split("-").length; if (segments == 4) { invalidHwid = false; } } - if (invalidHwid) { + if (invalidHwid) { // Invalid HWID throw new APIException(HttpStatus.BAD_REQUEST, "Invalid HWID"); } + // Check the license - License license = service.check( - body.getKey(), + License license = licenseService.check( + key, body.getProduct(), ip, - hwidString + hwid ); // Return OK with the license DTO return ResponseEntity.ok(new LicenseDTO( diff --git a/src/main/java/me/braydon/license/dto/LicenseCheckBodyDTO.java b/src/main/java/me/braydon/license/dto/LicenseCheckBodyDTO.java index d5f8d90..40f0845 100644 --- a/src/main/java/me/braydon/license/dto/LicenseCheckBodyDTO.java +++ b/src/main/java/me/braydon/license/dto/LicenseCheckBodyDTO.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.dto; import lombok.AllArgsConstructor; diff --git a/src/main/java/me/braydon/license/dto/LicenseDTO.java b/src/main/java/me/braydon/license/dto/LicenseDTO.java index bc079f5..cf5014a 100644 --- a/src/main/java/me/braydon/license/dto/LicenseDTO.java +++ b/src/main/java/me/braydon/license/dto/LicenseDTO.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.dto; import lombok.AllArgsConstructor; diff --git a/src/main/java/me/braydon/license/exception/APIException.java b/src/main/java/me/braydon/license/exception/APIException.java index d35fb30..dc351f3 100644 --- a/src/main/java/me/braydon/license/exception/APIException.java +++ b/src/main/java/me/braydon/license/exception/APIException.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.exception; import lombok.Getter; diff --git a/src/main/java/me/braydon/license/exception/LicenseExpiredException.java b/src/main/java/me/braydon/license/exception/LicenseExpiredException.java index 03268bd..57297ff 100644 --- a/src/main/java/me/braydon/license/exception/LicenseExpiredException.java +++ b/src/main/java/me/braydon/license/exception/LicenseExpiredException.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.exception; import me.braydon.license.model.License; diff --git a/src/main/java/me/braydon/license/exception/LicenseHwidLimitExceededException.java b/src/main/java/me/braydon/license/exception/LicenseHwidLimitExceededException.java index b22df78..7e3a136 100644 --- a/src/main/java/me/braydon/license/exception/LicenseHwidLimitExceededException.java +++ b/src/main/java/me/braydon/license/exception/LicenseHwidLimitExceededException.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.exception; import me.braydon.license.model.License; diff --git a/src/main/java/me/braydon/license/exception/LicenseIpLimitExceededException.java b/src/main/java/me/braydon/license/exception/LicenseIpLimitExceededException.java index 0dddf54..246a8b5 100644 --- a/src/main/java/me/braydon/license/exception/LicenseIpLimitExceededException.java +++ b/src/main/java/me/braydon/license/exception/LicenseIpLimitExceededException.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.exception; import me.braydon.license.model.License; diff --git a/src/main/java/me/braydon/license/exception/LicenseNotFoundException.java b/src/main/java/me/braydon/license/exception/LicenseNotFoundException.java index c25d77f..33699d8 100644 --- a/src/main/java/me/braydon/license/exception/LicenseNotFoundException.java +++ b/src/main/java/me/braydon/license/exception/LicenseNotFoundException.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.exception; import me.braydon.license.model.License; diff --git a/src/main/java/me/braydon/license/model/License.java b/src/main/java/me/braydon/license/model/License.java index 0bf6d85..dda6ad8 100644 --- a/src/main/java/me/braydon/license/model/License.java +++ b/src/main/java/me/braydon/license/model/License.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.model; import lombok.Getter; diff --git a/src/main/java/me/braydon/license/repository/LicenseRepository.java b/src/main/java/me/braydon/license/repository/LicenseRepository.java index 40890fc..712057f 100644 --- a/src/main/java/me/braydon/license/repository/LicenseRepository.java +++ b/src/main/java/me/braydon/license/repository/LicenseRepository.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.repository; import lombok.NonNull; diff --git a/src/main/java/me/braydon/license/service/CryptographyService.java b/src/main/java/me/braydon/license/service/CryptographyService.java new file mode 100644 index 0000000..eb7b85f --- /dev/null +++ b/src/main/java/me/braydon/license/service/CryptographyService.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ +package me.braydon.license.service; + +import lombok.Getter; +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import me.braydon.license.common.CryptographyUtils; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.KeyPair; +import java.util.Base64; + +/** + * @author Braydon + */ +@Service +@Slf4j(topic = "Cryptography") +@Getter +public final class CryptographyService { + /** + * Our {@link KeyPair}. + */ + @NonNull private final KeyPair keyPair; + + @SneakyThrows + public CryptographyService() { + File publicKeyFile = new File("public.key"); // The private key + File privateKeyFile = new File("private.key"); // The private key + if (!publicKeyFile.exists() || !privateKeyFile.exists()) { // Missing private key, generate new key pair. + keyPair = CryptographyUtils.generateKeyPair(); // Generate new key pair + writeKey(keyPair.getPublic().getEncoded(), publicKeyFile); // Write our public key + writeKey(keyPair.getPrivate().getEncoded(), privateKeyFile); // Write our private key + log.info("New key pair has been generated"); + log.info(Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded())); + return; + } + // Load our private key from the file + keyPair = new KeyPair(CryptographyUtils.readPublicKey(publicKeyFile), CryptographyUtils.readPrivateKey(privateKeyFile)); + log.info("Loaded private key from file " + privateKeyFile.getPath()); + } + + /** + * Write the given contents to the provided file. + * + * @param contents the content bytes to write + * @param file the file to write to + */ + private void writeKey(byte[] contents, @NonNull File file) { + try (FileOutputStream outputStream = new FileOutputStream(file)) { + outputStream.write(contents); + } catch (IOException ex) { + ex.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/braydon/license/service/DiscordService.java b/src/main/java/me/braydon/license/service/DiscordService.java index b7f7f35..9c69e61 100644 --- a/src/main/java/me/braydon/license/service/DiscordService.java +++ b/src/main/java/me/braydon/license/service/DiscordService.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.service; import com.google.common.cache.Cache; @@ -33,7 +38,6 @@ import net.dv8tion.jda.api.requests.GatewayIntent; import org.mindrot.jbcrypt.BCrypt; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.info.BuildProperties; import org.springframework.stereotype.Service; import java.awt.*; @@ -59,7 +63,7 @@ public final class DiscordService { /** * The version of this Springboot application. */ - @NonNull private final String applicationVersion; + @NonNull private String applicationVersion = "n/a"; /** * The salt to use for hashing license keys. @@ -140,9 +144,9 @@ public final class DiscordService { .build(); @Autowired - public DiscordService(@NonNull LicenseRepository licenseRepository, @NonNull BuildProperties buildProperties) { + public DiscordService(@NonNull LicenseRepository licenseRepository/*, @NonNull BuildProperties buildProperties*/) { this.licenseRepository = licenseRepository; - this.applicationVersion = buildProperties.getVersion(); + // this.applicationVersion = buildProperties.getVersion(); } @PostConstruct @SneakyThrows diff --git a/src/main/java/me/braydon/license/service/LicenseService.java b/src/main/java/me/braydon/license/service/LicenseService.java index 45fb026..a11c5c1 100644 --- a/src/main/java/me/braydon/license/service/LicenseService.java +++ b/src/main/java/me/braydon/license/service/LicenseService.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.license.service; import jakarta.annotation.PostConstruct;