Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java
38922 views
/*1* Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.crypto.provider;2627import java.security.*;28import java.security.spec.*;29import javax.crypto.*;30import javax.crypto.spec.*;3132/**33* This class implements the CMS DESede KeyWrap algorithm as defined34* in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap></a>35* "XML Encryption Syntax and Processing" section 5.6.236* "CMS Triple DES Key Wrap".37* Note: only <code>CBC</code> mode and <code>NoPadding</code> padding38* scheme can be used for this algorithm.39*40* @author Valerie Peng41*42*43* @see DESedeCipher44*/45public final class DESedeWrapCipher extends CipherSpi {4647private static final byte[] IV2 = {48(byte) 0x4a, (byte) 0xdd, (byte) 0xa2, (byte) 0x2c,49(byte) 0x79, (byte) 0xe8, (byte) 0x21, (byte) 0x0550};5152private static final int CHECKSUM_LEN = 8;53private static final int IV_LEN = 8;5455/*56* internal cipher object which does the real work.57*/58private FeedbackCipher cipher;5960/*61* iv for (re-)initializing the internal cipher object.62*/63private byte[] iv = null;6465/*66* key for re-initializing the internal cipher object.67*/68private Key cipherKey = null;6970/*71* are we encrypting or decrypting?72*/73private boolean decrypting = false;7475/**76* Creates an instance of CMS DESede KeyWrap cipher with default77* mode, i.e. "CBC" and padding scheme, i.e. "NoPadding".78*/79public DESedeWrapCipher() {80cipher = new CipherBlockChaining(new DESedeCrypt());81}8283/**84* Sets the mode of this cipher. Only "CBC" mode is accepted for this85* cipher.86*87* @param mode the cipher mode.88*89* @exception NoSuchAlgorithmException if the requested cipher mode90* is not "CBC".91*/92protected void engineSetMode(String mode)93throws NoSuchAlgorithmException {94if (!mode.equalsIgnoreCase("CBC")) {95throw new NoSuchAlgorithmException(mode + " cannot be used");96}97}9899/**100* Sets the padding mechanism of this cipher. Only "NoPadding" schmem101* is accepted for this cipher.102*103* @param padding the padding mechanism.104*105* @exception NoSuchPaddingException if the requested padding mechanism106* is not "NoPadding".107*/108protected void engineSetPadding(String padding)109throws NoSuchPaddingException {110if (!padding.equalsIgnoreCase("NoPadding")) {111throw new NoSuchPaddingException(padding + " cannot be used");112}113}114115/**116* Returns the block size (in bytes), i.e. 8 bytes.117*118* @return the block size (in bytes), i.e. 8 bytes.119*/120protected int engineGetBlockSize() {121return DESConstants.DES_BLOCK_SIZE;122}123124/**125* Returns the length in bytes that an output buffer would need to be126* given the input length <code>inputLen</code> (in bytes).127*128* <p>The actual output length of the next <code>update</code> or129* <code>doFinal</code> call may be smaller than the length returned130* by this method.131*132* @param inputLen the input length (in bytes).133*134* @return the required output buffer size (in bytes).135*/136protected int engineGetOutputSize(int inputLen) {137// can only return an upper-limit if not initialized yet.138int result = 0;139if (decrypting) {140result = inputLen - 16; // CHECKSUM_LEN + IV_LEN;141} else {142result = Math.addExact(inputLen, 16);143}144return (result < 0? 0:result);145}146147/**148* Returns the initialization vector (IV) in a new buffer.149*150* @return the initialization vector, or null if the underlying151* algorithm does not use an IV, or if the IV has not yet152* been set.153*/154protected byte[] engineGetIV() {155return (iv == null) ? null : iv.clone();156}157158/**159* Initializes this cipher with a key and a source of randomness.160*161* <p>The cipher only supports the following two operation modes:162* {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.163* <p>For modes other than the above two, UnsupportedOperationException164* will be thrown.165* <p>If this cipher requires an initialization vector (IV), it will get166* it from <code>random</code>.167*168* @param opmode the operation mode of this cipher. Only169* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.170* @param key the secret key.171* @param random the source of randomness.172*173* @exception InvalidKeyException if the given key is inappropriate174* or if parameters are required but not supplied.175*/176protected void engineInit(int opmode, Key key, SecureRandom random)177throws InvalidKeyException {178try {179engineInit(opmode, key, (AlgorithmParameterSpec) null, random);180} catch (InvalidAlgorithmParameterException iape) {181// should never happen182InvalidKeyException ike =183new InvalidKeyException("Parameters required");184ike.initCause(iape);185throw ike;186}187}188189/**190* Initializes this cipher with a key, a set of algorithm parameters,191* and a source of randomness.192*193* <p>The cipher only supports the following two operation modes:194* {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.195* <p>For modes other than the above two, UnsupportedOperationException196* will be thrown.197* <p>If this cipher requires an initialization vector (IV), it will get198* it from <code>random</code>.199*200* @param opmode the operation mode of this cipher. Only201* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.202* @param key the secret key.203* @param params the algorithm parameters.204* @param random the source of randomness.205*206* @exception InvalidKeyException if the given key is inappropriate.207* @exception InvalidAlgorithmParameterException if the given algorithm208* parameters are inappropriate for this cipher.209*/210protected void engineInit(int opmode, Key key,211AlgorithmParameterSpec params,212SecureRandom random)213throws InvalidKeyException, InvalidAlgorithmParameterException {214byte[] currIv = null;215if (opmode == Cipher.WRAP_MODE) {216decrypting = false;217if (params == null) {218iv = new byte[IV_LEN];219if (random == null) {220random = SunJCE.getRandom();221}222random.nextBytes(iv);223}224else if (params instanceof IvParameterSpec) {225iv = ((IvParameterSpec) params).getIV();226} else {227throw new InvalidAlgorithmParameterException228("Wrong parameter type: IV expected");229}230currIv = iv;231} else if (opmode == Cipher.UNWRAP_MODE) {232if (params != null) {233throw new InvalidAlgorithmParameterException234("No parameter accepted for unwrapping keys");235}236iv = null;237decrypting = true;238currIv = IV2;239} else {240throw new UnsupportedOperationException("This cipher can " +241"only be used for key wrapping and unwrapping");242}243cipher.init(decrypting, key.getAlgorithm(), key.getEncoded(),244currIv);245cipherKey = key;246}247248/**249* Initializes this cipher with a key, a set of algorithm parameters,250* and a source of randomness.251*252* <p>The cipher only supports the following two operation modes:253* {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.254* <p>For modes other than the above two, UnsupportedOperationException255* will be thrown.256* <p>If this cipher requires an initialization vector (IV), it will get257* it from <code>random</code>.258*259* @param opmode the operation mode of this cipher. Only260* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.261* @param key the secret key.262* @param params the algorithm parameters.263* @param random the source of randomness.264*265* @exception InvalidKeyException if the given key is inappropriate.266* @exception InvalidAlgorithmParameterException if the given algorithm267* parameters are inappropriate for this cipher.268*/269protected void engineInit(int opmode, Key key,270AlgorithmParameters params,271SecureRandom random)272throws InvalidKeyException, InvalidAlgorithmParameterException {273IvParameterSpec ivSpec = null;274if (params != null) {275try {276DESedeParameters paramsEng = new DESedeParameters();277paramsEng.engineInit(params.getEncoded());278ivSpec = paramsEng.engineGetParameterSpec(IvParameterSpec.class);279} catch (Exception ex) {280InvalidAlgorithmParameterException iape =281new InvalidAlgorithmParameterException282("Wrong parameter type: IV expected");283iape.initCause(ex);284throw iape;285}286}287engineInit(opmode, key, ivSpec, random);288}289290/**291* This operation is not supported by this cipher.292* Since it's impossible to initialize this cipher given the293* current Cipher.engineInit(...) implementation,294* IllegalStateException will always be thrown upon invocation.295*296* @param in the input buffer.297* @param inOffset the offset in <code>in</code> where the input298* starts.299* @param inLen the input length.300*301* @return n/a.302*303* @exception IllegalStateException upon invocation of this method.304*/305protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {306throw new IllegalStateException("Cipher has not been initialized");307}308309/**310* This operation is not supported by this cipher.311* Since it's impossible to initialize this cipher given the312* current Cipher.engineInit(...) implementation,313* IllegalStateException will always be thrown upon invocation.314*315* @param in the input buffer.316* @param inOffset the offset in <code>in</code> where the input317* starts.318* @param inLen the input length.319* @param out the buffer for the result.320* @param outOffset the offset in <code>out</code> where the result321* is stored.322*323* @return n/a.324*325* @exception IllegalStateException upon invocation of this method.326*/327protected int engineUpdate(byte[] in, int inOffset, int inLen,328byte[] out, int outOffset)329throws ShortBufferException {330throw new IllegalStateException("Cipher has not been initialized");331}332333/**334* This operation is not supported by this cipher.335* Since it's impossible to initialize this cipher given the336* current Cipher.engineInit(...) implementation,337* IllegalStateException will always be thrown upon invocation.338*339* @param in the input buffer.340* @param inOffset the offset in <code>in</code> where the input341* starts.342* @param inLen the input length.343*344* @return the new buffer with the result.345*346* @exception IllegalStateException upon invocation of this method.347*/348protected byte[] engineDoFinal(byte[] in, int inOffset, int inLen)349throws IllegalBlockSizeException, BadPaddingException {350throw new IllegalStateException("Cipher has not been initialized");351}352353/**354* This operation is not supported by this cipher.355* Since it's impossible to initialize this cipher given the356* current Cipher.engineInit(...) implementation,357* IllegalStateException will always be thrown upon invocation.358*359* @param input the input buffer.360* @param inputOffset the offset in {@code input} where the input361* starts.362* @param inputLen the input length.363* @param output the buffer for the result.364* @param outputOffset the ofset in {@code output} where the result365* is stored.366*367* @return the number of bytes stored in {@code out}.368*369* @exception IllegalStateException upon invocation of this method.370*/371protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,372byte[] output, int outputOffset)373throws IllegalBlockSizeException, ShortBufferException,374BadPaddingException {375throw new IllegalStateException("Cipher has not been initialized");376}377378/**379* Returns the parameters used with this cipher.380* Note that null maybe returned if this cipher does not use any381* parameters or when it has not be set, e.g. initialized with382* UNWRAP_MODE but wrapped key data has not been given.383*384* @return the parameters used with this cipher; can be null.385*/386protected AlgorithmParameters engineGetParameters() {387AlgorithmParameters params = null;388if (iv != null) {389String algo = cipherKey.getAlgorithm();390try {391params = AlgorithmParameters.getInstance(algo,392SunJCE.getInstance());393params.init(new IvParameterSpec(iv));394} catch (NoSuchAlgorithmException nsae) {395// should never happen396throw new RuntimeException("Cannot find " + algo +397" AlgorithmParameters implementation in SunJCE provider");398} catch (InvalidParameterSpecException ipse) {399// should never happen400throw new RuntimeException("IvParameterSpec not supported");401}402}403return params;404}405406/**407* Returns the key size of the given key object in number of bits.408* This cipher always return the same key size as the DESede ciphers.409*410* @param key the key object.411*412* @return the "effective" key size of the given key object.413*414* @exception InvalidKeyException if <code>key</code> is invalid.415*/416protected int engineGetKeySize(Key key) throws InvalidKeyException {417byte[] encoded = key.getEncoded();418if (encoded.length != 24) {419throw new InvalidKeyException("Invalid key length: " +420encoded.length + " bytes");421}422// Return the effective key length423return 112;424}425426/**427* Wrap a key.428*429* @param key the key to be wrapped.430*431* @return the wrapped key.432*433* @exception IllegalBlockSizeException if this cipher is a block434* cipher, no padding has been requested, and the length of the435* encoding of the key to be wrapped is not a436* multiple of the block size.437*438* @exception InvalidKeyException if it is impossible or unsafe to439* wrap the key with this cipher (e.g., a hardware protected key is440* being passed to a software only cipher).441*/442protected byte[] engineWrap(Key key)443throws IllegalBlockSizeException, InvalidKeyException {444byte[] keyVal = key.getEncoded();445if ((keyVal == null) || (keyVal.length == 0)) {446throw new InvalidKeyException("Cannot get an encoding of " +447"the key to be wrapped");448}449450byte[] cks = getChecksum(keyVal);451byte[] in = new byte[Math.addExact(keyVal.length, CHECKSUM_LEN)];452System.arraycopy(keyVal, 0, in, 0, keyVal.length);453System.arraycopy(cks, 0, in, keyVal.length, CHECKSUM_LEN);454455byte[] out = new byte[Math.addExact(iv.length, in.length)];456System.arraycopy(iv, 0, out, 0, iv.length);457458cipher.encrypt(in, 0, in.length, out, iv.length);459460// reverse the array content461for (int i = 0; i < out.length/2; i++) {462byte temp = out[i];463out[i] = out[out.length-1-i];464out[out.length-1-i] = temp;465}466try {467cipher.init(false, cipherKey.getAlgorithm(),468cipherKey.getEncoded(), IV2);469} catch (InvalidKeyException ike) {470// should never happen471throw new RuntimeException("Internal cipher key is corrupted");472} catch (InvalidAlgorithmParameterException iape) {473// should never happen474throw new RuntimeException("Internal cipher IV is invalid");475}476byte[] out2 = new byte[out.length];477cipher.encrypt(out, 0, out.length, out2, 0);478479// restore cipher state to prior to this call480try {481cipher.init(decrypting, cipherKey.getAlgorithm(),482cipherKey.getEncoded(), iv);483} catch (InvalidKeyException ike) {484// should never happen485throw new RuntimeException("Internal cipher key is corrupted");486} catch (InvalidAlgorithmParameterException iape) {487// should never happen488throw new RuntimeException("Internal cipher IV is invalid");489}490return out2;491}492493/**494* Unwrap a previously wrapped key.495*496* @param wrappedKey the key to be unwrapped.497*498* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.499*500* @param wrappedKeyType the type of the wrapped key.501* This is one of <code>Cipher.SECRET_KEY</code>,502* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.503*504* @return the unwrapped key.505*506* @exception NoSuchAlgorithmException if no installed providers507* can create keys of type <code>wrappedKeyType</code> for the508* <code>wrappedKeyAlgorithm</code>.509*510* @exception InvalidKeyException if <code>wrappedKey</code> does not511* represent a wrapped key of type <code>wrappedKeyType</code> for512* the <code>wrappedKeyAlgorithm</code>.513*/514protected Key engineUnwrap(byte[] wrappedKey,515String wrappedKeyAlgorithm,516int wrappedKeyType)517throws InvalidKeyException, NoSuchAlgorithmException {518if (wrappedKey.length == 0) {519throw new InvalidKeyException("The wrapped key is empty");520}521byte[] buffer = new byte[wrappedKey.length];522cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0);523524// reverse array content525for (int i = 0; i < buffer.length/2; i++) {526byte temp = buffer[i];527buffer[i] = buffer[buffer.length-1-i];528buffer[buffer.length-1-i] = temp;529}530iv = new byte[IV_LEN];531System.arraycopy(buffer, 0, iv, 0, iv.length);532try {533cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(),534iv);535} catch (InvalidAlgorithmParameterException iape) {536throw new InvalidKeyException("IV in wrapped key is invalid");537}538byte[] buffer2 = new byte[buffer.length - iv.length];539cipher.decrypt(buffer, iv.length, buffer2.length,540buffer2, 0);541int keyValLen = buffer2.length - CHECKSUM_LEN;542byte[] cks = getChecksum(buffer2, 0, keyValLen);543int offset = keyValLen;544for (int i = 0; i < CHECKSUM_LEN; i++) {545if (buffer2[offset + i] != cks[i]) {546throw new InvalidKeyException("Checksum comparison failed");547}548}549// restore cipher state to prior to this call550try {551cipher.init(decrypting, cipherKey.getAlgorithm(),552cipherKey.getEncoded(), IV2);553} catch (InvalidAlgorithmParameterException iape) {554throw new InvalidKeyException("IV in wrapped key is invalid");555}556byte[] out = new byte[keyValLen];557System.arraycopy(buffer2, 0, out, 0, keyValLen);558return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,559wrappedKeyType);560}561562private static final byte[] getChecksum(byte[] in) {563return getChecksum(in, 0, in.length);564}565private static final byte[] getChecksum(byte[] in, int offset, int len) {566MessageDigest md = null;567try {568md = MessageDigest.getInstance("SHA1");569} catch (NoSuchAlgorithmException nsae) {570throw new RuntimeException("SHA1 message digest not available");571}572md.update(in, offset, len);573byte[] cks = new byte[CHECKSUM_LEN];574System.arraycopy(md.digest(), 0, cks, 0, cks.length);575return cks;576}577}578579580