All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.license.LicenseNotary Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2005-2015 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package de.schlichtherle.license;

import de.schlichtherle.util.ObfuscatedString;
import de.schlichtherle.xml.GenericCertificate;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;

/**
 * This notary knows how to sign and verify a {@link GenericCertificate}.
 * 

* This class is not thread safe. * * @author Christian Schlichtherle */ public class LicenseNotary { /** The buffer size for I/O. */ private static final int BUFSIZE = 5 * 1024; /** => "param" */ static final String PARAM = new ObfuscatedString(new long[] { 0x9462FFDE0183752L, 0xE2A34A222A24DB14L }).toString(); /** => "alias" */ private static final String ALIAS = new ObfuscatedString(new long[] { 0xF7122BB1103EE24L, 0x5D073BF77D50CE8AL }).toString(); /** => "exc.noKeyPwd" */ private static final String EXC_NO_KEY_PWD = new ObfuscatedString(new long[] { 0x9BEEC1A930D89BC1L, 0x314F8BFE96B1D7BL, 0x7D41D459E6191D0AL }).toString(); /** => "exc.noKeyEntry" */ private static final String EXC_NO_KEY_ENTRY = new ObfuscatedString(new long[] { 0xECC3EE809CC45994L, 0x395EC0314F8227A1L, 0x90B1DBA3D701F0FBL }).toString(); /** => "exc.privateKeyOrPwdIsNotAllowed" */ private static final String EXC_PRIVATE_KEY_OR_PWD_IS_NOT_ALLOWED = new ObfuscatedString(new long[] { 0xD6E9FE0BD39F8075L, 0x351D278C14FABB1AL, 0xD64A9C9BD412AB10L, 0x1AEB0F657DB66448L, 0x41EE587D2CD73A1AL }).toString(); /** => "exc.noCertificateEntry" */ private static final String EXC_NO_CERTIFICATE_ENTRY = new ObfuscatedString(new long[] { 0xCA437064C1D0C41EL, 0xDDBBA0FF1F17FC35L, 0x5D2CD0D970444C3DL, 0xF94EAAC3F634D04CL }).toString(); /** => "SHA1withDSA" */ private static final String SHA1_WITH_DSA = new ObfuscatedString(new long[] { 0xEB0CFFD676FD2839L, 0x176DF514D5A0ED59L, 0xBFE1DE24AEF8E9B0L }).toString(); /** => "JKS" */ private static final String JKS = new ObfuscatedString(new long[] { 0xA97AF8FB6356CB08L, 0x20E47C2995D2FE7AL }).toString(); private KeyStoreParam param; // init by setKeyStoreParam() - should be accessed via getKeyStoreParam() only! private KeyStore keyStore; // init by getKeyStore() private PrivateKey privateKey; // lazy initialised by getPrivateKey() private PublicKey publicKey; // lazy initialised by getPublicKey() /** * Creates a new License Notary. *

* Warning: The notary created by this constructor is not * valid and cannot be used unless {@link #setKeyStoreParam(KeyStoreParam)} * is called! */ protected LicenseNotary() { } /** * * Creates a new License Notary. * * @param param the keyStore configuration parameters * - may not be {@code null}. * @throws NullPointerException if the given parameter object does not * obey the contract of its interface due to a {@code null} * pointer. * @throws IllegalPasswordException if any password in the parameter object * does not comply to the current policy. */ public LicenseNotary(final KeyStoreParam param) { setKeyStoreParam0(param); } /** Returns the keyStore configuration parameters. */ public KeyStoreParam getKeyStoreParam() { return param; } /** * Sets the keyStore configuration parameters. * Calling this method resets the notary as if it had just been created. * * @param param the keyStore configuration parameters * - may not be {@code null}. * @throws NullPointerException if the given parameter object does not * obey the contract of its interface due to a {@code null} * pointer. * @throws IllegalPasswordException if any password in the parameter object * does not comply to the current policy. */ public void setKeyStoreParam(final KeyStoreParam param) { setKeyStoreParam0(param); } private void setKeyStoreParam0(final KeyStoreParam param) { // Check parameters to implement fail-fast behaviour and enforce // a reasonably good security level. if (null == param.getAlias()) throw new NullPointerException(ALIAS); final Policy policy = Policy.getCurrent(); final String storePwd = param.getStorePwd(); policy.checkPwd(storePwd); final String keyPwd = param.getKeyPwd(); if (null != keyPwd) policy.checkPwd(keyPwd); this.param = param; keyStore = null; privateKey = null; publicKey = null; } /** * Encodes and signs the given {@code content} and returns a locked * generic certificate holding the encoded content and its digital * signature. *

* Please note the following: *

    *
  • Because this method locks the certificate, a subsequent call to * {@link #sign(GenericCertificate, Object)} or * {@link #verify(GenericCertificate)} is redundant * and will throw a {@code PropertyVetoException}. * Use {@link GenericCertificate#isLocked()} to detect whether a * generic certificate has been successfuly signed or verified before * or call {@link GenericCertificate#getContent()} and expect an * Exception to be thrown if it hasn't.
  • *
  • There is no way to unlock the returned certificate. * Call the copy constructor of {@link GenericCertificate} if you * need an unlocked copy of the certificate.
  • *
* * @param content the object to sign. This must either be a JavaBean or an * instance of any other class which is supported by * {@link de.schlichtherle.xml.PersistenceService} * - maybe {@code null}. * @return A locked generic certificate holding the encoded content and * its digital signature. * @throws Exception for various reasons. */ public GenericCertificate sign(final Object content) throws Exception { final GenericCertificate cert = new GenericCertificate(); sign(cert, content); return cert; } /** * Encodes and signs the given {@code content} in the given * {@code certificate} and locks it. *

* Please note the following: *

    *
  • This method will throw a {@code PropertyVetoException} if the * certificate is already locked, i.e. if it has been signed or * verified before.
  • *
  • Because this method locks the certificate, a subsequent call to * {@link #sign(GenericCertificate, Object)} or * {@link #verify(GenericCertificate)} is redundant * and will throw a {@code PropertyVetoException}. * Use {@link GenericCertificate#isLocked()} to detect whether a * generic certificate has been successfuly signed or verified before * or call {@link GenericCertificate#getContent()} and expect an * Exception to be thrown if it hasn't.
  • *
  • There is no way to unlock the certificate. * Call the copy constructor of {@link GenericCertificate} if you * need an unlocked copy of the certificate.
  • *
* * @param certificate the generic certificate used to hold the encoded * content and its digital signature. * @param content the object to sign. This must either be a JavaBean or an * instance of any other class which is supported by * {@code {@link de.schlichtherle.xml.PersistenceService}} * - maybe {@code null}. * @throws Exception for various reasons. */ void sign(GenericCertificate certificate, Object content) throws Exception { certificate.sign(content, getPrivateKey(), getSignatureEngine()); } /** * Verifies the digital signature of the encoded content in the given * {@code certificate} and locks it. *

* Please note the following: *

    *
  • This method will throw a {@code PropertyVetoException} if the * certificate is already locked, i.e. if it has been signed or * verified before.
  • *
  • Because this method locks the certificate, a subsequent call to * {@link #sign(GenericCertificate, Object)} or * {@link #verify(GenericCertificate)} is redundant * and will throw a {@code PropertyVetoException}. * Use {@link GenericCertificate#isLocked()} to detect whether a * generic certificate has been successfuly signed or verified before * or call {@link GenericCertificate#getContent()} and expect an * Exception to be thrown if it hasn't.
  • *
  • There is no way to unlock the certificate. * Call the copy constructor of {@link GenericCertificate} if you * need an unlocked copy of the certificate.
  • *
* * @param certificate the generic certificate to verify * - may not be {@code null}. * @throws Exception a subclass of this class may be thrown for various * reasons. */ public void verify(GenericCertificate certificate) throws Exception { certificate.verify(getPublicKey(), getSignatureEngine()); } /** * Returns the private key from the keyStore. * * @throws LicenseNotaryException if the parameters used to access the * corresponding key store are insufficient or incorrect. * Note that you should always use * {@link Throwable#getLocalizedMessage()} to get a (possibly * localized) meaningful detail message. * @throws IOException if there is an I/O or format problem with the * keyStore data. * @throws CertificateException if any of the certificates in the * keyStore could not be loaded. * @throws NoSuchAlgorithmException if the algorithm used to check * the integrity of the keyStore cannot be found. * @throws UnrecoverableKeyException if the key cannot get recovered * (e.g. the given password is wrong). */ protected PrivateKey getPrivateKey() throws LicenseNotaryException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { if (null == privateKey) { final KeyStoreParam param = getKeyStoreParam(); final String keyPwd = param.getKeyPwd(); final String alias = param.getAlias(); if (null == keyPwd) throw new LicenseNotaryException(EXC_NO_KEY_PWD, alias); final KeyStore keystore = getKeyStore(); try { privateKey = (PrivateKey) keystore.getKey( alias, keyPwd.toCharArray()); } catch (KeyStoreException keystoreIsAlreadyLoaded) { throw new AssertionError(keystoreIsAlreadyLoaded); } if (null == privateKey) throw new LicenseNotaryException(EXC_NO_KEY_ENTRY, alias); } return privateKey; } /** * Returns the public key from the keyStore * * @throws LicenseNotaryException if the parameters used to access the * corresponding key store are insufficient or incorrect. * Note that you should always use * {@link Throwable#getLocalizedMessage()} to get a (possibly * localized) meaningful detail message. * @throws IOException if there is an I/O or format problem with the * keyStore data. * @throws CertificateException if any of the certificates in the * keyStore could not be loaded. * @throws NoSuchAlgorithmException if the algorithm used to check * the integrity of the keyStore cannot be found. */ protected PublicKey getPublicKey() throws LicenseNotaryException, IOException, CertificateException, NoSuchAlgorithmException { if (null == publicKey) { final String alias = getKeyStoreParam().getAlias(); final KeyStore keystore = getKeyStore(); try { if ((null != getKeyStoreParam().getKeyPwd()) != keystore.isKeyEntry(alias)) throw new LicenseNotaryException( EXC_PRIVATE_KEY_OR_PWD_IS_NOT_ALLOWED, alias); final Certificate cert = keystore.getCertificate(alias); if (null == cert) throw new LicenseNotaryException( EXC_NO_CERTIFICATE_ENTRY, alias); publicKey = cert.getPublicKey(); } catch (KeyStoreException keystoreIsAlreadyLoaded) { throw new AssertionError(keystoreIsAlreadyLoaded); } } return publicKey; } /** * Returns a valid signature engine to be used for signing and verifying * a {@link GenericCertificate} - {@code null} is never returned. */ protected Signature getSignatureEngine() { try { return Signature.getInstance(SHA1_WITH_DSA); } catch (NoSuchAlgorithmException ex) { throw new AssertionError(ex); } } /** * Returns a loaded/initialized keyStore. * * @throws IOException if there is an I/O or format problem with the * keyStore data. * @throws CertificateException if any of the certificates in the * keyStore could not be loaded. * @throws NoSuchAlgorithmException if the algorithm used to check * the integrity of the keyStore cannot be found. */ protected KeyStore getKeyStore() throws IOException, CertificateException, NoSuchAlgorithmException { if (null != keyStore) return keyStore; InputStream in = null; try { keyStore = KeyStore.getInstance(JKS); in = new BufferedInputStream(param.getStream(), BUFSIZE); keyStore.load(in, getKeyStoreParam().getStorePwd().toCharArray()); } catch (KeyStoreException cannotHappen) { throw new AssertionError(cannotHappen); } finally { if (null != in) in.close(); } return keyStore; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy