mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8248268: Support KWP in addition to KW
Reviewed-by: xuelei
This commit is contained in:
parent
3482cb87fd
commit
136badb1f7
18 changed files with 2223 additions and 675 deletions
|
@ -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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -35,7 +35,8 @@ import java.security.spec.InvalidParameterSpecException;
|
|||
* in feedback-mode. IV is defined in the standards as follows:
|
||||
*
|
||||
* <pre>
|
||||
* IV ::= OCTET STRING -- 16 octets
|
||||
* IV ::= OCTET STRING -- 8 octets for KW, 4 octets for KWP, and 16 octets for
|
||||
* other feedback modes
|
||||
* </pre>
|
||||
*
|
||||
* @author Valerie Peng
|
||||
|
@ -46,7 +47,7 @@ public final class AESParameters extends AlgorithmParametersSpi {
|
|||
private BlockCipherParamsCore core;
|
||||
|
||||
public AESParameters() {
|
||||
core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE);
|
||||
core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE, 4, 8);
|
||||
}
|
||||
|
||||
protected void engineInit(AlgorithmParameterSpec paramSpec)
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -26,6 +26,7 @@
|
|||
package com.sun.crypto.provider;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import sun.security.util.*;
|
||||
import sun.security.util.HexDumpEncoder;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
@ -48,8 +49,11 @@ final class BlockCipherParamsCore {
|
|||
private int block_size = 0;
|
||||
private byte[] iv = null;
|
||||
|
||||
BlockCipherParamsCore(int blksize) {
|
||||
private int[] moreSizes = null;
|
||||
|
||||
BlockCipherParamsCore(int blksize, int... moreSizes) {
|
||||
block_size = blksize;
|
||||
this.moreSizes = moreSizes;
|
||||
}
|
||||
|
||||
void init(AlgorithmParameterSpec paramSpec)
|
||||
|
@ -59,9 +63,20 @@ final class BlockCipherParamsCore {
|
|||
("Inappropriate parameter specification");
|
||||
}
|
||||
byte[] tmpIv = ((IvParameterSpec)paramSpec).getIV();
|
||||
if (tmpIv.length != block_size) {
|
||||
throw new InvalidParameterSpecException("IV not " +
|
||||
block_size + " bytes long");
|
||||
boolean check = (tmpIv.length == block_size);
|
||||
if (!check && moreSizes != null) {
|
||||
for (int s : moreSizes) {
|
||||
if (tmpIv.length == s) {
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!check) {
|
||||
String expectedLen = block_size + (moreSizes == null? "" :
|
||||
Arrays.toString(moreSizes));
|
||||
throw new InvalidParameterSpecException("IV length not " +
|
||||
expectedLen + " bytes long");
|
||||
}
|
||||
iv = tmpIv.clone();
|
||||
}
|
||||
|
@ -73,15 +88,17 @@ final class BlockCipherParamsCore {
|
|||
if (der.available() != 0) {
|
||||
throw new IOException("IV parsing error: extra data");
|
||||
}
|
||||
if (tmpIv.length != block_size) {
|
||||
throw new IOException("IV not " + block_size +
|
||||
boolean check = (tmpIv.length == block_size);
|
||||
if (!check) {
|
||||
String expectedLen = block_size + (moreSizes == null? "" :
|
||||
Arrays.toString(moreSizes));
|
||||
throw new IOException("IV not " + expectedLen +
|
||||
" bytes long");
|
||||
}
|
||||
iv = tmpIv;
|
||||
}
|
||||
|
||||
void init(byte[] encoded, String decodingMethod)
|
||||
throws IOException {
|
||||
void init(byte[] encoded, String decodingMethod) throws IOException {
|
||||
if ((decodingMethod != null) &&
|
||||
(!decodingMethod.equalsIgnoreCase("ASN.1"))) {
|
||||
throw new IllegalArgumentException("Only support ASN.1 format");
|
||||
|
@ -90,8 +107,7 @@ final class BlockCipherParamsCore {
|
|||
}
|
||||
|
||||
<T extends AlgorithmParameterSpec> T getParameterSpec(Class<T> paramSpec)
|
||||
throws InvalidParameterSpecException
|
||||
{
|
||||
throws InvalidParameterSpecException {
|
||||
if (IvParameterSpec.class.isAssignableFrom(paramSpec)) {
|
||||
return paramSpec.cast(new IvParameterSpec(this.iv));
|
||||
} else {
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
|
||||
package com.sun.crypto.provider;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.PrivateKey;
|
||||
|
@ -36,7 +34,7 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
@ -60,11 +58,9 @@ final class ConstructKeys {
|
|||
* @return a public key constructed from the encodedKey.
|
||||
*/
|
||||
private static final PublicKey constructPublicKey(byte[] encodedKey,
|
||||
String encodedKeyAlgorithm)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException
|
||||
{
|
||||
String encodedKeyAlgorithm)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
PublicKey key = null;
|
||||
|
||||
try {
|
||||
KeyFactory keyFactory =
|
||||
KeyFactory.getInstance(encodedKeyAlgorithm,
|
||||
|
@ -111,15 +107,15 @@ final class ConstructKeys {
|
|||
* @return a private key constructed from the encodedKey.
|
||||
*/
|
||||
private static final PrivateKey constructPrivateKey(byte[] encodedKey,
|
||||
String encodedKeyAlgorithm)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException
|
||||
{
|
||||
String encodedKeyAlgorithm)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
PrivateKey key = null;
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
|
||||
|
||||
try {
|
||||
KeyFactory keyFactory =
|
||||
KeyFactory.getInstance(encodedKeyAlgorithm,
|
||||
SunJCE.getInstance());
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
|
||||
return keyFactory.generatePrivate(keySpec);
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
// Try to see whether there is another
|
||||
|
@ -127,6 +123,8 @@ final class ConstructKeys {
|
|||
try {
|
||||
KeyFactory keyFactory =
|
||||
KeyFactory.getInstance(encodedKeyAlgorithm);
|
||||
PKCS8EncodedKeySpec keySpec =
|
||||
new PKCS8EncodedKeySpec(encodedKey);
|
||||
key = keyFactory.generatePrivate(keySpec);
|
||||
} catch (NoSuchAlgorithmException nsae2) {
|
||||
throw new NoSuchAlgorithmException("No installed providers " +
|
||||
|
@ -144,8 +142,6 @@ final class ConstructKeys {
|
|||
new InvalidKeyException("Cannot construct private key");
|
||||
ike.initCause(ikse);
|
||||
throw ike;
|
||||
} finally {
|
||||
SharedSecrets.getJavaSecuritySpecAccess().clearEncodedKeySpec(keySpec);
|
||||
}
|
||||
|
||||
return key;
|
||||
|
@ -161,29 +157,48 @@ final class ConstructKeys {
|
|||
* @return a secret key constructed from the encodedKey.
|
||||
*/
|
||||
private static final SecretKey constructSecretKey(byte[] encodedKey,
|
||||
String encodedKeyAlgorithm)
|
||||
{
|
||||
return (new SecretKeySpec(encodedKey, encodedKeyAlgorithm));
|
||||
int ofs, int len, String encodedKeyAlgorithm) {
|
||||
return (new SecretKeySpec(encodedKey, ofs, len, encodedKeyAlgorithm));
|
||||
}
|
||||
|
||||
static final Key constructKey(byte[] encoding, String keyAlgorithm,
|
||||
int keyType)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
Key result = null;
|
||||
int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
return constructKey(encoding, 0, encoding.length, keyAlgorithm,
|
||||
keyType);
|
||||
}
|
||||
|
||||
static final Key constructKey(byte[] encoding, int ofs, int len,
|
||||
String keyAlgorithm, int keyType)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
switch (keyType) {
|
||||
case Cipher.SECRET_KEY:
|
||||
result = ConstructKeys.constructSecretKey(encoding,
|
||||
keyAlgorithm);
|
||||
break;
|
||||
try {
|
||||
return ConstructKeys.constructSecretKey(encoding, ofs, len,
|
||||
keyAlgorithm);
|
||||
} finally {
|
||||
Arrays.fill(encoding, ofs, len, (byte)0);
|
||||
}
|
||||
case Cipher.PRIVATE_KEY:
|
||||
result = ConstructKeys.constructPrivateKey(encoding,
|
||||
keyAlgorithm);
|
||||
break;
|
||||
byte[] encoding2 = encoding;
|
||||
try {
|
||||
if (ofs != 0 || len != encoding.length) {
|
||||
encoding2 = Arrays.copyOfRange(encoding, ofs, ofs + len);
|
||||
}
|
||||
return ConstructKeys.constructPrivateKey(encoding2,
|
||||
keyAlgorithm);
|
||||
} finally {
|
||||
Arrays.fill(encoding, ofs, len, (byte)0);
|
||||
if (encoding2 != encoding) {
|
||||
Arrays.fill(encoding2, (byte)0);
|
||||
}
|
||||
}
|
||||
case Cipher.PUBLIC_KEY:
|
||||
result = ConstructKeys.constructPublicKey(encoding,
|
||||
keyAlgorithm);
|
||||
break;
|
||||
if (ofs != 0 || len != encoding.length) {
|
||||
encoding = Arrays.copyOfRange(encoding, ofs, ofs + len);
|
||||
}
|
||||
return ConstructKeys.constructPublicKey(encoding, keyAlgorithm);
|
||||
default:
|
||||
throw new NoSuchAlgorithmException("Unsupported key type");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
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.put("SupportedKeyFormats", "RAW");
|
||||
psA("Cipher", "AES/KW/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES_KW_NoPadding",
|
||||
attrs);
|
||||
ps("Cipher", "AES/KW/PKCS5Padding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES_KW_PKCS5Padding",
|
||||
null, attrs);
|
||||
psA("Cipher", "AES/KWP/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES_KWP_NoPadding",
|
||||
attrs);
|
||||
|
||||
psA("Cipher", "AES_128/ECB/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding",
|
||||
attrs);
|
||||
|
@ -207,6 +217,15 @@ public final class SunJCE extends Provider {
|
|||
psA("Cipher", "AES_128/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_128/KW/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_NoPadding",
|
||||
attrs);
|
||||
ps("Cipher", "AES_128/KW/PKCS5Padding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_PKCS5Padding",
|
||||
null, attrs);
|
||||
psA("Cipher", "AES_128/KWP/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES128_KWP_NoPadding",
|
||||
attrs);
|
||||
|
||||
psA("Cipher", "AES_192/ECB/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding",
|
||||
|
@ -223,6 +242,15 @@ public final class SunJCE extends Provider {
|
|||
psA("Cipher", "AES_192/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_192/KW/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_NoPadding",
|
||||
attrs);
|
||||
ps("Cipher", "AES_192/KW/PKCS5Padding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_PKCS5Padding",
|
||||
null, attrs);
|
||||
psA("Cipher", "AES_192/KWP/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES192_KWP_NoPadding",
|
||||
attrs);
|
||||
|
||||
psA("Cipher", "AES_256/ECB/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding",
|
||||
|
@ -239,6 +267,15 @@ public final class SunJCE extends Provider {
|
|||
psA("Cipher", "AES_256/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_256/KW/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_NoPadding",
|
||||
attrs);
|
||||
ps("Cipher", "AES_256/KW/PKCS5Padding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_PKCS5Padding",
|
||||
null, attrs);
|
||||
psA("Cipher", "AES_256/KWP/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES256_KWP_NoPadding",
|
||||
attrs);
|
||||
|
||||
attrs.clear();
|
||||
attrs.put("SupportedModes", "CBC");
|
||||
|
@ -253,17 +290,6 @@ public final class SunJCE extends Provider {
|
|||
attrs.put("SupportedKeyFormats", "RAW");
|
||||
psA("Cipher", "ARCFOUR",
|
||||
"com.sun.crypto.provider.ARCFOURCipher", attrs);
|
||||
ps("Cipher", "AESWrap", "com.sun.crypto.provider.AESWrapCipher$General",
|
||||
null, attrs);
|
||||
psA("Cipher", "AESWrap_128",
|
||||
"com.sun.crypto.provider.AESWrapCipher$AES128",
|
||||
attrs);
|
||||
psA("Cipher", "AESWrap_192",
|
||||
"com.sun.crypto.provider.AESWrapCipher$AES192",
|
||||
attrs);
|
||||
psA("Cipher", "AESWrap_256",
|
||||
"com.sun.crypto.provider.AESWrapCipher$AES256",
|
||||
attrs);
|
||||
|
||||
attrs.clear();
|
||||
attrs.put("SupportedKeyFormats", "RAW");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue