/*
 * Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.crypto.provider;

import java.util.Arrays;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import static com.sun.crypto.provider.KWUtil.*;

This class is the impl class for AES KeyWrap algorithms as defined in "Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping"
/** * This class is the impl class for AES KeyWrap algorithms as defined in * <a href=https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf> * "Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping" */
abstract class KeyWrapCipher extends CipherSpi { // for AESWrap + AES/KW/NoPadding public static final class AES_KW_NoPadding extends KeyWrapCipher { public AES_KW_NoPadding() { super(new AESKeyWrap(), null, -1); } } // for AESWrap_128 + AES_128/KW/NoPadding public static final class AES128_KW_NoPadding extends KeyWrapCipher { public AES128_KW_NoPadding() { super(new AESKeyWrap(), null, 16); } } // for AESWrap_192 + AES_192/KW/NoPadding public static final class AES192_KW_NoPadding extends KeyWrapCipher { public AES192_KW_NoPadding() { super(new AESKeyWrap(), null, 24); } } // for AESWrap_256 + AES_256/KW/NoPadding public static final class AES256_KW_NoPadding extends KeyWrapCipher { public AES256_KW_NoPadding() { super(new AESKeyWrap(), null, 32); } } // for AES/KW/NoPadding public static final class AES_KW_PKCS5Padding extends KeyWrapCipher { public AES_KW_PKCS5Padding() { super(new AESKeyWrap(), new PKCS5Padding(8), -1); } } // for AES_128/KW/NoPadding public static final class AES128_KW_PKCS5Padding extends KeyWrapCipher { public AES128_KW_PKCS5Padding() { super(new AESKeyWrap(), new PKCS5Padding(8), 16); } } // for AES_192/KW/NoPadding public static final class AES192_KW_PKCS5Padding extends KeyWrapCipher { public AES192_KW_PKCS5Padding() { super(new AESKeyWrap(), new PKCS5Padding(8), 24); } } // for AES_256/KW/NoPadding public static final class AES256_KW_PKCS5Padding extends KeyWrapCipher { public AES256_KW_PKCS5Padding() { super(new AESKeyWrap(), new PKCS5Padding(8), 32); } } // for AES/KWP/NoPadding public static final class AES_KWP_NoPadding extends KeyWrapCipher { public AES_KWP_NoPadding() { super(new AESKeyWrapPadded(), null, -1); } } // for AES_128/KWP/NoPadding public static final class AES128_KWP_NoPadding extends KeyWrapCipher { public AES128_KWP_NoPadding() { super(new AESKeyWrapPadded(), null, 16); } } // for AES_192/KWP/NoPadding public static final class AES192_KWP_NoPadding extends KeyWrapCipher { public AES192_KWP_NoPadding() { super(new AESKeyWrapPadded(), null, 24); } } // for AES_256/KWP/NoPadding public static final class AES256_KWP_NoPadding extends KeyWrapCipher { public AES256_KWP_NoPadding() { super(new AESKeyWrapPadded(), null, 32); } } // store the specified bytes, e.g. in[inOfs...(inOfs+inLen-1)] into // 'dataBuf' starting at 'dataIdx'. // NOTE: if 'in' is null, this method will ensure that 'dataBuf' has enough // capacity for 'inLen' bytes but will NOT copy bytes from 'in'. private void store(byte[] in, int inOfs, int inLen) { // In NIST SP 800-38F, KWP input size is limited to be no longer // than 2^32 bytes. Otherwise, the length cannot be encoded in 32 bits // However, given the current spec requirement that recovered text // can only be returned after successful tag verification, we are // bound by limiting the data size to the size limit of java byte array, // e.g. Integer.MAX_VALUE, since all data are returned by doFinal(). int remain = Integer.MAX_VALUE - dataIdx; if (inLen > remain) { throw new ProviderException("SunJCE provider can only take " + remain + " more bytes"); } // resize 'dataBuf' to the smallest (n * BLKSIZE) + SEMI_BLKSIZE) if (dataBuf == null || dataBuf.length - dataIdx < inLen) { int newSize = Math.addExact(dataIdx, inLen); int lastBlk = (dataIdx + inLen - SEMI_BLKSIZE) % BLKSIZE; if (lastBlk != 0 || padding != null) { newSize = Math.addExact(newSize, BLKSIZE - lastBlk); } byte[] temp = new byte[newSize]; if (dataBuf != null && dataIdx > 0) { System.arraycopy(dataBuf, 0, temp, 0, dataIdx); } dataBuf = temp; } if (in != null) { System.arraycopy(in, inOfs, dataBuf, dataIdx, inLen); dataIdx += inLen; } } // internal cipher object which does the real work. // AESKeyWrap for KW, AESKeyWrapPadded for KWP private final FeedbackCipher cipher; // internal padding object; null if NoPadding private final Padding padding; // encrypt/wrap or decrypt/unwrap? private int opmode = -1; // must be set by init(..) /* * needed to support oids which associates a fixed key size * to the cipher object. */ private final int fixedKeySize; // in bytes, -1 if no restriction // internal data buffer for encrypt, decrypt calls // must use store() to store data into 'dataBuf' as it will resize if needed private byte[] dataBuf; private int dataIdx;
Creates an instance of KeyWrap cipher using the specified symmetric cipher whose block size must be 128-bit, and the supported mode and padding scheme.
/** * Creates an instance of KeyWrap cipher using the specified * symmetric cipher whose block size must be 128-bit, and * the supported mode and padding scheme. */
public KeyWrapCipher(FeedbackCipher cipher, Padding padding, int keySize) { this.cipher = cipher; this.padding = padding; this.fixedKeySize = keySize; this.dataBuf = null; this.dataIdx = 0; }
Sets the mode of this cipher. Must match the mode specified in the constructor.
Params:
  • mode – the cipher mode
Throws:
/** * Sets the mode of this cipher. Must match the mode specified in * the constructor. * * @param mode the cipher mode * * @exception NoSuchAlgorithmException if the requested cipher mode * does not match the supported mode */
@Override protected void engineSetMode(String mode) throws NoSuchAlgorithmException { if (mode != null && !cipher.getFeedback().equalsIgnoreCase(mode)) { throw new NoSuchAlgorithmException(mode + " cannot be used"); } }
Sets the padding mechanism of this cipher. The specified padding scheme should match what this cipher is configured with.
Params:
  • padding – the padding mechanism
Throws:
/** * Sets the padding mechanism of this cipher. The specified padding * scheme should match what this cipher is configured with. * * @param padding the padding mechanism * * @exception NoSuchPaddingException if the requested padding mechanism * does not match the supported padding scheme */
@Override protected void engineSetPadding(String padding) throws NoSuchPaddingException { if ((this.padding == null && !"NoPadding".equalsIgnoreCase(padding)) || this.padding instanceof PKCS5Padding && !"PKCS5Padding".equalsIgnoreCase(padding)) { throw new NoSuchPaddingException("Unsupported padding " + padding); } }
Returns:the block size (in bytes)
/** * @return the block size (in bytes) */
@Override protected int engineGetBlockSize() { return 8; }
Returns the length in bytes that an output buffer would need to be given the input length inLen (in bytes).

The actual output length of the next update or doFinal call may be smaller than the length returned by this method.

Params:
  • inLen – the input length (in bytes)
Returns:the required output buffer size (in bytes)
/** * Returns the length in bytes that an output buffer would need to be * given the input length <code>inLen</code> (in bytes). * * <p>The actual output length of the next <code>update</code> or * <code>doFinal</code> call may be smaller than the length returned * by this method. * * @param inLen the input length (in bytes) * * @return the required output buffer size (in bytes) */
protected int engineGetOutputSize(int inLen) { int result; if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { result = (dataIdx > 0? Math.addExact(inLen, dataIdx - SEMI_BLKSIZE) : inLen); // calculate padding length based on plaintext length int padLen = 0; if (padding != null) { padLen = padding.padLength(result); } else if (cipher instanceof AESKeyWrapPadded) { int n = result % SEMI_BLKSIZE; if (n != 0) { padLen = SEMI_BLKSIZE - n; } } // then add the first semiblock and padLen to result result = Math.addExact(result, SEMI_BLKSIZE + padLen); } else { result = inLen - SEMI_BLKSIZE; if (dataIdx > 0) { result = Math.addExact(result, dataIdx); } } return result; }
Returns the initialization vector (IV) in a new buffer.
Returns:the user-specified iv, or null if the underlying algorithm does not use an IV, or if the IV has not yet been set.
/** * Returns the initialization vector (IV) in a new buffer. * * @return the user-specified iv, or null if the underlying algorithm does * not use an IV, or if the IV has not yet been set. */
@Override protected byte[] engineGetIV() { byte[] iv = cipher.getIV(); return (iv == null? null : iv.clone()); } // actual impl for various engineInit(...) methods private void implInit(int opmode, Key key, byte[] iv, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { byte[] keyBytes = key.getEncoded(); if (keyBytes == null) { throw new InvalidKeyException("Null key"); } this.opmode = opmode; boolean decrypting = (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE); try { cipher.init(decrypting, key.getAlgorithm(), keyBytes, iv); dataBuf = null; dataIdx = 0; } finally { Arrays.fill(keyBytes, (byte) 0); } }
Initializes this cipher with a key and a source of randomness.
Params:
  • opmode – the operation mode of this cipher.
  • key – the secret key.
  • random – the source of randomness.
Throws:
/** * Initializes this cipher with a key and a source of randomness. * * @param opmode the operation mode of this cipher. * @param key the secret key. * @param random the source of randomness. * * @exception InvalidKeyException if the given key is inappropriate for * initializing this cipher. */
@Override protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { try { implInit(opmode, key, (byte[])null, random); } catch (InvalidAlgorithmParameterException iae) { // should never happen throw new AssertionError(iae); } }
Initializes this cipher with a key, a set of algorithm parameters, and a source of randomness.
Params:
  • opmode – the operation mode of this cipher.
  • key – the secret key.
  • params – the algorithm parameters; if not null, must be of type IvParameterSpec
  • random – the source of randomness.
Throws:
/** * Initializes this cipher with a key, a set of algorithm parameters, * and a source of randomness. * * @param opmode the operation mode of this cipher. * @param key the secret key. * @param params the algorithm parameters; if not null, must be of type * IvParameterSpec * @param random the source of randomness. * * @exception InvalidKeyException if the given key is inappropriate for * initializing this cipher * @exception InvalidAlgorithmParameterException if the given algorithm * parameters is invalid. */
@Override protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { if (params != null && !(params instanceof IvParameterSpec)) { throw new InvalidAlgorithmParameterException( "Only IvParameterSpec is accepted"); } byte[] iv = (params == null? null : ((IvParameterSpec)params).getIV()); implInit(opmode, key, iv, random); }
Initializes this cipher with a key, a set of algorithm parameters, and a source of randomness.
Params:
  • opmode – the operation mode of this cipher.
  • key – the secret key.
  • params – the algorithm parameters; if not null, must be able to be converted to IvParameterSpec.
  • random – the source of randomness.
Throws:
/** * Initializes this cipher with a key, a set of algorithm parameters, * and a source of randomness. * * @param opmode the operation mode of this cipher. * @param key the secret key. * @param params the algorithm parameters; if not null, must be able to * be converted to IvParameterSpec. * @param random the source of randomness. * * @exception InvalidKeyException if the given key is inappropriate. * @exception InvalidAlgorithmParameterException if the given algorithm * parameters is invalid. */
@Override protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { byte[] iv = null; if (params != null) { try { AlgorithmParameterSpec spec = params.getParameterSpec(IvParameterSpec.class); iv = ((IvParameterSpec)spec).getIV(); } catch (InvalidParameterSpecException ispe) { throw new InvalidAlgorithmParameterException( "Only IvParameterSpec is accepted"); } } try { implInit(opmode, key, iv, random); } catch (IllegalArgumentException iae) { throw new InvalidAlgorithmParameterException(iae.getMessage()); } }
See CipherSpi.engineUpdate(...) - buffers data internally as only single part operation is supported.
Params:
  • in – the input buffer.
  • inOffset – the offset in in where the input starts.
  • inLen – the input length.
Returns:null.
/** * See CipherSpi.engineUpdate(...) - buffers data internally as * only single part operation is supported. * * @param in the input buffer. * @param inOffset the offset in <code>in</code> where the input * starts. * @param inLen the input length. * * @return null. */
@Override protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) { if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE) { throw new IllegalStateException ("Cipher not initialized for update"); } implUpdate(in, inOffset, inLen); return null; }
See CipherSpi.engineUpdate(...) - buffers data internally as only single part operation is supported.
Params:
  • in – the input buffer.
  • inOffset – the offset in in where the input starts.
  • inLen – the input length.
  • out – the buffer for the result.
  • outOffset – the offset in out where the result is stored.
Throws:
Returns:n/a.
/** * See CipherSpi.engineUpdate(...) - buffers data internally as * only single part operation is supported. * * @param in the input buffer. * @param inOffset the offset in <code>in</code> where the input * starts. * @param inLen the input length. * @param out the buffer for the result. * @param outOffset the offset in <code>out</code> where the result * is stored. * * @return n/a. * * @exception IllegalStateException upon invocation of this method. */
@Override protected int engineUpdate(byte[] in, int inOffset, int inLen, byte[] out, int outOffset) throws ShortBufferException { if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE) { throw new IllegalStateException ("Cipher not initialized for update"); } implUpdate(in, inOffset, inLen); return 0; } // actual impl for various engineUpdate(...) methods private void implUpdate(byte[] in, int inOfs, int inLen) { if (inLen <= 0) return; if (opmode == Cipher.ENCRYPT_MODE && dataIdx == 0) { // the first semiblock is for iv, store data after it dataIdx = SEMI_BLKSIZE; } store(in, inOfs, inLen); }
See CipherSpi.engineDoFinal(...)
Params:
  • input – the input buffer
  • inputOffset – the offset in in where the input starts
  • inputLen – the input length.
Throws:
Returns:n/a.
/** * See CipherSpi.engineDoFinal(...) * * @param input the input buffer * @param inputOffset the offset in <code>in</code> where the input * starts * @param inputLen the input length. * * @return n/a. * * @exception IllegalStateException upon invocation of this method. */
@Override protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) throws IllegalBlockSizeException, BadPaddingException { int estOutLen = engineGetOutputSize(inLen); byte[] out = new byte[estOutLen]; try { int outLen = engineDoFinal(in, inOfs, inLen, out, 0); if (outLen < estOutLen) { try { return Arrays.copyOf(out, outLen); } finally { Arrays.fill(out, (byte)0); } } else { return out; } } catch (ShortBufferException sbe) { // should never happen throw new AssertionError(sbe); } }
See CipherSpi.doFinal(...)
Params:
  • in – the input buffer.
  • inOffset – the offset in in where the input starts.
  • inLen – the input length.
  • out – the buffer for the result.
  • outOffset – the ofset in out where the result is stored.
Throws:
Returns:n/a.
/** * See CipherSpi.doFinal(...) * * @param in the input buffer. * @param inOffset the offset in <code>in</code> where the input * starts. * @param inLen the input length. * @param out the buffer for the result. * @param outOffset the ofset in <code>out</code> where the result * is stored. * * @return n/a. * * @exception IllegalStateException upon invocation of this method. */
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) throws IllegalBlockSizeException, ShortBufferException, BadPaddingException { if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE) { throw new IllegalStateException ("Cipher not initialized for doFinal"); } int estOutLen = engineGetOutputSize(inLen); if (out.length - outOfs < estOutLen) { throw new ShortBufferException("Need at least " + estOutLen); } try { // cannot write out the result for decryption due to verification // requirement if (outOfs == 0 && opmode == Cipher.ENCRYPT_MODE) { return implDoFinal(in, inOfs, inLen, out); } else { // use 'dataBuf' as output buffer and then copy into 'out' // make sure 'dataBuf' is large enough store(null, 0, inLen); int outLen = implDoFinal(in, inOfs, inLen, dataBuf); if (outLen > estOutLen) { throw new AssertionError ("Actual output length exceeds estimated length"); } System.arraycopy(dataBuf, 0, out, outOfs, outLen); return outLen; } } finally { if (dataBuf != null) { Arrays.fill(dataBuf, (byte)0); } dataBuf = null; dataIdx = 0; } } // actual impl for various engineDoFinal(...) methods. // prepare 'out' buffer with the buffered bytes in 'dataBuf', // and the to-be-processed bytes in 'in', then perform single-part // encryption/decrytion over 'out' buffer private int implDoFinal(byte[] in, int inOfs, int inLen, byte[] out) throws IllegalBlockSizeException, BadPaddingException, ShortBufferException { int len = (out == dataBuf? dataIdx : 0); // copy over the buffered bytes if out != dataBuf if (out != dataBuf && dataIdx > 0) { System.arraycopy(dataBuf, 0, out, 0, dataIdx); len = dataIdx; } if (opmode == Cipher.ENCRYPT_MODE && len == 0) { len = SEMI_BLKSIZE; // reserve space for the ICV if encryption } if (inLen > 0) { System.arraycopy(in, inOfs, out, len, inLen); len += inLen; } try { return (opmode == Cipher.ENCRYPT_MODE ? helperEncrypt(out, len) : helperDecrypt(out, len)); } finally { if (dataBuf != null && dataBuf != out) { Arrays.fill(dataBuf, (byte)0); } } } // helper routine for in-place encryption. // 'inBuf' = semiblock | plain text | extra bytes if padding is used // 'inLen' = semiblock length + plain text length private int helperEncrypt(byte[] inBuf, int inLen) throws IllegalBlockSizeException, ShortBufferException { // pad data if padding is used if (padding != null) { int paddingLen = padding.padLength(inLen - SEMI_BLKSIZE); if (inLen + paddingLen > inBuf.length) { throw new AssertionError("encrypt buffer too small"); } try { padding.padWithLen(inBuf, inLen, paddingLen); inLen += paddingLen; } catch (ShortBufferException sbe) { // should never happen throw new AssertionError(sbe); } } return cipher.encryptFinal(inBuf, 0, inLen, null, 0); } // helper routine for in-place decryption. // 'inBuf' = cipher text // 'inLen' = cipher text length private int helperDecrypt(byte[] inBuf, int inLen) throws IllegalBlockSizeException, BadPaddingException, ShortBufferException { int outLen = cipher.decryptFinal(inBuf, 0, inLen, null, 0); // unpad data if padding is used if (padding != null) { int padIdx = padding.unpad(inBuf, 0, outLen); if (padIdx <= 0) { throw new BadPaddingException("Bad Padding: " + padIdx); } outLen = padIdx; } return outLen; }
Returns the parameters used with this cipher.
Returns:AlgorithmParameters object containing IV, or null if this cipher does not use any parameters.
/** * Returns the parameters used with this cipher. * * @return AlgorithmParameters object containing IV, or null if this cipher * does not use any parameters. */
@Override protected AlgorithmParameters engineGetParameters() { AlgorithmParameters params = null; byte[] iv = cipher.getIV(); if (iv == null) { iv = (cipher instanceof AESKeyWrap? AESKeyWrap.ICV1 : AESKeyWrapPadded.ICV2); } try { params = AlgorithmParameters.getInstance("AES"); params.init(new IvParameterSpec(iv)); } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { // should never happen throw new AssertionError(); } return params; }
Returns the key size of the given key object in number of bits.
Params:
  • key – the key object.
Throws:
Returns:the "effective" key size of the given key object.
/** * Returns the key size of the given key object in number of bits. * * @param key the key object. * * @return the "effective" key size of the given key object. * * @exception InvalidKeyException if <code>key</code> is invalid. */
protected int engineGetKeySize(Key key) throws InvalidKeyException { byte[] encoded = key.getEncoded(); if (encoded == null) { throw new InvalidKeyException("Cannot decide key length"); } // only need length Arrays.fill(encoded, (byte) 0); int keyLen = encoded.length; if (!key.getAlgorithm().equalsIgnoreCase("AES") || !AESCrypt.isKeySizeValid(keyLen) || (fixedKeySize != -1 && fixedKeySize != keyLen)) { throw new InvalidKeyException("Invalid key length: " + keyLen + " bytes"); } return Math.multiplyExact(keyLen, 8); }
Wrap a key.
Params:
  • key – the key to be wrapped.
Throws:
  • IllegalBlockSizeException – if this cipher is a block cipher, no padding has been requested, and the length of the encoding of the key to be wrapped is not a multiple of the block size.
  • InvalidKeyException – if it is impossible or unsafe to wrap the key with this cipher (e.g., a hardware protected key is being passed to a software only cipher).
Returns:the wrapped key.
/** * Wrap a key. * * @param key the key to be wrapped. * * @return the wrapped key. * * @exception IllegalBlockSizeException if this cipher is a block * cipher, no padding has been requested, and the length of the * encoding of the key to be wrapped is not a * multiple of the block size. * * @exception InvalidKeyException if it is impossible or unsafe to * wrap the key with this cipher (e.g., a hardware protected key is * being passed to a software only cipher). */
@Override protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { if (opmode != Cipher.WRAP_MODE) { throw new IllegalStateException("Cipher not initialized for wrap"); } byte[] encoded = key.getEncoded(); if ((encoded == null) || (encoded.length == 0)) { throw new InvalidKeyException("Cannot get an encoding of " + "the key to be wrapped"); } // output size is known, allocate output buffer byte[] out = new byte[engineGetOutputSize(encoded.length)]; // reserve the first semiblock and do not write data int len = SEMI_BLKSIZE; System.arraycopy(encoded, 0, out, len, encoded.length); len += encoded.length; // discard key data Arrays.fill(encoded, (byte) 0); try { int outLen = helperEncrypt(out, len); if (outLen != out.length) { throw new AssertionError("Wrong output buffer size"); } return out; } catch (ShortBufferException sbe) { // should never happen throw new AssertionError(); } }
Unwrap a previously wrapped key.
Params:
  • wrappedKey – the key to be unwrapped.
  • wrappedKeyAlgorithm – the algorithm the wrapped key is for.
  • wrappedKeyType – the type of the wrapped key. This is one of Cipher.SECRET_KEY, Cipher.PRIVATE_KEY, or Cipher.PUBLIC_KEY.
Throws:
  • NoSuchAlgorithmException – if no installed providers can create keys of type wrappedKeyType for the wrappedKeyAlgorithm.
  • InvalidKeyException – if wrappedKey does not represent a wrapped key of type wrappedKeyType for the wrappedKeyAlgorithm.
Returns:the unwrapped key.
/** * Unwrap a previously wrapped key. * * @param wrappedKey the key to be unwrapped. * * @param wrappedKeyAlgorithm the algorithm the wrapped key is for. * * @param wrappedKeyType the type of the wrapped key. * This is one of <code>Cipher.SECRET_KEY</code>, * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>. * * @return the unwrapped key. * * @exception NoSuchAlgorithmException if no installed providers * can create keys of type <code>wrappedKeyType</code> for the * <code>wrappedKeyAlgorithm</code>. * * @exception InvalidKeyException if <code>wrappedKey</code> does not * represent a wrapped key of type <code>wrappedKeyType</code> for * the <code>wrappedKeyAlgorithm</code>. */
@Override protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { if (opmode != Cipher.UNWRAP_MODE) { throw new IllegalStateException ("Cipher not initialized for unwrap"); } byte[] buf = wrappedKey.clone(); try { int outLen = helperDecrypt(buf, buf.length); return ConstructKeys.constructKey(buf, 0, outLen, wrappedKeyAlgorithm, wrappedKeyType); } catch (ShortBufferException sbe) { // should never happen throw new AssertionError(); } catch (IllegalBlockSizeException | BadPaddingException e) { throw new InvalidKeyException(e); } finally { Arrays.fill(buf, (byte) 0); } } }