OpenSsl.java
- /*
- * 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.
- *
- * <p>
- * 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.
- * </p>
- *
- * <p>
- * If {@code output.remaining()} bytes are insufficient to hold the
- * result, a {@code ShortBufferException} is thrown.
- * </p>
- *
- * <p>
- * 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.
- * </p>
- *
- * 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.
- *
- * <p>
- * All {@code input.remaining()} bytes starting at
- * {@code input.position()} are processed. The result is stored in the
- * output buffer.
- * </p>
- *
- * <p>
- * 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.
- * </p>
- *
- * 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).
- * <p>
- * 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).
- * </p>
- *
- * @param aad the buffer containing the Additional Authentication Data
- */
- public void updateAAD(final byte[] aad) {
- this.opensslBlockCipher.updateAAD(aad);
- }
- }