8248268: Support KWP in addition to KW

Reviewed-by: xuelei
This commit is contained in:
Valerie Peng 2021-06-02 21:31:57 +00:00
parent 3482cb87fd
commit 136badb1f7
18 changed files with 2223 additions and 675 deletions

View file

@ -0,0 +1,196 @@
/*
* Copyright (c) 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 implement the AES KeyWrap mode of operation 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"</a>
* and represents AES cipher in KW mode.
*/
class AESKeyWrap extends FeedbackCipher {
// default integrity check value (icv) if iv is not supplied
private static final byte[] ICV1 = { // SEMI_BLKSIZE long
(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
};
AESKeyWrap() {
super(new AESCrypt());
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>KW</code>
*/
@Override
String getFeedback() {
return "KW";
}
/**
* Save the current content of this cipher.
*/
@Override
void save() {
throw new UnsupportedOperationException("save not supported");
};
/**
* Restores the content of this cipher to the previous saved one.
*/
@Override
void restore() {
throw new UnsupportedOperationException("restore not supported");
};
/**
* Initializes the cipher in the specified mode with the given key
* and iv.
*
* @param decrypting flag indicating encryption or decryption
* @param algorithm the algorithm name
* @param key the key
* @param iv the iv
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
* @exception InvalidAlgorithmParameterException if the given iv is
* non-null and not the right length
*/
@Override
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (key == null) {
throw new InvalidKeyException("Invalid null key");
}
if (iv != null && iv.length != SEMI_BLKSIZE) {
throw new InvalidAlgorithmParameterException("Invalid IV");
}
embeddedCipher.init(decrypting, algorithm, key);
// iv is retrieved from IvParameterSpec.getIV() which is already cloned
this.iv = (iv == null? ICV1 : iv);
}
/**
* Resets the iv to its original value.
* This is used when doFinal is called in the Cipher class, so that the
* cipher can be reused (with its original iv).
*/
@Override
void reset() {
throw new UnsupportedOperationException("reset not supported");
};
// no support for multi-part encryption
@Override
int encrypt(byte[] pt, int ptOfs, int ptLen, byte[] ct, int ctOfs) {
throw new UnsupportedOperationException("multi-part not supported");
};
// no support for multi-part decryption
@Override
int decrypt(byte[] ct, int ctOfs, int ctLen, byte[] pt, int ptOfs) {
throw new UnsupportedOperationException("multi-part not supported");
};
/**
* Performs single-part encryption operation.
*
* <p>The input <code>pt</code>, starting at <code>0</code>
* and ending at <code>ptLen-1</code>, is encrypted.
* The result is stored in place into <code>pt</code>, starting at
* <code>0</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param pt the input buffer with the data to be encrypted
* @param dummy1 the offset in <code>pt</code> which is always 0
* @param ptLen the length of the input data
* @param dummy2 the output buffer for the encryption which is always pt
* @param dummy3 the offset in the output buffer which is always 0
* @return the number of bytes placed into <code>pt</code>
*/
@Override
int encryptFinal(byte[] pt, int dummy1, int ptLen, byte[] dummy2,
int dummy3) throws IllegalBlockSizeException {
// adjust the min value since pt contains the first semi-block
if (ptLen < MIN_INPUTLEN || (ptLen % SEMI_BLKSIZE) != 0) {
throw new IllegalBlockSizeException("data should" +
" be at least 16 bytes and multiples of 8");
}
return W(iv, pt, ptLen, embeddedCipher);
}
/**
* Performs single-part decryption operation.
*
* <p>The input <code>ct</code>, starting at <code>0</code>
* and ending at <code>ctLen-1</code>, is decrypted.
* The result is stored in place into <code>ct</code>, starting at
* <code>0</code>.
*
* <p>NOTE: Purpose of this special impl is for minimizing array
* copying, those unused arguments are named as dummyN.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param ct the input buffer with the data to be decrypted
* @param dummy1 the offset in <code>ct</code> which is always 0
* @param ctLen the length of the input data
* @param dummy2 the output buffer for the decryption which is always ct
* @param dummy3 the offset in the output buffer which is always 0
* @return the number of bytes placed into <code>ct</code>
*/
@Override
int decryptFinal(byte[] ct, int dummy1, int ctLen, byte[] dummy2,
int dummy3) throws IllegalBlockSizeException {
if (ctLen < MIN_INPUTLEN || (ctLen % SEMI_BLKSIZE) != 0) {
throw new IllegalBlockSizeException
("data should be at least 24 bytes and multiples of 8");
}
byte[] ivOut = new byte[SEMI_BLKSIZE];
ctLen = W_INV(ct, ctLen, ivOut, embeddedCipher);
// check against icv and fail if not match
if (!MessageDigest.isEqual(ivOut, this.iv)) {
throw new IllegalBlockSizeException("Integrity check failed");
}
return ctLen;
}
}

View file

@ -0,0 +1,260 @@
/*
* Copyright (c) 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 implement the AES KeyWrap With Padding mode of operation 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"</a>
* and represents AES cipher in KWP mode.
*/
class AESKeyWrapPadded extends FeedbackCipher {
// default integrity check value (icv) if iv is not supplied
private static final byte[] ICV2 = { // SEMI_BLKSIZE/2 long
(byte) 0xA6, (byte) 0x59, (byte) 0x59, (byte) 0xA6,
};
private static final byte[] PAD_BLK = new byte[SEMI_BLKSIZE - 1];
// set the first semiblock of dest with iv and inLen
private static void setIvAndLen(byte[] dest, byte[] iv, int inLen) {
assert(dest.length >= SEMI_BLKSIZE) : "buffer needs at least 8 bytes";
System.arraycopy(iv, 0, dest, 0, iv.length);
dest[4] = (byte) ((inLen >>> 24) & 0xFF);
dest[5] = (byte) ((inLen >>> 16) & 0xFF);
dest[6] = (byte) ((inLen >>> 8) & 0xFF);
dest[7] = (byte) (inLen & 0xFF);
}
// validate the recovered internal ivAndLen semiblock against iv and
// return the recovered input length
private static int validateIV(byte[] ivAndLen, byte[] iv)
throws IllegalBlockSizeException {
// check against iv and fail if not match
int match = 0;
for (int i = 0; i < ICV2.length; i++) {
match |= (ivAndLen[i] ^ iv[i]);
}
if (match != 0) {
throw new IllegalBlockSizeException("Integrity check failed");
}
int outLen = ivAndLen[4];
for (int k = 5; k < SEMI_BLKSIZE; k++) {
if (outLen != 0) {
outLen <<= 8;
}
outLen |= ivAndLen[k] & 0xFF;
}
return outLen;
}
AESKeyWrapPadded() {
super(new AESCrypt());
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>KW</code>
*/
@Override
String getFeedback() {
return "KWP";
}
/**
* Save the current content of this cipher.
*/
@Override
void save() {
throw new UnsupportedOperationException("save not supported");
};
/**
* Restores the content of this cipher to the previous saved one.
*/
@Override
void restore() {
throw new UnsupportedOperationException("restore not supported");
};
/**
* Initializes the cipher in the specified mode with the given key
* and iv.
*
* @param decrypting flag indicating encryption or decryption
* @param algorithm the algorithm name
* @param key the key
* @param iv the iv
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
* @exception InvalidAlgorithmParameterException if the given iv is
* non-null and not the right length
*/
@Override
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (key == null) {
throw new InvalidKeyException("Invalid null key");
}
if (iv != null && iv.length != ICV2.length) {
throw new InvalidAlgorithmParameterException("Invalid IV length");
}
embeddedCipher.init(decrypting, algorithm, key);
// iv is retrieved from IvParameterSpec.getIV() which is already cloned
this.iv = (iv == null? ICV2 : iv);
}
/**
* Resets the iv to its original value.
* This is used when doFinal is called in the Cipher class, so that the
* cipher can be reused (with its original iv).
*/
@Override
void reset() {
throw new UnsupportedOperationException("reset not supported");
};
// no support for multi-part encryption
@Override
int encrypt(byte[] pt, int ptOfs, int ptLen, byte[] ct, int ctOfs) {
throw new UnsupportedOperationException("multi-part not supported");
};
// no support for multi-part decryption
@Override
int decrypt(byte[] ct, int ctOfs, int ctLen, byte[] pt, int ptOfs) {
throw new UnsupportedOperationException("multi-part not supported");
};
/**
* Performs single-part encryption operation.
*
* <p>The input <code>pt</code>, starting at <code>0</code>
* and ending at <code>ptLen-1</code>, is encrypted.
* The result is stored in place into <code>pt</code>, starting at
* <code>0</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param pt the input buffer with the data to be encrypted
* @param dummy1 the offset in <code>pt</code> which is always 0
* @param ptLen the length of the input data
* @param dummy2 the output buffer for the encryption which is always pt
* @param dummy3 the offset in the output buffer which is always 0
* @return the number of bytes placed into <code>pt</code>
*/
@Override
int encryptFinal(byte[] pt, int dummy1, int ptLen, byte[] dummy2,
int dummy3) throws IllegalBlockSizeException {
int actualLen = ptLen - SEMI_BLKSIZE;
if (actualLen < 1) {
throw new IllegalBlockSizeException
("data should have at least 1 byte");
}
if (ptLen % SEMI_BLKSIZE != 0) {
int rem = SEMI_BLKSIZE - (ptLen % SEMI_BLKSIZE);
System.arraycopy(PAD_BLK, 0, pt, ptLen, rem);
ptLen += rem;
}
if (ptLen <= BLKSIZE) {
// overwrite the first semiblock with iv and input length
setIvAndLen(pt, iv, actualLen);
embeddedCipher.encryptBlock(pt, 0, pt, 0);
} else {
byte[] ivAndLen = new byte[SEMI_BLKSIZE];
setIvAndLen(ivAndLen, iv, actualLen);
ptLen = W(ivAndLen, pt, ptLen, embeddedCipher);
}
return ptLen;
}
/**
* Performs single-part decryption operation.
*
* <p>The input <code>ct</code>, starting at <code>0</code>
* and ending at <code>ctLen-1</code>, is decrypted.
* The result is stored in place into <code>ct</code>, starting at
* <code>0</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param ct the input buffer with the data to be decrypted
* @param dummy1 the offset in <code>ct</code> which is always 0
* @param ctLen the length of the input data
* @param dummy2 the output buffer for the decryption which is always ct
* @param dummy3 the offset in the output buffer which is always 0
* @return the number of bytes placed into <code>ct</code>
*/
@Override
int decryptFinal(byte[] ct, int dummy1, int ctLen, byte[] dummy2,
int dummy3) throws IllegalBlockSizeException {
if (ctLen < BLKSIZE || ctLen % SEMI_BLKSIZE != 0) {
throw new IllegalBlockSizeException
("data should be at least 16 bytes and multiples of 8");
}
byte[] ivAndLen = new byte[SEMI_BLKSIZE];
if (ctLen == BLKSIZE) {
embeddedCipher.decryptBlock(ct, 0, ct, 0);
System.arraycopy(ct, 0, ivAndLen, 0, SEMI_BLKSIZE);
System.arraycopy(ct, SEMI_BLKSIZE, ct, 0, SEMI_BLKSIZE);
ctLen -= SEMI_BLKSIZE;
} else {
ctLen = W_INV(ct, ctLen, ivAndLen, embeddedCipher);
}
int outLen = validateIV(ivAndLen, this.iv);
// check padding bytes
int padLen = ctLen - outLen;
if (padLen < 0 || padLen >= SEMI_BLKSIZE) {
throw new IllegalBlockSizeException("Invalid KWP pad length " +
padLen);
}
for (int k = padLen; k > 0; k--) {
if (ct[ctLen - k] != 0) {
throw new IllegalBlockSizeException("Invalid KWP pad value");
}
}
return outLen;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 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
@ -35,7 +35,8 @@ import java.security.spec.InvalidParameterSpecException;
* in feedback-mode. IV is defined in the standards as follows:
*
* <pre>
* IV ::= OCTET STRING -- 16 octets
* IV ::= OCTET STRING -- 8 octets for KW, 4 octets for KWP, and 16 octets for
* other feedback modes
* </pre>
*
* @author Valerie Peng
@ -46,7 +47,7 @@ public final class AESParameters extends AlgorithmParametersSpi {
private BlockCipherParamsCore core;
public AESParameters() {
core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE);
core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE, 4, 8);
}
protected void engineInit(AlgorithmParameterSpec paramSpec)

View file

@ -1,526 +0,0 @@
/*
* 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.*;
/**
* This class implements the AES KeyWrap algorithm as defined
* in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>
* "XML Encryption Syntax and Processing" section 5.6.3 "AES Key Wrap".
* Note: only <code>ECB</code> mode and <code>NoPadding</code> padding
* can be used for this algorithm.
*
* @author Valerie Peng
*
*
* @see AESCipher
*/
abstract class AESWrapCipher extends CipherSpi {
public static final class General extends AESWrapCipher {
public General() {
super(-1);
}
}
public static final class AES128 extends AESWrapCipher {
public AES128() {
super(16);
}
}
public static final class AES192 extends AESWrapCipher {
public AES192() {
super(24);
}
}
public static final class AES256 extends AESWrapCipher {
public AES256() {
super(32);
}
}
private static final byte[] IV = {
(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
};
private static final int blksize = AESConstants.AES_BLOCK_SIZE;
/*
* internal cipher object which does the real work.
*/
private AESCrypt cipher;
/*
* are we encrypting or decrypting?
*/
private boolean decrypting = false;
/*
* needed to support AES oids which associates a fixed key size
* to the cipher object.
*/
private final int fixedKeySize; // in bytes, -1 if no restriction
/**
* Creates an instance of AES KeyWrap cipher with default
* mode, i.e. "ECB" and padding scheme, i.e. "NoPadding".
*/
public AESWrapCipher(int keySize) {
cipher = new AESCrypt();
fixedKeySize = keySize;
}
/**
* Sets the mode of this cipher. Only "ECB" mode is accepted for this
* cipher.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode
* is not "ECB".
*/
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
if (!mode.equalsIgnoreCase("ECB")) {
throw new NoSuchAlgorithmException(mode + " cannot be used");
}
}
/**
* Sets the padding mechanism of this cipher. Only "NoPadding" schmem
* is accepted for this cipher.
*
* @param padding the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* is not "NoPadding".
*/
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
if (!padding.equalsIgnoreCase("NoPadding")) {
throw new NoSuchPaddingException(padding + " cannot be used");
}
}
/**
* Returns the block size (in bytes). i.e. 16 bytes.
*
* @return the block size (in bytes), i.e. 16 bytes.
*/
protected int engineGetBlockSize() {
return blksize;
}
/**
* Returns the length in bytes that an output buffer would need to be
* given the input length <code>inputLen</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 inputLen the input length (in bytes)
*
* @return the required output buffer size (in bytes)
*/
protected int engineGetOutputSize(int inputLen) {
// can only return an upper-limit if not initialized yet.
int result = 0;
if (decrypting) {
result = inputLen - 8;
} else {
result = Math.addExact(inputLen, 8);
}
return (result < 0? 0:result);
}
/**
* Returns the initialization vector (IV) which is null for this cipher.
*
* @return null for this cipher.
*/
protected byte[] engineGetIV() {
return null;
}
/**
* Initializes this cipher with a key and a source of randomness.
*
* <p>The cipher only supports the following two operation modes:<b>
* Cipher.WRAP_MODE, and <b>
* Cipher.UNWRAP_MODE.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
*
* @param opmode the operation mode of this cipher. Only
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
* @param key the secret key.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher.
*/
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
if (opmode == Cipher.WRAP_MODE) {
decrypting = false;
} else if (opmode == Cipher.UNWRAP_MODE) {
decrypting = true;
} else {
throw new UnsupportedOperationException("This cipher can " +
"only be used for key wrapping and unwrapping");
}
AESCipher.checkKeySize(key, fixedKeySize);
byte[] encoded = key.getEncoded();
try {
cipher.init(decrypting, key.getAlgorithm(), encoded);
} finally {
Arrays.fill(encoded, (byte)0);
}
}
/**
* Initializes this cipher with a key, a set of algorithm parameters,
* and a source of randomness.
*
* <p>The cipher only supports the following two operation modes:<b>
* Cipher.WRAP_MODE, and <b>
* Cipher.UNWRAP_MODE.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
*
* @param opmode the operation mode of this cipher. Only
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
* @param key the secret key.
* @param params the algorithm parameters; must be null for this cipher.
* @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 not null.
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException("This cipher " +
"does not accept any parameters");
}
engineInit(opmode, key, random);
}
/**
* Initializes this cipher with a key, a set of algorithm parameters,
* and a source of randomness.
*
* <p>The cipher only supports the following two operation modes:<b>
* Cipher.WRAP_MODE, and <b>
* Cipher.UNWRAP_MODE.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
*
* @param opmode the operation mode of this cipher. Only
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
* @param key the secret key.
* @param params the algorithm parameters; must be null for this cipher.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters is not null.
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException("This cipher " +
"does not accept any parameters");
}
engineInit(opmode, key, random);
}
/**
* This operation is not supported by this cipher.
* Since it's impossible to initialize this cipher given the
* current Cipher.engineInit(...) implementation,
* IllegalStateException will always be thrown upon invocation.
*
* @param in the input buffer.
* @param inOffset the offset in <code>in</code> where the input
* starts.
* @param inLen the input length.
*
* @return n/a.
*
* @exception IllegalStateException upon invocation of this method.
*/
protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* This operation is not supported by this cipher.
* Since it's impossible to initialize this cipher given the
* current Cipher.engineInit(...) implementation,
* IllegalStateException will always be thrown upon invocation.
*
* @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.
*/
protected int engineUpdate(byte[] in, int inOffset, int inLen,
byte[] out, int outOffset)
throws ShortBufferException {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* This operation is not supported by this cipher.
* Since it's impossible to initialize this cipher given the
* current Cipher.engineInit(...) implementation,
* IllegalStateException will always be thrown upon invocation.
*
* @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.
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* This operation is not supported by this cipher.
* Since it's impossible to initialize this cipher given the
* current Cipher.engineInit(...) implementation,
* IllegalStateException will always be thrown upon invocation.
*
* @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 inOffset, int inLen,
byte[] out, int outOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* Returns the parameters used with this cipher which is always null
* for this cipher.
*
* @return null since this cipher does not use any parameters.
*/
protected AlgorithmParameters engineGetParameters() {
return null;
}
/**
* 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();
Arrays.fill(encoded, (byte)0);
if (!AESCrypt.isKeySizeValid(encoded.length)) {
throw new InvalidKeyException("Invalid key length: " +
encoded.length + " bytes");
}
return Math.multiplyExact(encoded.length, 8);
}
/**
* 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).
*/
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
byte[] keyVal = key.getEncoded();
if ((keyVal == null) || (keyVal.length == 0)) {
throw new InvalidKeyException("Cannot get an encoding of " +
"the key to be wrapped");
}
try {
byte[] out = new byte[Math.addExact(keyVal.length, 8)];
if (keyVal.length == 8) {
System.arraycopy(IV, 0, out, 0, IV.length);
System.arraycopy(keyVal, 0, out, IV.length, 8);
cipher.encryptBlock(out, 0, out, 0);
} else {
if (keyVal.length % 8 != 0) {
throw new IllegalBlockSizeException("length of the " +
"to be wrapped key should be multiples of 8 bytes");
}
System.arraycopy(IV, 0, out, 0, IV.length);
System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);
int N = keyVal.length / 8;
byte[] buffer = new byte[blksize];
for (int j = 0; j < 6; j++) {
for (int i = 1; i <= N; i++) {
int T = i + j * N;
System.arraycopy(out, 0, buffer, 0, IV.length);
System.arraycopy(out, i * 8, buffer, IV.length, 8);
cipher.encryptBlock(buffer, 0, buffer, 0);
for (int k = 1; T != 0; k++) {
byte v = (byte) T;
buffer[IV.length - k] ^= v;
T >>>= 8;
}
System.arraycopy(buffer, 0, out, 0, IV.length);
System.arraycopy(buffer, 8, out, 8 * i, 8);
}
}
}
return out;
} finally {
Arrays.fill(keyVal, (byte)0);
}
}
/**
* 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>.
*/
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
int wrappedKeyLen = wrappedKey.length;
// ensure the wrappedKey length is multiples of 8 bytes and non-zero
if (wrappedKeyLen == 0) {
throw new InvalidKeyException("The wrapped key is empty");
}
if (wrappedKeyLen % 8 != 0) {
throw new InvalidKeyException
("The wrapped key has invalid key length");
}
byte[] out = new byte[wrappedKeyLen - 8];
byte[] buffer = new byte[blksize];
try {
if (wrappedKeyLen == 16) {
cipher.decryptBlock(wrappedKey, 0, buffer, 0);
for (int i = 0; i < IV.length; i++) {
if (IV[i] != buffer[i]) {
throw new InvalidKeyException("Integrity check failed");
}
}
System.arraycopy(buffer, IV.length, out, 0, out.length);
} else {
System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);
System.arraycopy(wrappedKey, IV.length, out, 0, out.length);
int N = out.length / 8;
for (int j = 5; j >= 0; j--) {
for (int i = N; i > 0; i--) {
int T = i + j * N;
System.arraycopy(out, 8 * (i - 1), buffer, IV.length, 8);
for (int k = 1; T != 0; k++) {
byte v = (byte) T;
buffer[IV.length - k] ^= v;
T >>>= 8;
}
cipher.decryptBlock(buffer, 0, buffer, 0);
System.arraycopy(buffer, IV.length, out, 8 * (i - 1), 8);
}
}
for (int i = 0; i < IV.length; i++) {
if (IV[i] != buffer[i]) {
throw new InvalidKeyException("Integrity check failed");
}
}
}
return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
wrappedKeyType);
} finally {
Arrays.fill(out, (byte)0);
Arrays.fill(buffer, (byte)0);
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 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
@ -26,6 +26,7 @@
package com.sun.crypto.provider;
import java.io.*;
import java.util.Arrays;
import sun.security.util.*;
import sun.security.util.HexDumpEncoder;
import java.security.spec.AlgorithmParameterSpec;
@ -48,8 +49,11 @@ final class BlockCipherParamsCore {
private int block_size = 0;
private byte[] iv = null;
BlockCipherParamsCore(int blksize) {
private int[] moreSizes = null;
BlockCipherParamsCore(int blksize, int... moreSizes) {
block_size = blksize;
this.moreSizes = moreSizes;
}
void init(AlgorithmParameterSpec paramSpec)
@ -59,9 +63,20 @@ final class BlockCipherParamsCore {
("Inappropriate parameter specification");
}
byte[] tmpIv = ((IvParameterSpec)paramSpec).getIV();
if (tmpIv.length != block_size) {
throw new InvalidParameterSpecException("IV not " +
block_size + " bytes long");
boolean check = (tmpIv.length == block_size);
if (!check && moreSizes != null) {
for (int s : moreSizes) {
if (tmpIv.length == s) {
check = true;
break;
}
}
}
if (!check) {
String expectedLen = block_size + (moreSizes == null? "" :
Arrays.toString(moreSizes));
throw new InvalidParameterSpecException("IV length not " +
expectedLen + " bytes long");
}
iv = tmpIv.clone();
}
@ -73,15 +88,17 @@ final class BlockCipherParamsCore {
if (der.available() != 0) {
throw new IOException("IV parsing error: extra data");
}
if (tmpIv.length != block_size) {
throw new IOException("IV not " + block_size +
boolean check = (tmpIv.length == block_size);
if (!check) {
String expectedLen = block_size + (moreSizes == null? "" :
Arrays.toString(moreSizes));
throw new IOException("IV not " + expectedLen +
" bytes long");
}
iv = tmpIv;
}
void init(byte[] encoded, String decodingMethod)
throws IOException {
void init(byte[] encoded, String decodingMethod) throws IOException {
if ((decodingMethod != null) &&
(!decodingMethod.equalsIgnoreCase("ASN.1"))) {
throw new IllegalArgumentException("Only support ASN.1 format");
@ -90,8 +107,7 @@ final class BlockCipherParamsCore {
}
<T extends AlgorithmParameterSpec> T getParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException
{
throws InvalidParameterSpecException {
if (IvParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast(new IvParameterSpec(this.iv));
} else {

View file

@ -25,8 +25,6 @@
package com.sun.crypto.provider;
import jdk.internal.access.SharedSecrets;
import java.security.Key;
import java.security.PublicKey;
import java.security.PrivateKey;
@ -36,7 +34,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
@ -60,11 +58,9 @@ final class ConstructKeys {
* @return a public key constructed from the encodedKey.
*/
private static final PublicKey constructPublicKey(byte[] encodedKey,
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException
{
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException {
PublicKey key = null;
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm,
@ -111,15 +107,15 @@ final class ConstructKeys {
* @return a private key constructed from the encodedKey.
*/
private static final PrivateKey constructPrivateKey(byte[] encodedKey,
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException
{
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException {
PrivateKey key = null;
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm,
SunJCE.getInstance());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException nsae) {
// Try to see whether there is another
@ -127,6 +123,8 @@ final class ConstructKeys {
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm);
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(encodedKey);
key = keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException nsae2) {
throw new NoSuchAlgorithmException("No installed providers " +
@ -144,8 +142,6 @@ final class ConstructKeys {
new InvalidKeyException("Cannot construct private key");
ike.initCause(ikse);
throw ike;
} finally {
SharedSecrets.getJavaSecuritySpecAccess().clearEncodedKeySpec(keySpec);
}
return key;
@ -161,29 +157,48 @@ final class ConstructKeys {
* @return a secret key constructed from the encodedKey.
*/
private static final SecretKey constructSecretKey(byte[] encodedKey,
String encodedKeyAlgorithm)
{
return (new SecretKeySpec(encodedKey, encodedKeyAlgorithm));
int ofs, int len, String encodedKeyAlgorithm) {
return (new SecretKeySpec(encodedKey, ofs, len, encodedKeyAlgorithm));
}
static final Key constructKey(byte[] encoding, String keyAlgorithm,
int keyType)
throws InvalidKeyException, NoSuchAlgorithmException {
Key result = null;
int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
return constructKey(encoding, 0, encoding.length, keyAlgorithm,
keyType);
}
static final Key constructKey(byte[] encoding, int ofs, int len,
String keyAlgorithm, int keyType)
throws InvalidKeyException, NoSuchAlgorithmException {
switch (keyType) {
case Cipher.SECRET_KEY:
result = ConstructKeys.constructSecretKey(encoding,
keyAlgorithm);
break;
try {
return ConstructKeys.constructSecretKey(encoding, ofs, len,
keyAlgorithm);
} finally {
Arrays.fill(encoding, ofs, len, (byte)0);
}
case Cipher.PRIVATE_KEY:
result = ConstructKeys.constructPrivateKey(encoding,
keyAlgorithm);
break;
byte[] encoding2 = encoding;
try {
if (ofs != 0 || len != encoding.length) {
encoding2 = Arrays.copyOfRange(encoding, ofs, ofs + len);
}
return ConstructKeys.constructPrivateKey(encoding2,
keyAlgorithm);
} finally {
Arrays.fill(encoding, ofs, len, (byte)0);
if (encoding2 != encoding) {
Arrays.fill(encoding2, (byte)0);
}
}
case Cipher.PUBLIC_KEY:
result = ConstructKeys.constructPublicKey(encoding,
keyAlgorithm);
break;
if (ofs != 0 || len != encoding.length) {
encoding = Arrays.copyOfRange(encoding, ofs, ofs + len);
}
return ConstructKeys.constructPublicKey(encoding, keyAlgorithm);
default:
throw new NoSuchAlgorithmException("Unsupported key type");
}
return result;
}
}

View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 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.*;
/**
* This class acts as the base 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"
*/
class KWUtil {
static final int BLKSIZE = 16;
static final int SEMI_BLKSIZE = BLKSIZE >> 1;
static final int MIN_INPUTLEN = BLKSIZE + SEMI_BLKSIZE;
/*
* The wrapping function W as defined in section 6.1 of NIST SP 800-38F as
* well as sec 2.2.1 of RFC 3394.
* @param firstSemiblk the first semi block value to overwrite the input
* with
* @param in input bytes
* @param inLen length of the to-be-processed bytes
* @param cipher the initialized cipher object used
* @return the processed output length, i.e. same as {@code inLen}.
*/
static final int W(byte[] icvIn, byte[] in, int inLen,
SymmetricCipher cipher) {
assert((inLen >= MIN_INPUTLEN) && ((inLen % SEMI_BLKSIZE) == 0)) :
("Invalid data length for W: " + inLen);
assert(icvIn.length == SEMI_BLKSIZE) : "Invalid ICV buffer size";
// overwrite the first block of in with the icv semiblock
System.arraycopy(icvIn, 0, in, 0, SEMI_BLKSIZE);
int n = inLen / SEMI_BLKSIZE - 1;
byte[] buffer = new byte[BLKSIZE];
byte[] out = in; // in-place
for (int j = 0; j < 6; j++) {
for (int i = 1; i <= n; i++) {
int T = i + j*n;
System.arraycopy(out, 0, buffer, 0, SEMI_BLKSIZE);
System.arraycopy(out, i << 3, buffer, SEMI_BLKSIZE, 8);
cipher.encryptBlock(buffer, 0, buffer, 0);
for (int k = 1; T != 0; k++) {
byte v = (byte) T;
buffer[SEMI_BLKSIZE - k] ^= v;
T >>>= SEMI_BLKSIZE;
}
System.arraycopy(buffer, 0, out, 0, SEMI_BLKSIZE);
System.arraycopy(buffer, SEMI_BLKSIZE, out, i << 3,
SEMI_BLKSIZE);
}
}
// for W, output length is same as input length
return inLen;
}
/*
* The unwrapping function W^-1 as defined in section 6.1 of NIST SP
* 800-38F as well as sec 2.2.2 of RFC 3394.
* - separated out the initial value from the remaining recovered data
* - no output buffer argument since we cannot write out the recovered
* data until the initial value and padding bytes are verified.
* @param in input bytes, i.e. the to-be-processed data
* @param inLen length of the to-be-processed bytes
* @param ivOut buffer for holding the recovered ICV semiblock
* @param cipher the initialized cipher object used
* @return the recovered data length, i.e. {@code (inLen - icvOut.length)}
*/
static final int W_INV(byte[] in, int inLen, byte[] icvOut,
SymmetricCipher cipher) {
assert((inLen >= MIN_INPUTLEN) && ((inLen % SEMI_BLKSIZE) == 0)) :
("Invalid data length for W_INV: " + inLen);
assert(icvOut.length == SEMI_BLKSIZE) : "Invalid ICV buffer size";
byte[] buffer = new byte[BLKSIZE];
System.arraycopy(in, 0, buffer, 0, SEMI_BLKSIZE);
System.arraycopy(in, SEMI_BLKSIZE, in, 0, inLen - SEMI_BLKSIZE);
int n = (inLen - SEMI_BLKSIZE) / SEMI_BLKSIZE;
for (int j = 5; j >= 0; j--) {
for (int i = n; i > 0; i--) {
int T = i + n*j;
int idx = (i-1) << 3;
System.arraycopy(in, idx, buffer, SEMI_BLKSIZE, SEMI_BLKSIZE);
for (int k = 1; T != 0; k++) {
byte v = (byte) T;
buffer[SEMI_BLKSIZE - k] ^= v;
T >>>= SEMI_BLKSIZE;
}
cipher.decryptBlock(buffer, 0, buffer, 0);
System.arraycopy(buffer, SEMI_BLKSIZE, in, idx, SEMI_BLKSIZE);
}
}
System.arraycopy(buffer, 0, icvOut, 0, SEMI_BLKSIZE);
return inLen - SEMI_BLKSIZE;
}
}

View file

@ -0,0 +1,753 @@
/*
* 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
* <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(16), -1);
}
}
// for AES_128/KW/NoPadding
public static final class AES128_KW_PKCS5Padding extends KeyWrapCipher {
public AES128_KW_PKCS5Padding() {
super(new AESKeyWrap(), new PKCS5Padding(16), 16);
}
}
// for AES_192/KW/NoPadding
public static final class AES192_KW_PKCS5Padding extends KeyWrapCipher {
public AES192_KW_PKCS5Padding() {
super(new AESKeyWrap(), new PKCS5Padding(16), 24);
}
}
// for AES_256/KW/NoPadding
public static final class AES256_KW_PKCS5Padding extends KeyWrapCipher {
public AES256_KW_PKCS5Padding() {
super(new AESKeyWrap(), new PKCS5Padding(16), 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.
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.
*/
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.
*
* @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.
*
* @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). i.e. 16 bytes.
*
* @return the block size (in bytes), i.e. 16 bytes.
*/
@Override
protected int engineGetBlockSize() {
return cipher.getBlockSize();
}
/**
* 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).
*
* @return the user-specified iv or null if default iv is used.
*/
@Override
protected byte[] engineGetIV() {
return cipher.getIV().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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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(...)
*
* @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) {
return Arrays.copyOf(out, outLen);
} else {
return out;
}
} catch (ShortBufferException sbe) {
// should never happen
throw new AssertionError(sbe);
}
}
/**
* 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 {
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;
}
return (opmode == Cipher.ENCRYPT_MODE?
helperEncrypt(out, len) : helperDecrypt(out, len));
}
// 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.
*
* @return AlgorithmParameters object containing IV.
*/
@Override
protected AlgorithmParameters engineGetParameters() {
AlgorithmParameters params = null;
byte[] iv = cipher.getIV();
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.
*
* @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.
*
* @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.
*
* @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);
}
}
}

View file

@ -192,6 +192,16 @@ public final class SunJCE extends Provider {
attrs.clear();
attrs.put("SupportedKeyFormats", "RAW");
psA("Cipher", "AES/KW/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES_KW_NoPadding",
attrs);
ps("Cipher", "AES/KW/PKCS5Padding",
"com.sun.crypto.provider.KeyWrapCipher$AES_KW_PKCS5Padding",
null, attrs);
psA("Cipher", "AES/KWP/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES_KWP_NoPadding",
attrs);
psA("Cipher", "AES_128/ECB/NoPadding",
"com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding",
attrs);
@ -207,6 +217,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_128/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding",
attrs);
psA("Cipher", "AES_128/KW/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_NoPadding",
attrs);
ps("Cipher", "AES_128/KW/PKCS5Padding",
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_PKCS5Padding",
null, attrs);
psA("Cipher", "AES_128/KWP/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES128_KWP_NoPadding",
attrs);
psA("Cipher", "AES_192/ECB/NoPadding",
"com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding",
@ -223,6 +242,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_192/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding",
attrs);
psA("Cipher", "AES_192/KW/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_NoPadding",
attrs);
ps("Cipher", "AES_192/KW/PKCS5Padding",
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_PKCS5Padding",
null, attrs);
psA("Cipher", "AES_192/KWP/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES192_KWP_NoPadding",
attrs);
psA("Cipher", "AES_256/ECB/NoPadding",
"com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding",
@ -239,6 +267,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_256/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding",
attrs);
psA("Cipher", "AES_256/KW/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_NoPadding",
attrs);
ps("Cipher", "AES_256/KW/PKCS5Padding",
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_PKCS5Padding",
null, attrs);
psA("Cipher", "AES_256/KWP/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES256_KWP_NoPadding",
attrs);
attrs.clear();
attrs.put("SupportedModes", "CBC");
@ -253,17 +290,6 @@ public final class SunJCE extends Provider {
attrs.put("SupportedKeyFormats", "RAW");
psA("Cipher", "ARCFOUR",
"com.sun.crypto.provider.ARCFOURCipher", attrs);
ps("Cipher", "AESWrap", "com.sun.crypto.provider.AESWrapCipher$General",
null, attrs);
psA("Cipher", "AESWrap_128",
"com.sun.crypto.provider.AESWrapCipher$AES128",
attrs);
psA("Cipher", "AESWrap_192",
"com.sun.crypto.provider.AESWrapCipher$AES192",
attrs);
psA("Cipher", "AESWrap_256",
"com.sun.crypto.provider.AESWrapCipher$AES256",
attrs);
attrs.clear();
attrs.put("SupportedKeyFormats", "RAW");