org.candlepin.common.config.EncryptedConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.candlepin.common.config.EncryptedConfiguration.java

Source

/**
 * Copyright (c) 2009 - 2012 Red Hat, Inc.
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 *
 * Red Hat trademarks are not licensed under GPLv2. No permission is
 * granted to use or replicate Red Hat trademarks that are incorporated
 * in this software or its documentation.
 */
package org.candlepin.common.config;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Properties;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * EncryptedValueConfigurationParser
 */
public class EncryptedConfiguration extends PropertiesFileConfiguration {
    private static Logger log = LoggerFactory.getLogger(EncryptedConfiguration.class);

    private String passphrase = null;

    public EncryptedConfiguration() {
        super();
    }

    public EncryptedConfiguration(String fileName) throws ConfigurationException {
        super(fileName);
    }

    public EncryptedConfiguration(String fileName, Charset encoding) throws ConfigurationException {
        super(fileName, encoding);
    }

    public EncryptedConfiguration(File file) throws ConfigurationException {
        super(file);
    }

    public EncryptedConfiguration(File file, Charset encoding) throws ConfigurationException {
        super(file, encoding);
    }

    public EncryptedConfiguration(InputStream inStream) throws ConfigurationException {
        super(inStream);
    }

    public EncryptedConfiguration(InputStream inStream, Charset encoding) throws ConfigurationException {
        super(inStream, encoding);
    }

    public EncryptedConfiguration(Properties properties) {
        super(properties);
    }

    public EncryptedConfiguration use(String passphraseProperty) throws ConfigurationException {
        passphrase = readPassphrase(passphraseProperty);
        return this;
    }

    public void toDecrypt(String... encryptedProperties) throws ConfigurationException {
        for (String p : encryptedProperties) {
            toDecrypt(p);
        }
    }

    public void toDecrypt(String property) throws ConfigurationException {
        if (passphrase == null) {
            log.debug("Passphrase is null.  Skipping decrypt.");
            return;
        }

        if (!containsKey(property)) {
            log.debug("Can't decrypt missing property: {}", property);
            return;
        }

        String toDecrypt = getString(property);
        if (!toDecrypt.startsWith("$1$")) {
            // this is not an encrypted password, just return it
            log.debug("Value for {} is not an encrypted string", property);
            return;
        }

        // remove the magic string
        toDecrypt = toDecrypt.substring(3);

        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            // NOTE: we are creating a 64byte digest here,
            // but only the first 16 bytes are used as the iv
            String ivString = passphrase + passphrase;
            String iv = DigestUtils.sha256Hex(ivString);
            String passphraseDigest = DigestUtils.sha256Hex(passphrase);

            // FIXME: katello-password creates a 64 byte key, but somewhere
            // it gets truncated to 32 bytes, so we have to do that here.
            SecretKeySpec spec = new SecretKeySpec(Arrays.copyOfRange(passphraseDigest.getBytes(), 0, 32), "AES");

            cipher.init(Cipher.DECRYPT_MODE, spec, new IvParameterSpec(iv.getBytes(), 0, 16));

            // NOTE: the encrypted password is stored hex base64
            byte[] b64bytes = Base64.decodeBase64(toDecrypt);
            String decrypted = new String(cipher.doFinal(b64bytes));
            setProperty(property, decrypted);
        } catch (Exception e) {
            log.error("Failure trying to decrypt value of {}", property, e);
            throw new ConfigurationException(e);
        }

    }

    protected String readPassphrase(String passphraseProperty) throws ConfigurationException {
        if (!containsKey(passphraseProperty)) {
            log.info("No secret file provided.");
            return null;
        }

        String secretFile = getString(passphraseProperty);

        if (StringUtils.isEmpty(secretFile)) {
            log.warn("{} is present in the configuration but unset", passphraseProperty);
            return null;
        }

        log.debug("reading secret file: {}", secretFile);

        try {
            /* XXX Maybe it'd be better to use the charset the caller specifies during
             * construction?  But just because the config is in that charset doesn't mean
             * the password file is.  Stick with system default for now. */
            InputStream bs = new FileInputStream(secretFile);
            return IOUtils.toString(bs, Charset.defaultCharset().name());
        } catch (Exception e) {
            String msg = String.format("Could not read: %s", secretFile);
            log.error(msg);
            throw new ConfigurationException(msg, e);
        }
    }
}