View Javadoc
1   /*
2    * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.crypto.provider;
27  
28  import java.util.Arrays;
29  import java.security.*;
30  import java.security.spec.*;
31  import javax.crypto.*;
32  import javax.crypto.spec.*;
33  
34  /**
35   * This class implements the AES KeyWrap algorithm as defined
36   * in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>
37   * "XML Encryption Syntax and Processing" section 5.6.3 "AES Key Wrap".
38   * Note: only <code>ECB</code> mode and <code>NoPadding</code> padding
39   * can be used for this algorithm.
40   *
41   * @author Valerie Peng
42   *
43   *
44   * @see AESCipher
45   */
46  abstract class AESWrapCipher extends CipherSpi {
47      public static final class General extends AESWrapCipher {
48          public General() {
49              super(-1);
50          }
51      }
52      public static final class AES128 extends AESWrapCipher {
53          public AES128() {
54              super(16);
55          }
56      }
57      public static final class AES192 extends AESWrapCipher {
58          public AES192() {
59              super(24);
60          }
61      }
62      public static final class AES256 extends AESWrapCipher {
63          public AES256() {
64              super(32);
65          }
66      }
67      private static final byte[] IV = {
68          (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
69          (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
70      };
71  
72      private static final int blksize = AESConstants.AES_BLOCK_SIZE;
73  
74      /*
75       * internal cipher object which does the real work.
76       */
77      private AESCrypt cipher;
78  
79      /*
80       * are we encrypting or decrypting?
81       */
82      private boolean decrypting = false;
83  
84      /*
85       * needed to support AES oids which associates a fixed key size
86       * to the cipher object.
87       */
88      private final int fixedKeySize; // in bytes, -1 if no restriction
89  
90      /**
91       * Creates an instance of AES KeyWrap cipher with default
92       * mode, i.e. "ECB" and padding scheme, i.e. "NoPadding".
93       */
94      public AESWrapCipher(int keySize) {
95          cipher = new AESCrypt();
96          fixedKeySize = keySize;
97  
98      }
99  
100     /**
101      * Sets the mode of this cipher. Only "ECB" mode is accepted for this
102      * cipher.
103      *
104      * @param mode the cipher mode
105      *
106      * @exception NoSuchAlgorithmException if the requested cipher mode
107      * is not "ECB".
108      */
109     protected void engineSetMode(String mode)
110         throws NoSuchAlgorithmException {
111         if (!mode.equalsIgnoreCase("ECB")) {
112             throw new NoSuchAlgorithmException(mode + " cannot be used");
113         }
114     }
115 
116     /**
117      * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
118      * is accepted for this cipher.
119      *
120      * @param padding the padding mechanism
121      *
122      * @exception NoSuchPaddingException if the requested padding mechanism
123      * is not "NoPadding".
124      */
125     protected void engineSetPadding(String padding)
126         throws NoSuchPaddingException {
127         if (!padding.equalsIgnoreCase("NoPadding")) {
128             throw new NoSuchPaddingException(padding + " cannot be used");
129         }
130     }
131 
132     /**
133      * Returns the block size (in bytes). i.e. 16 bytes.
134      *
135      * @return the block size (in bytes), i.e. 16 bytes.
136      */
137     protected int engineGetBlockSize() {
138         return blksize;
139     }
140 
141     /**
142      * Returns the length in bytes that an output buffer would need to be
143      * given the input length <code>inputLen</code> (in bytes).
144      *
145      * <p>The actual output length of the next <code>update</code> or
146      * <code>doFinal</code> call may be smaller than the length returned
147      * by this method.
148      *
149      * @param inputLen the input length (in bytes)
150      *
151      * @return the required output buffer size (in bytes)
152      */
153     protected int engineGetOutputSize(int inputLen) {
154         // can only return an upper-limit if not initialized yet.
155         int result = 0;
156         if (decrypting) {
157             result = inputLen - 8;
158         } else {
159             result = inputLen + 8;
160         }
161         return (result < 0? 0:result);
162     }
163 
164     /**
165      * Returns the initialization vector (IV) which is null for this cipher.
166      *
167      * @return null for this cipher.
168      */
169     protected byte[] engineGetIV() {
170         return null;
171     }
172 
173     /**
174      * Initializes this cipher with a key and a source of randomness.
175      *
176      * <p>The cipher only supports the following two operation modes:<b>
177      * Cipher.WRAP_MODE, and <b>
178      * Cipher.UNWRAP_MODE.
179      * <p>For modes other than the above two, UnsupportedOperationException
180      * will be thrown.
181      *
182      * @param opmode the operation mode of this cipher. Only
183      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
184      * @param key the secret key.
185      * @param random the source of randomness.
186      *
187      * @exception InvalidKeyException if the given key is inappropriate for
188      * initializing this cipher.
189      */
190     protected void engineInit(int opmode, Key key, SecureRandom random)
191         throws InvalidKeyException {
192         if (opmode == Cipher.WRAP_MODE) {
193             decrypting = false;
194         } else if (opmode == Cipher.UNWRAP_MODE) {
195             decrypting = true;
196         } else {
197             throw new UnsupportedOperationException("This cipher can " +
198                 "only be used for key wrapping and unwrapping");
199         }
200         AESCipher.checkKeySize(key, fixedKeySize);
201         cipher.init(decrypting, key.getAlgorithm(), key.getEncoded());
202     }
203 
204     /**
205      * Initializes this cipher with a key, a set of algorithm parameters,
206      * and a source of randomness.
207      *
208      * <p>The cipher only supports the following two operation modes:<b>
209      * Cipher.WRAP_MODE, and <b>
210      * Cipher.UNWRAP_MODE.
211      * <p>For modes other than the above two, UnsupportedOperationException
212      * will be thrown.
213      *
214      * @param opmode the operation mode of this cipher. Only
215      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
216      * @param key the secret key.
217      * @param params the algorithm parameters; must be null for this cipher.
218      * @param random the source of randomness.
219      *
220      * @exception InvalidKeyException if the given key is inappropriate for
221      * initializing this cipher
222      * @exception InvalidAlgorithmParameterException if the given algorithm
223      * parameters is not null.
224      */
225     protected void engineInit(int opmode, Key key,
226                               AlgorithmParameterSpec params,
227                               SecureRandom random)
228         throws InvalidKeyException, InvalidAlgorithmParameterException {
229         if (params != null) {
230             throw new InvalidAlgorithmParameterException("This cipher " +
231                 "does not accept any parameters");
232         }
233         engineInit(opmode, key, random);
234     }
235 
236     /**
237      * Initializes this cipher with a key, a set of algorithm parameters,
238      * and a source of randomness.
239      *
240      * <p>The cipher only supports the following two operation modes:<b>
241      * Cipher.WRAP_MODE, and <b>
242      * Cipher.UNWRAP_MODE.
243      * <p>For modes other than the above two, UnsupportedOperationException
244      * will be thrown.
245      *
246      * @param opmode the operation mode of this cipher. Only
247      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
248      * @param key the secret key.
249      * @param params the algorithm parameters; must be null for this cipher.
250      * @param random the source of randomness.
251      *
252      * @exception InvalidKeyException if the given key is inappropriate.
253      * @exception InvalidAlgorithmParameterException if the given algorithm
254      * parameters is not null.
255      */
256     protected void engineInit(int opmode, Key key,
257                               AlgorithmParameters params,
258                               SecureRandom random)
259         throws InvalidKeyException, InvalidAlgorithmParameterException {
260         if (params != null) {
261             throw new InvalidAlgorithmParameterException("This cipher " +
262                 "does not accept any parameters");
263         }
264         engineInit(opmode, key, random);
265     }
266 
267     /**
268      * This operation is not supported by this cipher.
269      * Since it's impossible to initialize this cipher given the
270      * current Cipher.engineInit(...) implementation,
271      * IllegalStateException will always be thrown upon invocation.
272      *
273      * @param in the input buffer.
274      * @param inOffset the offset in <code>in</code> where the input
275      * starts.
276      * @param inLen the input length.
277      *
278      * @return n/a.
279      *
280      * @exception IllegalStateException upon invocation of this method.
281      */
282     protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
283         throw new IllegalStateException("Cipher has not been initialized");
284     }
285 
286     /**
287      * This operation is not supported by this cipher.
288      * Since it's impossible to initialize this cipher given the
289      * current Cipher.engineInit(...) implementation,
290      * IllegalStateException will always be thrown upon invocation.
291      *
292      * @param in the input buffer.
293      * @param inOffset the offset in <code>in</code> where the input
294      * starts.
295      * @param inLen the input length.
296      * @param out the buffer for the result.
297      * @param outOffset the offset in <code>out</code> where the result
298      * is stored.
299      *
300      * @return n/a.
301      *
302      * @exception IllegalStateException upon invocation of this method.
303      */
304     protected int engineUpdate(byte[] in, int inOffset, int inLen,
305                                byte[] out, int outOffset)
306         throws ShortBufferException {
307         throw new IllegalStateException("Cipher has not been initialized");
308     }
309 
310     /**
311      * This operation is not supported by this cipher.
312      * Since it's impossible to initialize this cipher given the
313      * current Cipher.engineInit(...) implementation,
314      * IllegalStateException will always be thrown upon invocation.
315      *
316      * @param in the input buffer
317      * @param inOffset the offset in <code>in</code> where the input
318      * starts
319      * @param inLen the input length.
320      *
321      * @return n/a.
322      *
323      * @exception IllegalStateException upon invocation of this method.
324      */
325     protected byte[] engineDoFinal(byte[] input, int inputOffset,
326                                    int inputLen)
327         throws IllegalBlockSizeException, BadPaddingException {
328         throw new IllegalStateException("Cipher has not been initialized");
329     }
330 
331     /**
332      * This operation is not supported by this cipher.
333      * Since it's impossible to initialize this cipher given the
334      * current Cipher.engineInit(...) implementation,
335      * IllegalStateException will always be thrown upon invocation.
336      *
337      * @param in the input buffer.
338      * @param inOffset the offset in <code>in</code> where the input
339      * starts.
340      * @param inLen the input length.
341      * @param out the buffer for the result.
342      * @param outOffset the ofset in <code>out</code> where the result
343      * is stored.
344      *
345      * @return n/a.
346      *
347      * @exception IllegalStateException upon invocation of this method.
348      */
349     protected int engineDoFinal(byte[] in, int inOffset, int inLen,
350                                 byte[] out, int outOffset)
351         throws IllegalBlockSizeException, ShortBufferException,
352                BadPaddingException {
353         throw new IllegalStateException("Cipher has not been initialized");
354     }
355 
356     /**
357      * Returns the parameters used with this cipher which is always null
358      * for this cipher.
359      *
360      * @return null since this cipher does not use any parameters.
361      */
362     protected AlgorithmParameters engineGetParameters() {
363         return null;
364     }
365 
366     /**
367      * Returns the key size of the given key object in number of bits.
368      *
369      * @param key the key object.
370      *
371      * @return the "effective" key size of the given key object.
372      *
373      * @exception InvalidKeyException if <code>key</code> is invalid.
374      */
375     protected int engineGetKeySize(Key key) throws InvalidKeyException {
376         byte[] encoded = key.getEncoded();
377         if (!AESCrypt.isKeySizeValid(encoded.length)) {
378             throw new InvalidKeyException("Invalid key length: " +
379                                           encoded.length + " bytes");
380         }
381         return encoded.length * 8;
382     }
383 
384     /**
385      * Wrap a key.
386      *
387      * @param key the key to be wrapped.
388      *
389      * @return the wrapped key.
390      *
391      * @exception IllegalBlockSizeException if this cipher is a block
392      * cipher, no padding has been requested, and the length of the
393      * encoding of the key to be wrapped is not a
394      * multiple of the block size.
395      *
396      * @exception InvalidKeyException if it is impossible or unsafe to
397      * wrap the key with this cipher (e.g., a hardware protected key is
398      * being passed to a software only cipher).
399      */
400     protected byte[] engineWrap(Key key)
401         throws IllegalBlockSizeException, InvalidKeyException {
402         byte[] keyVal = key.getEncoded();
403         if ((keyVal == null) || (keyVal.length == 0)) {
404             throw new InvalidKeyException("Cannot get an encoding of " +
405                                           "the key to be wrapped");
406         }
407         byte[] out = new byte[keyVal.length + 8];
408 
409         if (keyVal.length == 8) {
410             System.arraycopy(IV, 0, out, 0, IV.length);
411             System.arraycopy(keyVal, 0, out, IV.length, 8);
412             cipher.encryptBlock(out, 0, out, 0);
413         } else {
414             if (keyVal.length % 8 != 0) {
415                 throw new IllegalBlockSizeException("length of the " +
416                     "to be wrapped key should be multiples of 8 bytes");
417             }
418             System.arraycopy(IV, 0, out, 0, IV.length);
419             System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);
420             int N = keyVal.length/8;
421             byte[] buffer = new byte[blksize];
422             for (int j = 0; j < 6; j++) {
423                 for (int i = 1; i <= N; i++) {
424                     int T = i + j*N;
425                     System.arraycopy(out, 0, buffer, 0, IV.length);
426                     System.arraycopy(out, i*8, buffer, IV.length, 8);
427                     cipher.encryptBlock(buffer, 0, buffer, 0);
428                     for (int k = 1; T != 0; k++) {
429                         byte v = (byte) T;
430                         buffer[IV.length - k] ^= v;
431                         T >>>= 8;
432                     }
433                     System.arraycopy(buffer, 0, out, 0, IV.length);
434                     System.arraycopy(buffer, 8, out, 8*i, 8);
435                 }
436             }
437         }
438         return out;
439     }
440 
441     /**
442      * Unwrap a previously wrapped key.
443      *
444      * @param wrappedKey the key to be unwrapped.
445      *
446      * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
447      *
448      * @param wrappedKeyType the type of the wrapped key.
449      * This is one of <code>Cipher.SECRET_KEY</code>,
450      * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
451      *
452      * @return the unwrapped key.
453      *
454      * @exception NoSuchAlgorithmException if no installed providers
455      * can create keys of type <code>wrappedKeyType</code> for the
456      * <code>wrappedKeyAlgorithm</code>.
457      *
458      * @exception InvalidKeyException if <code>wrappedKey</code> does not
459      * represent a wrapped key of type <code>wrappedKeyType</code> for
460      * the <code>wrappedKeyAlgorithm</code>.
461      */
462     protected Key engineUnwrap(byte[] wrappedKey,
463                                String wrappedKeyAlgorithm,
464                                int wrappedKeyType)
465         throws InvalidKeyException, NoSuchAlgorithmException {
466         int wrappedKeyLen = wrappedKey.length;
467         // ensure the wrappedKey length is multiples of 8 bytes and non-zero
468         if (wrappedKeyLen == 0) {
469             throw new InvalidKeyException("The wrapped key is empty");
470         }
471         if (wrappedKeyLen % 8 != 0) {
472             throw new InvalidKeyException
473                 ("The wrapped key has invalid key length");
474         }
475         byte[] out = new byte[wrappedKeyLen - 8];
476         byte[] buffer = new byte[blksize];
477         if (wrappedKeyLen == 16) {
478             cipher.decryptBlock(wrappedKey, 0, buffer, 0);
479             for (int i = 0; i < IV.length; i++) {
480                 if (IV[i] != buffer[i]) {
481                     throw new InvalidKeyException("Integrity check failed");
482                 }
483             }
484             System.arraycopy(buffer, IV.length, out, 0, out.length);
485         } else {
486             System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);
487             System.arraycopy(wrappedKey, IV.length, out, 0, out.length);
488             int N = out.length/8;
489             for (int j = 5; j >= 0; j--) {
490                 for (int i = N; i > 0; i--) {
491                     int T = i + j*N;
492                     System.arraycopy(out, 8*(i-1), buffer, IV.length, 8);
493                     for (int k = 1; T != 0; k++) {
494                         byte v = (byte) T;
495                         buffer[IV.length - k] ^= v;
496                         T >>>= 8;
497                     }
498                     cipher.decryptBlock(buffer, 0, buffer, 0);
499                     System.arraycopy(buffer, IV.length, out, 8*(i-1), 8);
500                 }
501             }
502             for (int i = 0; i < IV.length; i++) {
503                 if (IV[i] != buffer[i]) {
504                     throw new InvalidKeyException("Integrity check failed");
505                 }
506             }
507         }
508         return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
509                                           wrappedKeyType);
510     }
511 }