org.cryptonode.jncryptor.JNCryptorFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.cryptonode.jncryptor.JNCryptorFactory.java

Source

/*    Copyright 2013 Duncan Jones
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.cryptonode.jncryptor;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Factory used to obtain {@link JNCryptor} instances. A different instance is
 * available for each version of the original RNCryptor library. The most modern
 * implementation is always available by calling:
 * <p>
 * 
 * <pre>
 * RNCryptor cryptor = RNCryptorFactory.getCryptor();
 * </pre>
 * <p>
 * If a particular version is required, it can be obtained with the
 * {@link #getCryptor(int) getCryptor(int version)} method. A full list of the
 * available instances is obtained through the {@link #getCryptors()} method.
 * <p>
 * If the required version is not known, the ciphertext can be inspected to find
 * the correct version using the {@link #getCryptorForCiphertext(byte[])}
 * method. This method searches for the version byte stored in the ciphertext.
 */
public class JNCryptorFactory {

    private static final Logger LOGGER = LoggerFactory.getLogger(JNCryptorFactory.class);

    private static final SortedMap<Integer, JNCryptor> supportedVersions = new TreeMap<Integer, JNCryptor>();

    static {
        // Load classes defined in properties file
        try {
            InputStream in = JNCryptorFactory.class.getResourceAsStream("/jncryptor-classes.txt");

            if (in == null) {
                throw new IOException("Unable to read class list file.");
            }

            try {
                List<String> listOfClasses = IOUtils.readLines(in);
                for (String className : listOfClasses) {
                    Class.forName(className);
                    LOGGER.debug("Loaded class {}.", className);
                }
            } finally {
                in.close();
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * This is a class of static methods only.
     */
    private JNCryptorFactory() {
    }

    /**
     * Examines the first byte of the ciphertext, which contains a version byte.
     * Returns an {@link JNCryptor} that implements the settings for that version.
     * 
     * @param ciphertext
     *          the ciphertext to examine
     * @return an {@link JNCryptor} that supports that version
     * @throws ClassNotFoundException
     *           if the version does not correspond to a supported type
     */
    public static JNCryptor getCryptorForCiphertext(byte[] ciphertext) throws ClassNotFoundException {
        Validate.notNull(ciphertext, "Ciphertext cannot be null.");
        Validate.isTrue(ciphertext.length > 0, "Ciphertext cannot be zero length.");

        int version = ciphertext[0] & 0xFF; // Convert to unsigned int
        return getCryptor(version);
    }

    /**
     * Registers a mapping between a version number and an {@link JNCryptor}.
     * 
     * @param version
     *          the version number
     * @param cryptor
     *          the {@link JNCryptor}
     */
    static void registerCryptor(int version, JNCryptor cryptor) {
        if (supportedVersions.containsKey(version)) {
            throw new IllegalStateException(String.format("Support for version %#04x already exists.", version));
        }

        supportedVersions.put(version, cryptor);
        LOGGER.debug("Cryptor registered with support for version {}.", version);
    }

    /**
     * Retrieves an {@link JNCryptor} implementing the current data format
     * (determined by seeking the implementation with the largest version number).
     * 
     * @return an {@link JNCryptor}
     */
    public static JNCryptor getCryptor() {
        if (supportedVersions.isEmpty()) {
            throw new IllegalStateException("No implementations registered.");
        }

        return supportedVersions.get(supportedVersions.lastKey());
    }

    /**
     * Retrieves an {@link JNCryptor} implementing the specified version number.
     * 
     * @param version
     *          the version number. A positive number smaller than 256 (must be
     *          expressible in eight bits).
     * @return the {@link JNCryptor}
     * @throws ClassNotFoundException
     *           if no implementation exists for that version
     */
    public static JNCryptor getCryptor(int version) throws ClassNotFoundException {
        JNCryptor cryptor = supportedVersions.get(version);

        if (cryptor == null) {
            throw new ClassNotFoundException(String.format("No implementation found for version %d.", version));
        }

        return cryptor;
    }

    /**
     * Returns a list of the available {@link JNCryptor}s, arranged in ascending
     * order of version number.
     * 
     * @return an ordered list of {@code RNCryptor}s.
     */
    public static List<JNCryptor> getCryptors() {
        List<JNCryptor> result = new ArrayList<JNCryptor>(supportedVersions.size());

        for (Integer version : supportedVersions.keySet()) {
            result.add(supportedVersions.get(version));
        }

        return result;
    }

}