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