com.jaspersoft.jasperserver.api.security.encryption.EncryptionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspersoft.jasperserver.api.security.encryption.EncryptionManager.java

Source

/*
 * Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License  as
 * published by the Free Software Foundation, either version 3 of  the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero  General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public  License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.jaspersoft.jasperserver.api.security.encryption;

import com.jaspersoft.jasperserver.api.security.SecurityConfiguration;
import org.apache.log4j.Logger;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V1CertificateGenerator;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLDecoder;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;

import static com.jaspersoft.jasperserver.api.security.SecurityConfiguration.getEncryptionKeySize;
import static com.jaspersoft.jasperserver.api.security.SecurityConfiguration.getEncryptionParameters;
import static com.jaspersoft.jasperserver.api.security.SecurityConfiguration.getEncryptionType;
import static com.jaspersoft.jasperserver.api.security.SecurityConfiguration.isEncryptionOn;

/**
 * EncryptionManager is in charge of keeping track of encryption configuration
 * such as on/off, defaults, etc.  It will also have a handle to the type of
 * encryption being employed based on the configuration.
 *
 * @author nmacaraeg
 *         Date Time: 2/8/12 11:13 AM
 */
public class EncryptionManager {
    private static final Logger logger = Logger.getLogger(EncryptionManager.class);
    public static final String KEYPAIR_SESSION_KEY = "KEYPAIR_SESSION_KEY";
    public static final String UTF8_ENCODING = "UTF-8";
    private static Encryption encryption;

    private enum EncryptionTypes {
        RSA
    }

    static {
        //        if (EncryptionTypes.RSA.toString().equalsIgnoreCase(getEncryptionType()))
        encryption = new EncryptionRSA();
    }

    public boolean isEncryptionProcessOn() {
        return isEncryptionOn();
    }

    /**
     * Generates public and private keys. Public key gets stored into the session - public is sent over the wire
     * to the browser.
     *
     * @param isDynamicKeygenPerRequest - if true, generate the keys per every request.  If false, read keys from keystore
     * @throws java.io.IOException
     */
    public static KeyPair generateKeys(boolean isDynamicKeygenPerRequest) throws IOException, KeyStoreException {
        if (isDynamicKeygenPerRequest) {
            logger.debug("Generated keys on the fly successfully.");
            return encryption.generateKeypair(getEncryptionKeySize());
            //httpRequest.getSession().setAttribute(KEYPAIR_SESSION_KEY, keys);
        } else {
            synchronized (EncryptionManager.class) { //protect reading key store from race conditions
                InputStream is = null;
                OutputStream os = null;

                try {

                    String keystoreLocation = SecurityConfiguration.getKeystoreLocation();
                    String keystorePassword = SecurityConfiguration.getKeystorePassword();
                    String keyAlias = SecurityConfiguration.getKeystoreKeyAlias();
                    String keyPassword = SecurityConfiguration.getKeystoreKeyPassword();

                    if (keystoreLocation == null || keystoreLocation.trim().length() == 0 || keyAlias == null
                            || keyAlias.trim().length() == 0)
                        throw new RuntimeException("At least keystore location and key alias is required.  "
                                + "See docs on setting it up.");

                    File keystoreFile = null;
                    final URL keystoreResource = EncryptionManager.class.getClassLoader()
                            .getResource(keystoreLocation);
                    is = keystoreResource.openStream();
                    if (is == null) {//if not on the classpath, look for an external file on the system
                        keystoreFile = new File(keystoreLocation);
                        is = new FileInputStream(keystoreLocation);
                    } else
                        keystoreFile = new File(keystoreResource.toURI());

                    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                    keyStore.load(is, keystorePassword.toCharArray());

                    PublicKey publicKey = null;
                    PrivateKey privateKey = null;
                    KeyPair returnKeyPair = null;
                    if (keyStore.containsAlias(keyAlias)) {
                        KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore
                                .getEntry(keyAlias, new KeyStore.PasswordProtection(keyPassword.toCharArray()));

                        privateKey = privateKeyEntry.getPrivateKey();
                        publicKey = privateKeyEntry.getCertificate().getPublicKey();
                        returnKeyPair = new KeyPair(publicKey, privateKey);
                    } else {
                        if (is != null)
                            is.close();

                        os = new FileOutputStream(keystoreFile);

                        //generate private/public key pair
                        returnKeyPair = encryption.generateKeypair(getEncryptionKeySize());
                        Certificate certificate = createCertificate(returnKeyPair);
                        keyStore.setKeyEntry(keyAlias, returnKeyPair.getPrivate(), keyPassword.toCharArray(),
                                new Certificate[] { certificate });

                        keyStore.store(os, keystorePassword.toCharArray());
                    }

                    logger.debug("Red/generated key pair (" + keyAlias + ") in keystore ("
                            + keystoreFile.getAbsolutePath() + ") successfully.");
                    return returnKeyPair;
                } catch (Exception e) {
                    throw new RuntimeException("Exception loading keystore and reading the keys.", e);
                } finally {
                    if (is != null)
                        is.close();
                    if (os != null)
                        os.close();
                }
            }
        }
    }

