View Javadoc
1    /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.commons.crypto.cipher;
19  
20  import java.nio.ByteBuffer;
21  import java.security.GeneralSecurityException;
22  import java.security.InvalidAlgorithmParameterException;
23  import java.security.InvalidKeyException;
24  import java.security.Key;
25  import java.security.spec.AlgorithmParameterSpec;
26  import java.util.Objects;
27  import java.util.Properties;
28  
29  import javax.crypto.BadPaddingException;
30  import javax.crypto.Cipher;
31  import javax.crypto.IllegalBlockSizeException;
32  import javax.crypto.ShortBufferException;
33  
34  /**
35   * Implements the CryptoCipher using JNI into OpenSSL.
36   * <p>
37   * this class is not public/protected so does not appear in the main Javadoc Please ensure that property use is documented in the enum
38   * CryptoRandomFactory.RandomProvider
39   * </p>
40   */
41  final class OpenSslCipher implements CryptoCipher {
42  
43      private final OpenSsl openSslEngine;
44      private boolean initialized;
45  
46      private final String transformation;
47  
48      /**
49       * Constructs a {@link CryptoCipher} using JNI into OpenSSL
50       *
51       * @param props  properties for OpenSSL openSslEngine (unused)
52       * @param transformation  transformation for OpenSSL openSslEngine (algorithm/mode/padding)
53       * @throws GeneralSecurityException if OpenSSL openSslEngine initialize failed
54       */
55      public OpenSslCipher(final Properties props, final String transformation) // NOPMD
56              throws GeneralSecurityException {
57          this.transformation = transformation;
58  
59          final Throwable loadingFailureReason = OpenSsl.getLoadingFailureReason();
60          if (loadingFailureReason != null) {
61              throw new IllegalStateException(loadingFailureReason);
62          }
63  
64          openSslEngine = OpenSsl.getInstance(transformation);
65      }
66  
67      /**
68       * Closes the OpenSSL openSslEngine. Clean the OpenSsl native context.
69       */
70      @Override
71      public void close() {
72          openSslEngine.clean();
73      }
74  
75      /**
76       * Encrypts or decrypts data in a single-part operation, or finishes a
77       * multiple-part operation.
78       *
79       * @param input the input byte array
80       * @param inputOffset the offset in input where the input starts
81       * @param inputLen the input length
82       * @param output the byte array for the result
83       * @param outputOffset the offset in output where the result is stored
84       * @return the number of bytes stored in output
85       * @throws ShortBufferException if the given output byte array is too small
86       *         to hold the result
87       * @throws BadPaddingException if this openSslEngine is in decryption mode, and
88       *         (un)padding has been requested, but the decrypted data is not
89       *         bounded by the appropriate padding bytes
90       * @throws IllegalBlockSizeException if this openSslEngine is a block openSslEngine, no
91       *         padding has been requested (only in encryption mode), and the
92       *         total input length of the data processed by this openSslEngine is not a
93       *         multiple of block size; or if this encryption algorithm is unable
94       *         to process the input data provided.
95       */
96      @Override
97      public int doFinal(final byte[] input, final int inputOffset, final int inputLen,
98              final byte[] output, final int outputOffset) throws ShortBufferException,
99              IllegalBlockSizeException, BadPaddingException {
100         return openSslEngine.doFinal(input, inputOffset, inputLen, output,outputOffset);
101     }
102 
103     /**
104      * Encrypts or decrypts data in a single-part operation, or finishes a
105      * multiple-part operation. The data is encrypted or decrypted, depending on
106      * how this openSslEngine was initialized.
107      *
108      * @param inBuffer the input ByteBuffer
109      * @param outBuffer the output ByteBuffer
110      * @return int number of bytes stored in {@code output}
111      * @throws BadPaddingException if this openSslEngine is in decryption mode, and
112      *         (un)padding has been requested, but the decrypted data is not
113      *         bounded by the appropriate padding bytes
114      * @throws IllegalBlockSizeException if this openSslEngine is a block openSslEngine, no
115      *         padding has been requested (only in encryption mode), and the
116      *         total input length of the data processed by this openSslEngine is not a
117      *         multiple of block size; or if this encryption algorithm is unable
118      *         to process the input data provided.
119      * @throws ShortBufferException if the given output buffer is too small to
120      *         hold the result
121      */
122     @Override
123     public int doFinal(final ByteBuffer inBuffer, final ByteBuffer outBuffer)
124             throws ShortBufferException, IllegalBlockSizeException,
125             BadPaddingException {
126         return openSslEngine.doFinal(inBuffer, outBuffer);
127     }
128 
129     /**
130      * Returns the algorithm name of this {@code CryptoCipher} object.
131      *
132      * <p>
133      * This is the same name that was specified in one of the
134      * {@code CryptoCipherFactory#getInstance} calls that created this
135      * {@code CryptoCipher} object..
136      * </p>
137      *
138      * @return the algorithm name of this {@code CryptoCipher} object.
139      */
140     @Override
141     public String getAlgorithm() {
142         return transformation;
143     }
144 
145     /**
146      * Returns the block size (in bytes).
147      *
148      * @return the block size (in bytes), or 0 if the underlying algorithm is
149      * not a block openSslEngine
150      */
151     @Override
152     public int getBlockSize() {
153         return CryptoCipherFactory.AES_BLOCK_SIZE;
154     }
155 
156     /**
157      * Initializes the openSslEngine with mode, key and iv.
158      *
159      * @param mode {@link Cipher#ENCRYPT_MODE} or {@link Cipher#DECRYPT_MODE}
160      * @param key crypto key for the openSslEngine
161      * @param params the algorithm parameters
162      * @throws InvalidKeyException If key length is invalid
163      * @throws InvalidAlgorithmParameterException if IV length is wrong
164      */
165     @Override
166     public void init(final int mode, final Key key, final AlgorithmParameterSpec params)
167             throws InvalidKeyException, InvalidAlgorithmParameterException {
168         Objects.requireNonNull(key, "key");
169         Objects.requireNonNull(params, "params");
170 
171         final int cipherMode = mode == Cipher.ENCRYPT_MODE ? OpenSsl.ENCRYPT_MODE: OpenSsl.DECRYPT_MODE;
172         openSslEngine.init(cipherMode, key.getEncoded(), params);
173         initialized = true;
174     }
175 
176     /**
177      * Continues a multiple-part encryption/decryption operation. The data is
178      * encrypted or decrypted, depending on how this openSslEngine was initialized.
179      *
180      * @param input the input byte array
181      * @param inputOffset the offset in input where the input starts
182      * @param inputLen the input length
183      * @param output the byte array for the result
184      * @param outputOffset the offset in output where the result is stored
185      * @return the number of bytes stored in output
186      * @throws ShortBufferException if there is insufficient space in the output
187      *         byte array
188      */
189     @Override
190     public int update(final byte[] input, final int inputOffset, final int inputLen,
191             final byte[] output, final int outputOffset) throws ShortBufferException {
192         return openSslEngine
193                 .update(input, inputOffset, inputLen, output, outputOffset);
194     }
195 
196 
197     /**
198      * Continues a multiple-part encryption/decryption operation. The data is
199      * encrypted or decrypted, depending on how this openSslEngine was initialized.
200      *
201      * @param inBuffer the input ByteBuffer
202      * @param outBuffer the output ByteBuffer
203      * @return int number of bytes stored in {@code output}
204      * @throws ShortBufferException if there is insufficient space in the output
205      *         buffer
206      */
207     @Override
208     public int update(final ByteBuffer inBuffer, final ByteBuffer outBuffer)
209             throws ShortBufferException {
210         return openSslEngine.update(inBuffer, outBuffer);
211     }
212 
213     /**
214      * Continues a multi-part update of the Additional Authentication
215      * Data (AAD).
216      * <p>
217      * Calls to this method provide AAD to the opensslEngine when operating in
218      * modes such as AEAD (GCM).  If this opensslEngine is operating in
219      * either GCM mode, all AAD must be supplied before beginning
220      * operations on the ciphertext (via the {@code update} and
221      * {@code doFinal} methods).
222      * </p>
223      *
224      * @param aad the buffer containing the Additional Authentication Data
225      *
226      * @throws IllegalArgumentException if the {@code aad}
227      * byte array is null
228      * @throws IllegalStateException if this opensslEngine is in a wrong state
229      * (e.g., has not been initialized), does not accept AAD, or if
230      * operating in either GCM mode and one of the {@code update}
231      * methods has already been called for the active
232      * encryption/decryption operation
233      * @throws UnsupportedOperationException if the implementation {@code opensslEngine}
234      * doesn't support this operation.
235      */
236     @Override
237     public void updateAAD(final byte[] aad) throws IllegalArgumentException,
238             IllegalStateException, UnsupportedOperationException {
239         if (aad == null) {
240             throw new IllegalArgumentException("aad buffer is null");
241         }
242         if (!initialized) {
243             throw new IllegalStateException("Cipher not initialized");
244         }
245         if (aad.length == 0) {
246             return;
247         }
248 
249         openSslEngine.updateAAD(aad);
250     }
251 
252 
253     /**
254      * Continues a multi-part update of the Additional Authentication
255      * Data (AAD).
256      * <p>
257      * Calls to this method provide AAD to the opensslEngine when operating in
258      * modes such as AEAD (GCM).  If this opensslEngine is operating in
259      * either GCM mode, all AAD must be supplied before beginning
260      * operations on the ciphertext (via the {@code update} and
261      * {@code doFinal} methods).
262      * </p>
263      *
264      * @param aad the buffer containing the Additional Authentication Data
265      *
266      * @throws IllegalArgumentException if the {@code aad}
267      * byte array is null
268      * @throws IllegalStateException if this opensslEngine is in a wrong state
269      * (e.g., has not been initialized), does not accept AAD, or if
270      * operating in either GCM mode and one of the {@code update}
271      * methods has already been called for the active
272      * encryption/decryption operation
273      * @throws UnsupportedOperationException if the implementation {@code opensslEngine}
274      * doesn't support this operation.
275      */
276     @Override
277     public void updateAAD(final ByteBuffer aad) throws IllegalArgumentException,
278             IllegalStateException, UnsupportedOperationException {
279         if (aad == null) {
280             throw new IllegalArgumentException("aad buffer is null");
281         }
282         if (!initialized) {
283             throw new IllegalStateException("Cipher not initialized");
284         }
285 
286         final int aadLen = aad.limit() - aad.position();
287         if (aadLen == 0) {
288             return;
289         }
290         final byte[] aadBytes = new byte[aadLen];
291         aad.get(aadBytes);
292         openSslEngine.updateAAD(aadBytes);
293     }
294 }