--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.licensing;
+
+import com.marcozanon.macaco.MConstants;
+import com.marcozanon.macaco.json.MInvalidJsonValueException;
+import com.marcozanon.macaco.json.MJsonObject;
+import com.marcozanon.macaco.json.MJsonString;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+public class MLicenseManager {
+
+ /* License generation. */
+
+ public static void generateFullLicense(Path privateKeyFile, Path licenseSkeletonFile, Path fullLicenseFile) throws MLicensingException {
+ try {
+ String privateKeyPemString = new String(Files.readAllBytes(privateKeyFile), MConstants.DEFAULT_CHARSET);
+ String licenseSkeletonJsonString = new String(Files.readAllBytes(licenseSkeletonFile), MConstants.DEFAULT_CHARSET);
+ //
+ MJsonObject fullLicense = MLicenseManager.generateFullLicense(privateKeyPemString, licenseSkeletonJsonString);
+ //
+ Files.write(fullLicenseFile, fullLicense.getJsonValue(true).getBytes(MConstants.DEFAULT_CHARSET), StandardOpenOption.CREATE);
+ }
+ catch (IOException exception) {
+ throw new MLicensingException("Could not generate full license.", exception);
+ }
+ }
+
+ public static MJsonObject generateFullLicense(String privateKeyPemString, String licenseSkeletonJsonString) throws MLicensingException {
+ try {
+ byte[] privateKeyContent = Base64.getDecoder().decode(privateKeyPemString.replace("-----BEGIN PRIVATE KEY-----", "").replaceAll("\\R", "").replace("-----END PRIVATE KEY-----", ""));
+ RSAPrivateKey privateKey = (RSAPrivateKey)KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyContent));
+ //
+ MJsonObject licenseSkeleton = new MJsonObject(licenseSkeletonJsonString);
+ //
+ MJsonObject licenseData = (MJsonObject)licenseSkeleton.getValue("licenseData");
+ byte[] licenseDataHashContent = MessageDigest.getInstance("SHA-256").digest(licenseData.getJsonValue().getBytes(MConstants.DEFAULT_CHARSET));
+ //
+ Signature signature = Signature.getInstance("SHA256withRSA");
+ signature.initSign(privateKey);
+ signature.update(licenseDataHashContent);
+ byte[] licenseDataHashSignatureContent = signature.sign();
+ //
+ licenseSkeleton.setValue("licenseDataSignature", new MJsonString("\"" + Base64.getEncoder().encodeToString(licenseDataHashSignatureContent) + "\""));
+ //
+ return licenseSkeleton;
+ }
+ catch (InvalidKeyException exception) {
+ throw new MLicensingException("Could not generate full license.", exception);
+ }
+ catch (InvalidKeySpecException exception) {
+ throw new MLicensingException("Could not generate full license.", exception);
+ }
+ catch (MInvalidJsonValueException exception) {
+ throw new MLicensingException("Could not generate full license.", exception);
+ }
+ catch (NoSuchAlgorithmException exception) {
+ throw new MLicensingException("Could not generate full license.", exception);
+ }
+ catch (SignatureException exception) {
+ throw new MLicensingException("Could not generate full license.", exception);
+ }
+ }
+
+ /* License verification. */
+
+ public static MJsonObject verifyFullLicense(Path publicKeyFile, Path fullLicenseFile) throws MLicensingException {
+ try {
+ String publicKeyPemString = new String(Files.readAllBytes(publicKeyFile), MConstants.DEFAULT_CHARSET);
+ String fullLicenseJsonString = new String(Files.readAllBytes(fullLicenseFile), MConstants.DEFAULT_CHARSET);
+ //
+ return MLicenseManager.verifyFullLicense(publicKeyPemString, fullLicenseJsonString);
+ }
+ catch (IOException exception) {
+ throw new MLicensingException("Could not verify full license.", exception);
+ }
+ }
+
+ public static MJsonObject verifyFullLicense(String publicKeyPemString, String fullLicenseJsonString) throws MLicensingException {
+ try {
+ byte[] publicKeyContent = Base64.getDecoder().decode(publicKeyPemString.replace("-----BEGIN PUBLIC KEY-----", "").replaceAll("\\R", "").replace("-----END PUBLIC KEY-----", ""));
+ RSAPublicKey publicKey = (RSAPublicKey)KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyContent));
+ //
+ MJsonObject fullLicense = new MJsonObject(fullLicenseJsonString);
+ //
+ MJsonObject licenseData = (MJsonObject)fullLicense.getValue("licenseData");
+ byte[] licenseDataHashContent = MessageDigest.getInstance("SHA-256").digest(licenseData.getJsonValue().getBytes(MConstants.DEFAULT_CHARSET));
+ //
+ MJsonString licenseDataSignature = (MJsonString)fullLicense.getValue("licenseDataSignature");
+ byte[] licenseDataSignatureContent = Base64.getDecoder().decode(licenseDataSignature.getValue());
+ //
+ Signature signature = Signature.getInstance("SHA256withRSA");
+ signature.initVerify(publicKey);
+ signature.update(licenseDataHashContent);
+ signature.verify(licenseDataSignatureContent);
+ //
+ return fullLicense;
+ }
+ catch (InvalidKeyException exception) {
+ throw new MLicensingException("Could not verify full license.", exception);
+ }
+ catch (InvalidKeySpecException exception) {
+ throw new MLicensingException("Could not verify full license.", exception);
+ }
+ catch (MInvalidJsonValueException exception) {
+ throw new MLicensingException("Could not verify full license.", exception);
+ }
+ catch (NoSuchAlgorithmException exception) {
+ throw new MLicensingException("Could not verify full license.", exception);
+ }
+ catch (SignatureException exception) {
+ throw new MLicensingException("Could not verify full license.", exception);
+ }
+ }
+
+}