8248268: Support KWP in addition to KW
Reviewed-by: xuelei
This commit is contained in:
parent
3482cb87fd
commit
136badb1f7
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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:
|
* in feedback-mode. IV is defined in the standards as follows:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <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>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @author Valerie Peng
|
* @author Valerie Peng
|
||||||
@ -46,7 +47,7 @@ public final class AESParameters extends AlgorithmParametersSpi {
|
|||||||
private BlockCipherParamsCore core;
|
private BlockCipherParamsCore core;
|
||||||
|
|
||||||
public AESParameters() {
|
public AESParameters() {
|
||||||
core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE);
|
core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE, 4, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void engineInit(AlgorithmParameterSpec paramSpec)
|
protected void engineInit(AlgorithmParameterSpec paramSpec)
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,6 +26,7 @@
|
|||||||
package com.sun.crypto.provider;
|
package com.sun.crypto.provider;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
import sun.security.util.*;
|
import sun.security.util.*;
|
||||||
import sun.security.util.HexDumpEncoder;
|
import sun.security.util.HexDumpEncoder;
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
@ -48,8 +49,11 @@ final class BlockCipherParamsCore {
|
|||||||
private int block_size = 0;
|
private int block_size = 0;
|
||||||
private byte[] iv = null;
|
private byte[] iv = null;
|
||||||
|
|
||||||
BlockCipherParamsCore(int blksize) {
|
private int[] moreSizes = null;
|
||||||
|
|
||||||
|
BlockCipherParamsCore(int blksize, int... moreSizes) {
|
||||||
block_size = blksize;
|
block_size = blksize;
|
||||||
|
this.moreSizes = moreSizes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(AlgorithmParameterSpec paramSpec)
|
void init(AlgorithmParameterSpec paramSpec)
|
||||||
@ -59,9 +63,20 @@ final class BlockCipherParamsCore {
|
|||||||
("Inappropriate parameter specification");
|
("Inappropriate parameter specification");
|
||||||
}
|
}
|
||||||
byte[] tmpIv = ((IvParameterSpec)paramSpec).getIV();
|
byte[] tmpIv = ((IvParameterSpec)paramSpec).getIV();
|
||||||
if (tmpIv.length != block_size) {
|
boolean check = (tmpIv.length == block_size);
|
||||||
throw new InvalidParameterSpecException("IV not " +
|
if (!check && moreSizes != null) {
|
||||||
block_size + " bytes long");
|
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();
|
iv = tmpIv.clone();
|
||||||
}
|
}
|
||||||
@ -73,15 +88,17 @@ final class BlockCipherParamsCore {
|
|||||||
if (der.available() != 0) {
|
if (der.available() != 0) {
|
||||||
throw new IOException("IV parsing error: extra data");
|
throw new IOException("IV parsing error: extra data");
|
||||||
}
|
}
|
||||||
if (tmpIv.length != block_size) {
|
boolean check = (tmpIv.length == block_size);
|
||||||
throw new IOException("IV not " + block_size +
|
if (!check) {
|
||||||
|
String expectedLen = block_size + (moreSizes == null? "" :
|
||||||
|
Arrays.toString(moreSizes));
|
||||||
|
throw new IOException("IV not " + expectedLen +
|
||||||
" bytes long");
|
" bytes long");
|
||||||
}
|
}
|
||||||
iv = tmpIv;
|
iv = tmpIv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(byte[] encoded, String decodingMethod)
|
void init(byte[] encoded, String decodingMethod) throws IOException {
|
||||||
throws IOException {
|
|
||||||
if ((decodingMethod != null) &&
|
if ((decodingMethod != null) &&
|
||||||
(!decodingMethod.equalsIgnoreCase("ASN.1"))) {
|
(!decodingMethod.equalsIgnoreCase("ASN.1"))) {
|
||||||
throw new IllegalArgumentException("Only support ASN.1 format");
|
throw new IllegalArgumentException("Only support ASN.1 format");
|
||||||
@ -90,8 +107,7 @@ final class BlockCipherParamsCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<T extends AlgorithmParameterSpec> T getParameterSpec(Class<T> paramSpec)
|
<T extends AlgorithmParameterSpec> T getParameterSpec(Class<T> paramSpec)
|
||||||
throws InvalidParameterSpecException
|
throws InvalidParameterSpecException {
|
||||||
{
|
|
||||||
if (IvParameterSpec.class.isAssignableFrom(paramSpec)) {
|
if (IvParameterSpec.class.isAssignableFrom(paramSpec)) {
|
||||||
return paramSpec.cast(new IvParameterSpec(this.iv));
|
return paramSpec.cast(new IvParameterSpec(this.iv));
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
package com.sun.crypto.provider;
|
package com.sun.crypto.provider;
|
||||||
|
|
||||||
import jdk.internal.access.SharedSecrets;
|
|
||||||
|
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
@ -36,7 +34,7 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.util.Arrays;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@ -60,11 +58,9 @@ final class ConstructKeys {
|
|||||||
* @return a public key constructed from the encodedKey.
|
* @return a public key constructed from the encodedKey.
|
||||||
*/
|
*/
|
||||||
private static final PublicKey constructPublicKey(byte[] encodedKey,
|
private static final PublicKey constructPublicKey(byte[] encodedKey,
|
||||||
String encodedKeyAlgorithm)
|
String encodedKeyAlgorithm)
|
||||||
throws InvalidKeyException, NoSuchAlgorithmException
|
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||||
{
|
|
||||||
PublicKey key = null;
|
PublicKey key = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
KeyFactory keyFactory =
|
KeyFactory keyFactory =
|
||||||
KeyFactory.getInstance(encodedKeyAlgorithm,
|
KeyFactory.getInstance(encodedKeyAlgorithm,
|
||||||
@ -111,15 +107,15 @@ final class ConstructKeys {
|
|||||||
* @return a private key constructed from the encodedKey.
|
* @return a private key constructed from the encodedKey.
|
||||||
*/
|
*/
|
||||||
private static final PrivateKey constructPrivateKey(byte[] encodedKey,
|
private static final PrivateKey constructPrivateKey(byte[] encodedKey,
|
||||||
String encodedKeyAlgorithm)
|
String encodedKeyAlgorithm)
|
||||||
throws InvalidKeyException, NoSuchAlgorithmException
|
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||||
{
|
|
||||||
PrivateKey key = null;
|
PrivateKey key = null;
|
||||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
|
|
||||||
try {
|
try {
|
||||||
KeyFactory keyFactory =
|
KeyFactory keyFactory =
|
||||||
KeyFactory.getInstance(encodedKeyAlgorithm,
|
KeyFactory.getInstance(encodedKeyAlgorithm,
|
||||||
SunJCE.getInstance());
|
SunJCE.getInstance());
|
||||||
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
|
||||||
return keyFactory.generatePrivate(keySpec);
|
return keyFactory.generatePrivate(keySpec);
|
||||||
} catch (NoSuchAlgorithmException nsae) {
|
} catch (NoSuchAlgorithmException nsae) {
|
||||||
// Try to see whether there is another
|
// Try to see whether there is another
|
||||||
@ -127,6 +123,8 @@ final class ConstructKeys {
|
|||||||
try {
|
try {
|
||||||
KeyFactory keyFactory =
|
KeyFactory keyFactory =
|
||||||
KeyFactory.getInstance(encodedKeyAlgorithm);
|
KeyFactory.getInstance(encodedKeyAlgorithm);
|
||||||
|
PKCS8EncodedKeySpec keySpec =
|
||||||
|
new PKCS8EncodedKeySpec(encodedKey);
|
||||||
key = keyFactory.generatePrivate(keySpec);
|
key = keyFactory.generatePrivate(keySpec);
|
||||||
} catch (NoSuchAlgorithmException nsae2) {
|
} catch (NoSuchAlgorithmException nsae2) {
|
||||||
throw new NoSuchAlgorithmException("No installed providers " +
|
throw new NoSuchAlgorithmException("No installed providers " +
|
||||||
@ -144,8 +142,6 @@ final class ConstructKeys {
|
|||||||
new InvalidKeyException("Cannot construct private key");
|
new InvalidKeyException("Cannot construct private key");
|
||||||
ike.initCause(ikse);
|
ike.initCause(ikse);
|
||||||
throw ike;
|
throw ike;
|
||||||
} finally {
|
|
||||||
SharedSecrets.getJavaSecuritySpecAccess().clearEncodedKeySpec(keySpec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
@ -161,29 +157,48 @@ final class ConstructKeys {
|
|||||||
* @return a secret key constructed from the encodedKey.
|
* @return a secret key constructed from the encodedKey.
|
||||||
*/
|
*/
|
||||||
private static final SecretKey constructSecretKey(byte[] encodedKey,
|
private static final SecretKey constructSecretKey(byte[] encodedKey,
|
||||||
String encodedKeyAlgorithm)
|
int ofs, int len, String encodedKeyAlgorithm) {
|
||||||
{
|
return (new SecretKeySpec(encodedKey, ofs, len, encodedKeyAlgorithm));
|
||||||
return (new SecretKeySpec(encodedKey, encodedKeyAlgorithm));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final Key constructKey(byte[] encoding, String keyAlgorithm,
|
static final Key constructKey(byte[] encoding, String keyAlgorithm,
|
||||||
int keyType)
|
int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
|
||||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
return constructKey(encoding, 0, encoding.length, keyAlgorithm,
|
||||||
Key result = null;
|
keyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Key constructKey(byte[] encoding, int ofs, int len,
|
||||||
|
String keyAlgorithm, int keyType)
|
||||||
|
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||||
switch (keyType) {
|
switch (keyType) {
|
||||||
case Cipher.SECRET_KEY:
|
case Cipher.SECRET_KEY:
|
||||||
result = ConstructKeys.constructSecretKey(encoding,
|
try {
|
||||||
keyAlgorithm);
|
return ConstructKeys.constructSecretKey(encoding, ofs, len,
|
||||||
break;
|
keyAlgorithm);
|
||||||
|
} finally {
|
||||||
|
Arrays.fill(encoding, ofs, len, (byte)0);
|
||||||
|
}
|
||||||
case Cipher.PRIVATE_KEY:
|
case Cipher.PRIVATE_KEY:
|
||||||
result = ConstructKeys.constructPrivateKey(encoding,
|
byte[] encoding2 = encoding;
|
||||||
keyAlgorithm);
|
try {
|
||||||
break;
|
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:
|
case Cipher.PUBLIC_KEY:
|
||||||
result = ConstructKeys.constructPublicKey(encoding,
|
if (ofs != 0 || len != encoding.length) {
|
||||||
keyAlgorithm);
|
encoding = Arrays.copyOfRange(encoding, ofs, ofs + len);
|
||||||
break;
|
}
|
||||||
|
return ConstructKeys.constructPublicKey(encoding, keyAlgorithm);
|
||||||
|
default:
|
||||||
|
throw new NoSuchAlgorithmException("Unsupported key type");
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
129
src/java.base/share/classes/com/sun/crypto/provider/KWUtil.java
Normal file
129
src/java.base/share/classes/com/sun/crypto/provider/KWUtil.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -192,6 +192,16 @@ public final class SunJCE extends Provider {
|
|||||||
|
|
||||||
attrs.clear();
|
attrs.clear();
|
||||||
attrs.put("SupportedKeyFormats", "RAW");
|
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",
|
psA("Cipher", "AES_128/ECB/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding",
|
"com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding",
|
||||||
attrs);
|
attrs);
|
||||||
@ -207,6 +217,15 @@ public final class SunJCE extends Provider {
|
|||||||
psA("Cipher", "AES_128/GCM/NoPadding",
|
psA("Cipher", "AES_128/GCM/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding",
|
"com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding",
|
||||||
attrs);
|
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",
|
psA("Cipher", "AES_192/ECB/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES192_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",
|
psA("Cipher", "AES_192/GCM/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding",
|
"com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding",
|
||||||
attrs);
|
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",
|
psA("Cipher", "AES_256/ECB/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES256_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",
|
psA("Cipher", "AES_256/GCM/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding",
|
"com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding",
|
||||||
attrs);
|
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.clear();
|
||||||
attrs.put("SupportedModes", "CBC");
|
attrs.put("SupportedModes", "CBC");
|
||||||
@ -253,17 +290,6 @@ public final class SunJCE extends Provider {
|
|||||||
attrs.put("SupportedKeyFormats", "RAW");
|
attrs.put("SupportedKeyFormats", "RAW");
|
||||||
psA("Cipher", "ARCFOUR",
|
psA("Cipher", "ARCFOUR",
|
||||||
"com.sun.crypto.provider.ARCFOURCipher", attrs);
|
"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.clear();
|
||||||
attrs.put("SupportedKeyFormats", "RAW");
|
attrs.put("SupportedKeyFormats", "RAW");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -116,20 +116,31 @@ public enum KnownOIDs {
|
|||||||
AES_128$CBC$NoPadding("2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding"),
|
AES_128$CBC$NoPadding("2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding"),
|
||||||
AES_128$OFB$NoPadding("2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding"),
|
AES_128$OFB$NoPadding("2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding"),
|
||||||
AES_128$CFB$NoPadding("2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding"),
|
AES_128$CFB$NoPadding("2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding"),
|
||||||
AESWRAP_128("2.16.840.1.101.3.4.1.5"),
|
AES_128$KW$NoPadding("2.16.840.1.101.3.4.1.5", "AES_128/KW/NoPadding",
|
||||||
|
"AESWrap_128"),
|
||||||
AES_128$GCM$NoPadding("2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding"),
|
AES_128$GCM$NoPadding("2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding"),
|
||||||
|
AES_128$KWP$NoPadding("2.16.840.1.101.3.4.1.8", "AES_128/KWP/NoPadding",
|
||||||
|
"AESWrapPad_128"),
|
||||||
|
|
||||||
AES_192$ECB$NoPadding("2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding"),
|
AES_192$ECB$NoPadding("2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding"),
|
||||||
AES_192$CBC$NoPadding("2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding"),
|
AES_192$CBC$NoPadding("2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding"),
|
||||||
AES_192$OFB$NoPadding("2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding"),
|
AES_192$OFB$NoPadding("2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding"),
|
||||||
AES_192$CFB$NoPadding("2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding"),
|
AES_192$CFB$NoPadding("2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding"),
|
||||||
AESWRAP_192("2.16.840.1.101.3.4.1.25"),
|
AES_192$KW$NoPadding("2.16.840.1.101.3.4.1.25", "AES_192/KW/NoPadding",
|
||||||
|
"AESWrap_192"),
|
||||||
AES_192$GCM$NoPadding("2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding"),
|
AES_192$GCM$NoPadding("2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding"),
|
||||||
|
AES_192$KWP$NoPadding("2.16.840.1.101.3.4.1.28", "AES_192/KWP/NoPadding",
|
||||||
|
"AESWrapPad_192"),
|
||||||
|
|
||||||
AES_256$ECB$NoPadding("2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding"),
|
AES_256$ECB$NoPadding("2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding"),
|
||||||
AES_256$CBC$NoPadding("2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding"),
|
AES_256$CBC$NoPadding("2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding"),
|
||||||
AES_256$OFB$NoPadding("2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding"),
|
AES_256$OFB$NoPadding("2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding"),
|
||||||
AES_256$CFB$NoPadding("2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding"),
|
AES_256$CFB$NoPadding("2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding"),
|
||||||
AESWRAP_256("2.16.840.1.101.3.4.1.45"),
|
AES_256$KW$NoPadding("2.16.840.1.101.3.4.1.45", "AES_256/KW/NoPadding",
|
||||||
|
"AESWrap_256"),
|
||||||
AES_256$GCM$NoPadding("2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding"),
|
AES_256$GCM$NoPadding("2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding"),
|
||||||
|
AES_256$KWP$NoPadding("2.16.840.1.101.3.4.1.48", "AES_256/KWP/NoPadding",
|
||||||
|
"AESWrapPad_256"),
|
||||||
|
|
||||||
// hashAlgs 2.16.840.1.101.3.4.2.*
|
// hashAlgs 2.16.840.1.101.3.4.2.*
|
||||||
SHA_256("2.16.840.1.101.3.4.2.1", "SHA-256", "SHA256"),
|
SHA_256("2.16.840.1.101.3.4.2.1", "SHA-256", "SHA256"),
|
||||||
|
@ -228,5 +228,8 @@ public final class SecurityProviderConstants {
|
|||||||
// For backward compatility, refer to PKCS1 mapping for RSA
|
// For backward compatility, refer to PKCS1 mapping for RSA
|
||||||
// KeyPairGenerator and KeyFactory
|
// KeyPairGenerator and KeyFactory
|
||||||
store("PKCS1", KnownOIDs.PKCS1, KnownOIDs.RSA.value());
|
store("PKCS1", KnownOIDs.PKCS1, KnownOIDs.RSA.value());
|
||||||
|
|
||||||
|
store("AES/KW/NoPadding", null, "AESWrap");
|
||||||
|
store("AES/KWP/NoPadding", null, "AESWrapPad");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,10 +23,10 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 5008156
|
* @bug 5008156 8248268
|
||||||
* @run main NISTWrapKAT
|
* @run testng NISTWrapKAT
|
||||||
* @summary Verify that the "AESWrap" key wrap cipher work as
|
* @summary Verify that the AES-Key-Wrap and AES-Key-Wrap-Pad ciphers
|
||||||
* expected using NIST test vectors.
|
* work as expected using NIST test vectors.
|
||||||
* @author Valerie Peng
|
* @author Valerie Peng
|
||||||
*/
|
*/
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
@ -35,6 +35,9 @@ import javax.crypto.*;
|
|||||||
import javax.crypto.spec.*;
|
import javax.crypto.spec.*;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import org.testng.annotations.DataProvider;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
import org.testng.Assert;
|
||||||
|
|
||||||
public class NISTWrapKAT {
|
public class NISTWrapKAT {
|
||||||
|
|
||||||
@ -42,75 +45,328 @@ public class NISTWrapKAT {
|
|||||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
|
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
|
||||||
private static final String DATA =
|
private static final String DATA =
|
||||||
"00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f";
|
"00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f";
|
||||||
|
// from RFC 3394 sec4
|
||||||
private static String AES128_128 =
|
private static String KW_AES128_128 =
|
||||||
"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5";
|
"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5";
|
||||||
private static String AES192_128 =
|
private static String KW_AES192_128 =
|
||||||
"96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d";
|
"96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d";
|
||||||
private static String AES192_192 =
|
private static String KW_AES192_192 =
|
||||||
"031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2";
|
"031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2";
|
||||||
private static String AES256_128 =
|
private static String KW_AES256_128 =
|
||||||
"64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7";
|
"64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7";
|
||||||
private static String AES256_192 =
|
private static String KW_AES256_192 =
|
||||||
"a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1";
|
"a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1";
|
||||||
private static String AES256_256 =
|
private static String KW_AES256_256 =
|
||||||
"28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21";
|
"28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21";
|
||||||
|
|
||||||
public static void testKeyWrap(int keyLen, int dataLen,
|
private static String KWP_AES128_56 = "1B1D4BC2A90B1FA389412B3D40FECB20";
|
||||||
String expected) throws Exception {
|
private static String KWP_AES128_112 =
|
||||||
System.out.println("Testing AESWrap Cipher with " +
|
"EA0BFDE8AF063E8918E811A05D2A4C23A367B45315716B5B";
|
||||||
dataLen + "-byte data with " + 8*keyLen + "-bit key");
|
private static String KWP_AES192_56 = "87CE2C5C2D7196E09381056B319D91E9";
|
||||||
Cipher c = Cipher.getInstance("AESWrap", "SunJCE");
|
private static String KWP_AES192_112 =
|
||||||
byte[] keyVal = new byte[keyLen];
|
"900484950F84EB6ED74CE81DCDACA26E72BB29D4A6F7AC74";
|
||||||
byte[] dataVal = new byte[dataLen];
|
private static String KWP_AES192_168 =
|
||||||
|
"A402348F1956DB968FDDFD8976420F9DDEB7183CF16B91B0AEB74CAB196C343E";
|
||||||
|
private static String KWP_AES256_56 = "809BB1864A18938529E97EFCD9544E9A";
|
||||||
|
private static String KWP_AES256_112 =
|
||||||
|
"C68168173F141E6D5767611574A941259090DA78D7DF9DF7";
|
||||||
|
private static String KWP_AES256_168 =
|
||||||
|
"308D49692B5F8CF638D54BB4B985633504237329964C76EBB3F669870A708DBC";
|
||||||
|
private static String KWP_AES256_224 =
|
||||||
|
"0942747DB07032A3F04CDB2E7DE1CBA038F92BC355393AE9A0E4AE8C901912AC3D3AF0F16D240607";
|
||||||
|
// from RFC 5649 sec6
|
||||||
|
private static String KEK2 = "5840DF6E29B02AF1AB493B705BF16EA1AE8338F4DCC176A8";
|
||||||
|
|
||||||
// setup the key encryption key and the to-be-wrapped key
|
private static byte[] toBytes(String hex, int hexLen) {
|
||||||
BigInteger temp = new BigInteger(KEK.substring(0, keyLen*2), 16);
|
if (hexLen < hex.length()) {
|
||||||
|
hex = hex.substring(0, hexLen);
|
||||||
|
} else {
|
||||||
|
hexLen = hex.length();
|
||||||
|
}
|
||||||
|
int outLen = hexLen >> 1;
|
||||||
|
BigInteger temp = new BigInteger(hex, 16);
|
||||||
byte[] val = temp.toByteArray();
|
byte[] val = temp.toByteArray();
|
||||||
System.arraycopy(val, 0, keyVal, keyVal.length-val.length,
|
if (val.length == outLen) {
|
||||||
val.length);
|
return val;
|
||||||
temp = new BigInteger(DATA.substring(0, dataLen*2), 16);
|
} else {
|
||||||
val = temp.toByteArray();
|
byte[] out = new byte[outLen];
|
||||||
System.arraycopy(val, 0, dataVal, dataVal.length-val.length,
|
if (val.length < outLen) {
|
||||||
val.length);
|
// enlarge
|
||||||
|
System.arraycopy(val, 0, out, outLen - val.length, val.length);
|
||||||
|
} else {
|
||||||
|
// truncate
|
||||||
|
System.arraycopy(val, val.length - outLen, out, 0, outLen);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DataProvider
|
||||||
|
public Object[][] testData() {
|
||||||
|
return new Object[][] {
|
||||||
|
{ "AESWrap", KEK, 16, DATA, 16, KW_AES128_128 },
|
||||||
|
{ "AESWrap", KEK, 24, DATA, 16, KW_AES192_128 },
|
||||||
|
{ "AESWrap", KEK, 24, DATA, 24, KW_AES192_192 },
|
||||||
|
{ "AESWrap", KEK, 32, DATA, 16, KW_AES256_128 },
|
||||||
|
{ "AESWrap", KEK, 32, DATA, 24, KW_AES256_192 },
|
||||||
|
{ "AESWrap", KEK, 32, DATA, 32, KW_AES256_256 },
|
||||||
|
{ "AESWrap_128", KEK, 16, DATA, 16, KW_AES128_128 },
|
||||||
|
{ "AESWrap_192", KEK, 24, DATA, 16, KW_AES192_128 },
|
||||||
|
{ "AESWrap_256", KEK, 32, DATA, 16, KW_AES256_128 },
|
||||||
|
{ "AES/KW/NoPadding", KEK, 16, DATA, 16, KW_AES128_128 },
|
||||||
|
{ "AES/KW/NoPadding", KEK, 24, DATA, 16, KW_AES192_128 },
|
||||||
|
{ "AES/KW/NoPadding", KEK, 24, DATA, 24, KW_AES192_192 },
|
||||||
|
{ "AES/KW/NoPadding", KEK, 32, DATA, 16, KW_AES256_128 },
|
||||||
|
{ "AES/KW/NoPadding", KEK, 32, DATA, 24, KW_AES256_192 },
|
||||||
|
{ "AES/KW/NoPadding", KEK, 32, DATA, 32, KW_AES256_256 },
|
||||||
|
{ "AESWrapPad", KEK, 16, DATA, 7, KWP_AES128_56 },
|
||||||
|
{ "AESWrapPad", KEK, 16, DATA, 14, KWP_AES128_112 },
|
||||||
|
{ "AESWrapPad", KEK, 24, DATA, 7, KWP_AES192_56 },
|
||||||
|
{ "AESWrapPad", KEK, 24, DATA, 14, KWP_AES192_112 },
|
||||||
|
{ "AESWrapPad", KEK, 24, DATA, 21, KWP_AES192_168 },
|
||||||
|
{ "AESWrapPad", KEK, 32, DATA, 7, KWP_AES256_56 },
|
||||||
|
{ "AESWrapPad", KEK, 32, DATA, 14, KWP_AES256_112 },
|
||||||
|
{ "AESWrapPad", KEK, 32, DATA, 21, KWP_AES256_168 },
|
||||||
|
{ "AESWrapPad", KEK, 32, DATA, 28, KWP_AES256_224 },
|
||||||
|
{ "AESWrapPad_128", KEK, 16, DATA, 7, KWP_AES128_56 },
|
||||||
|
{ "AESWrapPad_192", KEK, 24, DATA, 7, KWP_AES192_56 },
|
||||||
|
{ "AESWrapPad_256", KEK, 32, DATA, 7, KWP_AES256_56 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 16, DATA, 7, KWP_AES128_56 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 16, DATA, 14, KWP_AES128_112 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 24, DATA, 7, KWP_AES192_56 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 24, DATA, 14, KWP_AES192_112 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 24, DATA, 21, KWP_AES192_168 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 32, DATA, 7, KWP_AES256_56 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 32, DATA, 14, KWP_AES256_112 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 32, DATA, 21, KWP_AES256_168 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK, 32, DATA, 28, KWP_AES256_224 },
|
||||||
|
{ "AES/KWP/NoPadding", KEK2, 24, "466F7250617369", 7,
|
||||||
|
"AFBEB0F07DFBF5419200F2CCB50BB24F" },
|
||||||
|
{ "AES/KWP/NoPadding", KEK2, 24,
|
||||||
|
"C37B7E6492584340BED12207808941155068F738", 20,
|
||||||
|
"138BDEAA9B8FA7FC61F97742E72248EE5AE6AE5360D1AE6A5F54F373FA543B6A" },
|
||||||
|
// some more test vectors for KW and KWP
|
||||||
|
// from csrc.nist.gov/groups/STM/cavp/documents/mac/kwtestvectors.zip
|
||||||
|
{ "AES/KW/NoPadding", "7575da3a93607cc2bfd8cec7aadfd9a6", 16,
|
||||||
|
"42136d3c384a3eeac95a066fd28fed3f", 16,
|
||||||
|
"031f6bd7e61e643df68594816f64caa3f56fabea2548f5fb" },
|
||||||
|
{ "AES/KW/NoPadding", "e5d058e7f1c22c016c4e1cc9b26b9f8f", 16,
|
||||||
|
"7f604e9b8d39d3c91e193fe6f196c1e3da6211a7c9a33b8873b64b138d1803" +
|
||||||
|
"e4", 32,
|
||||||
|
"60b9f8ac797c56e01e9b5f84d65816a980777869f67991a0e6dc19b8cd75c9" +
|
||||||
|
"b54db4a38456bbd6f3" },
|
||||||
|
{ "AES/KW/NoPadding", "67ae4270bcdd31e8326b7e7f94c80276", 16,
|
||||||
|
"57e748b62fbc37ba25e904ee973d01b136cf7c1d0c8c5c87", 24,
|
||||||
|
"96cec0e3272a21faa550a857957aa38ce3c1cf06f0dd9f5b5c5c422cef6c69" +
|
||||||
|
"a1" },
|
||||||
|
{ "AES/KW/NoPadding", "d7aa53aefad65cd95b57c8eee7b0a906", 16,
|
||||||
|
"4a8daee6774751fc4489e837b8f7fba6896c70bb3d5e53053c92eb58046ee4" +
|
||||||
|
"a7002e542311253b97", 40,
|
||||||
|
"84ebd38cf06c674dcf186977de4a40c6dde3e7f49361a43420a887d2931b29" +
|
||||||
|
"c23e2db72e95e4107001da925181bb7097" },
|
||||||
|
{ "AES/KW/NoPadding", "98311985c4661d7e811ee56070e6fecf", 16,
|
||||||
|
"18840c96813864ef3093b48cdde6ac5d78248b96d4a2cd1f15f0b56f98213d" +
|
||||||
|
"bf87e1ccad04e0d4f1954c233ea3e48fdad8f2b1156e54e19e3b5f4a66d2e9" +
|
||||||
|
"149032b876c51249165fe8c28e112a685b2d228a8ac308017574274af36a4e" +
|
||||||
|
"a3877bcc9850bafe8fc0e0a712faca0dea98396f9143bc5819fe4933a806e9" +
|
||||||
|
"b965133e3c695a45f0fbd6961798c400d7477287df64798b651e0d3009c13f" +
|
||||||
|
"7a2246c28f983509b9e5339919f2cdffcdfc550693cba9491c00334c4a62d8" +
|
||||||
|
"78c4d0ca57b1104bc0174968ea8e3730b9e68db49678b23cd508ebe3e12e94" +
|
||||||
|
"b0ad3791023a8ef95473f0b32f906738f34e94e45a4480ad768072e1853adb" +
|
||||||
|
"63996b9ac27a1dade70804b82290a2274c6dcc3ccd40a8b38a56a5eb03f590" +
|
||||||
|
"75de015e8f9096f53549f6374e02da947fb849287a447f757cc340b6bded71" +
|
||||||
|
"d480988b6d2fcd984fba841470519830304667fef0a577b4cf84f76aef9deb" +
|
||||||
|
"84dde36abfbd76673c17113dbea7a3e24bf9b57a8fd17173a1ef91497b732b" +
|
||||||
|
"3fa8889bed58a503a0d3e20bc27ec4dbf5d13a93cbad05495e3df15e1fe34a" +
|
||||||
|
"3a6d6f648ea4aa60b2f114f30944ae593675dac2db188f90a3c483fb82cec0" +
|
||||||
|
"f0d295544d798b62cdcb51c6c036af1a341d78babf87b92609c1d866e311a4" +
|
||||||
|
"6abccc8ba8c6a41420359bb061d7e752c0ed25990eef57c9f9e190572203f8" +
|
||||||
|
"c473edf8cfc8c26d34e37240f45ded97", 512,
|
||||||
|
"625aea9122b7b57b9f36446f9053acc42c6435a7f69d91b41547026f833291" +
|
||||||
|
"d488e477c7ccba698c143633a304f463d6af4a3e72c189234fcfc360013e65" +
|
||||||
|
"b07b7f7a36c529d3fdbbdbd6224bf100c14bc5354893b44790f54c739a2b1f" +
|
||||||
|
"5bda82d70fb600ed9b0606dbddea52e508b492b72d8779856274aaaaddc0a3" +
|
||||||
|
"edb6cfc788b603101bedfcc3f44baa62336bd950c2e349d5daf04f2e23ec26" +
|
||||||
|
"28893d214e277569c565e5e6aa8b72ffa14118a3b57f814b4deb179980b5ee" +
|
||||||
|
"efa4fd93f1751850466e929be537801babc2120f3ff1ffe5fea813ec7788ea" +
|
||||||
|
"f43f5ef657e5af48395c3ad11aaf741549090b58670695f7c95c68e00576ca" +
|
||||||
|
"18ef0313f2b4b757219fc8db3dc2db28721d6f912547ebfebcd96935c3100a" +
|
||||||
|
"a4e4df9955acae1b4e2c10df1166d46c4285ab631c6d2ce58ad3ae99c07c01" +
|
||||||
|
"9dcd15958694055281ccd6f803af290431f188cc4c429e84a4c30fd9c63968" +
|
||||||
|
"dfd0951c417efb71921c207de172a9546bdd3e2bb35b45e140892c649f88c3" +
|
||||||
|
"1a438f864e801a69f8010aa3d77a26601a7a89067c81b0f7e70d8e82f21f88" +
|
||||||
|
"c7d0bb0c8ca0db875d6c3f8c6f6d709bbb31c7da2e31f3571daa2c5ab13bfc" +
|
||||||
|
"16624cf35abd526e84269fb45bbd2fcd8c383d6fbb700bc4b5205b3ef8c432" +
|
||||||
|
"3dc0d9e0370e56a3d1e5e76aa4de082e4c2a0afd092845bd5dab52a4594318" +
|
||||||
|
"1461b76e3984b95f48bea80a94944241d04b5634c86274e7" },
|
||||||
|
{ "AES/KWP/NoPadding", "6decf10a1caf8e3b80c7a4be8c9c84e8", 16,
|
||||||
|
"49", 1, "01a7d657fc4a5b216f261cca4d052c2b" },
|
||||||
|
{ "AES/KWP/NoPadding", "a8e06da625a65b25cf5030826830b661", 16,
|
||||||
|
"43acff293120dd5d", 8, "b6f967616dd8d772e9fea295a456dba7" },
|
||||||
|
{ "AES/KWP/NoPadding", "7865e20f3c21659ab4690b629cdf3cc4", 16,
|
||||||
|
"bd6843d420378dc896", 9,
|
||||||
|
"41eca956d4aa047eb5cf4efe659661e74db6f8c564e23500" },
|
||||||
|
{ "AES/KWP/NoPadding", "be96dc195ec034d616486ed70e97fe83", 16,
|
||||||
|
"85b5437b6335ebba7635903a4493d12a77d9357a9e0dbc013456d85f1d3201",
|
||||||
|
31,
|
||||||
|
"974769b3a7b4d5d32985f87fddf9990631e5610fbfb278387b58b1f48e05c7" +
|
||||||
|
"7d2fb7575c5169eb0e" },
|
||||||
|
{ "AES/KWP/NoPadding", "0e54956a24c7d4a343f90269fb18a17f", 16,
|
||||||
|
"817ddabdc5d215eee233adff97e92193c6beec52a71340477f70243a794ce9" +
|
||||||
|
"54af51e356c9940e4ab198f0e68c543355f65ad179cb2d60dd369eaeb9ed14" +
|
||||||
|
"1fb18c9e4054ac7fdc83506896990a4d20833d2d6e9a34938796ee67c9d7d2" +
|
||||||
|
"3058544a4a35f2954103ce443a95a7e785602075ca0a73da37899e4568106b" +
|
||||||
|
"b2dbf1f901377d4d3380c70fa5175ebc550481ac6f15986a4407fde5c23ff3" +
|
||||||
|
"17e37544c0a25f87117506597db5bb79850c86247b73a5d0090417d63e4c25" +
|
||||||
|
"7ea0220c2c04db07a34f0ab7954e1dfa2007a1466795c4d0c2aa09ca3986c0" +
|
||||||
|
"28185b43a466526594afc9c891c263a7c608304bc1957c9873f544dc71e6f8" +
|
||||||
|
"47c48d32026ed03b2333825452ee7e12a50e1cd7d678319264c65f78001996" +
|
||||||
|
"d37fae7f9861fbd21cb506c2f8a3b0ee53c7debe17111b6e3f78a5c5677857" +
|
||||||
|
"b082c2c4943dfd1edf6337fea98a44fc25928361156ef38d865948b979cf6f" +
|
||||||
|
"4b46bd2119f12f0891cef7fc9d0638fd105fc05f9968d16948d1cb820751e8" +
|
||||||
|
"2e44cb68e99d4f072ffd1577da6c0631b5827bec7e1b9ec72d18b74cf5f233" +
|
||||||
|
"e85013c1668ceb5d7a1f5e0f016b0ff726a0a9d41e2cea8e14a2f56492b146" +
|
||||||
|
"06d3fafd8ac141335f39f90d56863735628e8f17be90e100ef0785f3cd57db" +
|
||||||
|
"8b9d89a6b2189dc2ea00c285d2657983f8bd7883c215477e67a55556401f1d" +
|
||||||
|
"8b27d4e0d541c7fb7ace370c2e428884", 512,
|
||||||
|
"876f3e53ba9cf4f6a521ac198bc813d0ede0f862ab6082e3e0a06ad82b4f27" +
|
||||||
|
"9582f7c43bb63574608446bc2a05f401a68f74086cf2776b4b3df6b3679c2e" +
|
||||||
|
"dfb91c024db54c6831e0752ae6f86c7596462de905ee0be908c1b9d043ecaf" +
|
||||||
|
"e2ad1cbddb904e18ebc9b7a107031be3a87059516a3d1257812d9c801b0b9f" +
|
||||||
|
"21539e70c47150c128d87c5e58fa6e4371aedde69c7b5cd16b73ac42267632" +
|
||||||
|
"8131f3ac48c602bb6e0741805aad9d23b33b3523b86cf0588cdf9dc6c4d5f9" +
|
||||||
|
"fa43d88ca17976eaf48fb37a41a598266da04144373df5631cc5126341c200" +
|
||||||
|
"a0c8499b29ae96e6e6e6c2bdf8d8903da62bf8ddae970569b695240e77f8ac" +
|
||||||
|
"5b191da5034008b6ef21936858e69bac372bbafd8794f6b03711503c187552" +
|
||||||
|
"8a9348681844edb199a0664d740f0f0b1f866c4248c80fe8b5700a3c4134cd" +
|
||||||
|
"ddb17676e0cd37d6d81831a0f4adfba071bb0935502480eccd48b28be5954e" +
|
||||||
|
"a6c7d873b51b8bd2b709c5b6132ed31296510915073c18f7012f0eff6a9aad" +
|
||||||
|
"5340a19fd5e372d35260b718d9e4807b1954c24e6a4fd48e4dbb8f395474e9" +
|
||||||
|
"9ab577367d2ab5ccaa18c947331047dc3986e213a878b41089aa221019dad4" +
|
||||||
|
"191a4feefd095f8606c2700a46d71cbb13efb6957df925ec26071c04d04d5a" +
|
||||||
|
"94e138e5fc5d1f059236aad76208077dcc607b1dd2086f9c04e33f955822b4" +
|
||||||
|
"57eecd68bd5f24836ecedbac675e6ed93d8a787cb57ad68e" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dataProvider = "testData")
|
||||||
|
public void testKeyWrap(String algo, String key, int keyLen,
|
||||||
|
String data, int dataLen, String expected) throws Exception {
|
||||||
|
System.out.println("Testing " + algo + " Cipher with wrapping " +
|
||||||
|
dataLen + "-byte key with " + 8*keyLen + "-bit KEK");
|
||||||
|
int allowed = Cipher.getMaxAllowedKeyLength("AES");
|
||||||
|
if (keyLen > allowed) {
|
||||||
|
System.out.println("=> skip, exceeds max allowed size " + allowed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Cipher c1 = Cipher.getInstance(algo, "SunJCE");
|
||||||
|
Cipher c2 = Cipher.getInstance(algo, "SunJCE");
|
||||||
|
Cipher c3 = Cipher.getInstance(algo, "SunJCE");
|
||||||
|
|
||||||
|
byte[] keyVal = toBytes(key, keyLen << 1);
|
||||||
|
byte[] dataVal = toBytes(data, dataLen << 1);
|
||||||
|
|
||||||
SecretKey cipherKey = new SecretKeySpec(keyVal, "AES");
|
SecretKey cipherKey = new SecretKeySpec(keyVal, "AES");
|
||||||
c.init(Cipher.WRAP_MODE, cipherKey);
|
|
||||||
SecretKey toBeWrappedKey = new SecretKeySpec(dataVal, "AES");
|
SecretKey toBeWrappedKey = new SecretKeySpec(dataVal, "AES");
|
||||||
|
|
||||||
|
c1.init(Cipher.WRAP_MODE, cipherKey);
|
||||||
|
IvParameterSpec ivSpec = new IvParameterSpec(c1.getIV());
|
||||||
|
c2.init(Cipher.WRAP_MODE, cipherKey, ivSpec);
|
||||||
|
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
|
||||||
|
params.init(ivSpec);
|
||||||
|
c3.init(Cipher.WRAP_MODE, cipherKey, params);
|
||||||
|
|
||||||
// first test WRAP with known values
|
// first test WRAP with known values
|
||||||
byte[] wrapped = c.wrap(toBeWrappedKey);
|
byte[] wrapped = c1.wrap(toBeWrappedKey);
|
||||||
byte[] expectedVal = new BigInteger(expected, 16).toByteArray();
|
byte[] wrapped2 = c2.wrap(toBeWrappedKey);
|
||||||
// need to add offset since BigInteger may pad "0x00" in the beginning
|
byte[] wrapped3 = c3.wrap(toBeWrappedKey);
|
||||||
int offset = expectedVal.length - wrapped.length;
|
|
||||||
for (int i=0; i<wrapped.length; i++) {
|
byte[] expectedVal = toBytes(expected, expected.length());
|
||||||
if (wrapped[i] != expectedVal[offset + i]) {
|
|
||||||
throw new Exception("Wrap failed; got different result");
|
if (!Arrays.equals(wrapped, expectedVal) ||
|
||||||
}
|
!Arrays.equals(wrapped2, expectedVal) ||
|
||||||
|
!Arrays.equals(wrapped3, expectedVal)) {
|
||||||
|
throw new Exception("Wrap test failed; got different result");
|
||||||
}
|
}
|
||||||
|
|
||||||
// then test UNWRAP and compare with the initial values
|
// then test UNWRAP and compare with the initial values
|
||||||
c.init(Cipher.UNWRAP_MODE, cipherKey);
|
c1.init(Cipher.UNWRAP_MODE, cipherKey);
|
||||||
Key unwrapped = c.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
|
ivSpec = new IvParameterSpec(c1.getIV());
|
||||||
if (!Arrays.equals(unwrapped.getEncoded(), dataVal)) {
|
c2.init(Cipher.UNWRAP_MODE, cipherKey, ivSpec);
|
||||||
|
params = AlgorithmParameters.getInstance("AES");
|
||||||
|
params.init(ivSpec);
|
||||||
|
c3.init(Cipher.UNWRAP_MODE, cipherKey, params);
|
||||||
|
|
||||||
|
Key unwrapped = c1.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
|
||||||
|
Key unwrapped2 = c2.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
|
||||||
|
Key unwrapped3 = c3.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
|
||||||
|
|
||||||
|
if (!Arrays.equals(unwrapped.getEncoded(), dataVal) ||
|
||||||
|
!Arrays.equals(unwrapped2.getEncoded(), dataVal) ||
|
||||||
|
!Arrays.equals(unwrapped3.getEncoded(), dataVal)) {
|
||||||
throw new Exception("Unwrap failed; got different result");
|
throw new Exception("Unwrap failed; got different result");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] argv) throws Exception {
|
@Test(dataProvider = "testData")
|
||||||
testKeyWrap(16, 16, AES128_128);
|
public void testEnc(String algo, String key, int keyLen, String data, int dataLen, String expected)
|
||||||
// only run the tests on longer key lengths if unlimited version
|
throws Exception {
|
||||||
// of JCE jurisdiction policy files are installed
|
System.out.println("Testing " + algo + " Cipher with enc " +
|
||||||
|
dataLen + "-byte data with " + 8*keyLen + "-bit KEK");
|
||||||
int allowed = Cipher.getMaxAllowedKeyLength("AES");
|
int allowed = Cipher.getMaxAllowedKeyLength("AES");
|
||||||
if (allowed >= 24*8) {
|
if (keyLen > allowed) {
|
||||||
testKeyWrap(24, 16, AES192_128);
|
System.out.println("=> skip, exceeds max allowed size " + allowed);
|
||||||
testKeyWrap(24, 24, AES192_192);
|
return;
|
||||||
}
|
}
|
||||||
if (allowed >= 32*8) {
|
Cipher c1 = Cipher.getInstance(algo, "SunJCE");
|
||||||
testKeyWrap(32, 16, AES256_128);
|
Cipher c2 = Cipher.getInstance(algo, "SunJCE");
|
||||||
testKeyWrap(32, 24, AES256_192);
|
Cipher c3 = Cipher.getInstance(algo, "SunJCE");
|
||||||
testKeyWrap(32, 32, AES256_256);
|
|
||||||
|
byte[] keyVal = toBytes(key, keyLen << 1);
|
||||||
|
byte[] dataVal = toBytes(data, dataLen << 1);
|
||||||
|
|
||||||
|
SecretKey cipherKey = new SecretKeySpec(keyVal, "AES");
|
||||||
|
c1.init(Cipher.ENCRYPT_MODE, cipherKey);
|
||||||
|
IvParameterSpec ivSpec = new IvParameterSpec(c1.getIV());
|
||||||
|
c2.init(Cipher.ENCRYPT_MODE, cipherKey, ivSpec);
|
||||||
|
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
|
||||||
|
params.init(ivSpec);
|
||||||
|
c3.init(Cipher.ENCRYPT_MODE, cipherKey, params);
|
||||||
|
|
||||||
|
// first test encryption with known values
|
||||||
|
byte[] ct11 = c1.update(dataVal);
|
||||||
|
byte[] ct12 = c1.doFinal();
|
||||||
|
byte[] ct2 = c1.doFinal(dataVal);
|
||||||
|
byte[] ct22 = c2.doFinal(dataVal);
|
||||||
|
byte[] ct32 = c3.doFinal(dataVal);
|
||||||
|
|
||||||
|
byte[] expectedVal = toBytes(expected, expected.length());
|
||||||
|
|
||||||
|
if (ct11 != null || !Arrays.equals(ct12, ct2) ||
|
||||||
|
!Arrays.equals(ct2, expectedVal) ||
|
||||||
|
!Arrays.equals(ct22, expectedVal) ||
|
||||||
|
!Arrays.equals(ct32, expectedVal)) {
|
||||||
|
throw new Exception("Encryption failed; got different result");
|
||||||
|
}
|
||||||
|
|
||||||
|
// then test decryption and compare with the initial values
|
||||||
|
c1.init(Cipher.DECRYPT_MODE, cipherKey);
|
||||||
|
ivSpec = new IvParameterSpec(c1.getIV());
|
||||||
|
c2.init(Cipher.DECRYPT_MODE, cipherKey, ivSpec);
|
||||||
|
params = AlgorithmParameters.getInstance("AES");
|
||||||
|
params.init(ivSpec);
|
||||||
|
c3.init(Cipher.DECRYPT_MODE, cipherKey, params);
|
||||||
|
|
||||||
|
byte[] pt11 = c1.update(ct12);
|
||||||
|
byte[] pt12 = c1.doFinal();
|
||||||
|
byte[] pt2 = c1.doFinal(ct2);
|
||||||
|
byte[] pt22 = c2.doFinal(ct2);
|
||||||
|
byte[] pt32 = c3.doFinal(ct2);
|
||||||
|
|
||||||
|
if (pt11 != null || !Arrays.equals(pt12, pt2) ||
|
||||||
|
!Arrays.equals(pt2, dataVal) || !Arrays.equals(pt22, dataVal) ||
|
||||||
|
!Arrays.equals(pt32, dataVal)) {
|
||||||
|
throw new Exception("Decryption failed; got different result");
|
||||||
}
|
}
|
||||||
System.out.println("All Tests Passed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -53,7 +53,7 @@ import javax.crypto.spec.PBEParameterSpec;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8048599
|
* @bug 8048599 8248268
|
||||||
* @summary Tests for key wrap and unwrap operations
|
* @summary Tests for key wrap and unwrap operations
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -86,6 +86,10 @@ public class TestCipherKeyWrapperTest {
|
|||||||
AESWrap_128("AES", "AESWrap_128", 128),
|
AESWrap_128("AES", "AESWrap_128", 128),
|
||||||
AESWrap_192("AES", "AESWrap_192", 192),
|
AESWrap_192("AES", "AESWrap_192", 192),
|
||||||
AESWrap_256("AES", "AESWrap_256", 256),
|
AESWrap_256("AES", "AESWrap_256", 256),
|
||||||
|
AESWrapPad("AES", "AESWrapPad", -1),
|
||||||
|
AESWrapPad_128("AES", "AESWrapPad_128", 128),
|
||||||
|
AESWrapPad_192("AES", "AESWrapPad_192", 192),
|
||||||
|
AESWrapPad_256("AES", "AESWrapPad_256", 256),
|
||||||
DESedeWrap("desede", "DESedeWrap", -1),
|
DESedeWrap("desede", "DESedeWrap", -1),
|
||||||
NegtiveWrap("AES", "DESedeWrap", -1);
|
NegtiveWrap("AES", "DESedeWrap", -1);
|
||||||
|
|
||||||
@ -287,6 +291,8 @@ public class TestCipherKeyWrapperTest {
|
|||||||
new Random().nextBytes(salt);
|
new Random().nextBytes(salt);
|
||||||
pbeParams = new PBEParameterSpec(salt, iterCnt);
|
pbeParams = new PBEParameterSpec(salt, iterCnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.println("Testing " + wrapAlgo + " cipher wrap/unwrap");
|
||||||
// Wrap & UnWrap operation
|
// Wrap & UnWrap operation
|
||||||
Cipher wrapCI = Cipher.getInstance(wrapAlgo);
|
Cipher wrapCI = Cipher.getInstance(wrapAlgo);
|
||||||
if (isPBE && !isAESBlowfish) {
|
if (isPBE && !isAESBlowfish) {
|
||||||
@ -297,7 +303,6 @@ public class TestCipherKeyWrapperTest {
|
|||||||
} else {
|
} else {
|
||||||
wrapCI.init(Cipher.WRAP_MODE, initKey);
|
wrapCI.init(Cipher.WRAP_MODE, initKey);
|
||||||
}
|
}
|
||||||
out.println("keysize : " + wrapKey.getEncoded().length);
|
|
||||||
byte[] keyWrapper = wrapCI.wrap(wrapKey);
|
byte[] keyWrapper = wrapCI.wrap(wrapKey);
|
||||||
if (isPBE && !isAESBlowfish) {
|
if (isPBE && !isAESBlowfish) {
|
||||||
wrapCI.init(Cipher.UNWRAP_MODE, initKey, pbeParams);
|
wrapCI.init(Cipher.UNWRAP_MODE, initKey, pbeParams);
|
||||||
@ -309,6 +314,7 @@ public class TestCipherKeyWrapperTest {
|
|||||||
Key unwrappedKey = wrapCI.unwrap(keyWrapper, algo, keyType);
|
Key unwrappedKey = wrapCI.unwrap(keyWrapper, algo, keyType);
|
||||||
// Comparison
|
// Comparison
|
||||||
if (!Arrays.equals(wrapKey.getEncoded(), unwrappedKey.getEncoded())) {
|
if (!Arrays.equals(wrapKey.getEncoded(), unwrappedKey.getEncoded())) {
|
||||||
|
out.println("keysize : " + wrapKey.getEncoded().length);
|
||||||
throw new RuntimeException("Comparation failed testing "
|
throw new RuntimeException("Comparation failed testing "
|
||||||
+ transformation + ":" + wrapAlgo + ":" + keyType);
|
+ transformation + ":" + wrapAlgo + ":" + keyType);
|
||||||
}
|
}
|
||||||
|
187
test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/TestGeneral.java
Normal file
187
test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/TestGeneral.java
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8248268
|
||||||
|
* @summary Verify general properties of the AES/KW/NoPadding,
|
||||||
|
* AES/KW/PKCS5Padding, and AES/KWP/NoPadding.
|
||||||
|
* @run main TestGeneral
|
||||||
|
*/
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import javax.crypto.*;
|
||||||
|
import javax.crypto.spec.*;
|
||||||
|
|
||||||
|
public class TestGeneral {
|
||||||
|
|
||||||
|
private static final SecretKey KEY = new SecretKeySpec(new byte[16], "AES");;
|
||||||
|
private static final int KW_IV_LEN = 8;
|
||||||
|
private static final int KWP_IV_LEN = 4;
|
||||||
|
private static final int MAX_KW_PKCS5PAD_LEN = 16; // 1-16
|
||||||
|
private static final int MAX_KWP_PAD_LEN = 7; // 0...7
|
||||||
|
|
||||||
|
public static void testEnc(Cipher c, byte[] in, int inLen, int ivLen,
|
||||||
|
int maxPadLen) throws Exception {
|
||||||
|
|
||||||
|
System.out.println("input len: " + inLen);
|
||||||
|
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
|
||||||
|
|
||||||
|
int estOutLen = c.getOutputSize(inLen);
|
||||||
|
|
||||||
|
byte[] out = c.doFinal(in, 0, inLen);
|
||||||
|
|
||||||
|
// for encryption output, the estimate should match the actual
|
||||||
|
if (estOutLen != out.length) {
|
||||||
|
System.out.println("=> estimated: " + estOutLen);
|
||||||
|
System.out.println("=> actual enc out length: " + out.length);
|
||||||
|
throw new RuntimeException("Failed enc output len check");
|
||||||
|
}
|
||||||
|
|
||||||
|
// encryption outout should always be multiple of 8 and at least 8-byte
|
||||||
|
// longer than input
|
||||||
|
if ((out.length % 8 != 0) || (out.length - inLen < 8)) {
|
||||||
|
throw new RuntimeException("Invalid length of encrypted data: " +
|
||||||
|
out.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.init(Cipher.DECRYPT_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
|
||||||
|
estOutLen = c.getOutputSize(out.length);
|
||||||
|
|
||||||
|
byte[] in2 = c.doFinal(out);
|
||||||
|
|
||||||
|
// for decryption output, the estimate should match the actual for
|
||||||
|
// AES/KW/NoPadding and slightly larger than the actual for the rest
|
||||||
|
if (estOutLen < in2.length || (estOutLen - in2.length) > maxPadLen) {
|
||||||
|
System.out.println("=> estimated: " + estOutLen);
|
||||||
|
System.out.println("=> actual dec out length: " + in2.length);
|
||||||
|
throw new RuntimeException("Failed dec output len check");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Arrays.equals(in, 0, inLen, in2, 0, inLen)) {
|
||||||
|
throw new RuntimeException("Failed decrypted data check");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testWrap(Cipher c, byte[] in, int inLen, int ivLen,
|
||||||
|
int maxPadLen) throws Exception {
|
||||||
|
|
||||||
|
System.out.println("key len: " + inLen);
|
||||||
|
c.init(Cipher.WRAP_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
|
||||||
|
|
||||||
|
int estOutLen = c.getOutputSize(inLen);
|
||||||
|
|
||||||
|
byte[] out = c.wrap(new SecretKeySpec(in, 0, inLen, "Any"));
|
||||||
|
|
||||||
|
// for encryption output, the estimate should match the actual
|
||||||
|
if (estOutLen != out.length) {
|
||||||
|
System.out.println("=> estimated: " + estOutLen);
|
||||||
|
System.out.println("=> actual wrap out length: " + out.length);
|
||||||
|
throw new RuntimeException("Failed wrap output len check");
|
||||||
|
}
|
||||||
|
|
||||||
|
// encryption outout should always be multiple of 8 and at least 8-byte
|
||||||
|
// longer than input
|
||||||
|
if ((out.length % 8 != 0) || (out.length - inLen < 8)) {
|
||||||
|
throw new RuntimeException("Invalid length of encrypted data: " +
|
||||||
|
out.length);
|
||||||
|
}
|
||||||
|
c.init(Cipher.UNWRAP_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
|
||||||
|
estOutLen = c.getOutputSize(out.length);
|
||||||
|
|
||||||
|
Key key2 = c.unwrap(out, "Any", Cipher.SECRET_KEY);
|
||||||
|
|
||||||
|
if (!(key2 instanceof SecretKey)) {
|
||||||
|
throw new RuntimeException("Failed unwrap output type check");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] in2 = key2.getEncoded();
|
||||||
|
// for decryption output, the estimate should match the actual for
|
||||||
|
// AES/KW/NoPadding and slightly larger than the actual for the rest
|
||||||
|
if (estOutLen < in2.length || (estOutLen - in2.length) > maxPadLen) {
|
||||||
|
System.out.println("=> estimated: " + estOutLen);
|
||||||
|
System.out.println("=> actual unwrap out length: " + in2.length);
|
||||||
|
throw new RuntimeException("Failed unwrap output len check");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inLen != in2.length ||
|
||||||
|
!Arrays.equals(in, 0, inLen, in2, 0, inLen)) {
|
||||||
|
throw new RuntimeException("Failed unwrap data check");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testIv(Cipher c) throws Exception {
|
||||||
|
c.init(Cipher.ENCRYPT_MODE, KEY);
|
||||||
|
byte[] defIv = c.getIV();
|
||||||
|
// try init w/ an iv w/ different length
|
||||||
|
try {
|
||||||
|
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv, 0,
|
||||||
|
defIv.length/2));
|
||||||
|
} catch (InvalidAlgorithmParameterException iape) {
|
||||||
|
System.out.println("Invalid IV rejected as expected");
|
||||||
|
}
|
||||||
|
Arrays.fill(defIv, (byte) 0xFF);
|
||||||
|
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv));
|
||||||
|
byte[] newIv = c.getIV();
|
||||||
|
if (!Arrays.equals(newIv, defIv)) {
|
||||||
|
throw new RuntimeException("Failed iv check");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] argv) throws Exception {
|
||||||
|
// test all possible pad lengths, i.e. 1 - 16
|
||||||
|
byte[] data = new byte[128];
|
||||||
|
new Random().nextBytes(data);
|
||||||
|
|
||||||
|
String ALGO = "AES/KW/PKCS5Padding";
|
||||||
|
System.out.println("Testing " + ALGO);
|
||||||
|
Cipher c = Cipher.getInstance(ALGO, "SunJCE");
|
||||||
|
for (int i = 0; i < MAX_KW_PKCS5PAD_LEN; i++) {
|
||||||
|
testEnc(c, data, data.length - i, KW_IV_LEN, MAX_KW_PKCS5PAD_LEN);
|
||||||
|
testWrap(c, data, data.length - i, KW_IV_LEN, MAX_KW_PKCS5PAD_LEN);
|
||||||
|
}
|
||||||
|
testIv(c);
|
||||||
|
|
||||||
|
ALGO = "AES/KW/NoPadding";
|
||||||
|
System.out.println("Testing " + ALGO);
|
||||||
|
c = Cipher.getInstance(ALGO, "SunJCE");
|
||||||
|
testEnc(c, data, data.length, KW_IV_LEN, 0);
|
||||||
|
testEnc(c, data, data.length >> 1, KW_IV_LEN, 0);
|
||||||
|
testWrap(c, data, data.length, KW_IV_LEN, 0);
|
||||||
|
testWrap(c, data, data.length >> 1, KW_IV_LEN, 0);
|
||||||
|
testIv(c);
|
||||||
|
|
||||||
|
ALGO = "AES/KWP/NoPadding";
|
||||||
|
System.out.println("Testing " + ALGO);
|
||||||
|
c = Cipher.getInstance(ALGO, "SunJCE");
|
||||||
|
for (int i = 0; i < MAX_KWP_PAD_LEN; i++) {
|
||||||
|
testEnc(c, data, data.length - i, KWP_IV_LEN, MAX_KWP_PAD_LEN);
|
||||||
|
testWrap(c, data, data.length - i, KWP_IV_LEN, MAX_KWP_PAD_LEN);
|
||||||
|
}
|
||||||
|
testIv(c);
|
||||||
|
|
||||||
|
System.out.println("All Tests Passed");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8248268
|
||||||
|
* @summary Verify cipher key size restriction is enforced properly with IKE
|
||||||
|
* @run main TestKeySizeCheck
|
||||||
|
*/
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import javax.crypto.*;
|
||||||
|
import javax.crypto.spec.*;
|
||||||
|
|
||||||
|
public class TestKeySizeCheck {
|
||||||
|
|
||||||
|
private static final byte[] BYTES_32 = new byte[32];
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < BYTES_32.length; i++) {
|
||||||
|
BYTES_32[i] = (byte) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SecretKey getKey(int sizeInBytes) {
|
||||||
|
if (sizeInBytes <= BYTES_32.length) {
|
||||||
|
return new SecretKeySpec(BYTES_32, 0, sizeInBytes, "AES");
|
||||||
|
} else {
|
||||||
|
return new SecretKeySpec(new byte[sizeInBytes], "AES");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getModeStr(int mode) {
|
||||||
|
return (mode == Cipher.ENCRYPT_MODE? "ENC" : "WRAP");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test(String algo, int[] invalidKeySizes)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
System.out.println("Testing " + algo);
|
||||||
|
Cipher c = Cipher.getInstance(algo, "SunJCE");
|
||||||
|
|
||||||
|
int[] modes = { Cipher.ENCRYPT_MODE, Cipher.WRAP_MODE };
|
||||||
|
for (int ks : invalidKeySizes) {
|
||||||
|
System.out.println("keysize: " + ks);
|
||||||
|
SecretKey key = getKey(ks);
|
||||||
|
|
||||||
|
for (int m : modes) {
|
||||||
|
try {
|
||||||
|
c.init(m, key);
|
||||||
|
throw new RuntimeException("Expected IKE not thrown for "
|
||||||
|
+ getModeStr(m));
|
||||||
|
} catch (InvalidKeyException ike) {
|
||||||
|
System.out.println(" => expected IKE thrown for "
|
||||||
|
+ getModeStr(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] argv) throws Exception {
|
||||||
|
|
||||||
|
test("AESWrap", new int[] { 120, 264 });
|
||||||
|
test("AESWrap_128", new int[] { 192, 256 });
|
||||||
|
test("AESWrap_192", new int[] { 128, 256 });
|
||||||
|
test("AESWrap_256", new int[] { 128, 192 });
|
||||||
|
test("AESWrapPad", new int[] { 120, 264 });
|
||||||
|
test("AESWrapPad_128", new int[] { 192, 256 });
|
||||||
|
test("AESWrapPad_192", new int[] { 128, 256 });
|
||||||
|
test("AESWrapPad_256", new int[] { 128, 192 });
|
||||||
|
|
||||||
|
test("AES/KW/NoPadding", new int[] { 120, 264 });
|
||||||
|
test("AES_128/KW/NoPadding", new int[] { 192, 256 });
|
||||||
|
test("AES_192/KW/NoPadding", new int[] { 128, 256 });
|
||||||
|
test("AES_256/KW/NoPadding", new int[] { 128, 192 });
|
||||||
|
test("AES/KWP/NoPadding", new int[] { 120, 264 });
|
||||||
|
test("AES_128/KWP/NoPadding", new int[] { 192, 256 });
|
||||||
|
test("AES_192/KWP/NoPadding", new int[] { 128, 256 });
|
||||||
|
test("AES_256/KWP/NoPadding", new int[] { 128, 192 });
|
||||||
|
|
||||||
|
System.out.println("All Tests Passed");
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -38,8 +38,8 @@ import javax.crypto.SecretKey;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8075286
|
* @bug 8075286 8248268
|
||||||
* @summary Test the AESWrap algorithm OIDs in JDK.
|
* @summary Test the AES-Key-Wrap and AES-Key-Wrap-Pad algorithm OIDs in JDK.
|
||||||
* OID and Algorithm transformation string should match.
|
* OID and Algorithm transformation string should match.
|
||||||
* Both could be able to be used to generate the algorithm instance.
|
* Both could be able to be used to generate the algorithm instance.
|
||||||
* @run main TestAESWrapOids
|
* @run main TestAESWrapOids
|
||||||
@ -49,9 +49,15 @@ public class TestAESWrapOids {
|
|||||||
private static final String PROVIDER_NAME = "SunJCE";
|
private static final String PROVIDER_NAME = "SunJCE";
|
||||||
|
|
||||||
private static final List<DataTuple> DATA = Arrays.asList(
|
private static final List<DataTuple> DATA = Arrays.asList(
|
||||||
new DataTuple("2.16.840.1.101.3.4.1.5", "AESWrap_128", 128),
|
new DataTuple("2.16.840.1.101.3.4.1.5", "AESWrap_128"),
|
||||||
new DataTuple("2.16.840.1.101.3.4.1.25", "AESWrap_192", 192),
|
new DataTuple("2.16.840.1.101.3.4.1.25", "AESWrap_192"),
|
||||||
new DataTuple("2.16.840.1.101.3.4.1.45", "AESWrap_256", 256));
|
new DataTuple("2.16.840.1.101.3.4.1.45", "AESWrap_256"),
|
||||||
|
new DataTuple("2.16.840.1.101.3.4.1.5", "AES_128/KW/NoPadding"),
|
||||||
|
new DataTuple("2.16.840.1.101.3.4.1.25", "AES_192/KW/NoPadding"),
|
||||||
|
new DataTuple("2.16.840.1.101.3.4.1.45", "AES_256/KW/NoPadding"),
|
||||||
|
new DataTuple("2.16.840.1.101.3.4.1.8", "AES_128/KWP/NoPadding"),
|
||||||
|
new DataTuple("2.16.840.1.101.3.4.1.28", "AES_192/KWP/NoPadding"),
|
||||||
|
new DataTuple("2.16.840.1.101.3.4.1.48", "AES_256/KWP/NoPadding"));
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
for (DataTuple dataTuple : DATA) {
|
for (DataTuple dataTuple : DATA) {
|
||||||
@ -145,10 +151,15 @@ public class TestAESWrapOids {
|
|||||||
private final String algorithm;
|
private final String algorithm;
|
||||||
private final int keyLength;
|
private final int keyLength;
|
||||||
|
|
||||||
private DataTuple(String oid, String algorithm, int keyLength) {
|
private DataTuple(String oid, String algorithm) {
|
||||||
this.oid = oid;
|
this.oid = oid;
|
||||||
this.algorithm = algorithm;
|
this.algorithm = algorithm;
|
||||||
this.keyLength = keyLength;
|
this.keyLength = switch (oid) {
|
||||||
|
case "2.16.840.1.101.3.4.1.5", "2.16.840.1.101.3.4.1.8"->128;
|
||||||
|
case "2.16.840.1.101.3.4.1.25", "2.16.840.1.101.3.4.1.28"->192;
|
||||||
|
case "2.16.840.1.101.3.4.1.45", "2.16.840.1.101.3.4.1.48"->256;
|
||||||
|
default->throw new RuntimeException("Unrecognized oid: " + oid);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 4953556 8210838
|
* @bug 4953556 8210838 8248268
|
||||||
* @summary ensure that IllegalStateException is thrown if the
|
* @summary ensure that IllegalStateException is thrown if the
|
||||||
* Cipher object is initialized with a wrong mode, e.g. WRAP_MODE
|
* Cipher object is initialized with a wrong mode, e.g. WRAP_MODE
|
||||||
* for update()/doFinal() calls.
|
* for update()/doFinal() calls.
|
||||||
@ -39,44 +39,52 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
|
|
||||||
public class TestCipherMode {
|
public class TestCipherMode {
|
||||||
|
|
||||||
private static final String ALGO = "DES";
|
private static final String[] TRANSFORMATIONS = {
|
||||||
|
"DES/ECB/PKCS5Padding",
|
||||||
|
"AES/KW/NoPadding",
|
||||||
|
"AES/KW/PKCS5Padding",
|
||||||
|
"AES/KWP/NoPadding",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final SecretKey DES_KEY =
|
||||||
|
new SecretKeySpec(new byte[8], "DES");
|
||||||
|
private static final SecretKey AES_KEY =
|
||||||
|
new SecretKeySpec(new byte[16], "AES");
|
||||||
|
|
||||||
public static void main(String[] argv) throws Exception {
|
public static void main(String[] argv) throws Exception {
|
||||||
TestCipherMode test = new TestCipherMode();
|
for (String t : TRANSFORMATIONS) {
|
||||||
System.out.println("Testing ENCRYPT_MODE...");
|
System.out.println("Testing SunJCE provider, Cipher " + t );
|
||||||
test.checkMode(Cipher.ENCRYPT_MODE, "encryption");
|
|
||||||
System.out.println("Testing DECRYPT_MODE...");
|
TestCipherMode test = new TestCipherMode(t);
|
||||||
test.checkMode(Cipher.DECRYPT_MODE, "decryption");
|
System.out.println("Testing ENCRYPT_MODE...");
|
||||||
System.out.println("Testing WRAP_MODE...");
|
test.checkMode(Cipher.ENCRYPT_MODE, "encryption");
|
||||||
test.checkMode(Cipher.WRAP_MODE, "key wrapping");
|
System.out.println("Testing DECRYPT_MODE...");
|
||||||
System.out.println("Testing UNWRAP_MODE...");
|
test.checkMode(Cipher.DECRYPT_MODE, "decryption");
|
||||||
test.checkMode(Cipher.UNWRAP_MODE, "key unwrapping");
|
System.out.println("Testing WRAP_MODE...");
|
||||||
|
test.checkMode(Cipher.WRAP_MODE, "key wrapping");
|
||||||
|
System.out.println("Testing UNWRAP_MODE...");
|
||||||
|
test.checkMode(Cipher.UNWRAP_MODE, "key unwrapping");
|
||||||
|
}
|
||||||
System.out.println("All Tests Passed");
|
System.out.println("All Tests Passed");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cipher c = null;
|
private Cipher c = null;
|
||||||
private SecretKey key = null;
|
private SecretKey key = null;
|
||||||
|
|
||||||
private TestCipherMode() throws NoSuchAlgorithmException,
|
private TestCipherMode(String transformation)
|
||||||
NoSuchProviderException, NoSuchPaddingException {
|
throws NoSuchAlgorithmException, NoSuchProviderException,
|
||||||
c = Cipher.getInstance(ALGO + "/ECB/PKCS5Padding", "SunJCE");
|
NoSuchPaddingException {
|
||||||
String output = c.toString();
|
c = Cipher.getInstance(transformation, "SunJCE");
|
||||||
if (!output.equals(
|
this.key = switch (transformation.split("/")[0]) {
|
||||||
"Cipher.DES/ECB/PKCS5Padding, mode: not initialized, algorithm from: SunJCE")) {
|
case "DES" -> DES_KEY;
|
||||||
throw new RuntimeException(
|
case "AES" -> AES_KEY;
|
||||||
"Unexpected Cipher.toString() output:" + output);
|
default -> throw new RuntimeException
|
||||||
}
|
("Error: Unsupported key algorithm");
|
||||||
key = new SecretKeySpec(new byte[8], ALGO);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkMode(int mode, String opString) throws Exception {
|
private void checkMode(int mode, String opString) throws Exception {
|
||||||
c.init(mode, key);
|
c.init(mode, key);
|
||||||
String output = c.toString();
|
|
||||||
if (!output.contains("Cipher.DES/ECB/PKCS5Padding")
|
|
||||||
&& !output.contains(opString)
|
|
||||||
&& !output.contains("Algorithm from: SunJCE")) {
|
|
||||||
throw new Exception("Unexpected toString() output:" + output);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case Cipher.ENCRYPT_MODE:
|
case Cipher.ENCRYPT_MODE:
|
||||||
@ -89,7 +97,7 @@ public class TestCipherMode {
|
|||||||
System.out.println("expected ISE is thrown for wrap()");
|
System.out.println("expected ISE is thrown for wrap()");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
c.unwrap(new byte[16], ALGO, Cipher.SECRET_KEY);
|
c.unwrap(new byte[16], key.getAlgorithm(), Cipher.SECRET_KEY);
|
||||||
throw new Exception("ERROR: should throw ISE for unwrap()");
|
throw new Exception("ERROR: should throw ISE for unwrap()");
|
||||||
} catch (IllegalStateException ise) {
|
} catch (IllegalStateException ise) {
|
||||||
System.out.println("expected ISE is thrown for unwrap()");
|
System.out.println("expected ISE is thrown for unwrap()");
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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 org.openjdk.bench.javax.crypto.full;
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
|
import org.openjdk.jmh.annotations.Mode;
|
||||||
|
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||||
|
import org.openjdk.jmh.annotations.Param;
|
||||||
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
||||||
|
import java.security.Key;
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.spec.GCMParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.spec.InvalidParameterSpecException;
|
||||||
|
|
||||||
|
|
||||||
|
public class AESKeyWrapBench extends CryptoBase {
|
||||||
|
|
||||||
|
@Param({"AES/KW/NoPadding" , "AES/KW/PKCS5Padding", "AES/KWP/NoPadding"})
|
||||||
|
private String algorithm;
|
||||||
|
|
||||||
|
@Param({"128"})
|
||||||
|
private int keyLength;
|
||||||
|
|
||||||
|
@Param({"16", "24"})
|
||||||
|
private int dataSize;
|
||||||
|
|
||||||
|
SecretKeySpec toBeWrappedKey;
|
||||||
|
byte[] wrappedKey;
|
||||||
|
private Cipher encryptCipher;
|
||||||
|
private Cipher decryptCipher;
|
||||||
|
SecretKeySpec ks;
|
||||||
|
|
||||||
|
@Setup
|
||||||
|
public void setup() throws NoSuchAlgorithmException, NoSuchPaddingException,
|
||||||
|
InvalidKeyException, IllegalBlockSizeException {
|
||||||
|
setupProvider();
|
||||||
|
|
||||||
|
byte[] keystring = fillSecureRandom(new byte[keyLength / 8]);
|
||||||
|
ks = new SecretKeySpec(keystring, "AES");
|
||||||
|
encryptCipher = makeCipher(prov, algorithm);
|
||||||
|
encryptCipher.init(Cipher.WRAP_MODE, ks);
|
||||||
|
decryptCipher = makeCipher(prov, algorithm);
|
||||||
|
decryptCipher.init(Cipher.UNWRAP_MODE, ks);
|
||||||
|
byte[] data = fillRandom(new byte[dataSize]);
|
||||||
|
toBeWrappedKey = new SecretKeySpec(data, "Custom");
|
||||||
|
wrappedKey = encryptCipher.wrap(toBeWrappedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
|
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||||
|
public byte[] wrap() throws InvalidKeyException, IllegalBlockSizeException {
|
||||||
|
return encryptCipher.wrap(toBeWrappedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
|
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||||
|
public Key unwrap() throws InvalidKeyException, NoSuchAlgorithmException {
|
||||||
|
return decryptCipher.unwrap(wrappedKey, "Custom", Cipher.SECRET_KEY);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user