Joaquín Reñé
2026-03-27 4ee50e257b32f6ec0f72907305d1f2b1212808a4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
 */
package net.curisit.securis;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Date;
import java.util.Map;
import net.curisit.securis.beans.LicenseBean;
import net.curisit.securis.beans.RequestBean;
import net.curisit.securis.beans.SignedLicenseBean;
import net.curisit.securis.utils.JsonUtils;
import net.curisit.securis.utils.SignatureHelper;
import org.apache.commons.codec.binary.Base64;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import jakarta.inject.Singleton;
/**
* LicenseGenerator
* <p>
* Factory for building and signing {@link LicenseBean} instances. Uses a process-wide
* singleton and expects a PKCS#8 private key at:
* <code>~/.SeCuris/keys/securis_private_key.pkcs8</code>.
* @author JRA
* Last reviewed by JRA on Oct 5, 2025.
*/
@Singleton
public class LicenseGenerator {
    private static final Logger LOG = LogManager.getLogger(LicenseGenerator.class);
    private static LicenseGenerator singleton = new LicenseGenerator();
    /** 
     * getInstance<p>
     * Singleton accessor. 
     */
    public static LicenseGenerator getInstance() {
        return singleton;
    }
    /**
     * generateLicense<p>
     * Generate a license bean with the specified data
     * 
     * @param req
     * @param licCode
     * @param metadata
     * @param expirationDate
     * @param licenseCode
     * @param appName
     * @return
     * @throws SeCurisException
     */
    public LicenseBean generateLicense(RequestBean req, Map<String, Object> metadata, Date expirationDate, String licenseCode, String appName)
            throws SeCurisException {
        LOG.debug("Generating license: MAC: {}, Customer code: {}, AppName: {}", req.getMacAddresses(), req.getCustomerCode(), appName);
        LicenseBean license = new LicenseBean(req);
        license.setAppName(appName);
        license.setLicenseCode(licenseCode);
        license.setExpirationDate(expirationDate);
        license.setMetadata(metadata);
        sign(license);
        return license;
    }
    /**
    * save
    * <p>
    * Persist a pretty-printed JSON representation of the signed license to disk.
    *
    * @param license source license
    * @param file target file path
    * @throws SeCurisException if serialization or IO fails
    */
    public void save(LicenseBean license, File file) throws SeCurisException {
        SignedLicenseBean signedLic = new SignedLicenseBean(license);
        byte[] json;
        try {
            json = JsonUtils.toPrettyJSON(signedLic).getBytes("utf-8");
            Files.write(Paths.get(file.toURI()), json, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        } catch (UnsupportedEncodingException e) {
            LOG.error("Error creating json doc from license: " + license, e);
            throw new SeCurisException("Error creating json doc from license: " + license, e);
        } catch (IOException e) {
            LOG.error("Error creating license file: " + file, e);
            throw new SeCurisException("Error creating json doc from license: " + license, e);
        }
        LOG.info("License saved in {}", file);
    }
    /**
    * sign
    * <p>
    * Compute a Base64 signature for the given license and set it into the bean.
    *
    * @param licBean license to sign (in-place)
    * @return Base64 signature string
    * @throws SeCurisException if the signature process fails
    */
    public String sign(LicenseBean licBean) throws SeCurisException {
        SignatureHelper sh = SignatureHelper.getInstance();
        Signature signature;
        try {
            signature = Signature.getInstance(SignatureHelper.SIGNATURE_GENERATION_ALGORITHM);
            signature.initSign(sh.generatePrivateKey(new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "keys"
                    + File.separator + "securis_private_key.pkcs8")));
            sh.prepareSignature(signature, licBean);
            byte[] signatureData = signature.sign();
            licBean.setSignature(Base64.encodeBase64String(signatureData));
            return licBean.getSignature();
        } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | IOException | SignatureException e) {
            LOG.error("Error signing license for {}", licBean, e);
        }
        throw new SeCurisException("License could not be generated");
    }
}