1 /* KeyWrappingAlgorithmAdapter.java -- Base Adapter for Key Wrapping algorithms
2    Copyright (C) 2006, 2010  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package gnu.javax.crypto.jce.cipher;
40 
41 import gnu.java.security.Configuration;
42 import gnu.java.security.Registry;
43 import gnu.javax.crypto.jce.spec.BlockCipherParameterSpec;
44 import gnu.javax.crypto.kwa.IKeyWrappingAlgorithm;
45 import gnu.javax.crypto.kwa.KeyUnwrappingException;
46 import gnu.javax.crypto.kwa.KeyWrappingAlgorithmFactory;
47 
48 import java.security.AlgorithmParameters;
49 import java.security.InvalidAlgorithmParameterException;
50 import java.security.InvalidKeyException;
51 import java.security.Key;
52 import java.security.KeyFactory;
53 import java.security.NoSuchAlgorithmException;
54 import java.security.SecureRandom;
55 import java.security.spec.AlgorithmParameterSpec;
56 import java.security.spec.InvalidKeySpecException;
57 import java.security.spec.InvalidParameterSpecException;
58 import java.security.spec.X509EncodedKeySpec;
59 import java.util.HashMap;
60 import java.util.Map;
61 import java.util.logging.Logger;
62 
63 import javax.crypto.BadPaddingException;
64 import javax.crypto.Cipher;
65 import javax.crypto.CipherSpi;
66 import javax.crypto.IllegalBlockSizeException;
67 import javax.crypto.NoSuchPaddingException;
68 import javax.crypto.ShortBufferException;
69 import javax.crypto.spec.IvParameterSpec;
70 import javax.crypto.spec.SecretKeySpec;
71 
72 /**
73  * An abstract base class to facilitate implementations of JCE Adapters for
74  * symmetric key block ciphers capable of providing key-wrapping functionality.
75  */
76 abstract class KeyWrappingAlgorithmAdapter
77     extends CipherSpi
78 {
79   private static final Logger log = Configuration.DEBUG ?
80         Logger.getLogger(KeyWrappingAlgorithmAdapter.class.getName()) : null;
81   /** JCE canonical name of a null-padder. */
82   private static final String NO_PADDING = "nopadding";
83   /** Concrete Key Wrapping Algorithm SPI. */
84   protected IKeyWrappingAlgorithm kwAlgorithm;
85   /** Size in bytes of the padding block to be provided by external padders. */
86   protected int kwaBlockSize;
87   /** KEK size in bytes. */
88   protected int kwaKeySize;
89   /** Name of the supported mode. */
90   protected String supportedMode;
91   /** Operational mode in which this instance was initialised. */
92   protected int opmode = -1;
93   /** Initialisation Vector if/when user wants to override default one. */
94   byte[] iv;
95 
96   /**
97    * Creates a new JCE Adapter for the designated Key Wrapping Algorithm name.
98    *
99    * @param name the canonical name of the key-wrapping algorithm.
100    * @param blockSize the block size in bytes of the underlying symmetric-key
101    *          block cipher algorithm.
102    * @param keySize the allowed size in bytes of the KEK bytes to initialise the
103    *          underlying symmetric-key block cipher algorithm with.
104    * @param supportedMode canonical name of the block mode the underlying cipher
105    *          is supporting.
106    */
KeyWrappingAlgorithmAdapter(String name, int blockSize, int keySize, String supportedMode)107   protected KeyWrappingAlgorithmAdapter(String name, int blockSize, int keySize,
108                                         String supportedMode)
109   {
110     super();
111 
112     this.kwAlgorithm = KeyWrappingAlgorithmFactory.getInstance(name);
113     this.kwaBlockSize = blockSize;
114     this.kwaKeySize = keySize;
115     this.supportedMode = supportedMode;
116   }
117 
118   /**
119    * Wraps the encoded form of a designated {@link Key}.
120    *
121    * @param key the key-material to wrap.
122    * @return the wrapped key.
123    * @throws InvalidKeyException If the key cannot be wrapped.
124    */
engineWrap(Key key)125   protected byte[] engineWrap(Key key)
126       throws InvalidKeyException, IllegalBlockSizeException
127   {
128     byte[] keyMaterial = key.getEncoded();
129     byte[] result = kwAlgorithm.wrap(keyMaterial, 0, keyMaterial.length);
130     return result;
131   }
132 
133   /**
134    * Unwraps a previously-wrapped key-material.
135    *
136    * @param wrappedKey the wrapped key-material to unwrap.
137    * @param wrappedKeyAlgorithm the canonical name of the algorithm, which the
138    *          unwrapped key-material represents. This name is used to
139    *          instantiate a concrete instance of a {@link Key} for that
140    *          algorithm. For example, if the value of this parameter is
141    *          <code>DSS</code> and the type (the next parameter) is
142    *          {@link Cipher#PUBLIC_KEY} then an attempt to construct a concrete
143    *          instance of a {@link java.security.interfaces.DSAPublicKey},
144    *          using the unwrapped key material, shall be made.
145    * @param wrappedKeyType the type of wrapped key-material. MUST be one of
146    *          {@link Cipher#PRIVATE_KEY}, {@link Cipher#PUBLIC_KEY}, or
147    *          {@link Cipher#SECRET_KEY}.
148    * @return the unwrapped key-material as an instance of {@link Key} or one of
149    *         its subclasses.
150    * @throws InvalidKeyException If the key cannot be unwrapped, or if
151    *           <code>wrappedKeyType</code> is an inappropriate type for the
152    *           unwrapped key.
153    * @throws NoSuchAlgorithmException If the <code>wrappedKeyAlgorithm</code>
154    *           is unknown to every currently installed Security Provider.
155    */
engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)156   protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
157                              int wrappedKeyType)
158       throws InvalidKeyException, NoSuchAlgorithmException
159   {
160     byte[] keyBytes;
161     try
162       {
163         keyBytes = kwAlgorithm.unwrap(wrappedKey, 0, wrappedKey.length);
164       }
165     catch (KeyUnwrappingException x)
166       {
167         InvalidKeyException y = new InvalidKeyException("engineUnwrap()");
168         y.initCause(x);
169         throw y;
170       }
171     Key result;
172     switch (wrappedKeyType)
173       {
174       case Cipher.SECRET_KEY:
175         result = new SecretKeySpec(keyBytes, wrappedKeyAlgorithm);
176         break;
177       case Cipher.PRIVATE_KEY:
178       case Cipher.PUBLIC_KEY:
179         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
180         KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
181         try
182           {
183             if (wrappedKeyType == Cipher.PRIVATE_KEY)
184               result = keyFactory.generatePrivate(keySpec);
185             else
186               result = keyFactory.generatePublic(keySpec);
187           }
188         catch (InvalidKeySpecException x)
189           {
190             InvalidKeyException y = new InvalidKeyException("engineUnwrap()");
191             y.initCause(x);
192             throw y;
193           }
194         break;
195       default:
196         IllegalArgumentException x = new IllegalArgumentException("Invalid 'wrappedKeyType': "
197                                                                   + wrappedKeyType);
198         InvalidKeyException y = new InvalidKeyException("engineUnwrap()");
199         y.initCause(x);
200         throw y;
201       }
202     return result;
203   }
204 
engineGetBlockSize()205   protected int engineGetBlockSize()
206   {
207     return kwaBlockSize;
208   }
209 
engineGetIV()210   protected byte[] engineGetIV()
211   {
212     return iv == null ? null : (byte[]) iv.clone();
213   }
214 
engineGetOutputSize(int inputLength)215   protected int engineGetOutputSize(int inputLength)
216   {
217     switch (opmode)
218     {
219       case Cipher.WRAP_MODE:
220         return getOutputSizeForWrap(inputLength);
221       case Cipher.UNWRAP_MODE:
222         return getOutputSizeForUnwrap(inputLength);
223       default:
224         throw new IllegalStateException();
225     }
226   }
227 
engineGetParameters()228   protected AlgorithmParameters engineGetParameters()
229   {
230     BlockCipherParameterSpec spec = new BlockCipherParameterSpec(iv,
231                                                                  kwaBlockSize,
232                                                                  kwaKeySize);
233     AlgorithmParameters result = null;
234     try
235       {
236         result = AlgorithmParameters.getInstance("BlockCipherParameters");
237         result.init(spec);
238       }
239     catch (NoSuchAlgorithmException x)
240       {
241         if (Configuration.DEBUG)
242           log.fine("Unable to find BlockCipherParameters. Return null");
243       }
244     catch (InvalidParameterSpecException x)
245       {
246         if (Configuration.DEBUG)
247           log.fine("Unable to initialise BlockCipherParameters. Return null");
248       }
249     return result;
250   }
251 
engineInit(int opmode, Key key, SecureRandom random)252   protected void engineInit(int opmode, Key key, SecureRandom random)
253       throws InvalidKeyException
254   {
255     checkOpMode(opmode);
256     byte[] kekBytes = checkAndGetKekBytes(key);
257     initAlgorithm(opmode, kekBytes, null, random);
258   }
259 
engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)260   protected void engineInit(int opmode, Key key, AlgorithmParameters params,
261                             SecureRandom random)
262       throws InvalidAlgorithmParameterException, InvalidKeyException
263   {
264     AlgorithmParameterSpec spec = null;
265     try
266       {
267         if (params != null)
268           spec = params.getParameterSpec(BlockCipherParameterSpec.class);
269       }
270     catch (InvalidParameterSpecException x)
271       {
272         if (Configuration.DEBUG)
273           log.fine("Unable to translate algorithm parameters into an instance "
274                    + "of BlockCipherParameterSpec. Discard");
275       }
276     engineInit(opmode, key, spec, random);
277   }
278 
engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)279   protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
280                             SecureRandom random)
281       throws InvalidAlgorithmParameterException, InvalidKeyException
282   {
283     checkOpMode(opmode);
284     byte[] kekBytes = checkAndGetKekBytes(key);
285     byte[] ivBytes = null;
286     if (params instanceof BlockCipherParameterSpec)
287       ivBytes = ((BlockCipherParameterSpec) params).getIV();
288     else if (params instanceof IvParameterSpec)
289       ivBytes = ((IvParameterSpec) params).getIV();
290 
291     initAlgorithm(opmode, kekBytes, ivBytes, random);
292   }
293 
engineSetMode(String mode)294   protected void engineSetMode(String mode) throws NoSuchAlgorithmException
295   {
296     if (! supportedMode.equalsIgnoreCase(mode))
297       throw new UnsupportedOperationException("Only " + supportedMode
298                                               + " is supported");
299   }
300 
301   /**
302    * NoPadding is the only padding algorithm supported by Key Wrapping Algorithm
303    * implementations in RI.
304    */
engineSetPadding(String padding)305   protected void engineSetPadding(String padding) throws NoSuchPaddingException
306   {
307     if (! NO_PADDING.equalsIgnoreCase(padding))
308       throw new UnsupportedOperationException("Only NoPadding is supported");
309   }
310 
engineUpdate(byte[] input, int inputOffset, int inputLength)311   protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLength)
312   {
313     throw new UnsupportedOperationException();
314   }
315 
engineUpdate(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)316   protected int engineUpdate(byte[] input, int inputOffset, int inputLength,
317                              byte[] output, int outputOffset)
318       throws ShortBufferException
319   {
320     throw new UnsupportedOperationException();
321   }
322 
engineDoFinal(byte[] input, int inputOffset, int inputLength)323   protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLength)
324       throws IllegalBlockSizeException, BadPaddingException
325   {
326     throw new UnsupportedOperationException();
327   }
328 
engineDoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)329   protected int engineDoFinal(byte[] input, int inputOffset, int inputLength,
330                               byte[] output, int outputOffset)
331       throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
332   {
333     throw new UnsupportedOperationException();
334   }
335 
336   /**
337    * Return the minimum size in bytes of a place holder large enough to receive
338    * the cipher text resulting from a wrap method with the designated size of
339    * the plain text.
340    * <p>
341    * This default implementation ALWAYS returns the smallest multiple of the
342    * <code>kwaBlockSize</code> --passed to this method through its
343    * constructor-- greater than or equal to the designated
344    * <code>inputLength</code>.
345    *
346    * @param inputLength the size of a plain text.
347    * @return an estimate of the size, in bytes, of the place holder to receive
348    * the resulting bytes of a wrap method.
349    */
getOutputSizeForWrap(int inputLength)350   protected int getOutputSizeForWrap(int inputLength)
351   {
352     return kwaBlockSize * (inputLength + kwaBlockSize - 1) / kwaBlockSize;
353   }
354 
355   /**
356    * Return the minimum size in bytes of a place holder large enough to receive
357    * the plain text resulting from an unwrap method with the designated size of
358    * the cipher text.
359    * <p>
360    * This default implementation ALWAYS returns the smallest multiple of the
361    * <code>paddingBlockSize</code> --passed to this method through its
362    * constructor-- greater than or equal to the designated
363    * <code>inputLength</code>.
364    *
365    * @param inputLength the size of a cipher text.
366    * @return an estimate of the size, in bytes, of the place holder to receive
367    *         the resulting bytes of an uwrap method.
368    */
getOutputSizeForUnwrap(int inputLength)369   protected int getOutputSizeForUnwrap(int inputLength)
370   {
371     return kwaBlockSize * (inputLength + kwaBlockSize - 1) / kwaBlockSize;
372   }
373 
checkOpMode(int opmode)374   private void checkOpMode(int opmode)
375   {
376     switch (opmode)
377     {
378       case Cipher.WRAP_MODE:
379       case Cipher.UNWRAP_MODE:
380         return;
381     }
382     throw new IllegalArgumentException("Unsupported operational mode: " + opmode);
383   }
384 
385   /**
386    * Returns the key bytes, iff it was in RAW format.
387    *
388    * @param key the opaque JCE secret key to use as the KEK.
389    * @return the bytes of the encoded form of the designated kek, iff it was in
390    *         RAW format.
391    * @throws InvalidKeyException if the designated key is not in the RAW format.
392    */
checkAndGetKekBytes(Key key)393   private byte[] checkAndGetKekBytes(Key key) throws InvalidKeyException
394   {
395     if (! Registry.RAW_ENCODING_SHORT_NAME.equalsIgnoreCase(key.getFormat()))
396       throw new InvalidKeyException("Only RAW key format is supported");
397     byte[] result = key.getEncoded();
398     int kekSize = result.length;
399     if (kekSize != kwaKeySize)
400       throw new InvalidKeyException("Invalid key material size. Expected "
401                                     + kwaKeySize + " but found " + kekSize);
402     return result;
403   }
404 
initAlgorithm(int opmode, byte[] kek, byte[] ivBytes, SecureRandom rnd)405   private void initAlgorithm(int opmode, byte[] kek, byte[] ivBytes,
406                              SecureRandom rnd)
407       throws InvalidKeyException
408   {
409     this.opmode = opmode;
410     Map attributes = new HashMap();
411     attributes.put(IKeyWrappingAlgorithm.KEY_ENCRYPTION_KEY_MATERIAL, kek);
412     if (ivBytes != null)
413       {
414         this.iv = (byte[]) ivBytes.clone();
415         attributes.put(IKeyWrappingAlgorithm.INITIAL_VALUE, this.iv);
416       }
417     else
418       this.iv = null;
419     if (rnd != null)
420       attributes.put(IKeyWrappingAlgorithm.SOURCE_OF_RANDOMNESS, rnd);
421 
422     kwAlgorithm.init(attributes);
423   }
424 }
425