mirror of
https://github.com/gnmyt/LicenseAPI.git
synced 2026-01-10 10:11:47 +00:00
Implement signatures in LicenseValidator.java
This commit is contained in:
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <br><br>
|
||||
* <b>NOTE:</b> This method will wait 1 second between each retry.
|
||||
* </p>
|
||||
*
|
||||
* @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<LicenseMeta> 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user