    /**
     * Certificate (version 1) generated using Bouncy Castle lib.
     *
     * Reference: http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation
     *
     * @param keyPair
     * @return
     */
    private static Certificate createCertificate(KeyPair keyPair) {
        try {
            Calendar expiry = Calendar.getInstance();
            expiry.add(Calendar.YEAR, 100);

            X509V1CertificateGenerator certGenerator = new X509V1CertificateGenerator();
            final X509Principal dnName = new X509Principal("CN=Jaspersoft Inc.");
            certGenerator.setSubjectDN(dnName);
            certGenerator.setIssuerDN(dnName);
            certGenerator.setNotAfter(expiry.getTime());
            certGenerator.setNotBefore(new Date());
            certGenerator.setPublicKey(keyPair.getPublic());
            certGenerator.setSerialNumber(BigInteger.valueOf(expiry.getTimeInMillis()));
            certGenerator.setSignatureAlgorithm("MD5withRSA");

            return certGenerator.generate(keyPair.getPrivate(), BouncyCastleProvider.PROVIDER_NAME);
        } catch (Exception e) {
            logger.error("Error creating certificate.", e);
            throw new RuntimeException("Error creating certificate.", e);
        }
    }

    /*
    * Decrypt an encrypted string which is tied to the session's KeyPair.
    * The encryption used the KeyPair's public key.
    * The decryption will use the KeyPair's private key.
    *
    * @param httpRequest a HttpServletRequest.
    * @param attribute the attribute key to set in the request.
    * @param encryptedValue an encrypted string value tied to the session's keypair.
    *
    * @return list of decrypted params.  If any of the params fail decryption, RunTimeException is thrown.
    */
    public List<String> decrypt(PrivateKey privateKey, String... encryptedValues) {
        List<String> retList = new ArrayList<String>(encryptedValues.length);
        try {
            if (privateKey != null) {
                for (String encryptedValue : encryptedValues) {
                    if (encryptedValue.trim().length() == 0) {
                        retList.add(encryptedValue);
                        continue;
                    }

                    String decryptedValue = encryption.decrypt(encryptedValue, privateKey);

                    if (decryptedValue != null && decryptedValue.length() > 0) {
                        try {
                            decryptedValue = URLDecoder.decode(decryptedValue, UTF8_ENCODING);

                        } catch (UnsupportedEncodingException e) {
                            logger.error(UTF8_ENCODING + " encoding not supported");
                        }
                    } else {
                        decryptedValue = encryptedValue;
                        logger.warn("Cannot decrypt encryptedValue (hidden for security reasons).");
                    }

                    retList.add(decryptedValue);
                }

                return retList;
            } else {
                throw new RuntimeException("privateKey is NULL when attempting to decrypt!");
            }
        } catch (Exception e) {
            logger.warn("failed to decrypt. assuming plain text ");
            retList.addAll(Arrays.asList(encryptedValues));
            return retList;
        }
    }

    /**
     * @return true only if the param is in the set of enc properties in SecurityConfiguration.
     */
    public static boolean isEncryptedParam(String param) {
        return getEncryptionParameters().contains(param);
    }

    /**
     * @return true only if the param is in the map.
     */
    public static boolean maybeEncryptedJSONParam(String param) {
        Set<String> encParamSet = getEncryptionParameters();
        for (String encParam : encParamSet) {
            if (encParam.startsWith(param))
                return true;
        }

        return false;
    }

    /*
    * Creates the JSONObject response which is expected by the encrypt method
    * in jcryption.js.  This is the public key.
    */
    protected static JSONObject buildPublicKeyJSON(PublicKey pubKey) throws IOException {
        try {
            if (encryption instanceof EncryptionRSA) {
                String exponent = EncryptionRSA.getPublicKeyExponent(pubKey);
                String modulus = EncryptionRSA.getPublicKeyModulus(pubKey);
                String maxDigits = String.valueOf(EncryptionRSA.getMaxDigits(getEncryptionKeySize()));

                JSONObject jsonObj = new JSONObject();
                jsonObj.put("e", exponent);
                jsonObj.put("n", modulus);
                jsonObj.put("maxdigits", maxDigits);

                return jsonObj;
            } else
                throw new RuntimeException("Encryption algorithm" + getEncryptionType() + " is not implemented.");
        } catch (Exception e) {
            throw new RuntimeException("Unable to respond with a key pair.", e);
        }
    }

}