35 | * All features from validated licenses (license HostId,
36 | * signature and expiration are OK), but features itself can be
37 | * invalid (expired)
38 | *
39 | */
40 | public abstract class NvLicenseManager implements LicenseManager {
41 |
42 | private HashMap
91 | *
94 | *
95 | * @param encodedKey encoded byte array
96 | * @return public key
97 | *
98 | * @throws NoSuchAlgorithmException wrong algorithm (should not occur)
99 | * @throws NoSuchProviderException wrong provider (should not occur)
100 | * @throws InvalidKeySpecException invalid key (should not occur)
101 | */
102 | public static PublicKey getPublic(byte[] encodedKey)
103 | throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
104 | return getPublic(encodedKey, KPG_ALGORITHM, PROVIDER);
105 | }
106 |
107 | /**
108 | * Returns the public key from the encoded byte array.
109 | * The bytes can be recovered from a Hex string saved in a file etc.
110 | *
111 | * @param encodedKey the encoded public key in bytes.
112 | * @param kpgAlgorithm key pair algorithm
113 | * @param provider provider
114 | *
115 | * @throws NoSuchAlgorithmException wrong algorithm
116 | * @throws NoSuchProviderException wrong provider
117 | * @throws InvalidKeySpecException invalid key (should not occur)
118 | */
119 | public static PublicKey getPublic(byte[] encodedKey, String kpgAlgorithm, String provider)
120 | throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
121 |
122 | KeyFactory kf = KeyFactory.getInstance(kpgAlgorithm, provider);
123 |
124 | return kf.generatePublic(new X509EncodedKeySpec(encodedKey));
125 | }
126 |
127 | /**
128 | * Returns the private key for default arguments from the encoded byte array
129 | *
130 | *
131 | *
134 | *
135 | * @param encodedKey encoded byte array
136 | *
137 | * @return private key
138 | *
139 | * @throws NoSuchAlgorithmException
140 | * @throws NoSuchProviderException
141 | * @throws InvalidKeySpecException
142 | */
143 | public static PrivateKey getPrivate(byte[] encodedKey)
144 | throws InvalidKeySpecException, NoSuchProviderException, NoSuchAlgorithmException {
145 | return getPrivate(encodedKey, KPG_ALGORITHM, PROVIDER);
146 | }
147 |
148 | /**
149 | * This method gets the private key from the encoded byte.
150 | * The bytes can be recovered from a Hex string saved in a file etc.
151 | * @param encodedKey the encoded private key in bytes.
152 | */
153 | public static PrivateKey getPrivate(byte[] encodedKey, String kpgAlgorithm, String provider)
154 | throws InvalidKeySpecException, NoSuchProviderException, NoSuchAlgorithmException {
155 |
156 | KeyFactory kf = KeyFactory.getInstance(kpgAlgorithm, provider);
157 |
158 | return kf.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
159 | }
160 |
161 | /**
162 | * Generates key pair for default arguments
163 | *
164 | *
169 | *
170 | * @return a new key pair
171 | *
172 | * @throws NoSuchAlgorithmException invalid algorithm
173 | * @throws NoSuchProviderException invalid provider
174 | */
175 | public static KeyPair getKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
176 | return getKeyPair(KEYLEN, KPG_ALGORITHM, PROVIDER, RNG_ALGORITHM);
177 | }
178 |
179 | /**
180 | * Generates the key pair with arguments.
181 | *
182 | * @param keyLen key length
183 | * @param kpgAlgorithm algorithm for key pair generator (DSA, RSA, ...)
184 | * @param provider algorithm provider (SUN, ...)
185 | * @param rngAlgorithm algorithm for random generator (SHA1PRNG, ...)
186 | * @return a new key pair
187 | *
188 | * @throws NoSuchAlgorithmException invalid algorithm
189 | * @throws NoSuchProviderException invalid provider
190 | *
191 | */
192 | public static KeyPair getKeyPair(int keyLen, String kpgAlgorithm, String provider, String rngAlgorithm)
193 | throws NoSuchAlgorithmException, NoSuchProviderException {
194 |
195 | KeyPairGenerator kpg;
196 |
197 | kpg = KeyPairGenerator.getInstance(kpgAlgorithm, provider);
198 | kpg.initialize(keyLen, SecureRandom.getInstance(rngAlgorithm, provider));
199 |
200 | return kpg.generateKeyPair();
201 | }
202 |
203 | ///////////////////////////////////////////////////////////////////////////////////////////
204 | // Signature handling
205 | ///////////////////////////////////////////////////////////////////////////////////////////
206 |
207 | /**
208 | * Validates the license using the signature from the license using the default arguments
209 | *
210 | *
211 | *
214 | *
215 | * @param lic the license.
216 | * @param publicKey public key as a hex string
217 | * @return a boolean whether the license data is valid.
218 | * @throws GeneralSecurityException if any exception was thrown
219 | */
220 | public static boolean verify(NvAbstractLicense lic, final String publicKey) throws GeneralSecurityException {
221 | return verify(lic, publicKey, SIG_ALGORITHM, PROVIDER);
222 | }
223 |
224 | /**
225 | * Validates the license using the signature from the license. Algorithm and provider
226 | * are passed a arguments
227 | *
228 | * @param lic the license.
229 | * @param publicKey public key as a hex string
230 | * @param sigAlgorithm algorithm for the signature (SHA1withDSA, ...)
231 | * @param provider algorithm provider (SUN, ...)
232 | * @return a boolean whether the license data is valid.
233 | * @throws GeneralSecurityException if any exception was thrown
234 | */
235 | public static boolean verify(NvAbstractLicense lic, final String publicKey, String sigAlgorithm, String provider)
236 | throws GeneralSecurityException {
237 |
238 | String errMsg = "License verification failed: ";
239 | String signature = lic.getSignature();
240 |
241 | if ((signature == null) || (signature.trim().length() == 0)) {
242 | throw new GeneralSecurityException(errMsg + "no signature");
243 | }
244 |
245 | lic.setSignature(null);
246 |
247 | try {
248 |
249 | String json = toJson(lic, false);
250 | byte[] sigArr = ByteHex.convert(signature);
251 | boolean isValid = false;
252 | Signature sig = Signature.getInstance(sigAlgorithm, provider);
253 | PublicKey key = getPublic(ByteHex.convert(publicKey));
254 |
255 | sig.initVerify(key);
256 | sig.update(json.getBytes());
257 | isValid = sig.verify(sigArr);
258 | lic.setSignature(signature);
259 |
260 | return isValid;
261 |
262 | } catch (NoSuchAlgorithmException e) {
263 | errMsg += "no such algorithm (" + e.getMessage() + ")";
264 | } catch (NoSuchProviderException e) {
265 | errMsg += "no such provider (" + e.getMessage() + ")";
266 | } catch (InvalidKeySpecException e) {
267 | errMsg += "invalid key specification (" + e.getMessage() + ")";
268 | } catch (InvalidKeyException e) {
269 | errMsg += "invalid key (" + e.getMessage() + ")";
270 | } catch (SignatureException e) {
271 | errMsg += "signature exception (" + e.getMessage() + ")";
272 | } catch (IllegalArgumentException e) {
273 | errMsg += "signature not a hex string";
274 | }
275 |
276 | lic.setSignature(signature);
277 |
278 | throw new GeneralSecurityException(errMsg);
279 | }
280 |
281 | /**
282 | * Signing the license data based on the private key using the default arguments
283 | *
284 | *
285 | *
288 | *
289 | * @param lic the license.
290 | * @param privateKey private key as a hex string
291 | * @return a boolean whether the license data is valid.
292 | * @throws GeneralSecurityException if any exception was thrown
293 | */
294 | public static void sign(NvAbstractLicense lic, final String privateKey) throws GeneralSecurityException {
295 | sign(lic, privateKey, SIG_ALGORITHM, PROVIDER);
296 | }
297 |
298 | /**
299 | * Signes the license data based on the private key. Algorithm and provider are passed as arguments.
300 | *
301 | * @param lic the license.
302 | * @param privateKey private key as a hex string
303 | * @param sigAlgorithm algorithm for the signature (SHA1withDSA, ...)
304 | * @param provider algorithm provider (SUN, ...)
305 | *
306 | * @param lic the license.
307 | * @throws GeneralSecurityException if any exception was thrown
308 | */
309 | public static void sign(NvAbstractLicense lic, final String privateKey, String sigAlgorithm, String provider)
310 | throws GeneralSecurityException {
311 |
312 | String errMsg = "License sign failed: ";
313 |
314 | try {
315 |
316 | Signature sig = Signature.getInstance(sigAlgorithm, provider);
317 | PrivateKey key = getPrivate(ByteHex.convert(privateKey));
318 | String json = toJson(lic, false);
319 |
320 | sig.initSign(key);
321 | sig.update(json.getBytes());
322 |
323 | byte[] result = sig.sign();
324 |
325 | lic.setSignature(ByteHex.convert(result));
326 |
327 | } catch (NoSuchAlgorithmException e) {
328 | throw new GeneralSecurityException(errMsg + "no such algorithm (" + e.getMessage() + ")");
329 | } catch (NoSuchProviderException e) {
330 | throw new GeneralSecurityException(errMsg + "no such provider (" + e.getMessage() + ")");
331 | } catch (InvalidKeySpecException e) {
332 | throw new GeneralSecurityException(errMsg + "invalid key specification (" + e.getMessage() + ")");
333 | } catch (InvalidKeyException e) {
334 | throw new GeneralSecurityException(errMsg + "invalid key (" + e.getMessage() + ")");
335 | } catch (SignatureException e) {
336 | throw new GeneralSecurityException(errMsg + "signature exception (" + e.getMessage() + ")");
337 | } catch (IllegalArgumentException e) {
338 | throw new GeneralSecurityException(errMsg + "signature not a hex string");
339 | }
340 | }
341 |
342 | ///////////////////////////////////////////////////////////////////////////////////////////
343 | // Converting tools
344 | ///////////////////////////////////////////////////////////////////////////////////////////
345 |
346 | /**
347 | * Converts a license Java object to the JSON string with PrettyFormat feature
348 | *
349 | * @param license the license as Java object
350 | * @return the license as JSON string
351 | */
352 | public static String toJson(NvAbstractLicense license) {
353 | return toJson(license, true);
354 | }
355 |
356 | /**
357 | * Converts a license Java object to the Json string. Using PrettyFormat
358 | * generates better human readable Json string.
359 | *
360 | * @param license the license as Java object
361 | * @param prettyFormat true if output should be pretty formatted
362 | * @return the license as Json string
363 | */
364 | public static String toJson(NvAbstractLicense license, boolean prettyFormat) {
365 |
366 | SerializeWriter out = new SerializeWriter();
367 | SerializeConfig config = new SerializeConfig();
368 |
369 | try {
370 |
371 | JSONSerializer serializer = new JSONSerializer(out, config);
372 |
373 | serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
374 | serializer.config(SerializerFeature.PrettyFormat, prettyFormat);
375 | serializer.setDateFormat(DATE_FORMAT);
376 |
377 | // license.configureSerializer(config);
378 | serializer.write(license);
379 |
380 | return out.toString();
381 |
382 | } finally {
383 | out.close();
384 | }
385 |
386 | // return JSON.toJSONStringWithDateFormat(license, "YYYY-MM-dd", features);
387 | }
388 |
389 | /**
390 | * Converts a JSON string to the Java object. The JSON
391 | * must be a serialization of {@link NvAbstractLicense} subclass
392 | *
393 | * @param json a license as JSON object
394 | * @return license as Java object
395 | */
396 | public static