diff --git a/integrations/java/src/main/java/de/licenseapi/LicenseValidator.java b/integrations/java/src/main/java/de/licenseapi/LicenseValidator.java index 6348c94..29e61cf 100644 --- a/integrations/java/src/main/java/de/licenseapi/LicenseValidator.java +++ b/integrations/java/src/main/java/de/licenseapi/LicenseValidator.java @@ -2,113 +2,32 @@ package de.licenseapi; import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import de.licenseapi.entities.License; import de.licenseapi.entities.LicenseMeta; import de.licenseapi.entities.LicenseStatus; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Base64; import java.util.Date; -public class LicenseValidator { - - private final int API_VERSION = 1; - private final String baseUrl; - private final String validationKey; - - private int retries = 3; - - /** - * Creates a new {@link LicenseValidator} with the given validation key. - * - * @param baseUrl The base url of your LicenseAPI server (e.g. https://your-server.de) - * @param validationKey The validation key of your project. You can find it in the project - * settings. - */ - public LicenseValidator(String baseUrl, String validationKey) { - this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; - this.validationKey = validationKey; - } - - /** - * Retrieves the license from the license key. This method will return the raw response from the server. - * - * @param licenseKey The license key you want to validate - * @return the raw response from the server or {@code null} if the validation key is invalid. - */ - private String retrieveLicenseRaw(String licenseKey) { - try { - String url = String.format("%s/api/v%s/validate/%s", baseUrl, API_VERSION, URLDecoder.decode(licenseKey, "UTF-8")); - - HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - connection.setRequestMethod("GET"); - connection.setRequestProperty("User-Agent", "LicenseAPI-Java-Client"); - connection.setRequestProperty("X-Validation-Key", validationKey); - - if (connection.getResponseCode() != 200) return null; - - StringBuilder response = new StringBuilder(); - - BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - for (String line; (line = reader.readLine()) != null; ) { - response.append(line); - } - - return response.toString(); - } catch (Exception e) { - return null; - } - } - - /** - * Retrieves the license from the license key. This method will return the raw response from the server. - *

- * This method will retry the request if the response is {@code null}. - * The amount of retries can be set with {@link #setRetries(int)}. - * If the amount of retries is 0, the license will be validated once. - * The default amount of retries is 3. - *

- * NOTE: This method will wait 1 second between each retry. - *

- * - * @param licenseKey The license key you want to validate - * @return the raw response from the server or {@code null} if the validation key is invalid. - */ - private String retrieveLicenseSecure(String licenseKey) { - String response = retrieveLicenseRaw(licenseKey); - if (response == null) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - int currentRetries = 0; - while (currentRetries < retries) { - response = retrieveLicenseRaw(licenseKey); - if (response != null) break; - currentRetries++; - } - } - - return response; - } +public abstract class LicenseValidator { /** * Parses the license object from the response * - * @param status the status of the license * @param license the license object * @return the parsed license object */ - private License parseLicense(LicenseStatus status, JsonObject license) { - if (status != LicenseStatus.VALID) return new License(status, null, null, null, null, -1, 0, null); + protected License parseLicense(JsonObject license) { + LicenseStatus status = LicenseStatus.valueOf(license.get("status").getAsString()); + if (status != LicenseStatus.VALID) return new License(status, null, null, null, null, 0, null); String licenseKey = license.get("key").getAsString(); @@ -124,55 +43,61 @@ public class LicenseValidator { ArrayList metaList = new ArrayList<>(); for (String key : meta.keySet()) metaList.add(new LicenseMeta(key, meta.get(key).getAsString())); - int maxUses = license.get("maxUses").getAsInt(); int currentUses = license.get("currentUses").getAsInt(); Instant instant = Instant.from(DateTimeFormatter.ISO_INSTANT.parse(license.get("expirationDate").getAsString())); - return new License(status, licenseKey, groupList, permissionList, metaList, maxUses, currentUses, + if (Instant.now().isAfter(instant) && instant.getEpochSecond() != 0) + status = LicenseStatus.EXPIRED; + + return new License(status, licenseKey, groupList, permissionList, metaList, currentUses, Date.from(instant).getTime() == 0 ? null : Date.from(instant)); } /** - * Retrieves the license from the license key. - * - * @param licenseKey The license key you want to validate - * @return a {@link License} object with the license information or {@code null} if the validation key is invalid. + * Reads a public key from a string + * @param key The public key as a string + * @return The public key + * @throws Exception If the key is invalid */ - public License retrieveLicense(String licenseKey) { - String response = retrieveLicenseSecure(licenseKey); - if (response == null) return null; + public static PublicKey keyFromString(String key) throws Exception { + String cleanKey = key.replaceAll("-----BEGIN PUBLIC KEY-----", "") + .replaceAll("-----END PUBLIC KEY-----", "") + .replaceAll("\\s", ""); - JsonObject object = JsonParser.parseString(response).getAsJsonObject(); - String status = object.get("status").getAsString(); - if (status.equals("INVALID_KEY")) return null; + byte[] keyBytes = Base64.getDecoder().decode(cleanKey); - return parseLicense(LicenseStatus.valueOf(status), object.getAsJsonObject("license")); + X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePublic(spec); } /** - * Gets the current amount of retries. If the amount of retries is 0, the license will be validated once. - * - * @param retries the new amount of retries + * Verifies a license + * @param publicKey The public key + * @param signature The signature + * @param data The data + * @return If the license is valid + * @throws Exception If the license is invalid */ - public void setRetries(int retries) { - this.retries = retries; + protected boolean verifyLicense(PublicKey publicKey, String signature, String data) throws Exception { + Signature sig = Signature.getInstance("SHA256withRSA"); + sig.initVerify(publicKey); + sig.update(data.getBytes(StandardCharsets.UTF_8)); + return sig.verify(hexStringToByteArray(signature)); } /** - * Gets the current amount of retries. If the amount of retries is 0, the license will be validated once. - * - * @return the current amount of retries + * Converts a hex string to a byte array + * @param s The hex string + * @return The byte array */ - public int getRetries() { - return retries; + protected byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); + } + return data; } - /** - * Gets the current validation key - * - * @return the current validation key - */ - public String getValidationKey() { - return validationKey; - } }