Add cryptography

This commit is contained in:
Braydon 2023-12-02 03:26:57 -05:00
parent 9215ac87b0
commit 49b8fe39a5
22 changed files with 457 additions and 37 deletions

View File

@ -1,13 +1,15 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.example; package me.braydon.example;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.AllArgsConstructor; import lombok.*;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import okhttp3.*; import okhttp3.*;
import oshi.SystemInfo; import oshi.SystemInfo;
import oshi.hardware.CentralProcessor; import oshi.hardware.CentralProcessor;
@ -15,9 +17,18 @@ import oshi.hardware.ComputerSystem;
import oshi.hardware.HardwareAbstractionLayer; import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem; import oshi.software.os.OperatingSystem;
import javax.crypto.Cipher;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; 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.OffsetDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -32,51 +43,93 @@ import java.util.Map;
* @author Braydon * @author Braydon
* @see <a href="https://git.rainnny.club/Rainnny/LicenseServer">License Server</a> * @see <a href="https://git.rainnny.club/Rainnny/LicenseServer">License Server</a>
*/ */
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. * 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. * The {@link Gson} instance to use.
*/ */
private static final Gson GSON = new GsonBuilder() private static final Gson GSON = new GsonBuilder().serializeNulls().create();
.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 * Check the license with the given
* key for the given product. * key for the given product.
* *
* @param key the key to check * @param key the key to check
* @param product the product the key belongs to
* @return the license response * @return the license response
* @see LicenseResponse for response * @see LicenseResponse for response
*/ */
@NonNull @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 String hardwareId = getHardwareId(); // Get the hardware id of the machine
// Build the json body // Build the json body
Map<String, Object> body = new HashMap<>(); Map<String, Object> body = new HashMap<>();
body.put("key", key); body.put("key", encrypt(key));
body.put("product", product); body.put("product", product);
body.put("hwid", hardwareId); body.put("hwid", encrypt(hardwareId));
String bodyJson = GSON.toJson(body); // The json body 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 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() Request request = new Request.Builder()
.url(CHECK_ENDPOINT) .url(appUrl + CHECK_ENDPOINT)
.post(requestBody) .post(requestBody)
.build(); // Build the POST request .build(); // Build the POST request
Response response = null; // The response of the request Response response = null; // The response of the request
int responseCode = -1; // The response code of the request int responseCode = -1; // The response code of the request
try { // Attempt to execute the request try { // Attempt to execute the request
response = client.newCall(request).execute(); response = httpClient.newCall(request).execute();
responseCode = response.code(); responseCode = response.code();
// If the response is successful, we can parse the response // 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"); return new LicenseResponse(responseCode, "An unknown error occurred");
} }
/**
* Fetch the public key.
* <p>
* If the public key is not already present, we
* fetch it from the server. Otherwise, the public
* key is loaded from the file.
* </p>
*
* @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 * Get the unique hardware
* identifier of this machine. * identifier of this machine.
@ -134,7 +224,7 @@ public final class LicenseExample {
* @return the hardware id * @return the hardware id
*/ */
@NonNull @NonNull
private static String getHardwareId() { private String getHardwareId() {
SystemInfo systemInfo = new SystemInfo(); SystemInfo systemInfo = new SystemInfo();
OperatingSystem operatingSystem = systemInfo.getOperatingSystem(); OperatingSystem operatingSystem = systemInfo.getOperatingSystem();
HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware(); HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
@ -155,9 +245,25 @@ public final class LicenseExample {
+ String.format("%08x", processorIdentifier.hashCode()) + "-" + processors; + String.format("%08x", processorIdentifier.hashCode()) + "-" + processors;
} }
@AllArgsConstructor /**
@Getter * Encrypt the given input.
@ToString *
* @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 { public static class LicenseResponse {
/** /**
* The status code of the response. * The status code of the response.

View File

@ -1,11 +1,19 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.example; package me.braydon.example;
import java.io.File;
/** /**
* @author Braydon * @author Braydon
*/ */
public final class Main { public final class Main {
public static void main(String[] args) { 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 if (!response.isValid()) { // License isn't valid
System.err.println("Invalid license: " + response.getError()); System.err.println("Invalid license: " + response.getError());
return; return;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license; package me.braydon.license;
import com.google.gson.Gson; import com.google.gson.Gson;

View File

@ -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
}
}

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.common; package me.braydon.license.common;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.common; package me.braydon.license.common;
import lombok.NonNull; import lombok.NonNull;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.common; package me.braydon.license.common;
import lombok.NonNull; import lombok.NonNull;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.common; package me.braydon.license.common;
import lombok.NonNull; import lombok.NonNull;

View File

@ -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<Resource> 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));
}
}

View File

@ -1,12 +1,19 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.controller; package me.braydon.license.controller;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.NonNull; import lombok.NonNull;
import me.braydon.license.common.CryptographyUtils;
import me.braydon.license.common.IPUtils; import me.braydon.license.common.IPUtils;
import me.braydon.license.dto.LicenseCheckBodyDTO; import me.braydon.license.dto.LicenseCheckBodyDTO;
import me.braydon.license.dto.LicenseDTO; import me.braydon.license.dto.LicenseDTO;
import me.braydon.license.exception.APIException; import me.braydon.license.exception.APIException;
import me.braydon.license.model.License; import me.braydon.license.model.License;
import me.braydon.license.service.CryptographyService;
import me.braydon.license.service.LicenseService; import me.braydon.license.service.LicenseService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -14,6 +21,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.security.PrivateKey;
import java.util.Map; import java.util.Map;
/** /**
@ -22,14 +30,20 @@ import java.util.Map;
@RestController @RestController
@RequestMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE) @RequestMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE)
public final class LicenseController { public final class LicenseController {
/**
* The {@link CryptographyService} to use.
*/
@NonNull private final CryptographyService cryptographyService;
/** /**
* The {@link LicenseService} to use. * The {@link LicenseService} to use.
*/ */
@NonNull private final LicenseService service; @NonNull private final LicenseService licenseService;
@Autowired @Autowired
public LicenseController(@NonNull LicenseService service) { public LicenseController(@NonNull CryptographyService cryptographyService, @NonNull LicenseService licenseService) {
this.service = service; this.cryptographyService = cryptographyService;
this.licenseService = licenseService;
} }
/** /**
@ -53,24 +67,34 @@ public final class LicenseController {
if (IPUtils.getIpType(ip) == -1) { if (IPUtils.getIpType(ip) == -1) {
throw new APIException(HttpStatus.BAD_REQUEST, "Invalid IP address"); throw new APIException(HttpStatus.BAD_REQUEST, "Invalid IP address");
} }
// Ensure the HWID is valid String key;
String hwidString = body.getHwid(); 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; boolean invalidHwid = true;
if (hwidString.contains("-")) { if (hwid.contains("-")) {
int segments = hwidString.substring(0, hwidString.lastIndexOf("-")).split("-").length; int segments = hwid.substring(0, hwid.lastIndexOf("-")).split("-").length;
if (segments == 4) { if (segments == 4) {
invalidHwid = false; invalidHwid = false;
} }
} }
if (invalidHwid) { if (invalidHwid) { // Invalid HWID
throw new APIException(HttpStatus.BAD_REQUEST, "Invalid HWID"); throw new APIException(HttpStatus.BAD_REQUEST, "Invalid HWID");
} }
// Check the license // Check the license
License license = service.check( License license = licenseService.check(
body.getKey(), key,
body.getProduct(), body.getProduct(),
ip, ip,
hwidString hwid
); );
// Return OK with the license DTO // Return OK with the license DTO
return ResponseEntity.ok(new LicenseDTO( return ResponseEntity.ok(new LicenseDTO(

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.dto; package me.braydon.license.dto;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.dto; package me.braydon.license.dto;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.exception; package me.braydon.license.exception;
import lombok.Getter; import lombok.Getter;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.exception; package me.braydon.license.exception;
import me.braydon.license.model.License; import me.braydon.license.model.License;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.exception; package me.braydon.license.exception;
import me.braydon.license.model.License; import me.braydon.license.model.License;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.exception; package me.braydon.license.exception;
import me.braydon.license.model.License; import me.braydon.license.model.License;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.exception; package me.braydon.license.exception;
import me.braydon.license.model.License; import me.braydon.license.model.License;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.model; package me.braydon.license.model;
import lombok.Getter; import lombok.Getter;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.repository; package me.braydon.license.repository;
import lombok.NonNull; import lombok.NonNull;

View File

@ -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();
}
}
}

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.service; package me.braydon.license.service;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
@ -33,7 +38,6 @@ import net.dv8tion.jda.api.requests.GatewayIntent;
import org.mindrot.jbcrypt.BCrypt; import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.info.BuildProperties;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.awt.*; import java.awt.*;
@ -59,7 +63,7 @@ public final class DiscordService {
/** /**
* The version of this Springboot application. * 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. * The salt to use for hashing license keys.
@ -140,9 +144,9 @@ public final class DiscordService {
.build(); .build();
@Autowired @Autowired
public DiscordService(@NonNull LicenseRepository licenseRepository, @NonNull BuildProperties buildProperties) { public DiscordService(@NonNull LicenseRepository licenseRepository/*, @NonNull BuildProperties buildProperties*/) {
this.licenseRepository = licenseRepository; this.licenseRepository = licenseRepository;
this.applicationVersion = buildProperties.getVersion(); // this.applicationVersion = buildProperties.getVersion();
} }
@PostConstruct @SneakyThrows @PostConstruct @SneakyThrows

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.license.service; package me.braydon.license.service;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;