From 1dd710aec0c8a4af30e7174adad65f285eafb361 Mon Sep 17 00:00:00 2001 From: Marco Zanon Date: Thu, 29 Feb 2024 17:51:41 +0000 Subject: [PATCH] Added the licensing package and implemented the license manager. --- 9.x/CHANGELOG | 5 + .../macaco/licensing/MLicenseManager.java | 135 ++++++++++++++++++ .../macaco/licensing/MLicensingException.java | 32 +++++ 3 files changed, 172 insertions(+) create mode 100644 9.x/src/main/java/com/marcozanon/macaco/licensing/MLicenseManager.java create mode 100644 9.x/src/main/java/com/marcozanon/macaco/licensing/MLicensingException.java diff --git a/9.x/CHANGELOG b/9.x/CHANGELOG index 01b6c91..9ff8ba1 100644 --- a/9.x/CHANGELOG +++ b/9.x/CHANGELOG @@ -2,6 +2,11 @@ Macaco Copyright (c) 2009-2024 Marco Zanon . See LICENSE for details. +------------------ +9.2.0 (2024-02-29) +------------------ +* Added the licensing package and implemented the license manager. + ------------------ 9.1.0 (2024-02-29) ------------------ diff --git a/9.x/src/main/java/com/marcozanon/macaco/licensing/MLicenseManager.java b/9.x/src/main/java/com/marcozanon/macaco/licensing/MLicenseManager.java new file mode 100644 index 0000000..f90fe07 --- /dev/null +++ b/9.x/src/main/java/com/marcozanon/macaco/licensing/MLicenseManager.java @@ -0,0 +1,135 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * 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); + } + } + +} diff --git a/9.x/src/main/java/com/marcozanon/macaco/licensing/MLicensingException.java b/9.x/src/main/java/com/marcozanon/macaco/licensing/MLicensingException.java new file mode 100644 index 0000000..c5ac150 --- /dev/null +++ b/9.x/src/main/java/com/marcozanon/macaco/licensing/MLicensingException.java @@ -0,0 +1,32 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.licensing; + +import com.marcozanon.macaco.MException; + +@SuppressWarnings("serial") +public class MLicensingException extends MException { + + /* */ + + public MLicensingException() { + super(); + } + + public MLicensingException(String message) { + super(message); + } + + public MLicensingException(Throwable error) { + super(error); + } + + public MLicensingException(String message, Throwable error) { + super(message, error); + } + +} -- 2.30.2