* 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. * * @return the hardware id */ @NonNull private String getHardwareId() { SystemInfo systemInfo = new SystemInfo(); OperatingSystem operatingSystem = systemInfo.getOperatingSystem(); HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware(); CentralProcessor centralProcessor = hardwareAbstractionLayer.getProcessor(); ComputerSystem computerSystem = hardwareAbstractionLayer.getComputerSystem(); // Retrieve necessary hardware information String vendor = operatingSystem.getManufacturer(); String processorSerialNumber = computerSystem.getSerialNumber(); String uuid = computerSystem.getHardwareUUID(); String processorIdentifier = centralProcessor.getProcessorIdentifier().getIdentifier(); int processors = centralProcessor.getLogicalProcessorCount(); // Generate a unique hardware id using the retrieved information return String.format("%08x", vendor.hashCode()) + "-" + String.format("%08x", processorSerialNumber.hashCode()) + "-" + String.format("%08x", uuid.hashCode()) + "-" + String.format("%08x", processorIdentifier.hashCode()) + "-" + processors; } /** * 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. */ private final long status; /** * The error in the response, null if none. */ private String error; /** * The description of the license, present if valid. */ private String description; /** * The Discord snowflake of the license owner, present * if valid and there is an owner. */ private long ownerSnowflake; /** * The Discord name of the license owner, present * if valid and there is an owner. */ private String ownerName; /** * The plan for this license. */ @NonNull private String plan; /** * The latest version of the product this license is for. */ @NonNull private String latestVersion; /** * The optional expiration {@link Date} of the license. */ private Date expires; public LicenseResponse(long status, @NonNull String error) { this.status = status; this.error = error; } /** * Check if the license is valid. * * @return true if valid, otherwise false */ public boolean isValid() { return status == 200; } /** * Check if the license is permanent. * * @return true if permanent, otherwise false */ public boolean isPermanent() { return expires == null; } } }