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

org.apache.commons.crypto.cipher.OpenSsl Maven / Gradle / Ivy

 /*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.commons.crypto.cipher;

import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;

import org.apache.commons.crypto.Crypto;
import org.apache.commons.crypto.utils.Transformation;
import org.apache.commons.crypto.utils.Utils;

/**
 * OpenSSL cryptographic wrapper using JNI. Currently only AES-CTR is supported.
 * It's flexible to add other crypto algorithms/modes.
 */
final class OpenSsl {

    /** Currently only support AES/CTR/NoPadding. */
    private enum AlgorithmMode {
        AES_CTR, AES_CBC, AES_GCM;

        /**
         * Gets the mode.
         *
         * @param algorithm the algorithm.
         * @param mode the mode.
         * @return the Algorithm mode.
         * @throws NoSuchAlgorithmException if the algorithm is not available.
         */
        static int get(final String algorithm, final String mode) throws NoSuchAlgorithmException {
            try {
                return AlgorithmMode.valueOf(algorithm + "_" + mode).ordinal();
            } catch (final Exception e) {
                throw new NoSuchAlgorithmException("Algorithm not supported: " + algorithm + " and mode: " + mode);
            }
        }
    }
    // Mode constant defined by OpenSsl JNI
    public static final int ENCRYPT_MODE = 1;

    public static final int DECRYPT_MODE = 0;

    private static final Throwable loadingFailureReason;

    static {
        Throwable loadingFailure = null;
        try {
            if (Crypto.isNativeCodeLoaded()) {
                OpenSslNative.initIDs();
            } else {
                loadingFailure = Crypto.getLoadingError();
            }
        } catch (final Exception | UnsatisfiedLinkError t) {
            loadingFailure = t;
        } finally {
            loadingFailureReason = loadingFailure;
        }
    }

    /**
     * Gets an {@code OpenSslCipher} that implements the specified
     * transformation.
     *
     * @param transformation the name of the transformation, e.g.,
     *        AES/CTR/NoPadding.
     * @return OpenSslCipher an {@code OpenSslCipher} object
     * @throws NoSuchAlgorithmException if {@code transformation} is null,
     *         empty, in an invalid format, or if OpenSsl doesn't implement the
     *         specified algorithm.
     * @throws NoSuchPaddingException if {@code transformation} contains a
     *         padding scheme that is not available.
     * @throws IllegalStateException if native code cannot be initialized
     */
    public static OpenSsl getInstance(final String transformation)
            throws NoSuchAlgorithmException, NoSuchPaddingException {
        if (loadingFailureReason != null) {
            throw new IllegalStateException(loadingFailureReason);
        }
        final Transformation transform = Transformation.parse(transformation);
        final int algorithmMode = AlgorithmMode.get(transform.getAlgorithm(), transform.getMode());
        final int padding = transform.getPadding().ordinal();
        final long context = OpenSslNative.initContext(algorithmMode, padding);
        return new OpenSsl(context, algorithmMode, padding);
    }

    /**
     * Gets the failure reason when loading OpenSsl native.
     *
     * @return the failure reason; null if it was loaded and initialized successfully
     */
    public static Throwable getLoadingFailureReason() {
        return loadingFailureReason;
    }

    private final AbstractOpenSslFeedbackCipher opensslBlockCipher;

    /**
     * Constructs a {@link OpenSsl} instance based on context, algorithm and padding.
     *
     * @param context the context.
     * @param algorithm the algorithm.
     * @param padding the padding.
     */
    private OpenSsl(final long context, final int algorithm, final int padding) {
        if (algorithm == AlgorithmMode.AES_GCM.ordinal()) {
            opensslBlockCipher = new OpenSslGaloisCounterMode(context, algorithm, padding);
        } else {
            opensslBlockCipher = new OpenSslCommonMode(context, algorithm, padding);
        }
    }

    /** Forcibly clean the context. */
    public void clean() {
        if (opensslBlockCipher != null) {
            opensslBlockCipher.clean();
        }
    }

    /**
     * Finalizes to encrypt or decrypt data in a single-part operation, or finishes a
     * multiple-part operation.
     *
     * @param input the input byte array
     * @param inputOffset the offset in input where the input starts
     * @param inputLen the input length
     * @param output the byte array for the result
     * @param outputOffset the offset in output where the result is stored
     * @return the number of bytes stored in output
     * @throws ShortBufferException if the given output byte array is too small
     *         to hold the result
     * @throws BadPaddingException if this cipher is in decryption mode, and
     *         (un)padding has been requested, but the decrypted data is not
     *         bounded by the appropriate padding bytes
     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
     *         padding has been requested (only in encryption mode), and the
     *         total input length of the data processed by this cipher is not a
     *         multiple of block size; or if this encryption algorithm is unable
     *         to process the input data provided.
     */
    public int doFinal(final byte[] input, final int inputOffset, final int inputLen, final byte[] output, final int outputOffset)
            throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
        return opensslBlockCipher.doFinal(input, inputOffset, inputLen, output, outputOffset);
    }

    /**
     * Finishes a multiple-part operation. The data is encrypted or decrypted,
     * depending on how this cipher was initialized.
     *
     * 

* The result is stored in the output buffer. Upon return, the output * buffer's position will have advanced by n, where n is the value returned * by this method; the output buffer's limit will not have changed. *

* *

* If {@code output.remaining()} bytes are insufficient to hold the * result, a {@code ShortBufferException} is thrown. *

* *

* Upon finishing, this method resets this cipher object to the state it was * in when previously initialized. That is, the object is available to * encrypt or decrypt more data. *

* * If any exception is thrown, this cipher object need to be reset before it * can be used again. * * @param input the input ByteBuffer * @param output the output ByteBuffer * @return int number of bytes stored in {@code output} * @throws ShortBufferException if the given output byte array is too small * to hold the result. * @throws IllegalBlockSizeException if this cipher is a block cipher, no * padding has been requested (only in encryption mode), and the * total input length of the data processed by this cipher is not a * multiple of block size; or if this encryption algorithm is unable * to process the input data provided. * @throws BadPaddingException if this cipher is in decryption mode, and * (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes */ public int doFinal(final ByteBuffer input, final ByteBuffer output) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { Utils.checkArgument(output.isDirect(), "Direct buffer is required."); return opensslBlockCipher.doFinal(input, output); } @Override protected void finalize() throws Throwable { clean(); } /** * Initializes this cipher with a key and IV. * * @param mode {@link #ENCRYPT_MODE} or {@link #DECRYPT_MODE} * @param key crypto key * @param params the algorithm parameters * @throws InvalidAlgorithmParameterException if IV length is wrong */ public void init(final int mode, final byte[] key, final AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { opensslBlockCipher.init(mode, key, params); } /** * Updates a multiple-part encryption/decryption operation. The data is * encrypted or decrypted, depending on how this cipher was initialized. * * @param input the input byte array * @param inputOffset the offset in input where the input starts * @param inputLen the input length * @param output the byte array for the result * @param outputOffset the offset in output where the result is stored * @return the number of bytes stored in output * @throws ShortBufferException if there is insufficient space in the output * byte array */ public int update(final byte[] input, final int inputOffset, final int inputLen, final byte[] output, final int outputOffset) throws ShortBufferException { return opensslBlockCipher.update(input, inputOffset, inputLen, output, outputOffset); } /** * Updates a multiple-part encryption or decryption operation. The data is * encrypted or decrypted, depending on how this cipher was initialized. * *

* All {@code input.remaining()} bytes starting at * {@code input.position()} are processed. The result is stored in the * output buffer. *

* *

* Upon return, the input buffer's position will be equal to its limit; its * limit will not have changed. The output buffer's position will have * advanced by n, when n is the value returned by this method; the output * buffer's limit will not have changed. *

* * If {@code output.remaining()} bytes are insufficient to hold the * result, a {@code ShortBufferException} is thrown. * * @param input the input ByteBuffer * @param output the output ByteBuffer * @return int number of bytes stored in {@code output} * @throws ShortBufferException if there is insufficient space in the output * buffer */ public int update(final ByteBuffer input, final ByteBuffer output) throws ShortBufferException { Utils.checkArgument(input.isDirect() && output.isDirect(), "Direct buffers are required."); return opensslBlockCipher.update(input, output); } /** * Continues a multi-part update of the Additional Authentication * Data (AAD). *

* Calls to this method provide AAD to the cipher when operating in * modes such as AEAD (GCM). If this cipher is operating in * either GCM mode, all AAD must be supplied before beginning * operations on the ciphertext (via the {@code update} and * {@code doFinal} methods). *

* * @param aad the buffer containing the Additional Authentication Data */ public void updateAAD(final byte[] aad) { this.opensslBlockCipher.updateAAD(aad); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy