feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,645 @@
/*
* Copyright (c) 2002, 2017, 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.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.BadPaddingException;
import java.nio.ByteBuffer;
/**
* This class implements the AES algorithm in its various modes
* (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, <code>CBC</code>,
* <code>PCBC</code>) and padding schemes (<code>PKCS5Padding</code>,
* <code>NoPadding</code>, <code>ISO10126Padding</code>).
*
* @author Valerie Peng
*
*
* @see AESCrypt
* @see CipherBlockChaining
* @see ElectronicCodeBook
* @see CipherFeedback
* @see OutputFeedback
*/
abstract class AESCipher extends CipherSpi {
public static final class General extends AESCipher {
public General() {
super(-1);
}
}
abstract static class OidImpl extends AESCipher {
protected OidImpl(int keySize, String mode, String padding) {
super(keySize);
try {
engineSetMode(mode);
engineSetPadding(padding);
} catch (GeneralSecurityException gse) {
// internal error; re-throw as provider exception
ProviderException pe =new ProviderException("Internal Error");
pe.initCause(gse);
throw pe;
}
}
}
public static final class AES128_ECB_NoPadding extends OidImpl {
public AES128_ECB_NoPadding() {
super(16, "ECB", "NOPADDING");
}
}
public static final class AES192_ECB_NoPadding extends OidImpl {
public AES192_ECB_NoPadding() {
super(24, "ECB", "NOPADDING");
}
}
public static final class AES256_ECB_NoPadding extends OidImpl {
public AES256_ECB_NoPadding() {
super(32, "ECB", "NOPADDING");
}
}
public static final class AES128_CBC_NoPadding extends OidImpl {
public AES128_CBC_NoPadding() {
super(16, "CBC", "NOPADDING");
}
}
public static final class AES192_CBC_NoPadding extends OidImpl {
public AES192_CBC_NoPadding() {
super(24, "CBC", "NOPADDING");
}
}
public static final class AES256_CBC_NoPadding extends OidImpl {
public AES256_CBC_NoPadding() {
super(32, "CBC", "NOPADDING");
}
}
public static final class AES128_OFB_NoPadding extends OidImpl {
public AES128_OFB_NoPadding() {
super(16, "OFB", "NOPADDING");
}
}
public static final class AES192_OFB_NoPadding extends OidImpl {
public AES192_OFB_NoPadding() {
super(24, "OFB", "NOPADDING");
}
}
public static final class AES256_OFB_NoPadding extends OidImpl {
public AES256_OFB_NoPadding() {
super(32, "OFB", "NOPADDING");
}
}
public static final class AES128_CFB_NoPadding extends OidImpl {
public AES128_CFB_NoPadding() {
super(16, "CFB", "NOPADDING");
}
}
public static final class AES192_CFB_NoPadding extends OidImpl {
public AES192_CFB_NoPadding() {
super(24, "CFB", "NOPADDING");
}
}
public static final class AES256_CFB_NoPadding extends OidImpl {
public AES256_CFB_NoPadding() {
super(32, "CFB", "NOPADDING");
}
}
public static final class AES128_GCM_NoPadding extends OidImpl {
public AES128_GCM_NoPadding() {
super(16, "GCM", "NOPADDING");
}
}
public static final class AES192_GCM_NoPadding extends OidImpl {
public AES192_GCM_NoPadding() {
super(24, "GCM", "NOPADDING");
}
}
public static final class AES256_GCM_NoPadding extends OidImpl {
public AES256_GCM_NoPadding() {
super(32, "GCM", "NOPADDING");
}
}
// utility method used by AESCipher and AESWrapCipher
static final void checkKeySize(Key key, int fixedKeySize)
throws InvalidKeyException {
if (fixedKeySize != -1) {
if (key == null) {
throw new InvalidKeyException("The key must not be null");
}
byte[] value = key.getEncoded();
if (value == null) {
throw new InvalidKeyException("Key encoding must not be null");
} else if (value.length != fixedKeySize) {
throw new InvalidKeyException("The key must be " +
fixedKeySize + " bytes");
}
}
}
/*
* internal CipherCore object which does the real work.
*/
private CipherCore core = null;
/*
* 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
/*
* needed to enforce ISE thrown when updateAAD is called after update for GCM mode.
*/
private boolean updateCalled;
/**
* Creates an instance of AES cipher with default ECB mode and
* PKCS5Padding.
*/
protected AESCipher(int keySize) {
core = new CipherCore(new AESCrypt(), AESConstants.AES_BLOCK_SIZE);
fixedKeySize = keySize;
}
/**
* Sets the mode of this cipher.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode does
* not exist
*/
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
core.setMode(mode);
}
/**
* Sets the padding mechanism of this cipher.
*
* @param padding the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* does not exist
*/
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
core.setPadding(paddingScheme);
}
/**
* Returns the block size (in bytes).
*
* @return the block size (in bytes), or 0 if the underlying algorithm is
* not a block cipher
*/
protected int engineGetBlockSize() {
return AESConstants.AES_BLOCK_SIZE;
}
/**
* Returns the length in bytes that an output buffer would need to be in
* order to hold the result of the next <code>update</code> or
* <code>doFinal</code> operation, given the input length
* <code>inputLen</code> (in bytes).
*
* <p>This call takes into account any unprocessed (buffered) data from a
* previous <code>update</code> call, and padding.
*
* <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) {
return core.getOutputSize(inputLen);
}
/**
* Returns the initialization vector (IV) in a new buffer.
*
* <p>This is useful in the case where a random IV has been created
* (see <a href = "#init">init</a>),
* or in the context of password-based encryption or
* decryption, where the IV is derived from a user-provided password.
*
* @return the initialization vector in a new buffer, or null if the
* underlying algorithm does not use an IV, or if the IV has not yet
* been set.
*/
protected byte[] engineGetIV() {
return core.getIV();
}
/**
* Returns the parameters used with this cipher.
*
* <p>The returned parameters may be the same that were used to initialize
* this cipher, or may contain the default set of parameters or a set of
* randomly generated parameters used by the underlying cipher
* implementation (provided that the underlying cipher implementation
* uses a default set of parameters or creates new parameters if it needs
* parameters but was not initialized with any).
*
* @return the parameters used with this cipher, or null if this cipher
* does not use any parameters.
*/
protected AlgorithmParameters engineGetParameters() {
return core.getParameters("AES");
}
/**
* Initializes this cipher with a key and a source of randomness.
*
* <p>The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher requires an initialization vector (IV), it will get
* it from <code>random</code>.
* This behaviour should only be used in encryption or key wrapping
* mode, however.
* When initializing a cipher that requires an IV for decryption or
* key unwrapping, the IV
* (same IV that was used for encryption or key wrapping) must be provided
* explicitly as a
* parameter, in order to get the correct result.
*
* <p>This method also cleans existing buffer and other related state
* information.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @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 {
checkKeySize(key, fixedKeySize);
updateCalled = false;
core.init(opmode, key, random);
}
/**
* Initializes this cipher with a key, a set of
* algorithm parameters, and a source of randomness.
*
* <p>The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @param key the encryption key
* @param params the algorithm parameters
* @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 are inappropriate for this cipher
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
updateCalled = false;
core.init(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
updateCalled = false;
core.init(opmode, key, params, random);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in a new buffer.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized)
*/
protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) {
updateCalled = true;
return core.update(input, inputOffset, inputLen);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
*/
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
updateCalled = true;
return core.update(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in a new buffer.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
byte[] out = core.doFinal(input, inputOffset, inputLen);
updateCalled = false;
return out;
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
int outLen = core.doFinal(input, inputOffset, inputLen, output,
outputOffset);
updateCalled = false;
return outLen;
}
/**
* Returns the key size of the given key object.
*
* @param key the key object.
*
* @return the 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 (!AESCrypt.isKeySizeValid(encoded.length)) {
throw new InvalidKeyException("Invalid AES 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 {
return core.wrap(key);
}
/**
* Unwrap a previously wrapped key.
*
* @param wrappedKey the key to be unwrapped.
*
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
*
* @param wrappedKeyType the type of the wrapped key.
* This is one of <code>Cipher.SECRET_KEY</code>,
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
*
* @return the unwrapped key.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys of type <code>wrappedKeyType</code> for the
* <code>wrappedKeyAlgorithm</code>.
*
* @exception InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key of type <code>wrappedKeyType</code> for
* the <code>wrappedKeyAlgorithm</code>.
*/
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
/**
* Continues a multi-part update of the Additional Authentication
* Data (AAD), using a subset of the provided buffer.
* <p>
* Calls to this method provide AAD to the cipher when operating in
* modes such as AEAD (GCM/CCM). If this cipher is operating in
* either GCM or CCM mode, all AAD must be supplied before beginning
* operations on the ciphertext (via the {@code update} and {@code
* doFinal} methods).
*
* @param src the buffer containing the AAD
* @param offset the offset in {@code src} where the AAD input starts
* @param len the number of AAD bytes
*
* @throws IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized), does not accept AAD, or if
* operating in either GCM or CCM mode and one of the {@code update}
* methods has already been called for the active
* encryption/decryption operation
* @throws UnsupportedOperationException if this method
* has not been overridden by an implementation
*
* @since 1.8
*/
@Override
protected void engineUpdateAAD(byte[] src, int offset, int len) {
if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
}
core.updateAAD(src, offset, len);
}
/**
* Continues a multi-part update of the Additional Authentication
* Data (AAD).
* <p>
* Calls to this method provide AAD to the cipher when operating in
* modes such as AEAD (GCM/CCM). If this cipher is operating in
* either GCM or CCM mode, all AAD must be supplied before beginning
* operations on the ciphertext (via the {@code update} and {@code
* doFinal} methods).
* <p>
* All {@code src.remaining()} bytes starting at
* {@code src.position()} are processed.
* Upon return, the input buffer's position will be equal
* to its limit; its limit will not have changed.
*
* @param src the buffer containing the AAD
*
* @throws IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized), does not accept AAD, or if
* operating in either GCM or CCM mode and one of the {@code update}
* methods has already been called for the active
* encryption/decryption operation
* @throws UnsupportedOperationException if this method
* has not been overridden by an implementation
*
* @since 1.8
*/
@Override
protected void engineUpdateAAD(ByteBuffer src) {
if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
}
if (src != null) {
int aadLen = src.limit() - src.position();
if (aadLen > 0) {
if (src.hasArray()) {
int aadOfs = Math.addExact(src.arrayOffset(), src.position());
core.updateAAD(src.array(), aadOfs, aadLen);
src.position(src.limit());
} else {
byte[] aad = new byte[aadLen];
src.get(aad);
core.updateAAD(aad, 0, aadLen);
}
}
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2002, 2007, 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;
/**
* This class defines the constants used by the AES implementation.
*
* @author Valerie Peng
*
*
* @see AESCipher
*/
interface AESConstants {
// AES block size in bytes.
int AES_BLOCK_SIZE = 16;
// Valid AES key sizes in bytes.
// NOTE: The values need to be listed in an *increasing* order
// since DHKeyAgreement depends on this fact.
int[] AES_KEYSIZES = { 16, 24, 32 };
}

View File

@@ -0,0 +1,691 @@
/*
* Copyright (c) 2002, 2018, 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.
*/
/* $Id: Rijndael.java,v 1.6 2000/02/10 01:31:41 gelderen Exp $
*
* Copyright (C) 1995-2000 The Cryptix Foundation Limited.
* All rights reserved.
*
* Use, modification, copying and distribution of this softwareas is subject
* the terms and conditions of the Cryptix General Licence. You should have
* received a copy of the Cryptix General Licence along with this library;
* if not, you can download a copy from http://www.cryptix.org/ .
*/
package com.sun.crypto.provider;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
/**
* Rijndael --pronounced Reindaal-- is a symmetric cipher with a 128-bit
* block size and variable key-size (128-, 192- and 256-bit).
* <p>
* Rijndael was designed by <a href="mailto:rijmen@esat.kuleuven.ac.be">Vincent
* Rijmen</a> and <a href="mailto:Joan.Daemen@village.uunet.be">Joan Daemen</a>.
*/
final class AESCrypt extends SymmetricCipher implements AESConstants
{
private boolean ROUNDS_12 = false;
private boolean ROUNDS_14 = false;
/** Session and Sub keys */
private int[][] sessionK = null;
private int[] K = null;
/** Cipher encryption/decryption key */
// skip re-generating Session and Sub keys if the cipher key is
// the same
private byte[] lastKey = null;
/** ROUNDS * 4 */
private int limit = 0;
AESCrypt() {
// empty
}
/**
* Returns this cipher's block size.
*
* @return this cipher's block size
*/
int getBlockSize() {
return AES_BLOCK_SIZE;
}
void init(boolean decrypting, String algorithm, byte[] key)
throws InvalidKeyException {
if (!algorithm.equalsIgnoreCase("AES")
&& !algorithm.equalsIgnoreCase("Rijndael")) {
throw new InvalidKeyException
("Wrong algorithm: AES or Rijndael required");
}
if (!isKeySizeValid(key.length)) {
throw new InvalidKeyException("Invalid AES key length: " +
key.length + " bytes");
}
if (!MessageDigest.isEqual(key, lastKey)) {
// re-generate session key 'sessionK' when cipher key changes
makeSessionKey(key);
lastKey = key.clone(); // save cipher key
}
// set sub key to the corresponding session Key
this.K = sessionK[(decrypting? 1:0)];
}
/**
* Expand an int[(ROUNDS+1)][4] into int[(ROUNDS+1)*4].
* For decryption round keys, need to rotate right by 4 ints.
* @param kr The round keys for encryption or decryption.
* @param decrypting True if 'kr' is for decryption and false otherwise.
*/
private static final int[] expandToSubKey(int[][] kr, boolean decrypting) {
int total = kr.length;
int[] expK = new int[total*4];
if (decrypting) {
// decrypting, rotate right by 4 ints
// i.e. i==0
for(int j=0; j<4; j++) {
expK[j] = kr[total-1][j];
}
for(int i=1; i<total; i++) {
for(int j=0; j<4; j++) {
expK[i*4 + j] = kr[i-1][j];
}
}
} else {
// encrypting, straight expansion
for(int i=0; i<total; i++) {
for(int j=0; j<4; j++) {
expK[i*4 + j] = kr[i][j];
}
}
}
return expK;
}
private static int[]
alog = new int[256],
log = new int[256];
private static final byte[]
S = new byte[256],
Si = new byte[256];
private static final int[]
T1 = new int[256],
T2 = new int[256],
T3 = new int[256],
T4 = new int[256],
T5 = new int[256],
T6 = new int[256],
T7 = new int[256],
T8 = new int[256];
private static final int[]
U1 = new int[256],
U2 = new int[256],
U3 = new int[256],
U4 = new int[256];
private static final byte[] rcon = new byte[30];
// Static code - to intialise S-boxes and T-boxes
static
{
int ROOT = 0x11B;
int i, j = 0;
//
// produce log and alog tables, needed for multiplying in the
// field GF(2^m) (generator = 3)
//
alog[0] = 1;
for (i = 1; i < 256; i++)
{
j = (alog[i-1] << 1) ^ alog[i-1];
if ((j & 0x100) != 0) {
j ^= ROOT;
}
alog[i] = j;
}
for (i = 1; i < 255; i++) {
log[alog[i]] = i;
}
byte[][] A = new byte[][]
{
{1, 1, 1, 1, 1, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 1, 1, 1},
{1, 1, 0, 0, 0, 1, 1, 1},
{1, 1, 1, 0, 0, 0, 1, 1},
{1, 1, 1, 1, 0, 0, 0, 1}
};
byte[] B = new byte[] { 0, 1, 1, 0, 0, 0, 1, 1};
//
// substitution box based on F^{-1}(x)
//
int t;
byte[][] box = new byte[256][8];
box[1][7] = 1;
for (i = 2; i < 256; i++) {
j = alog[255 - log[i]];
for (t = 0; t < 8; t++) {
box[i][t] = (byte)((j >>> (7 - t)) & 0x01);
}
}
//
// affine transform: box[i] <- B + A*box[i]
//
byte[][] cox = new byte[256][8];
for (i = 0; i < 256; i++) {
for (t = 0; t < 8; t++) {
cox[i][t] = B[t];
for (j = 0; j < 8; j++) {
cox[i][t] ^= A[t][j] * box[i][j];
}
}
}
//
// S-boxes and inverse S-boxes
//
for (i = 0; i < 256; i++) {
S[i] = (byte)(cox[i][0] << 7);
for (t = 1; t < 8; t++) {
S[i] ^= cox[i][t] << (7-t);
}
Si[S[i] & 0xFF] = (byte) i;
}
//
// T-boxes
//
byte[][] G = new byte[][] {
{2, 1, 1, 3},
{3, 2, 1, 1},
{1, 3, 2, 1},
{1, 1, 3, 2}
};
byte[][] AA = new byte[4][8];
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) AA[i][j] = G[i][j];
AA[i][i+4] = 1;
}
byte pivot, tmp;
byte[][] iG = new byte[4][4];
for (i = 0; i < 4; i++) {
pivot = AA[i][i];
if (pivot == 0) {
t = i + 1;
while ((AA[t][i] == 0) && (t < 4)) {
t++;
}
if (t == 4) {
throw new RuntimeException("G matrix is not invertible");
}
else {
for (j = 0; j < 8; j++) {
tmp = AA[i][j];
AA[i][j] = AA[t][j];
AA[t][j] = tmp;
}
pivot = AA[i][i];
}
}
for (j = 0; j < 8; j++) {
if (AA[i][j] != 0) {
AA[i][j] = (byte)
alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF])
% 255];
}
}
for (t = 0; t < 4; t++) {
if (i != t) {
for (j = i+1; j < 8; j++) {
AA[t][j] ^= mul(AA[i][j], AA[t][i]);
}
AA[t][i] = 0;
}
}
}
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
iG[i][j] = AA[i][j + 4];
}
}
int s;
for (t = 0; t < 256; t++) {
s = S[t];
T1[t] = mul4(s, G[0]);
T2[t] = mul4(s, G[1]);
T3[t] = mul4(s, G[2]);
T4[t] = mul4(s, G[3]);
s = Si[t];
T5[t] = mul4(s, iG[0]);
T6[t] = mul4(s, iG[1]);
T7[t] = mul4(s, iG[2]);
T8[t] = mul4(s, iG[3]);
U1[t] = mul4(t, iG[0]);
U2[t] = mul4(t, iG[1]);
U3[t] = mul4(t, iG[2]);
U4[t] = mul4(t, iG[3]);
}
//
// round constants
//
rcon[0] = 1;
int r = 1;
for (t = 1; t < 30; t++) {
r = mul(2, r);
rcon[t] = (byte) r;
}
log = null;
alog = null;
}
// multiply two elements of GF(2^m)
private static final int mul (int a, int b) {
return (a != 0 && b != 0) ?
alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] :
0;
}
// convenience method used in generating Transposition boxes
private static final int mul4 (int a, byte[] b) {
if (a == 0) return 0;
a = log[a & 0xFF];
int a0 = (b[0] != 0) ? alog[(a + log[b[0] & 0xFF]) % 255] & 0xFF : 0;
int a1 = (b[1] != 0) ? alog[(a + log[b[1] & 0xFF]) % 255] & 0xFF : 0;
int a2 = (b[2] != 0) ? alog[(a + log[b[2] & 0xFF]) % 255] & 0xFF : 0;
int a3 = (b[3] != 0) ? alog[(a + log[b[3] & 0xFF]) % 255] & 0xFF : 0;
return a0 << 24 | a1 << 16 | a2 << 8 | a3;
}
// check if the specified length (in bytes) is a valid keysize for AES
static final boolean isKeySizeValid(int len) {
for (int i = 0; i < AES_KEYSIZES.length; i++) {
if (len == AES_KEYSIZES[i]) {
return true;
}
}
return false;
}
/**
* Encrypt exactly one block of plaintext.
*/
void encryptBlock(byte[] in, int inOffset,
byte[] out, int outOffset) {
// Array bound checks are done in caller code, i.e.
// FeedbackCipher.encrypt/decrypt(...) to improve performance.
implEncryptBlock(in, inOffset, out, outOffset);
}
// Encryption operation. Possibly replaced with a compiler intrinsic.
private void implEncryptBlock(byte[] in, int inOffset,
byte[] out, int outOffset)
{
int keyOffset = 0;
int t0 = ((in[inOffset++] ) << 24 |
(in[inOffset++] & 0xFF) << 16 |
(in[inOffset++] & 0xFF) << 8 |
(in[inOffset++] & 0xFF) ) ^ K[keyOffset++];
int t1 = ((in[inOffset++] ) << 24 |
(in[inOffset++] & 0xFF) << 16 |
(in[inOffset++] & 0xFF) << 8 |
(in[inOffset++] & 0xFF) ) ^ K[keyOffset++];
int t2 = ((in[inOffset++] ) << 24 |
(in[inOffset++] & 0xFF) << 16 |
(in[inOffset++] & 0xFF) << 8 |
(in[inOffset++] & 0xFF) ) ^ K[keyOffset++];
int t3 = ((in[inOffset++] ) << 24 |
(in[inOffset++] & 0xFF) << 16 |
(in[inOffset++] & 0xFF) << 8 |
(in[inOffset++] & 0xFF) ) ^ K[keyOffset++];
// apply round transforms
while( keyOffset < limit )
{
int a0, a1, a2;
a0 = T1[(t0 >>> 24) ] ^
T2[(t1 >>> 16) & 0xFF] ^
T3[(t2 >>> 8) & 0xFF] ^
T4[(t3 ) & 0xFF] ^ K[keyOffset++];
a1 = T1[(t1 >>> 24) ] ^
T2[(t2 >>> 16) & 0xFF] ^
T3[(t3 >>> 8) & 0xFF] ^
T4[(t0 ) & 0xFF] ^ K[keyOffset++];
a2 = T1[(t2 >>> 24) ] ^
T2[(t3 >>> 16) & 0xFF] ^
T3[(t0 >>> 8) & 0xFF] ^
T4[(t1 ) & 0xFF] ^ K[keyOffset++];
t3 = T1[(t3 >>> 24) ] ^
T2[(t0 >>> 16) & 0xFF] ^
T3[(t1 >>> 8) & 0xFF] ^
T4[(t2 ) & 0xFF] ^ K[keyOffset++];
t0 = a0; t1 = a1; t2 = a2;
}
// last round is special
int tt = K[keyOffset++];
out[outOffset++] = (byte)(S[(t0 >>> 24) ] ^ (tt >>> 24));
out[outOffset++] = (byte)(S[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
out[outOffset++] = (byte)(S[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
out[outOffset++] = (byte)(S[(t3 ) & 0xFF] ^ (tt ));
tt = K[keyOffset++];
out[outOffset++] = (byte)(S[(t1 >>> 24) ] ^ (tt >>> 24));
out[outOffset++] = (byte)(S[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
out[outOffset++] = (byte)(S[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
out[outOffset++] = (byte)(S[(t0 ) & 0xFF] ^ (tt ));
tt = K[keyOffset++];
out[outOffset++] = (byte)(S[(t2 >>> 24) ] ^ (tt >>> 24));
out[outOffset++] = (byte)(S[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
out[outOffset++] = (byte)(S[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
out[outOffset++] = (byte)(S[(t1 ) & 0xFF] ^ (tt ));
tt = K[keyOffset++];
out[outOffset++] = (byte)(S[(t3 >>> 24) ] ^ (tt >>> 24));
out[outOffset++] = (byte)(S[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
out[outOffset++] = (byte)(S[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
out[outOffset ] = (byte)(S[(t2 ) & 0xFF] ^ (tt ));
}
/**
* Decrypt exactly one block of plaintext.
*/
void decryptBlock(byte[] in, int inOffset,
byte[] out, int outOffset) {
// Array bound checks are done in caller code, i.e.
// FeedbackCipher.encrypt/decrypt(...) to improve performance.
implDecryptBlock(in, inOffset, out, outOffset);
}
// Decrypt operation. Possibly replaced with a compiler intrinsic.
private void implDecryptBlock(byte[] in, int inOffset,
byte[] out, int outOffset)
{
int keyOffset = 4;
int t0 = ((in[inOffset++] ) << 24 |
(in[inOffset++] & 0xFF) << 16 |
(in[inOffset++] & 0xFF) << 8 |
(in[inOffset++] & 0xFF) ) ^ K[keyOffset++];
int t1 = ((in[inOffset++] ) << 24 |
(in[inOffset++] & 0xFF) << 16 |
(in[inOffset++] & 0xFF) << 8 |
(in[inOffset++] & 0xFF) ) ^ K[keyOffset++];
int t2 = ((in[inOffset++] ) << 24 |
(in[inOffset++] & 0xFF) << 16 |
(in[inOffset++] & 0xFF) << 8 |
(in[inOffset++] & 0xFF) ) ^ K[keyOffset++];
int t3 = ((in[inOffset++] ) << 24 |
(in[inOffset++] & 0xFF) << 16 |
(in[inOffset++] & 0xFF) << 8 |
(in[inOffset ] & 0xFF) ) ^ K[keyOffset++];
int a0, a1, a2;
if(ROUNDS_12)
{
a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++];
a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++];
a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^
T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^
T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++];
t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++];
t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++];
t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^
T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^
T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++];
if(ROUNDS_14)
{
a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++];
a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++];
a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^
T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^
T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++];
t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++];
t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++];
t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^
T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^
T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++];
}
}
a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++];
a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++];
a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^
T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^
T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++];
t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++];
t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++];
t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^
T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^
T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++];
a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++];
a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++];
a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^
T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^
T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++];
t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++];
t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++];
t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^
T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^
T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++];
a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++];
a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++];
a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^
T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^
T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++];
t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++];
t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++];
t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^
T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^
T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++];
a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++];
a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++];
a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^
T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^
T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++];
t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++];
t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++];
t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^
T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^
T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++];
a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^
T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++];
a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^
T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++];
a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^
T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++];
t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^
T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++];
t1 = K[0];
out[outOffset++] = (byte)(Si[(a0 >>> 24) ] ^ (t1 >>> 24));
out[outOffset++] = (byte)(Si[(t3 >>> 16) & 0xFF] ^ (t1 >>> 16));
out[outOffset++] = (byte)(Si[(a2 >>> 8) & 0xFF] ^ (t1 >>> 8));
out[outOffset++] = (byte)(Si[(a1 ) & 0xFF] ^ (t1 ));
t1 = K[1];
out[outOffset++] = (byte)(Si[(a1 >>> 24) ] ^ (t1 >>> 24));
out[outOffset++] = (byte)(Si[(a0 >>> 16) & 0xFF] ^ (t1 >>> 16));
out[outOffset++] = (byte)(Si[(t3 >>> 8) & 0xFF] ^ (t1 >>> 8));
out[outOffset++] = (byte)(Si[(a2 ) & 0xFF] ^ (t1 ));
t1 = K[2];
out[outOffset++] = (byte)(Si[(a2 >>> 24) ] ^ (t1 >>> 24));
out[outOffset++] = (byte)(Si[(a1 >>> 16) & 0xFF] ^ (t1 >>> 16));
out[outOffset++] = (byte)(Si[(a0 >>> 8) & 0xFF] ^ (t1 >>> 8));
out[outOffset++] = (byte)(Si[(t3 ) & 0xFF] ^ (t1 ));
t1 = K[3];
out[outOffset++] = (byte)(Si[(t3 >>> 24) ] ^ (t1 >>> 24));
out[outOffset++] = (byte)(Si[(a2 >>> 16) & 0xFF] ^ (t1 >>> 16));
out[outOffset++] = (byte)(Si[(a1 >>> 8) & 0xFF] ^ (t1 >>> 8));
out[outOffset ] = (byte)(Si[(a0 ) & 0xFF] ^ (t1 ));
}
/**
* Expand a user-supplied key material into a session key.
*
* @param k The 128/192/256-bit cipher key to use.
* @exception InvalidKeyException If the key is invalid.
*/
private void makeSessionKey(byte[] k) throws InvalidKeyException {
if (k == null) {
throw new InvalidKeyException("Empty key");
}
if (!isKeySizeValid(k.length)) {
throw new InvalidKeyException("Invalid AES key length: " +
k.length + " bytes");
}
int ROUNDS = getRounds(k.length);
int ROUND_KEY_COUNT = (ROUNDS + 1) * 4;
int BC = 4;
int[][] Ke = new int[ROUNDS + 1][4]; // encryption round keys
int[][] Kd = new int[ROUNDS + 1][4]; // decryption round keys
int KC = k.length/4; // keylen in 32-bit elements
int[] tk = new int[KC];
int i, j;
// copy user material bytes into temporary ints
for (i = 0, j = 0; i < KC; i++, j+=4) {
tk[i] = (k[j] ) << 24 |
(k[j+1] & 0xFF) << 16 |
(k[j+2] & 0xFF) << 8 |
(k[j+3] & 0xFF);
}
// copy values into round key arrays
int t = 0;
for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
Ke[t / 4][t % 4] = tk[j];
Kd[ROUNDS - (t / 4)][t % 4] = tk[j];
}
int tt, rconpointer = 0;
while (t < ROUND_KEY_COUNT) {
// extrapolate using phi (the round key evolution function)
tt = tk[KC - 1];
tk[0] ^= (S[(tt >>> 16) & 0xFF] ) << 24 ^
(S[(tt >>> 8) & 0xFF] & 0xFF) << 16 ^
(S[(tt ) & 0xFF] & 0xFF) << 8 ^
(S[(tt >>> 24) ] & 0xFF) ^
(rcon[rconpointer++] ) << 24;
if (KC != 8)
for (i = 1, j = 0; i < KC; i++, j++) tk[i] ^= tk[j];
else {
for (i = 1, j = 0; i < KC / 2; i++, j++) tk[i] ^= tk[j];
tt = tk[KC / 2 - 1];
tk[KC / 2] ^= (S[(tt ) & 0xFF] & 0xFF) ^
(S[(tt >>> 8) & 0xFF] & 0xFF) << 8 ^
(S[(tt >>> 16) & 0xFF] & 0xFF) << 16 ^
(S[(tt >>> 24) ] ) << 24;
for (j = KC / 2, i = j + 1; i < KC; i++, j++) tk[i] ^= tk[j];
}
// copy values into round key arrays
for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
Ke[t / 4][t % 4] = tk[j];
Kd[ROUNDS - (t / 4)][t % 4] = tk[j];
}
}
for (int r = 1; r < ROUNDS; r++) {
// inverse MixColumn where needed
for (j = 0; j < BC; j++) {
tt = Kd[r][j];
Kd[r][j] = U1[(tt >>> 24) & 0xFF] ^
U2[(tt >>> 16) & 0xFF] ^
U3[(tt >>> 8) & 0xFF] ^
U4[ tt & 0xFF];
}
}
// assemble the encryption (Ke) and decryption (Kd) round keys
// and expand them into arrays of ints.
int[] expandedKe = expandToSubKey(Ke, false); // decrypting==false
int[] expandedKd = expandToSubKey(Kd, true); // decrypting==true
ROUNDS_12 = (ROUNDS>=12);
ROUNDS_14 = (ROUNDS==14);
limit = ROUNDS*4;
// store the expanded sub keys into 'sessionK'
sessionK = new int[][] { expandedKe, expandedKd };
}
/**
* Return The number of rounds for a given Rijndael keysize.
*
* @param keySize The size of the user key material in bytes.
* MUST be one of (16, 24, 32).
* @return The number of rounds.
*/
private static int getRounds(int keySize) {
return (keySize >> 2) + 6;
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2002, 2013, 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.security.SecureRandom;
import java.security.InvalidParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KeyGeneratorSpi;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* This class generates a AES key.
*
* @author Valerie Peng
*
*/
public final class AESKeyGenerator extends KeyGeneratorSpi {
private SecureRandom random = null;
private int keySize = 16; // default keysize (in number of bytes)
/**
* Empty constructor.
*/
public AESKeyGenerator() {
}
/**
* Initializes this key generator.
*
* @param random the source of randomness for this generator
*/
protected void engineInit(SecureRandom random) {
this.random = random;
}
/**
* Initializes this key generator with the specified parameter
* set and a user-provided source of randomness.
*
* @param params the key generation parameters
* @param random the source of randomness for this key generator
*
* @exception InvalidAlgorithmParameterException if <code>params</code> is
* inappropriate for this key generator
*/
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidAlgorithmParameterException {
throw new InvalidAlgorithmParameterException
("AES key generation does not take any parameters");
}
/**
* Initializes this key generator for a certain keysize, using the given
* source of randomness.
*
* @param keysize the keysize. This is an algorithm-specific
* metric specified in number of bits.
* @param random the source of randomness for this key generator
*/
protected void engineInit(int keysize, SecureRandom random) {
if (((keysize % 8) != 0) ||
(!AESCrypt.isKeySizeValid(keysize/8))) {
throw new InvalidParameterException
("Wrong keysize: must be equal to 128, 192 or 256");
}
this.keySize = keysize/8;
this.engineInit(random);
}
/**
* Generates the AES key.
*
* @return the new AES key
*/
protected SecretKey engineGenerateKey() {
SecretKeySpec aesKey = null;
if (this.random == null) {
this.random = SunJCE.getRandom();
}
byte[] keyBytes = new byte[keySize];
this.random.nextBytes(keyBytes);
aesKey = new SecretKeySpec(keyBytes, "AES");
return aesKey;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2002, 2011, 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.io.*;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
/**
* This class implements the parameter (IV) used with the AES algorithm
* in feedback-mode. IV is defined in the standards as follows:
*
* <pre>
* IV ::= OCTET STRING -- 16 octets
* </pre>
*
* @author Valerie Peng
*
*/
public final class AESParameters extends AlgorithmParametersSpi {
private BlockCipherParamsCore core;
public AESParameters() {
core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE);
}
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
core.init(paramSpec);
}
protected void engineInit(byte[] encoded)
throws IOException {
core.init(encoded);
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException {
core.init(encoded, decodingMethod);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException {
if (AlgorithmParameterSpec.class.isAssignableFrom(paramSpec)) {
return core.getParameterSpec(paramSpec);
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter Specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
return core.getEncoded();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException {
return core.getEncoded();
}
protected String engineToString() {
return core.toString();
}
}

View File

@@ -0,0 +1,511 @@
/*
* Copyright (c) 2004, 2017, 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);
cipher.init(decrypting, key.getAlgorithm(), key.getEncoded());
}
/**
* 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 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[] 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();
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");
}
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;
}
/**
* 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];
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);
}
}

View File

@@ -0,0 +1,263 @@
/*
* Copyright (c) 2003, 2017, 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.security.*;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.*;
/**
* Implementation of the ARCFOUR cipher, an algorithm apparently compatible
* with RSA Security's RC4(tm) cipher. The description of this algorithm was
* taken from Bruce Schneier's book Applied Cryptography, 2nd ed.,
* section 17.1.
*
* We support keys from 40 to 1024 bits. ARCFOUR would allow for keys shorter
* than 40 bits, but that is too insecure for us to permit.
*
* Note that we subclass CipherSpi directly and do not use the CipherCore
* framework. That was designed to simplify implementation of block ciphers
* and does not offer any advantages for stream ciphers such as ARCFOUR.
*
* @since 1.5
* @author Andreas Sterbenz
*/
public final class ARCFOURCipher extends CipherSpi {
// state array S, 256 entries. The entries are 8-bit, but we use an int[]
// because int arithmetic is much faster than in Java than bytes.
private final int[] S;
// state indices i and j. Called is and js to avoid collision with
// local variables. 'is' is set to -1 after a call to doFinal()
private int is, js;
// the bytes of the last key used (if any)
// we need this to re-initialize after a call to doFinal()
private byte[] lastKey;
// called by the JCE framework
public ARCFOURCipher() {
S = new int[256];
}
// core key setup code. initializes S, is, and js
// assumes key is non-null and between 40 and 1024 bit
private void init(byte[] key) {
// initialize S[i] to i
for (int i = 0; i < 256; i++) {
S[i] = i;
}
// we avoid expanding key to 256 bytes and instead keep a separate
// counter ki = i mod key.length.
for (int i = 0, j = 0, ki = 0; i < 256; i++) {
int Si = S[i];
j = (j + Si + key[ki]) & 0xff;
S[i] = S[j];
S[j] = Si;
ki++;
if (ki == key.length) {
ki = 0;
}
}
// set indices to 0
is = 0;
js = 0;
}
// core crypt code. OFB style, so works for both encryption and decryption
private void crypt(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) {
if (is < 0) {
// doFinal() was called, need to reset the cipher to initial state
init(lastKey);
}
while (inLen-- > 0) {
is = (is + 1) & 0xff;
int Si = S[is];
js = (js + Si) & 0xff;
int Sj = S[js];
S[is] = Sj;
S[js] = Si;
out[outOfs++] = (byte)(in[inOfs++] ^ S[(Si + Sj) & 0xff]);
}
}
// Modes do not make sense with stream ciphers, but allow ECB
// see JCE spec.
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if (mode.equalsIgnoreCase("ECB") == false) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
}
// Padding does not make sense with stream ciphers, but allow NoPadding
// see JCE spec.
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
if (padding.equalsIgnoreCase("NoPadding") == false) {
throw new NoSuchPaddingException("Padding must be NoPadding");
}
}
// Return 0 to indicate stream cipher
// see JCE spec.
protected int engineGetBlockSize() {
return 0;
}
// output length is always the same as input length
// see JCE spec
protected int engineGetOutputSize(int inputLen) {
return inputLen;
}
// no IV, return null
// see JCE spec
protected byte[] engineGetIV() {
return null;
}
// no parameters
// see JCE spec
protected AlgorithmParameters engineGetParameters() {
return null;
}
// see JCE spec
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
init(opmode, key);
}
// see JCE spec
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
}
init(opmode, key);
}
// see JCE spec
protected void engineInit(int opmode, Key key,
AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
}
init(opmode, key);
}
// init method. Check opmode and key, then call init(byte[]).
private void init(int opmode, Key key) throws InvalidKeyException {
if ((opmode < Cipher.ENCRYPT_MODE) || (opmode > Cipher.UNWRAP_MODE)) {
throw new InvalidKeyException("Unknown opmode: " + opmode);
}
lastKey = getEncodedKey(key);
init(lastKey);
}
// return the encoding of key if key is a valid ARCFOUR key.
// otherwise, throw an InvalidKeyException
private static byte[] getEncodedKey(Key key) throws InvalidKeyException {
String keyAlg = key.getAlgorithm();
if (!keyAlg.equals("RC4") && !keyAlg.equals("ARCFOUR")) {
throw new InvalidKeyException("Not an ARCFOUR key: " + keyAlg);
}
if ("RAW".equals(key.getFormat()) == false) {
throw new InvalidKeyException("Key encoding format must be RAW");
}
byte[] encodedKey = key.getEncoded();
if ((encodedKey.length < 5) || (encodedKey.length > 128)) {
throw new InvalidKeyException
("Key length must be between 40 and 1024 bit");
}
return encodedKey;
}
// see JCE spec
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
byte[] out = new byte[inLen];
crypt(in, inOfs, inLen, out, 0);
return out;
}
// see JCE spec
protected int engineUpdate(byte[] in, int inOfs, int inLen,
byte[] out, int outOfs) throws ShortBufferException {
if (out.length - outOfs < inLen) {
throw new ShortBufferException("Output buffer too small");
}
crypt(in, inOfs, inLen, out, outOfs);
return inLen;
}
// see JCE spec
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) {
byte[] out = engineUpdate(in, inOfs, inLen);
is = -1;
return out;
}
// see JCE spec
protected int engineDoFinal(byte[] in, int inOfs, int inLen,
byte[] out, int outOfs) throws ShortBufferException {
int outLen = engineUpdate(in, inOfs, inLen, out, outOfs);
is = -1;
return outLen;
}
// see JCE spec
protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,
InvalidKeyException {
byte[] encoded = key.getEncoded();
if ((encoded == null) || (encoded.length == 0)) {
throw new InvalidKeyException("Could not obtain encoded key");
}
return engineDoFinal(encoded, 0, encoded.length);
}
// see JCE spec
protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
int type) throws InvalidKeyException, NoSuchAlgorithmException {
byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
return ConstructKeys.constructKey(encoded, algorithm, type);
}
// see JCE spec
protected int engineGetKeySize(Key key) throws InvalidKeyException {
byte[] encodedKey = getEncodedKey(key);
return Math.multiplyExact(encodedKey.length, 8);
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2002, 2011, 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.io.*;
import sun.security.util.*;
import sun.misc.HexDumpEncoder;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.spec.IvParameterSpec;
/**
* This class implements the parameter (IV) used with Block Ciphers
* in feedback-mode. IV is defined in the standards as follows:
*
* <pre>
* IV ::= OCTET STRING -- length depends on the block size of the
* block ciphers
* </pre>
*
* @author Valerie Peng
*
*/
final class BlockCipherParamsCore {
private int block_size = 0;
private byte[] iv = null;
BlockCipherParamsCore(int blksize) {
block_size = blksize;
}
void init(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
if (!(paramSpec instanceof IvParameterSpec)) {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
byte[] tmpIv = ((IvParameterSpec)paramSpec).getIV();
if (tmpIv.length != block_size) {
throw new InvalidParameterSpecException("IV not " +
block_size + " bytes long");
}
iv = tmpIv.clone();
}
void init(byte[] encoded) throws IOException {
DerInputStream der = new DerInputStream(encoded);
byte[] tmpIv = der.getOctetString();
if (der.available() != 0) {
throw new IOException("IV parsing error: extra data");
}
if (tmpIv.length != block_size) {
throw new IOException("IV not " + block_size +
" bytes long");
}
iv = tmpIv;
}
void init(byte[] encoded, String decodingMethod)
throws IOException {
if ((decodingMethod != null) &&
(!decodingMethod.equalsIgnoreCase("ASN.1"))) {
throw new IllegalArgumentException("Only support ASN.1 format");
}
init(encoded);
}
<T extends AlgorithmParameterSpec> T getParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException
{
if (IvParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast(new IvParameterSpec(this.iv));
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
}
byte[] getEncoded() throws IOException {
DerOutputStream out = new DerOutputStream();
out.putOctetString(this.iv);
return out.toByteArray();
}
byte[] getEncoded(String encodingMethod)
throws IOException {
return getEncoded();
}
/*
* Returns a formatted string describing the parameters.
*/
public String toString() {
String LINE_SEP = System.getProperty("line.separator");
String ivString = LINE_SEP + " iv:" + LINE_SEP + "[";
HexDumpEncoder encoder = new HexDumpEncoder();
ivString += encoder.encodeBuffer(this.iv);
ivString += "]" + LINE_SEP;
return ivString;
}
}

View File

@@ -0,0 +1,428 @@
/*
* Copyright (c) 1998, 2017, 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.security.*;
import java.security.spec.*;
import sun.security.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.BadPaddingException;
/**
* This class implements the Blowfish algorithm in its various modes
* (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, <code>CBC</code>,
* <code>PCBC</code>) and padding schemes (<code>PKCS5Padding</code>,
* <code>NoPadding</code>, <code>ISO10126Padding</code>).
*
* <p> Blowfish is a 64-bit block cipher with a variable-length key.
*
* @author Jan Luehe
*
*
* @see BlowfishCrypt
* @see CipherBlockChaining
* @see ElectronicCodeBook
* @see CipherFeedback
* @see OutputFeedback
*/
public final class BlowfishCipher extends CipherSpi {
/*
* internal CipherCore object which does the real work.
*/
private CipherCore core = null;
/**
* Creates an instance of Blowfish cipher with default ECB mode and
* PKCS5Padding.
*/
public BlowfishCipher() {
core = new CipherCore(new BlowfishCrypt(),
BlowfishConstants.BLOWFISH_BLOCK_SIZE);
}
/**
* Sets the mode of this cipher.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode does
* not exist
*/
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
core.setMode(mode);
}
/**
* Sets the padding mechanism of this cipher.
*
* @param paddingScheme the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* does not exist
*/
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
core.setPadding(paddingScheme);
}
/**
* Returns the block size (in bytes).
*
* @return the block size (in bytes), or 0 if the underlying algorithm is
* not a block cipher
*/
protected int engineGetBlockSize() {
return BlowfishConstants.BLOWFISH_BLOCK_SIZE;
}
/**
* Returns the length in bytes that an output buffer would need to be in
* order to hold the result of the next <code>update</code> or
* <code>doFinal</code> operation, given the input length
* <code>inputLen</code> (in bytes).
*
* <p>This call takes into account any unprocessed (buffered) data from a
* previous <code>update</code> call, and padding.
*
* <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) {
return core.getOutputSize(inputLen);
}
/**
* Returns the initialization vector (IV) in a new buffer.
*
* <p>This is useful in the case where a random IV has been created
* (see <a href = "#init">init</a>),
* or in the context of password-based encryption or
* decryption, where the IV is derived from a user-supplied password.
*
* @return the initialization vector in a new buffer, or null if the
* underlying algorithm does not use an IV, or if the IV has not yet
* been set.
*/
protected byte[] engineGetIV() {
return core.getIV();
}
/**
* Returns the parameters used with this cipher.
*
* <p>The returned parameters may be the same that were used to initialize
* this cipher, or may contain the default set of parameters or a set of
* randomly generated parameters used by the underlying cipher
* implementation (provided that the underlying cipher implementation
* uses a default set of parameters or creates new parameters if it needs
* parameters but was not initialized with any).
*
* @return the parameters used with this cipher, or null if this cipher
* does not use any parameters.
*/
protected AlgorithmParameters engineGetParameters() {
return core.getParameters("Blowfish");
}
/**
* Initializes this cipher with a key and a source of randomness.
*
* <p>The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher requires an initialization vector (IV), it will get
* it from <code>random</code>.
* This behaviour should only be used in encryption or key wrapping
* mode, however.
* When initializing a cipher that requires an IV for decryption or
* key unwrapping, the IV
* (same IV that was used for encryption or key wrapping) must be provided
* explicitly as a
* parameter, in order to get the correct result.
*
* <p>This method also cleans existing buffer and other related state
* information.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @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 {
core.init(opmode, key, random);
}
/**
* Initializes this cipher with a key, a set of
* algorithm parameters, and a source of randomness.
*
* <p>The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @param key the encryption key
* @param params the algorithm parameters
* @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 are inappropriate for this cipher
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in a new buffer.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized)
*/
protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) {
return core.update(input, inputOffset, inputLen);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
*/
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
return core.update(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in a new buffer.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
return core.doFinal(input, inputOffset, inputLen);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
return core.doFinal(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Returns the key size of the given key object.
*
* @param key the key object.
*
* @return the key size of the given key object.
*
* @exception InvalidKeyException if <code>key</code> is invalid.
*/
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return Math.multiplyExact(key.getEncoded().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 {
return core.wrap(key);
}
/**
* Unwrap a previously wrapped key.
*
* @param wrappedKey the key to be unwrapped.
*
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
*
* @param wrappedKeyType the type of the wrapped key.
* This is one of <code>Cipher.SECRET_KEY</code>,
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
*
* @return the unwrapped key.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys of type <code>wrappedKeyType</code> for the
* <code>wrappedKeyAlgorithm</code>.
*
* @exception InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key of type <code>wrappedKeyType</code> for
* the <code>wrappedKeyAlgorithm</code>.
*/
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 1998, 2007, 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;
/**
* This class defines the constants used by the Blowfish algorithm
* implementation.
*
* @author Jan Luehe
*
* @see BlowfishCipher
* @see BlowfishCrypt
*/
interface BlowfishConstants {
int BLOWFISH_BLOCK_SIZE = 8; // number of bytes
int BLOWFISH_MAX_KEYSIZE = 56; // number of bytes
}

View File

@@ -0,0 +1,558 @@
/*
* Copyright (c) 1998, 2007, 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.security.InvalidKeyException;
/**
* This is the internal Blowfish class responsible for encryption and
* decryption of a byte array of size <code>BLOWFISH_BLOCK_SIZE</code>.
*
* @author Jan Luehe
* @author David Brownell
*
* @see BlowfishCipher
*/
final class BlowfishCrypt extends SymmetricCipher
implements BlowfishConstants {
/*
* Are we encrypting or decrypting?
*/
private boolean decrypting = false;
/**
* Gets this cipher's block size.
*
* @return this cipher's block size
*/
int getBlockSize() {
return BLOWFISH_BLOCK_SIZE;
}
void init(boolean decrypting, String algorithm, byte[] rawKey)
throws InvalidKeyException {
this.decrypting = decrypting;
if (!algorithm.equalsIgnoreCase("Blowfish")) {
throw new InvalidKeyException("Wrong algorithm: Blowfish required");
}
if (rawKey.length > BLOWFISH_MAX_KEYSIZE) {
throw new InvalidKeyException("Key too long (> 448 bits)");
}
// Step 1: Init P and then S arrays from pi bytes
int i, j, count;
System.arraycopy(pi, 0, p, 0, 18);
System.arraycopy(pi, 18, s0, 0, 256);
System.arraycopy(pi, 18 + 256, s1, 0, 256);
System.arraycopy(pi, 18 + 512, s2, 0, 256);
System.arraycopy(pi, 18 + 768, s3, 0, 256);
// Step 2: XOR all parts of P with key data
int tmp = 0;
int nLen = rawKey.length;
int nKeyPos = 0;
for (i = 0; i < 18; i++) {
for (j = 0; j < 4; j++) {
tmp <<= 8;
tmp |= 0x0ff & rawKey[nKeyPos];
if (++nKeyPos == nLen) nKeyPos = 0;
}
p[i] ^= tmp;
}
// Steps 3-7: Replace all P, S values with computed values
int[] data = new int[2];
for (i = 0; i < 18; i+=2) {
encryptBlock(data);
p[i] = data[0];
p[i+1] = data[1];
}
for (j = 0; j < 256; j+=2) {
encryptBlock(data);
s0[j] = data[0];
s0[j+1] = data[1];
}
for (j = 0; j < 256; j+=2) {
encryptBlock(data);
s1[j] = data[0];
s1[j+1] = data[1];
}
for (j = 0; j < 256; j+=2) {
encryptBlock(data);
s2[j] = data[0];
s2[j+1] = data[1];
}
for (j = 0; j < 256; j+=2) {
encryptBlock(data);
s3[j] = data[0];
s3[j+1] = data[1];
}
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + len - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
*/
void encryptBlock(byte[] plain, int plainOffset,
byte[] cipher, int cipherOffset)
{
cipherBlock(plain, plainOffset, cipher, cipherOffset);
}
/**
* Performs decryption operation.
*
* <p>The input cipher text <code>cipher</code>, starting at
* <code>cipherOffset</code> and ending at
* <code>(cipherOffset + len - 1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param cipher the buffer with the input data to be decrypted
* @param cipherOffset the offset in <code>cipherOffset</code>
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
*/
void decryptBlock(byte[] cipher, int cipherOffset,
byte[] plain, int plainOffset)
{
cipherBlock(cipher, cipherOffset, plain, plainOffset);
}
/**
* Encrypts, or decrypts, the blocks of data passed in.
*/
private void cipherBlock(byte[] in, int inOffset,
byte[] out, int outOffset) {
temp[0] = ((in[inOffset ] ) << 24) |
((in[inOffset + 1] & 0xff) << 16) |
((in[inOffset + 2] & 0xff) << 8) |
((in[inOffset + 3] & 0xff) );
temp[1] = ((in[inOffset + 4] ) << 24) |
((in[inOffset + 5] & 0xff) << 16) |
((in[inOffset + 6] & 0xff) << 8) |
((in[inOffset + 7] & 0xff) );
if (decrypting) {
decryptBlock(temp);
} else {
encryptBlock(temp);
}
int t = temp[0];
out[outOffset ] = (byte)(t >> 24);
out[outOffset + 1] = (byte)(t >> 16);
out[outOffset + 2] = (byte)(t >> 8);
out[outOffset + 3] = (byte)(t );
t = temp[1];
out[outOffset + 4] = (byte)(t >> 24);
out[outOffset + 5] = (byte)(t >> 16);
out[outOffset + 6] = (byte)(t >> 8);
out[outOffset + 7] = (byte)(t );
}
/**
* Encrypts a single block, in place.
*/
private void encryptBlock(int[] value) {
int left = value[0];
int right = value[1];
left ^= p[0];
right ^= F(left) ^ p[1];
left ^= F(right) ^ p[2];
right ^= F(left) ^ p[3];
left ^= F(right) ^ p[4];
right ^= F(left) ^ p[5];
left ^= F(right) ^ p[6];
right ^= F(left) ^ p[7];
left ^= F(right) ^ p[8];
right ^= F(left) ^ p[9];
left ^= F(right) ^ p[10];
right ^= F(left) ^ p[11];
left ^= F(right) ^ p[12];
right ^= F(left) ^ p[13];
left ^= F(right) ^ p[14];
right ^= F(left) ^ p[15];
left ^= F(right) ^ p[16];
right ^= p[17];
value[0] = right;
value[1] = left;
}
/**
* Decrypts a single block, in place.
*/
private void decryptBlock(int[] value) {
int left = value[1];
int right = value[0];
right ^= p[17];
left ^= p[16] ^ F(right);
right ^= p[15] ^ F(left);
left ^= p[14] ^ F(right);
right ^= p[13] ^ F(left);
left ^= p[12] ^ F(right);
right ^= p[11] ^ F(left);
left ^= p[10] ^ F(right);
right ^= p[9] ^ F(left);
left ^= p[8] ^ F(right);
right ^= p[7] ^ F(left);
left ^= p[6] ^ F(right);
right ^= p[5] ^ F(left);
left ^= p[4] ^ F(right);
right ^= p[3] ^ F(left);
left ^= p[2] ^ F(right);
right ^= p[1] ^ F(left);
left ^= p[0];
value[0] = left;
value[1] = right;
}
/**
* Calculates the S-Box function F().
*
* This gets used "rounds" times on each encryption/decryption.
*/
private int F(int v) {
return (( s0[ v >>> 24 ]
+ s1[(v >> 16) & 0xff])
^ s2[(v >> 8) & 0xff])
+ s3[ v & 0xff];
}
private final int[] p = new int[18]; // subkeys
private final int[] s0 = new int[256]; // s-boxes
private final int[] s1 = new int[256];
private final int[] s2 = new int[256];
private final int[] s3 = new int[256];
private final int[] temp = new int[2]; // to avoid encrypt/decrypt mallocs
// many digits of pi, for initializing p and s
private static final int[] pi = {
// p [rounds + 2]
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
0x9216d5d9, 0x8979fb1b,
// s [4][256]
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
};
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 1998, 2013, 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.security.SecureRandom;
import java.security.InvalidParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KeyGeneratorSpi;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* This class generates a secret key for use with the Blowfish algorithm.
*
* @author Jan Luehe
*
*/
public final class BlowfishKeyGenerator extends KeyGeneratorSpi {
private SecureRandom random = null;
private int keysize = 16; // default keysize (in number of bytes)
/**
* Empty constructor
*/
public BlowfishKeyGenerator() {
}
/**
* Initializes this key generator.
*
* @param random the source of randomness for this generator
*/
protected void engineInit(SecureRandom random) {
this.random = random;
}
/**
* Initializes this key generator with the specified parameter
* set and a user-provided source of randomness.
*
* @param params the key generation parameters
* @param random the source of randomness for this key generator
*
* @exception InvalidAlgorithmParameterException if <code>params</code> is
* inappropriate for this key generator
*/
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidAlgorithmParameterException
{
throw new InvalidAlgorithmParameterException
("Blowfish key generation does not take any parameters");
}
/**
* Initializes this key generator for a certain keysize, using the given
* source of randomness.
*
* @param keysize the keysize. This is an algorithm-specific
* metric specified in number of bits.
* @param random the source of randomness for this key generator
*/
protected void engineInit(int keysize, SecureRandom random) {
if (((keysize % 8) != 0) || (keysize < 32) || (keysize > 448)) {
throw new InvalidParameterException("Keysize must be "
+ "multiple of 8, and can "
+ "only range from 32 to 448 "
+ "(inclusive)");
}
this.keysize = keysize / 8;
this.engineInit(random);
}
/**
* Generates a Blowfish key.
*
* @return the new Blowfish key
*/
protected SecretKey engineGenerateKey() {
if (this.random == null) {
this.random = SunJCE.getRandom();
}
byte[] keyBytes = new byte[this.keysize];
this.random.nextBytes(keyBytes);
return new SecretKeySpec(keyBytes, "Blowfish");
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 1998, 2011, 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.io.*;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
/**
* This class implements the parameter (IV) used with the Blowfish algorithm in
* feedback-mode. IV is defined in the standards as follows:
*
* <pre>
* IV ::= OCTET STRING -- 8 octets
* </pre>
*
* @author Jan Luehe
*
*/
public final class BlowfishParameters extends AlgorithmParametersSpi {
private BlockCipherParamsCore core;
public BlowfishParameters() {
core = new BlockCipherParamsCore
(BlowfishConstants.BLOWFISH_BLOCK_SIZE);
}
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
core.init(paramSpec);
}
protected void engineInit(byte[] encoded)
throws IOException {
core.init(encoded);
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException {
core.init(encoded, decodingMethod);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException {
if (AlgorithmParameterSpec.class.isAssignableFrom(paramSpec)) {
return core.getParameterSpec(paramSpec);
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter Specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
return core.getEncoded();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException {
return core.getEncoded();
}
protected String engineToString() {
return core.toString();
}
}

View File

@@ -0,0 +1,216 @@
/*
* Copyright (c) 1997, 2018, 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.security.InvalidKeyException;
import java.security.ProviderException;
import java.util.Objects;
/**
* This class represents ciphers in cipher block chaining (CBC) mode.
*
* <p>This mode is implemented independently of a particular cipher.
* Ciphers to which this mode should apply (e.g., DES) must be
* <i>plugged-in</i> using the constructor.
*
* <p>NOTE: This class does not deal with buffering or padding.
*
* @author Gigi Ankeny
*/
class CipherBlockChaining extends FeedbackCipher {
/*
* random bytes that are initialized with iv
*/
protected byte[] r;
/*
* output buffer
*/
private byte[] k;
// variables for save/restore calls
private byte[] rSave = null;
CipherBlockChaining(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
k = new byte[blockSize];
r = new byte[blockSize];
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>CBC</code>
*/
String getFeedback() {
return "CBC";
}
/**
* 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
*/
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException {
if ((key == null) || (iv == null) || (iv.length != blockSize)) {
throw new InvalidKeyException("Internal error");
}
this.iv = iv;
reset();
embeddedCipher.init(decrypting, algorithm, key);
}
/**
* 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).
*/
void reset() {
System.arraycopy(iv, 0, r, 0, blockSize);
}
/**
* Save the current content of this cipher.
*/
void save() {
if (rSave == null) {
rSave = new byte[blockSize];
}
System.arraycopy(r, 0, rSave, 0, blockSize);
}
/**
* Restores the content of this cipher to the previous saved one.
*/
void restore() {
System.arraycopy(rSave, 0, r, 0, blockSize);
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + plainLen - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @exception ProviderException if <code>len</code> is not
* a multiple of the block size
* @return the length of the encrypted data
*/
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) {
if (plainLen <= 0) {
return plainLen;
}
RangeUtil.blockSizeCheck(plainLen, blockSize);
RangeUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
RangeUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
return implEncrypt(plain, plainOffset, plainLen,
cipher, cipherOffset);
}
private int implEncrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
{
int endIndex = plainOffset + plainLen;
for (; plainOffset < endIndex;
plainOffset += blockSize, cipherOffset += blockSize) {
for (int i = 0; i < blockSize; i++) {
k[i] = (byte)(plain[i + plainOffset] ^ r[i]);
}
embeddedCipher.encryptBlock(k, 0, cipher, cipherOffset);
System.arraycopy(cipher, cipherOffset, r, 0, blockSize);
}
return plainLen;
}
/**
* Performs decryption operation.
*
* <p>The input cipher text <code>cipher</code>, starting at
* <code>cipherOffset</code> and ending at
* <code>(cipherOffset + cipherLen - 1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* <p>It is also the application's responsibility to make sure that
* <code>init</code> has been called before this method is called.
* (This check is omitted here, to avoid double checking.)
*
* @param cipher the buffer with the input data to be decrypted
* @param cipherOffset the offset in <code>cipherOffset</code>
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
* @exception ProviderException if <code>len</code> is not
* a multiple of the block size
* @return the length of the decrypted data
*/
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) {
if (cipherLen <= 0) {
return cipherLen;
}
RangeUtil.blockSizeCheck(cipherLen, blockSize);
RangeUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen);
RangeUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen);
return implDecrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
}
private int implDecrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
{
int endIndex = cipherOffset + cipherLen;
for (; cipherOffset < endIndex;
cipherOffset += blockSize, plainOffset += blockSize) {
embeddedCipher.decryptBlock(cipher, cipherOffset, k, 0);
for (int i = 0; i < blockSize; i++) {
plain[i + plainOffset] = (byte)(k[i] ^ r[i]);
}
System.arraycopy(cipher, cipherOffset, r, 0, blockSize);
}
return cipherLen;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,285 @@
/*
* Copyright (c) 1997, 2018, 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.security.InvalidKeyException;
import java.security.ProviderException;
/**
* This class represents ciphers in cipher-feedback (CFB) mode.
*
* <p>This mode is implemented independently of a particular cipher.
* Ciphers to which this mode should apply (e.g., DES) must be
* <i>plugged-in</i> using the constructor.
*
* <p>NOTE: This class does not deal with buffering or padding.
*
* @author Gigi Ankeny
*/
final class CipherFeedback extends FeedbackCipher {
/*
* encrypt/decrypt output buffer
*/
private final byte[] k;
/*
* register value, initialized with iv
*/
private final byte[] register;
/*
* number of bytes for each stream unit, defaults to the blocksize
* of the embedded cipher
*/
private int numBytes;
// variables for save/restore calls
private byte[] registerSave = null;
CipherFeedback(SymmetricCipher embeddedCipher, int numBytes) {
super(embeddedCipher);
if (numBytes > blockSize) {
numBytes = blockSize;
}
this.numBytes = numBytes;
k = new byte[blockSize];
register = new byte[blockSize];
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>CFB</code>
*/
String getFeedback() {
return "CFB";
}
/**
* 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
*/
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException {
if ((key == null) || (iv == null) || (iv.length != blockSize)) {
throw new InvalidKeyException("Internal error");
}
this.iv = iv;
reset();
// always encrypt mode for embedded cipher
embeddedCipher.init(false, algorithm, key);
}
/**
* 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).
*/
void reset() {
System.arraycopy(iv, 0, register, 0, blockSize);
}
/**
* Save the current content of this cipher.
*/
void save() {
if (registerSave == null) {
registerSave = new byte[blockSize];
}
System.arraycopy(register, 0, registerSave, 0, blockSize);
}
/**
* Restores the content of this cipher to the previous saved one.
*/
void restore() {
System.arraycopy(registerSave, 0, register, 0, blockSize);
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + plainLen - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @exception ProviderException if <code>plainLen</code> is not
* a multiple of the <code>numBytes</code>
* @return the length of the encrypted data
*/
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) {
RangeUtil.blockSizeCheck(plainLen, numBytes);
RangeUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
RangeUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
int nShift = blockSize - numBytes;
int loopCount = plainLen / numBytes;
for (; loopCount > 0 ;
plainOffset += numBytes, cipherOffset += numBytes,
loopCount--) {
embeddedCipher.encryptBlock(register, 0, k, 0);
if (nShift != 0) {
System.arraycopy(register, numBytes, register, 0, nShift);
}
for (int i = 0; i < numBytes; i++) {
register[nShift + i] = cipher[i + cipherOffset] =
(byte)(k[i] ^ plain[i + plainOffset]);
}
}
return plainLen;
}
/**
* Performs the last encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + plainLen - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @return the number of bytes placed into <code>cipher</code>
*/
int encryptFinal(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) {
int oddBytes = plainLen % numBytes;
int len = encrypt(plain, plainOffset, (plainLen - oddBytes),
cipher, cipherOffset);
plainOffset += len;
cipherOffset += len;
if (oddBytes != 0) {
embeddedCipher.encryptBlock(register, 0, k, 0);
for (int i = 0; i < oddBytes; i++) {
cipher[i + cipherOffset] =
(byte)(k[i] ^ plain[i + plainOffset]);
}
}
return plainLen;
}
/**
* Performs decryption operation.
*
* <p>The input cipher text <code>cipher</code>, starting at
* <code>cipherOffset</code> and ending at
* <code>(cipherOffset + cipherLen - 1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* @param cipher the buffer with the input data to be decrypted
* @param cipherOffset the offset in <code>cipherOffset</code>
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
* @exception ProviderException if <code>cipherLen</code> is not
* a multiple of the <code>numBytes</code>
* @return the length of the decrypted data
*/
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) {
RangeUtil.blockSizeCheck(cipherLen, numBytes);
RangeUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen);
RangeUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen);
int nShift = blockSize - numBytes;
int loopCount = cipherLen / numBytes;
for (; loopCount > 0;
plainOffset += numBytes, cipherOffset += numBytes,
loopCount--) {
embeddedCipher.encryptBlock(register, 0, k, 0);
if (nShift != 0) {
System.arraycopy(register, numBytes, register, 0, nShift);
}
for (int i = 0; i < numBytes; i++) {
register[i + nShift] = cipher[i + cipherOffset];
plain[i + plainOffset]
= (byte)(cipher[i + cipherOffset] ^ k[i]);
}
}
return cipherLen;
}
/**
* Performs the last decryption operation.
*
* <p>The input cipher text <code>cipher</code>, starting at
* <code>cipherOffset</code> and ending at
* <code>(cipherOffset + cipherLen - 1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* @param cipher the buffer with the input data to be decrypted
* @param cipherOffset the offset in <code>cipherOffset</code>
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
* @return the length of the decrypted data
*/
int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) {
int oddBytes = cipherLen % numBytes;
int len = decrypt(cipher, cipherOffset, (cipherLen - oddBytes),
plain, plainOffset);
cipherOffset += len;
plainOffset += len;
if (oddBytes != 0) {
embeddedCipher.encryptBlock(register, 0, k, 0);
for (int i = 0; i < oddBytes; i++) {
plain[i + plainOffset]
= (byte)(cipher[i + cipherOffset] ^ k[i]);
}
}
return cipherLen;
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (c) 2004, 2013, 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 javax.crypto.IllegalBlockSizeException;
import javax.crypto.ShortBufferException;
/**
* This class represents ciphers in cipher text stealing (CTS) mode.
* <br>CTS provides a way to allow block ciphers to operate on partial
* blocks without padding, and all bits of the message go through
* the encryption algorithm, rather than simply being XOR'd.
* <br>More details can be found in RFC 2040 section 8 "Description
* of RC5-CTS".
*
* <p>This mode is implemented independently of a particular cipher.
* Ciphers to which this mode should apply (e.g., DES) must be
* <i>plugged-in</i> using the constructor.
*
* <p>NOTE#1: CTS requires the input data to be at least one block
* long. Thus, callers of this class has to buffer the input data
* to make sure the input data passed to encryptFinal()/decryptFinal()
* is not shorter than a block.
* <p>NOTE#2: This class does not deal with buffering or padding
* just like all other cipher mode implementations.
*
* @author Valerie Peng
*/
final class CipherTextStealing extends CipherBlockChaining {
CipherTextStealing(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>CBC</code>
*/
String getFeedback() {
return "CTS";
}
/**
* Performs the last encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + len - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* <p>It is the application's responsibility to make sure that
* <code>plainLen</code> is a multiple of the embedded cipher's block size,
* as any excess bytes are ignored.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @return the number of bytes placed into <code>cipher</code>
*/
int encryptFinal(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
throws IllegalBlockSizeException {
if (plainLen < blockSize) {
throw new IllegalBlockSizeException("input is too short!");
} else if (plainLen == blockSize) {
encrypt(plain, plainOffset, plainLen, cipher, cipherOffset);
} else {
// number of bytes in the last block
int nLeft = plainLen % blockSize;
if (nLeft == 0) {
encrypt(plain, plainOffset, plainLen, cipher, cipherOffset);
// swap the last two blocks after encryption
int lastBlkIndex = cipherOffset + plainLen - blockSize;
int nextToLastBlkIndex = lastBlkIndex - blockSize;
byte[] tmp = new byte[blockSize];
System.arraycopy(cipher, lastBlkIndex, tmp, 0, blockSize);
System.arraycopy(cipher, nextToLastBlkIndex,
cipher, lastBlkIndex, blockSize);
System.arraycopy(tmp, 0, cipher, nextToLastBlkIndex,
blockSize);
} else {
int newPlainLen = plainLen - (blockSize + nLeft);
if (newPlainLen > 0) {
encrypt(plain, plainOffset, newPlainLen, cipher,
cipherOffset);
plainOffset += newPlainLen;
cipherOffset += newPlainLen;
}
// Do final CTS step for last two blocks (the second of which
// may or may not be incomplete).
byte[] tmp = new byte[blockSize];
// now encrypt the next-to-last block
for (int i = 0; i < blockSize; i++) {
tmp[i] = (byte) (plain[plainOffset+i] ^ r[i]);
}
byte[] tmp2 = new byte[blockSize];
embeddedCipher.encryptBlock(tmp, 0, tmp2, 0);
System.arraycopy(tmp2, 0, cipher,
cipherOffset+blockSize, nLeft);
// encrypt the last block
for (int i=0; i<nLeft; i++) {
tmp2[i] = (byte)
(plain[plainOffset+blockSize+i] ^ tmp2[i]);
}
embeddedCipher.encryptBlock(tmp2, 0, cipher, cipherOffset);
}
}
return plainLen;
}
/**
* Performs decryption operation.
*
* <p>The input cipher text <code>cipher</code>, starting at
* <code>cipherOffset</code> and ending at
* <code>(cipherOffset + len - 1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* <p>It is the application's responsibility to make sure that
* <code>cipherLen</code> is a multiple of the embedded cipher's block
* size, as any excess bytes are ignored.
*
* <p>It is also the application's responsibility to make sure that
* <code>init</code> has been called before this method is called.
* (This check is omitted here, to avoid double checking.)
*
* @param cipher the buffer with the input data to be decrypted
* @param cipherOffset the offset in <code>cipherOffset</code>
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
* @return the number of bytes placed into <code>plain</code>
*/
int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
throws IllegalBlockSizeException {
if (cipherLen < blockSize) {
throw new IllegalBlockSizeException("input is too short!");
} else if (cipherLen == blockSize) {
decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
} else {
// number of bytes in the last block
int nLeft = cipherLen % blockSize;
if (nLeft == 0) {
// swap the last two blocks before decryption
int lastBlkIndex = cipherOffset + cipherLen - blockSize;
int nextToLastBlkIndex =
cipherOffset + cipherLen - 2*blockSize;
byte[] tmp = new byte[2*blockSize];
System.arraycopy(cipher, lastBlkIndex, tmp, 0, blockSize);
System.arraycopy(cipher, nextToLastBlkIndex,
tmp, blockSize, blockSize);
int cipherLen2 = cipherLen-2*blockSize;
decrypt(cipher, cipherOffset, cipherLen2, plain, plainOffset);
decrypt(tmp, 0, 2*blockSize, plain, plainOffset+cipherLen2);
} else {
int newCipherLen = cipherLen-(blockSize+nLeft);
if (newCipherLen > 0) {
decrypt(cipher, cipherOffset, newCipherLen, plain,
plainOffset);
cipherOffset += newCipherLen;
plainOffset += newCipherLen;
}
// Do final CTS step for last two blocks (the second of which
// may or may not be incomplete).
// now decrypt the next-to-last block
byte[] tmp = new byte[blockSize];
embeddedCipher.decryptBlock(cipher, cipherOffset, tmp, 0);
for (int i = 0; i < nLeft; i++) {
plain[plainOffset+blockSize+i] =
(byte) (cipher[cipherOffset+blockSize+i] ^ tmp[i]);
}
// decrypt the last block
System.arraycopy(cipher, cipherOffset+blockSize, tmp, 0,
nLeft);
embeddedCipher.decryptBlock(tmp, 0, plain, plainOffset);
//System.arraycopy(r, 0, tmp, 0, r.length);
for (int i=0; i<blockSize; i++) {
plain[plainOffset+i] = (byte)
(plain[plainOffset+i]^r[i]);
}
}
}
return cipherLen;
}
}

View File

@@ -0,0 +1,259 @@
/*
* Copyright (c) 1999, 2013, 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.security.Key;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.KeyFactory;
import java.security.InvalidKeyException;
import java.security.NoSuchProviderException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.SecretKey;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.BadPaddingException;
import javax.crypto.spec.SecretKeySpec;
/**
* This class entends the javax.crypto.CipherSpi class with a concrete
* implementation of the methods for wrapping and unwrapping
* keys.
*
* @author Sharon Liu
*
*
* @see javax.crypto.CipherSpi
* @see BlowfishCipher
* @see DESCipher
* @see PBEWithMD5AndDESCipher
*/
public abstract class CipherWithWrappingSpi extends CipherSpi {
/**
* 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 final byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException
{
byte[] result = null;
try {
byte[] encodedKey = key.getEncoded();
if ((encodedKey == null) || (encodedKey.length == 0)) {
throw new InvalidKeyException("Cannot get an encoding of " +
"the key to be wrapped");
}
result = engineDoFinal(encodedKey, 0, encodedKey.length);
} catch (BadPaddingException e) {
// Should never happen
}
return result;
}
/**
* 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 InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key, or if the algorithm associated with the
* wrapped key is different from <code>wrappedKeyAlgorithm</code>
* and/or its key type is different from <code>wrappedKeyType</code>.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys for the <code>wrappedKeyAlgorithm</code>.
*/
protected final Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException
{
byte[] encodedKey;
Key result = null;
try {
encodedKey = engineDoFinal(wrappedKey, 0,
wrappedKey.length);
} catch (BadPaddingException ePadding) {
throw new InvalidKeyException();
} catch (IllegalBlockSizeException eBlockSize) {
throw new InvalidKeyException();
}
switch (wrappedKeyType) {
case Cipher.SECRET_KEY:
result = constructSecretKey(encodedKey,
wrappedKeyAlgorithm);
break;
case Cipher.PRIVATE_KEY:
result = constructPrivateKey(encodedKey,
wrappedKeyAlgorithm);
break;
case Cipher.PUBLIC_KEY:
result = constructPublicKey(encodedKey,
wrappedKeyAlgorithm);
break;
}
return result;
}
/**
* Construct a public key from its encoding.
*
* @param encodedKey the encoding of a public key.
*
* @param encodedKeyAlgorithm the algorithm the encodedKey is for.
*
* @return a public key constructed from the encodedKey.
*/
private final PublicKey constructPublicKey(byte[] encodedKey,
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException
{
PublicKey key = null;
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm,
SunJCE.getInstance());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
key = keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException nsae) {
// Try to see whether there is another
// provider which supports this algorithm
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm);
X509EncodedKeySpec keySpec =
new X509EncodedKeySpec(encodedKey);
key = keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException nsae2) {
throw new NoSuchAlgorithmException("No installed providers " +
"can create keys for the " +
encodedKeyAlgorithm +
"algorithm");
} catch (InvalidKeySpecException ikse2) {
// Should never happen.
}
} catch (InvalidKeySpecException ikse) {
// Should never happen.
}
return key;
}
/**
* Construct a private key from its encoding.
*
* @param encodedKey the encoding of a private key.
*
* @param encodedKeyAlgorithm the algorithm the wrapped key is for.
*
* @return a private key constructed from the encodedKey.
*/
private final PrivateKey constructPrivateKey(byte[] encodedKey,
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException
{
PrivateKey key = null;
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
// provider which supports this algorithm
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm);
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(encodedKey);
key = keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException nsae2) {
throw new NoSuchAlgorithmException("No installed providers " +
"can create keys for the " +
encodedKeyAlgorithm +
"algorithm");
} catch (InvalidKeySpecException ikse2) {
// Should never happen.
}
} catch (InvalidKeySpecException ikse) {
// Should never happen.
}
return key;
}
/**
* Construct a secret key from its encoding.
*
* @param encodedKey the encoding of a secret key.
*
* @param encodedKeyAlgorithm the algorithm the secret key is for.
*
* @return a secret key constructed from the encodedKey.
*/
private final SecretKey constructSecretKey(byte[] encodedKey,
String encodedKeyAlgorithm)
{
return (new SecretKeySpec(encodedKey, encodedKeyAlgorithm));
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 1999, 2013, 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.security.Key;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.KeyFactory;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
* This class is a helper class which construct key objects
* from encoded keys.
*
* @author Sharon Liu
*
*/
final class ConstructKeys {
/**
* Construct a public key from its encoding.
*
* @param encodedKey the encoding of a public key.
*
* @param encodedKeyAlgorithm the algorithm the encodedKey is for.
*
* @return a public key constructed from the encodedKey.
*/
private static final PublicKey constructPublicKey(byte[] encodedKey,
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException
{
PublicKey key = null;
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm,
SunJCE.getInstance());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
key = keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException nsae) {
// Try to see whether there is another
// provider which supports this algorithm
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm);
X509EncodedKeySpec keySpec =
new X509EncodedKeySpec(encodedKey);
key = keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException nsae2) {
throw new NoSuchAlgorithmException("No installed providers " +
"can create keys for the " +
encodedKeyAlgorithm +
"algorithm");
} catch (InvalidKeySpecException ikse2) {
InvalidKeyException ike =
new InvalidKeyException("Cannot construct public key");
ike.initCause(ikse2);
throw ike;
}
} catch (InvalidKeySpecException ikse) {
InvalidKeyException ike =
new InvalidKeyException("Cannot construct public key");
ike.initCause(ikse);
throw ike;
}
return key;
}
/**
* Construct a private key from its encoding.
*
* @param encodedKey the encoding of a private key.
*
* @param encodedKeyAlgorithm the algorithm the wrapped key is for.
*
* @return a private key constructed from the encodedKey.
*/
private static final PrivateKey constructPrivateKey(byte[] encodedKey,
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException
{
PrivateKey key = null;
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
// provider which supports this algorithm
try {
KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm);
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(encodedKey);
key = keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException nsae2) {
throw new NoSuchAlgorithmException("No installed providers " +
"can create keys for the " +
encodedKeyAlgorithm +
"algorithm");
} catch (InvalidKeySpecException ikse2) {
InvalidKeyException ike =
new InvalidKeyException("Cannot construct private key");
ike.initCause(ikse2);
throw ike;
}
} catch (InvalidKeySpecException ikse) {
InvalidKeyException ike =
new InvalidKeyException("Cannot construct private key");
ike.initCause(ikse);
throw ike;
}
return key;
}
/**
* Construct a secret key from its encoding.
*
* @param encodedKey the encoding of a secret key.
*
* @param encodedKeyAlgorithm the algorithm the secret key is for.
*
* @return a secret key constructed from the encodedKey.
*/
private static final SecretKey constructSecretKey(byte[] encodedKey,
String encodedKeyAlgorithm)
{
return (new SecretKeySpec(encodedKey, encodedKeyAlgorithm));
}
static final Key constructKey(byte[] encoding, String keyAlgorithm,
int keyType)
throws InvalidKeyException, NoSuchAlgorithmException {
Key result = null;
switch (keyType) {
case Cipher.SECRET_KEY:
result = ConstructKeys.constructSecretKey(encoding,
keyAlgorithm);
break;
case Cipher.PRIVATE_KEY:
result = ConstructKeys.constructPrivateKey(encoding,
keyAlgorithm);
break;
case Cipher.PUBLIC_KEY:
result = ConstructKeys.constructPublicKey(encoding,
keyAlgorithm);
break;
}
return result;
}
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright (c) 2002, 2018, 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.security.InvalidKeyException;
/**
* This class represents ciphers in counter (CTR) mode.
*
* <p>This mode is implemented independently of a particular cipher.
* Ciphers to which this mode should apply (e.g., DES) must be
* <i>plugged-in</i> using the constructor.
*
* <p>NOTE: This class does not deal with buffering or padding.
*
* @author Andreas Sterbenz
* @since 1.4.2
*/
final class CounterMode extends FeedbackCipher {
// current counter value
private final byte[] counter;
// encrypted bytes of the previous counter value
private final byte[] encryptedCounter;
// number of bytes in encryptedCounter already used up
private int used;
// variables for save/restore calls
private byte[] counterSave = null;
private byte[] encryptedCounterSave = null;
private int usedSave = 0;
CounterMode(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
counter = new byte[blockSize];
encryptedCounter = new byte[blockSize];
}
/**
* Gets the name of the feedback mechanism
*
* @return the name of the feedback mechanism
*/
String getFeedback() {
return "CTR";
}
/**
* 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).
*/
void reset() {
System.arraycopy(iv, 0, counter, 0, blockSize);
used = blockSize;
}
/**
* Save the current content of this cipher.
*/
void save() {
if (counterSave == null) {
counterSave = new byte[blockSize];
encryptedCounterSave = new byte[blockSize];
}
System.arraycopy(counter, 0, counterSave, 0, blockSize);
System.arraycopy(encryptedCounter, 0, encryptedCounterSave, 0,
blockSize);
usedSave = used;
}
/**
* Restores the content of this cipher to the previous saved one.
*/
void restore() {
System.arraycopy(counterSave, 0, counter, 0, blockSize);
System.arraycopy(encryptedCounterSave, 0, encryptedCounter, 0,
blockSize);
used = usedSave;
}
/**
* 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
*/
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException {
if ((key == null) || (iv == null) || (iv.length != blockSize)) {
throw new InvalidKeyException("Internal error");
}
this.iv = iv;
reset();
// always encrypt mode for embedded cipher
embeddedCipher.init(false, algorithm, key);
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + len - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param in the buffer with the input data to be encrypted
* @param inOffset the offset in <code>plain</code>
* @param len the length of the input data
* @param out the buffer for the result
* @param outOff the offset in <code>cipher</code>
* @return the length of the encrypted data
*/
int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
return crypt(in, inOff, len, out, outOff);
}
// CTR encrypt and decrypt are identical
int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
return crypt(in, inOff, len, out, outOff);
}
/**
* Increment the counter value.
*/
private static void increment(byte[] b) {
int n = b.length - 1;
while ((n >= 0) && (++b[n] == 0)) {
n--;
}
}
/**
* Do the actual encryption/decryption operation.
* Essentially we XOR the input plaintext/ciphertext stream with a
* keystream generated by encrypting the counter values. Counter values
* are encrypted on demand.
*/
private int crypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
if (len == 0) {
return 0;
}
RangeUtil.nullAndBoundsCheck(in, inOff, len);
RangeUtil.nullAndBoundsCheck(out, outOff, len);
int result = len;
while (len-- > 0) {
if (used >= blockSize) {
embeddedCipher.encryptBlock(counter, 0, encryptedCounter, 0);
increment(counter);
used = 0;
}
out[outOff++] = (byte)(in[inOff++] ^ encryptedCounter[used++]);
}
return result;
}
}

View File

@@ -0,0 +1,428 @@
/*
* Copyright (c) 1997, 2009, 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.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.BadPaddingException;
/**
* This class implements the DES algorithm in its various modes
* (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, <code>CBC</code>,
* <code>PCBC</code>) and padding schemes (<code>PKCS5Padding</code>,
* <code>NoPadding</code>, <code>ISO10126Padding</code>).
*
* @author Gigi Ankeny
* @author Jan Luehe
* @see DESCrypt
* @see CipherBlockChaining
* @see ElectronicCodeBook
* @see CipherFeedback
* @see OutputFeedback
*/
public final class DESCipher extends CipherSpi {
/*
* internal CipherCore object which does the real work.
*/
private CipherCore core = null;
/**
* Creates an instance of DES cipher with default ECB mode and
* PKCS5Padding.
*/
public DESCipher() {
core = new CipherCore(new DESCrypt(), DESConstants.DES_BLOCK_SIZE);
}
/**
* Sets the mode of this cipher.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode does
* not exist
*/
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
core.setMode(mode);
}
/**
* Sets the padding mechanism of this cipher.
*
* @param paddingScheme the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* does not exist
*/
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
core.setPadding(paddingScheme);
}
/**
* Returns the block size (in bytes).
*
* @return the block size (in bytes), or 0 if the underlying algorithm is
* not a block cipher
*/
protected int engineGetBlockSize() {
return DESConstants.DES_BLOCK_SIZE;
}
/**
* Returns the length in bytes that an output buffer would need to be in
* order to hold the result of the next <code>update</code> or
* <code>doFinal</code> operation, given the input length
* <code>inputLen</code> (in bytes).
*
* <p>This call takes into account any unprocessed (buffered) data from a
* previous <code>update</code> call, and padding.
*
* <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) {
return core.getOutputSize(inputLen);
}
/**
* Returns the initialization vector (IV) in a new buffer.
*
* <p>This is useful in the case where a random IV has been created
* (see <a href = "#init">init</a>),
* or in the context of password-based encryption or
* decryption, where the IV is derived from a user-provided password.
*
* @return the initialization vector in a new buffer, or null if the
* underlying algorithm does not use an IV, or if the IV has not yet
* been set.
*/
protected byte[] engineGetIV() {
return core.getIV();
}
/**
* Returns the parameters used with this cipher.
*
* <p>The returned parameters may be the same that were used to initialize
* this cipher, or may contain the default set of parameters or a set of
* randomly generated parameters used by the underlying cipher
* implementation (provided that the underlying cipher implementation
* uses a default set of parameters or creates new parameters if it needs
* parameters but was not initialized with any).
*
* @return the parameters used with this cipher, or null if this cipher
* does not use any parameters.
*/
protected AlgorithmParameters engineGetParameters() {
return core.getParameters("DES");
}
/**
* Initializes this cipher with a key and a source of randomness.
*
* <p>The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher requires an initialization vector (IV), it will get
* it from <code>random</code>.
* This behaviour should only be used in encryption or key wrapping
* mode, however.
* When initializing a cipher that requires an IV for decryption or
* key unwrapping, the IV
* (same IV that was used for encryption or key wrapping) must be provided
* explicitly as a
* parameter, in order to get the correct result.
*
* <p>This method also cleans existing buffer and other related state
* information.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @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 {
core.init(opmode, key, random);
}
/**
* Initializes this cipher with a key, a set of
* algorithm parameters, and a source of randomness.
*
* <p>The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @param key the encryption key
* @param params the algorithm parameters
* @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 are inappropriate for this cipher
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in a new buffer.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized)
*/
protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) {
return core.update(input, inputOffset, inputLen);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
*/
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
return core.update(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in a new buffer.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
return core.doFinal(input, inputOffset, inputLen);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
return core.doFinal(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Returns the key size of the given key object.
*
* @param key the key object.
*
* @return the 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.length != 8) {
throw new InvalidKeyException("Invalid key length: " +
encoded.length + " bytes");
}
return 56;
}
/**
* 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 {
return core.wrap(key);
}
/**
* Unwrap a previously wrapped key.
*
* @param wrappedKey the key to be unwrapped.
*
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
*
* @param wrappedKeyType the type of the wrapped key.
* This is one of <code>Cipher.SECRET_KEY</code>,
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
*
* @return the unwrapped key.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys of type <code>wrappedKeyType</code> for the
* <code>wrappedKeyAlgorithm</code>.
*
* @exception InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key of type <code>wrappedKeyType</code> for
* the <code>wrappedKeyAlgorithm</code>.
*/
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 1997, 2007, 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;
/**
* This class defines the constants used by the DES implementation.
*
* @author Gigi Ankeny
*
* @see DESCipher
*/
interface DESConstants {
int DES_BLOCK_SIZE = 8;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,175 @@
/*
* Copyright (c) 1997, 2023, 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.io.IOException;
import java.io.InvalidObjectException;
import java.security.MessageDigest;
import java.security.KeyRep;
import java.security.InvalidKeyException;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESKeySpec;
/**
* This class represents a DES key.
*
* @author Jan Luehe
*
*/
final class DESKey implements SecretKey {
private static final long serialVersionUID = 7724971015953279128L;
private byte[] key;
/**
* Uses the first 8 bytes of the given key as the DES key.
*
* @param key the buffer with the DES key bytes.
*
* @exception InvalidKeyException if less than 8 bytes are available for
* the key.
*/
DESKey(byte[] key) throws InvalidKeyException {
this(key, 0);
}
/**
* Uses the first 8 bytes in <code>key</code>, beginning at
* <code>offset</code>, as the DES key
*
* @param key the buffer with the DES key bytes.
* @param offset the offset in <code>key</code>, where the DES key bytes
* start.
*
* @exception InvalidKeyException if less than 8 bytes are available for
* the key.
*/
DESKey(byte[] key, int offset) throws InvalidKeyException {
if (key == null || key.length - offset < DESKeySpec.DES_KEY_LEN) {
throw new InvalidKeyException("Wrong key size");
}
this.key = new byte[DESKeySpec.DES_KEY_LEN];
System.arraycopy(key, offset, this.key, 0, DESKeySpec.DES_KEY_LEN);
DESKeyGenerator.setParityBit(this.key, 0);
}
public synchronized byte[] getEncoded() {
// Return a copy of the key, rather than a reference,
// so that the key data cannot be modified from outside
return this.key.clone();
}
public String getAlgorithm() {
return "DES";
}
public String getFormat() {
return "RAW";
}
/**
* Calculates a hash code value for the object.
* Objects that are equal will also have the same hashcode.
*/
public int hashCode() {
int retval = 0;
for (int i = 1; i < this.key.length; i++) {
retval += this.key[i] * i;
}
return(retval ^ "des".hashCode());
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof SecretKey))
return false;
String thatAlg = ((SecretKey)obj).getAlgorithm();
if (!(thatAlg.equalsIgnoreCase("DES")))
return false;
byte[] thatKey = ((SecretKey)obj).getEncoded();
boolean ret = MessageDigest.isEqual(this.key, thatKey);
java.util.Arrays.fill(thatKey, (byte)0x00);
return ret;
}
/**
* Restores the state of this object from the stream.
*
* @param s the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject();
if ((key == null) || (key.length != DESKeySpec.DES_KEY_LEN)) {
throw new InvalidObjectException("Wrong key size");
}
key = key.clone();
DESKeyGenerator.setParityBit(key, 0);
}
/**
* Replace the DES key to be serialized.
*
* @return the standard KeyRep object to be serialized
*
* @throws java.io.ObjectStreamException if a new object representing
* this DES key could not be created
*/
private Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.SECRET,
getAlgorithm(),
getFormat(),
getEncoded());
}
/**
* Ensures that the bytes of this key are
* set to zero when there are no more references to it.
*/
protected void finalize() throws Throwable {
try {
synchronized (this) {
if (this.key != null) {
java.util.Arrays.fill(this.key, (byte)0x00);
this.key = null;
}
}
} finally {
super.finalize();
}
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (c) 1997, 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 javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.DESKeySpec;
import java.security.InvalidKeyException;
import java.security.spec.KeySpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.spec.SecretKeySpec;
/**
* This class implements the DES key factory of the Sun provider.
*
* @author Jan Luehe
*
*/
public final class DESKeyFactory extends SecretKeyFactorySpi {
/**
* Empty constructor
*/
public DESKeyFactory() {
}
/**
* Generates a <code>SecretKey</code> object from the provided key
* specification (key material).
*
* @param keySpec the specification (key material) of the secret key
*
* @return the secret key
*
* @exception InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
protected SecretKey engineGenerateSecret(KeySpec keySpec)
throws InvalidKeySpecException {
try {
if (keySpec instanceof DESKeySpec) {
return new DESKey(((DESKeySpec)keySpec).getKey());
}
if (keySpec instanceof SecretKeySpec) {
return new DESKey(((SecretKeySpec)keySpec).getEncoded());
}
throw new InvalidKeySpecException(
"Inappropriate key specification");
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e.getMessage());
}
}
/**
* Returns a specification (key material) of the given key
* in the requested format.
*
* @param key the key
*
* @param keySpec the requested format in which the key material shall be
* returned
*
* @return the underlying key specification (key material) in the
* requested format
*
* @exception InvalidKeySpecException if the requested key specification is
* inappropriate for the given key, or the given key cannot be processed
* (e.g., the given key has an unrecognized algorithm or format).
*/
protected KeySpec engineGetKeySpec(SecretKey key, Class<?> keySpec)
throws InvalidKeySpecException {
try {
if ((key instanceof SecretKey)
&& (key.getAlgorithm().equalsIgnoreCase("DES"))
&& (key.getFormat().equalsIgnoreCase("RAW"))) {
// Check if requested key spec is amongst the valid ones
if ((keySpec != null) &&
keySpec.isAssignableFrom(DESKeySpec.class)) {
return new DESKeySpec(key.getEncoded());
} else {
throw new InvalidKeySpecException
("Inappropriate key specification");
}
} else {
throw new InvalidKeySpecException
("Inappropriate key format/algorithm");
}
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException("Secret key has wrong size");
}
}
/**
* Translates a <code>SecretKey</code> object, whose provider may be
* unknown or potentially untrusted, into a corresponding
* <code>SecretKey</code> object of this key factory.
*
* @param key the key whose provider is unknown or untrusted
*
* @return the translated key
*
* @exception InvalidKeyException if the given key cannot be processed by
* this key factory.
*/
protected SecretKey engineTranslateKey(SecretKey key)
throws InvalidKeyException {
try {
if ((key != null) &&
(key.getAlgorithm().equalsIgnoreCase("DES")) &&
(key.getFormat().equalsIgnoreCase("RAW"))) {
// Check if key originates from this factory
if (key instanceof com.sun.crypto.provider.DESKey) {
return key;
}
// Convert key to spec
DESKeySpec desKeySpec
= (DESKeySpec)engineGetKeySpec(key, DESKeySpec.class);
// Create key from spec, and return it
return engineGenerateSecret(desKeySpec);
} else {
throw new InvalidKeyException
("Inappropriate key format/algorithm");
}
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException("Cannot translate key");
}
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) 1997, 2013, 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.security.SecureRandom;
import java.security.InvalidParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KeyGeneratorSpi;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESKeySpec;
/**
* This class generates a DES key.
*
* @author Jan Luehe
*
*/
public final class DESKeyGenerator extends KeyGeneratorSpi {
private SecureRandom random = null;
/**
* Empty constructor
*/
public DESKeyGenerator() {
}
/**
* Initializes this key generator.
*
* @param random the source of randomness for this generator
*/
protected void engineInit(SecureRandom random) {
this.random = random;
}
/**
* Initializes this key generator with the specified parameter
* set and a user-provided source of randomness.
*
* @param params the key generation parameters
* @param random the source of randomness for this key generator
*
* @exception InvalidAlgorithmParameterException if <code>params</code> is
* inappropriate for this key generator
*/
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidAlgorithmParameterException {
throw new InvalidAlgorithmParameterException
("DES key generation does not take any parameters");
}
/**
* Initializes this key generator for a certain keysize, using the given
* source of randomness.
*
* @param keysize the keysize. This is an algorithm-specific
* metric specified in number of bits.
* @param random the source of randomness for this key generator
*/
protected void engineInit(int keysize, SecureRandom random) {
if (keysize != 56) {
throw new InvalidParameterException("Wrong keysize: must "
+ "be equal to 56");
}
this.engineInit(random);
}
/**
* Generates the DES key.
*
* @return the new DES key
*/
protected SecretKey engineGenerateKey() {
DESKey desKey = null;
if (this.random == null) {
this.random = SunJCE.getRandom();
}
try {
byte[] key = new byte[DESKeySpec.DES_KEY_LEN];
do {
this.random.nextBytes(key);
setParityBit(key, 0);
} while (DESKeySpec.isWeak(key, 0));
desKey = new DESKey(key);
} catch (InvalidKeyException e) {
// this is never thrown
}
return desKey;
}
/*
* Does parity adjustment, using bit in position 8 as the parity bit,
* for 8 key bytes, starting at <code>offset</code>.
*
* The 8 parity bits of a DES key are only used for sanity-checking
* of the key, to see if the key could actually be a key. If you check
* the parity of the quantity, and it winds up not having the correct
* parity, then you'll know something went wrong.
*
* A key that is not parity adjusted (e.g. e4e4e4e4e4e4e4e4) produces the
* same output as a key that is parity adjusted (e.g. e5e5e5e5e5e5e5e5),
* because it is the 56 bits of the DES key that are cryptographically
* significant/"effective" -- the other 8 bits are just used for parity
* checking.
*/
static void setParityBit(byte[] key, int offset) {
if (key == null)
return;
for (int i = 0; i < DESKeySpec.DES_KEY_LEN; i++) {
int b = key[offset] & 0xfe;
b |= (Integer.bitCount(b) & 1) ^ 1;
key[offset++] = (byte)b;
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 1998, 2011, 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.io.*;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
/**
* This class implements the parameter (IV) used with the DES algorithm in
* feedback-mode. IV is defined in the standards as follows:
*
* <pre>
* IV ::= OCTET STRING -- 8 octets
* </pre>
*
* @author Jan Luehe
*
*/
public final class DESParameters extends AlgorithmParametersSpi {
private BlockCipherParamsCore core;
public DESParameters() {
core = new BlockCipherParamsCore(DESConstants.DES_BLOCK_SIZE);
}
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
core.init(paramSpec);
}
protected void engineInit(byte[] encoded)
throws IOException {
core.init(encoded);
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException {
core.init(encoded, decodingMethod);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException {
if (AlgorithmParameterSpec.class.isAssignableFrom(paramSpec)) {
return core.getParameterSpec(paramSpec);
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter Specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
return core.getEncoded();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException {
return core.getEncoded();
}
protected String engineToString() {
return core.toString();
}
}

View File

@@ -0,0 +1,426 @@
/*
* Copyright (c) 1997, 2010, 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.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class implements the DESede algorithm (DES-EDE, tripleDES) in
* its various modes (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>,
* <code>CBC</code>, <code>PCBC</code>) and padding schemes
* (<code>PKCS5Padding</code>, <code>NoPadding</code>,
* <code>ISO10126Padding</code>).
*
* @author Gigi Ankeny
*
*
* @see DESCipher
*/
public final class DESedeCipher extends CipherSpi {
/*
* internal CipherCore object which does the real work.
*/
private CipherCore core = null;
/**
* Creates an instance of DESede cipher with default ECB mode and
* PKCS5Padding.
*/
public DESedeCipher() {
core = new CipherCore(new DESedeCrypt(), DESConstants.DES_BLOCK_SIZE);
}
/**
* Sets the mode of this cipher.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode does
* not exist
*/
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
core.setMode(mode);
}
/**
* Sets the padding mechanism of this cipher.
*
* @param paddingScheme the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* does not exist
*/
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
core.setPadding(paddingScheme);
}
/**
* Returns the block size (in bytes).
*
* @return the block size (in bytes), or 0 if the underlying algorithm is
* not a block cipher
*/
protected int engineGetBlockSize() {
return DESConstants.DES_BLOCK_SIZE;
}
/**
* Returns the length in bytes that an output buffer would need to be in
* order to hold the result of the next <code>update</code> or
* <code>doFinal</code> operation, given the input length
* <code>inputLen</code> (in bytes).
*
* <p>This call takes into account any unprocessed (buffered) data from a
* previous <code>update</code> call, and padding.
*
* <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) {
return core.getOutputSize(inputLen);
}
/**
* Returns the initialization vector (IV) in a new buffer.
*
* <p>This is useful in the case where a random IV has been created
* (see <a href = "#init">init</a>),
* or in the context of password-based encryption or
* decryption, where the IV is derived from a user-provided password.
*
* @return the initialization vector in a new buffer, or null if the
* underlying algorithm does not use an IV, or if the IV has not yet
* been set.
*/
protected byte[] engineGetIV() {
return core.getIV();
}
/**
* Initializes this cipher with a key and a source of randomness.
*
* <p>The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher requires an initialization vector (IV), it will get
* it from <code>random</code>.
* This behaviour should only be used in encryption or key wrapping
* mode, however.
* When initializing a cipher that requires an IV for decryption or
* key unwrapping, the IV
* (same IV that was used for encryption or key wrapping) must be provided
* explicitly as a
* parameter, in order to get the correct result.
*
* <p>This method also cleans existing buffer and other related state
* information.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @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 {
core.init(opmode, key, random);
}
/**
* Initializes this cipher with a key, a set of
* algorithm parameters, and a source of randomness.
*
* <p>The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @param key the encryption key
* @param params the algorithm parameters
* @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 are inappropriate for this cipher
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in a new buffer.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized)
*/
protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) {
return core.update(input, inputOffset, inputLen);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
*/
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
return core.update(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in a new buffer.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
return core.doFinal(input, inputOffset, inputLen);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
return core.doFinal(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Returns the parameters used with this cipher.
*
* <p>The returned parameters may be the same that were used to initialize
* this cipher, or may contain the default set of parameters or a set of
* randomly generated parameters used by the underlying cipher
* implementation (provided that the underlying cipher implementation
* uses a default set of parameters or creates new parameters if it needs
* parameters but was not initialized with any).
*
* @return the parameters used with this cipher, or null if this cipher
* does not use any parameters.
*/
protected AlgorithmParameters engineGetParameters() {
return core.getParameters("DESede");
}
/**
* Returns the key size of the given key object.
*
* @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.length != 24) {
throw new InvalidKeyException("Invalid key length: " +
encoded.length + " bytes");
}
// Return the effective key length
return 112;
}
/**
* 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 {
return core.wrap(key);
}
/**
* Unwrap a previously wrapped key.
*
* @param wrappedKey the key to be unwrapped.
*
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
*
* @param wrappedKeyType the type of the wrapped key.
* This is one of <code>Cipher.SECRET_KEY</code>,
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
*
* @return the unwrapped key.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys of type <code>wrappedKeyType</code> for the
* <code>wrappedKeyAlgorithm</code>.
*
* @exception InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key of type <code>wrappedKeyType</code> for
* the <code>wrappedKeyAlgorithm</code>.
*/
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright (c) 1997, 2007, 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.security.InvalidKeyException;
/**
* This class implements the Triple DES algorithm (DES encryption, followed by
* DES decryption, followed by DES encryption) on a byte array of size
* <code>DES_BLOCK_SIZE</code>. Each DES operation has its own key.
*
* @author Gigi Ankeny
* @author Jan Luehe
*
*
* @see DESConstants
* @see DESCipher
*/
final class DESedeCrypt extends DESCrypt implements DESConstants {
/*
* the expanded key used in encrypt/decrypt/encrypt phase
*/
private byte[] key1 = null;
private byte[] key2 = null;
private byte[] key3 = null;
private byte[] buf1, buf2;
/*
* constructor
*/
DESedeCrypt() {
buf1 = new byte[DES_BLOCK_SIZE];
buf2 = new byte[DES_BLOCK_SIZE];
}
void init(boolean decrypting, String algorithm, byte[] keys)
throws InvalidKeyException {
if (!algorithm.equalsIgnoreCase("DESede")
&& !algorithm.equalsIgnoreCase("TripleDES")) {
throw new InvalidKeyException
("Wrong algorithm: DESede or TripleDES required");
}
if (keys.length != DES_BLOCK_SIZE * 3) {
throw new InvalidKeyException("Wrong key size");
}
byte[] keybuf = new byte[DES_BLOCK_SIZE];
// retrieve the first key
key1 = new byte[128];
System.arraycopy(keys, 0, keybuf, 0, DES_BLOCK_SIZE);
expandKey(keybuf);
System.arraycopy(expandedKey, 0, key1, 0, 128);
// check if the third key is the same
if (keyEquals(keybuf, 0, keys, DES_BLOCK_SIZE*2, DES_BLOCK_SIZE)) {
key3 = key1;
} else {
key3 = new byte[128];
System.arraycopy(keys, DES_BLOCK_SIZE*2, keybuf, 0,
DES_BLOCK_SIZE);
expandKey(keybuf);
System.arraycopy(expandedKey, 0, key3, 0, 128);
}
// retrieve the second key
key2 = new byte[128];
System.arraycopy(keys, DES_BLOCK_SIZE, keybuf, 0, DES_BLOCK_SIZE);
expandKey(keybuf);
System.arraycopy(expandedKey, 0, key2, 0, 128);
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + blockSize - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
*/
void encryptBlock(byte[] plain, int plainOffset,
byte[] cipher, int cipherOffset)
{
expandedKey = key1;
decrypting = false;
cipherBlock(plain, plainOffset, buf1, 0);
expandedKey = key2;
decrypting = true;
cipherBlock(buf1, 0, buf2, 0);
expandedKey = key3;
decrypting = false;
cipherBlock(buf2, 0, cipher, cipherOffset);
}
/**
* Performs decryption operation.
*
* <p>The input cipher text <code>cipher</code>, starting at
* <code>cipherOffset</code> and ending at
* <code>(cipherOffset + blockSize - 1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* @param cipher the buffer with the input data to be decrypted
* @param cipherOffset the offset in <code>cipherOffset</code>
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
*/
void decryptBlock(byte[] cipher, int cipherOffset,
byte[] plain, int plainOffset)
{
expandedKey = key3;
decrypting = true;
cipherBlock(cipher, cipherOffset, buf1, 0);
expandedKey = key2;
decrypting = false;
cipherBlock(buf1, 0, buf2, 0);
expandedKey = key1;
decrypting = true;
cipherBlock(buf2, 0, plain, plainOffset);
}
private boolean keyEquals(byte[] key1, int off1,
byte[] key2, int off2, int len) {
for (int i=0; i<len; i++) {
if (key1[i+off1] != key2[i+off2])
return false;
}
return true;
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (c) 1997, 2023, 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.io.IOException;
import java.io.InvalidObjectException;
import java.security.MessageDigest;
import java.security.KeyRep;
import java.security.InvalidKeyException;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESedeKeySpec;
/**
* This class represents a DES-EDE key.
*
* @author Jan Luehe
*
*/
final class DESedeKey implements SecretKey {
private static final long serialVersionUID = 2463986565756745178L;
private byte[] key;
/**
* Creates a DES-EDE key from a given key.
*
* @param key the given key
*
* @exception InvalidKeyException if the given key has a wrong size
*/
DESedeKey(byte[] key) throws InvalidKeyException {
this(key, 0);
}
/**
* Uses the first 24 bytes in <code>key</code>, beginning at
* <code>offset</code>, as the DES-EDE key
*
* @param key the buffer with the DES-EDE key
* @param offset the offset in <code>key</code>, where the DES-EDE key
* starts
*
* @exception InvalidKeyException if the given key has a wrong size
*/
DESedeKey(byte[] key, int offset) throws InvalidKeyException {
if (key==null || ((key.length-offset)<DESedeKeySpec.DES_EDE_KEY_LEN)) {
throw new InvalidKeyException("Wrong key size");
}
this.key = new byte[DESedeKeySpec.DES_EDE_KEY_LEN];
System.arraycopy(key, offset, this.key, 0,
DESedeKeySpec.DES_EDE_KEY_LEN);
DESKeyGenerator.setParityBit(this.key, 0);
DESKeyGenerator.setParityBit(this.key, 8);
DESKeyGenerator.setParityBit(this.key, 16);
}
public synchronized byte[] getEncoded() {
return this.key.clone();
}
public String getAlgorithm() {
return "DESede";
}
public String getFormat() {
return "RAW";
}
/**
* Calculates a hash code value for the object.
* Objects that are equal will also have the same hashcode.
*/
public int hashCode() {
int retval = 0;
for (int i = 1; i < this.key.length; i++) {
retval += this.key[i] * i;
}
return(retval ^ "desede".hashCode());
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof SecretKey))
return false;
String thatAlg = ((SecretKey)obj).getAlgorithm();
if (!(thatAlg.equalsIgnoreCase("DESede"))
&& !(thatAlg.equalsIgnoreCase("TripleDES")))
return false;
byte[] thatKey = ((SecretKey)obj).getEncoded();
boolean ret = MessageDigest.isEqual(this.key, thatKey);
java.util.Arrays.fill(thatKey, (byte)0x00);
return ret;
}
/**
* Restores the state of this object from the stream.
*
* @param s the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject();
if ((key == null) || (key.length != DESedeKeySpec.DES_EDE_KEY_LEN)) {
throw new InvalidObjectException("Wrong key size");
}
key = key.clone();
DESKeyGenerator.setParityBit(key, 0);
DESKeyGenerator.setParityBit(key, 8);
DESKeyGenerator.setParityBit(key, 16);
}
/**
* Replace the DESede key to be serialized.
*
* @return the standard KeyRep object to be serialized
*
* @throws java.io.ObjectStreamException if a new object representing
* this DESede key could not be created
*/
private Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.SECRET,
getAlgorithm(),
getFormat(),
getEncoded());
}
/**
* Ensures that the bytes of this key are
* set to zero when there are no more references to it.
*/
protected void finalize() throws Throwable {
try {
synchronized (this) {
if (this.key != null) {
java.util.Arrays.fill(this.key, (byte)0x00);
this.key = null;
}
}
} finally {
super.finalize();
}
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (c) 1997, 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 javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.DESedeKeySpec;
import java.security.InvalidKeyException;
import java.security.spec.KeySpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.spec.SecretKeySpec;
/**
* This class implements the DES-EDE key factory of the Sun provider.
*
* @author Jan Luehe
*
*/
public final class DESedeKeyFactory extends SecretKeyFactorySpi {
/**
* Empty constructor
*/
public DESedeKeyFactory() {
}
/**
* Generates a <code>SecretKey</code> object from the provided key
* specification (key material).
*
* @param keySpec the specification (key material) of the secret key
*
* @return the secret key
*
* @exception InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
protected SecretKey engineGenerateSecret(KeySpec keySpec)
throws InvalidKeySpecException {
try {
if (keySpec instanceof DESedeKeySpec) {
return new DESedeKey(((DESedeKeySpec)keySpec).getKey());
}
if (keySpec instanceof SecretKeySpec) {
return new DESedeKey(((SecretKeySpec)keySpec).getEncoded());
}
throw new InvalidKeySpecException
("Inappropriate key specification");
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e.getMessage());
}
}
/**
* Returns a specification (key material) of the given key
* in the requested format.
*
* @param key the key
*
* @param keySpec the requested format in which the key material shall be
* returned
*
* @return the underlying key specification (key material) in the
* requested format
*
* @exception InvalidKeySpecException if the requested key specification is
* inappropriate for the given key, or the given key cannot be processed
* (e.g., the given key has an unrecognized algorithm or format).
*/
protected KeySpec engineGetKeySpec(SecretKey key, Class<?> keySpec)
throws InvalidKeySpecException {
try {
if ((key instanceof SecretKey)
&& (key.getAlgorithm().equalsIgnoreCase("DESede"))
&& (key.getFormat().equalsIgnoreCase("RAW"))) {
// Check if requested key spec is amongst the valid ones
if (keySpec.isAssignableFrom(DESedeKeySpec.class)) {
return new DESedeKeySpec(key.getEncoded());
} else {
throw new InvalidKeySpecException
("Inappropriate key specification");
}
} else {
throw new InvalidKeySpecException
("Inappropriate key format/algorithm");
}
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException("Secret key has wrong size");
}
}
/**
* Translates a <code>SecretKey</code> object, whose provider may be
* unknown or potentially untrusted, into a corresponding
* <code>SecretKey</code> object of this key factory.
*
* @param key the key whose provider is unknown or untrusted
*
* @return the translated key
*
* @exception InvalidKeyException if the given key cannot be processed by
* this key factory.
*/
protected SecretKey engineTranslateKey(SecretKey key)
throws InvalidKeyException {
try {
if ((key != null)
&& (key.getAlgorithm().equalsIgnoreCase("DESede"))
&& (key.getFormat().equalsIgnoreCase("RAW"))) {
// Check if key originates from this factory
if (key instanceof com.sun.crypto.provider.DESedeKey) {
return key;
}
// Convert key to spec
DESedeKeySpec desEdeKeySpec
= (DESedeKeySpec)engineGetKeySpec(key,
DESedeKeySpec.class);
// Create key from spec, and return it
return engineGenerateSecret(desEdeKeySpec);
} else {
throw new InvalidKeyException
("Inappropriate key format/algorithm");
}
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException("Cannot translate key");
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 1997, 2013, 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 javax.crypto.KeyGeneratorSpi;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESedeKeySpec;
import java.security.SecureRandom;
import java.security.InvalidParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.spec.AlgorithmParameterSpec;
/**
* This class generates a Triple DES key.
*
* @author Jan Luehe
*
*/
public final class DESedeKeyGenerator extends KeyGeneratorSpi {
private SecureRandom random = null;
private int keysize = 168;
/**
* Empty constructor
*/
public DESedeKeyGenerator() {
}
/**
* Initializes this key generator.
*
* @param random the source of randomness for this generator
*/
protected void engineInit(SecureRandom random) {
this.random = random;
}
/**
* Initializes this key generator with the specified parameter
* set and a user-provided source of randomness.
*
* @param params the key generation parameters
* @param random the source of randomness for this key generator
*
* @exception InvalidAlgorithmParameterException if <code>params</code> is
* inappropriate for this key generator
*/
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidAlgorithmParameterException {
throw new InvalidAlgorithmParameterException
("Triple DES key generation does not take any parameters");
}
/**
* Initializes this key generator for a certain keysize, using the given
* source of randomness.
*
* @param keysize the keysize. This is an algorithm-specific
* metric specified in number of bits. A keysize with 112 bits of entropy
* corresponds to a Triple DES key with 2 intermediate keys, and a keysize
* with 168 bits of entropy corresponds to a Triple DES key with 3
* intermediate keys.
* @param random the source of randomness for this key generator
*/
protected void engineInit(int keysize, SecureRandom random) {
if ((keysize != 112) && (keysize != 168)) {
throw new InvalidParameterException("Wrong keysize: must be "
+ "equal to 112 or 168");
}
this.keysize = keysize;
this.engineInit(random);
}
/**
* Generates the Triple DES key.
*
* @return the new Triple DES key
*/
protected SecretKey engineGenerateKey() {
if (this.random == null) {
this.random = SunJCE.getRandom();
}
byte[] rawkey = new byte[DESedeKeySpec.DES_EDE_KEY_LEN];
if (keysize == 168) {
// 3 intermediate keys
this.random.nextBytes(rawkey);
// Do parity adjustment for each intermediate key
DESKeyGenerator.setParityBit(rawkey, 0);
DESKeyGenerator.setParityBit(rawkey, 8);
DESKeyGenerator.setParityBit(rawkey, 16);
} else {
// 2 intermediate keys
byte[] tmpkey = new byte[16];
this.random.nextBytes(tmpkey);
DESKeyGenerator.setParityBit(tmpkey, 0);
DESKeyGenerator.setParityBit(tmpkey, 8);
System.arraycopy(tmpkey, 0, rawkey, 0, tmpkey.length);
// Copy the first 8 bytes into the last
System.arraycopy(tmpkey, 0, rawkey, 16, 8);
java.util.Arrays.fill(tmpkey, (byte)0x00);
}
DESedeKey desEdeKey = null;
try {
desEdeKey = new DESedeKey(rawkey);
} catch (InvalidKeyException ike) {
// this never happens
throw new RuntimeException(ike.getMessage());
}
java.util.Arrays.fill(rawkey, (byte)0x00);
return desEdeKey;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 1998, 2011, 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.io.*;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
/**
* This class implements the parameter (IV) used with the Triple DES algorithm
* in feedback-mode. IV is defined in the standards as follows:
*
* <pre>
* IV ::= OCTET STRING -- 8 octets
* </pre>
*
* @author Jan Luehe
*
*/
public final class DESedeParameters extends AlgorithmParametersSpi {
private BlockCipherParamsCore core;
public DESedeParameters() {
core = new BlockCipherParamsCore(DESConstants.DES_BLOCK_SIZE);
}
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
core.init(paramSpec);
}
protected void engineInit(byte[] encoded)
throws IOException {
core.init(encoded);
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException {
core.init(encoded, decodingMethod);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException {
if (AlgorithmParameterSpec.class.isAssignableFrom(paramSpec)) {
return core.getParameterSpec(paramSpec);
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter Specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
return core.getEncoded();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException {
return core.getEncoded();
}
protected String engineToString() {
return core.toString();
}
}

View File

@@ -0,0 +1,578 @@
/*
* Copyright (c) 2004, 2017, 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.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class implements the CMS DESede KeyWrap algorithm as defined
* in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap></a>
* "XML Encryption Syntax and Processing" section 5.6.2
* "CMS Triple DES Key Wrap".
* Note: only <code>CBC</code> mode and <code>NoPadding</code> padding
* scheme can be used for this algorithm.
*
* @author Valerie Peng
*
*
* @see DESedeCipher
*/
public final class DESedeWrapCipher extends CipherSpi {
private static final byte[] IV2 = {
(byte) 0x4a, (byte) 0xdd, (byte) 0xa2, (byte) 0x2c,
(byte) 0x79, (byte) 0xe8, (byte) 0x21, (byte) 0x05
};
private static final int CHECKSUM_LEN = 8;
private static final int IV_LEN = 8;
/*
* internal cipher object which does the real work.
*/
private FeedbackCipher cipher;
/*
* iv for (re-)initializing the internal cipher object.
*/
private byte[] iv = null;
/*
* key for re-initializing the internal cipher object.
*/
private Key cipherKey = null;
/*
* are we encrypting or decrypting?
*/
private boolean decrypting = false;
/**
* Creates an instance of CMS DESede KeyWrap cipher with default
* mode, i.e. "CBC" and padding scheme, i.e. "NoPadding".
*/
public DESedeWrapCipher() {
cipher = new CipherBlockChaining(new DESedeCrypt());
}
/**
* Sets the mode of this cipher. Only "CBC" mode is accepted for this
* cipher.
*
* @param mode the cipher mode.
*
* @exception NoSuchAlgorithmException if the requested cipher mode
* is not "CBC".
*/
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
if (!mode.equalsIgnoreCase("CBC")) {
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. 8 bytes.
*
* @return the block size (in bytes), i.e. 8 bytes.
*/
protected int engineGetBlockSize() {
return DESConstants.DES_BLOCK_SIZE;
}
/**
* 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 - 16; // CHECKSUM_LEN + IV_LEN;
} else {
result = Math.addExact(inputLen, 16);
}
return (result < 0? 0:result);
}
/**
* Returns the initialization vector (IV) in a new buffer.
*
* @return the initialization vector, or null if the underlying
* algorithm does not use an IV, or if the IV has not yet
* been set.
*/
protected byte[] engineGetIV() {
return (iv == null) ? null : iv.clone();
}
/**
* Initializes this cipher with a key and a source of randomness.
*
* <p>The cipher only supports the following two operation modes:
* {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
* <p>If this cipher requires an initialization vector (IV), it will get
* it from <code>random</code>.
*
* @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
* or if parameters are required but not supplied.
*/
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
try {
engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
} catch (InvalidAlgorithmParameterException iape) {
// should never happen
InvalidKeyException ike =
new InvalidKeyException("Parameters required");
ike.initCause(iape);
throw ike;
}
}
/**
* 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:
* {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
* <p>If this cipher requires an initialization vector (IV), it will get
* it from <code>random</code>.
*
* @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.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this cipher.
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
byte[] currIv = null;
if (opmode == Cipher.WRAP_MODE) {
decrypting = false;
if (params == null) {
iv = new byte[IV_LEN];
if (random == null) {
random = SunJCE.getRandom();
}
random.nextBytes(iv);
}
else if (params instanceof IvParameterSpec) {
iv = ((IvParameterSpec) params).getIV();
} else {
throw new InvalidAlgorithmParameterException
("Wrong parameter type: IV expected");
}
currIv = iv;
} else if (opmode == Cipher.UNWRAP_MODE) {
if (params != null) {
throw new InvalidAlgorithmParameterException
("No parameter accepted for unwrapping keys");
}
iv = null;
decrypting = true;
currIv = IV2;
} else {
throw new UnsupportedOperationException("This cipher can " +
"only be used for key wrapping and unwrapping");
}
cipher.init(decrypting, key.getAlgorithm(), key.getEncoded(),
currIv);
cipherKey = key;
}
/**
* 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:
* {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
* <p>If this cipher requires an initialization vector (IV), it will get
* it from <code>random</code>.
*
* @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.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this cipher.
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
IvParameterSpec ivSpec = null;
if (params != null) {
try {
DESedeParameters paramsEng = new DESedeParameters();
paramsEng.engineInit(params.getEncoded());
ivSpec = paramsEng.engineGetParameterSpec(IvParameterSpec.class);
} catch (Exception ex) {
InvalidAlgorithmParameterException iape =
new InvalidAlgorithmParameterException
("Wrong parameter type: IV expected");
iape.initCause(ex);
throw iape;
}
}
engineInit(opmode, key, ivSpec, 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 in the input buffer.
* @param inOffset the offset in <code>in</code> where the input
* starts.
* @param inLen the input length.
*
* @return the new buffer with the result.
*
* @exception IllegalStateException upon invocation of this method.
*/
protected byte[] engineDoFinal(byte[] in, int inOffset, int inLen)
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 input the input buffer.
* @param inputOffset the offset in {@code input} where the input
* starts.
* @param inputLen the input length.
* @param output the buffer for the result.
* @param outputOffset the ofset in {@code output} where the result
* is stored.
*
* @return the number of bytes stored in {@code out}.
*
* @exception IllegalStateException upon invocation of this method.
*/
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* Returns the parameters used with this cipher.
* Note that null maybe returned if this cipher does not use any
* parameters or when it has not be set, e.g. initialized with
* UNWRAP_MODE but wrapped key data has not been given.
*
* @return the parameters used with this cipher; can be null.
*/
protected AlgorithmParameters engineGetParameters() {
AlgorithmParameters params = null;
if (iv != null) {
String algo = cipherKey.getAlgorithm();
try {
params = AlgorithmParameters.getInstance(algo,
SunJCE.getInstance());
params.init(new IvParameterSpec(iv));
} catch (NoSuchAlgorithmException nsae) {
// should never happen
throw new RuntimeException("Cannot find " + algo +
" AlgorithmParameters implementation in SunJCE provider");
} catch (InvalidParameterSpecException ipse) {
// should never happen
throw new RuntimeException("IvParameterSpec not supported");
}
}
return params;
}
/**
* Returns the key size of the given key object in number of bits.
* This cipher always return the same key size as the DESede ciphers.
*
* @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.length != 24) {
throw new InvalidKeyException("Invalid key length: " +
encoded.length + " bytes");
}
// Return the effective key length
return 112;
}
/**
* 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");
}
byte[] cks = getChecksum(keyVal);
byte[] in = new byte[Math.addExact(keyVal.length, CHECKSUM_LEN)];
System.arraycopy(keyVal, 0, in, 0, keyVal.length);
System.arraycopy(cks, 0, in, keyVal.length, CHECKSUM_LEN);
byte[] out = new byte[Math.addExact(iv.length, in.length)];
System.arraycopy(iv, 0, out, 0, iv.length);
cipher.encrypt(in, 0, in.length, out, iv.length);
// reverse the array content
for (int i = 0; i < out.length/2; i++) {
byte temp = out[i];
out[i] = out[out.length-1-i];
out[out.length-1-i] = temp;
}
try {
cipher.init(false, cipherKey.getAlgorithm(),
cipherKey.getEncoded(), IV2);
} catch (InvalidKeyException ike) {
// should never happen
throw new RuntimeException("Internal cipher key is corrupted");
} catch (InvalidAlgorithmParameterException iape) {
// should never happen
throw new RuntimeException("Internal cipher IV is invalid");
}
byte[] out2 = new byte[out.length];
cipher.encrypt(out, 0, out.length, out2, 0);
// restore cipher state to prior to this call
try {
cipher.init(decrypting, cipherKey.getAlgorithm(),
cipherKey.getEncoded(), iv);
} catch (InvalidKeyException ike) {
// should never happen
throw new RuntimeException("Internal cipher key is corrupted");
} catch (InvalidAlgorithmParameterException iape) {
// should never happen
throw new RuntimeException("Internal cipher IV is invalid");
}
return out2;
}
/**
* 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 {
if (wrappedKey.length == 0) {
throw new InvalidKeyException("The wrapped key is empty");
}
byte[] buffer = new byte[wrappedKey.length];
cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0);
// reverse array content
for (int i = 0; i < buffer.length/2; i++) {
byte temp = buffer[i];
buffer[i] = buffer[buffer.length-1-i];
buffer[buffer.length-1-i] = temp;
}
iv = new byte[IV_LEN];
System.arraycopy(buffer, 0, iv, 0, iv.length);
try {
cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(),
iv);
} catch (InvalidAlgorithmParameterException iape) {
throw new InvalidKeyException("IV in wrapped key is invalid");
}
byte[] buffer2 = new byte[buffer.length - iv.length];
cipher.decrypt(buffer, iv.length, buffer2.length,
buffer2, 0);
int keyValLen = buffer2.length - CHECKSUM_LEN;
byte[] cks = getChecksum(buffer2, 0, keyValLen);
int offset = keyValLen;
for (int i = 0; i < CHECKSUM_LEN; i++) {
if (buffer2[offset + i] != cks[i]) {
throw new InvalidKeyException("Checksum comparison failed");
}
}
// restore cipher state to prior to this call
try {
cipher.init(decrypting, cipherKey.getAlgorithm(),
cipherKey.getEncoded(), IV2);
} catch (InvalidAlgorithmParameterException iape) {
throw new InvalidKeyException("IV in wrapped key is invalid");
}
byte[] out = new byte[keyValLen];
System.arraycopy(buffer2, 0, out, 0, keyValLen);
return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
wrappedKeyType);
}
private static final byte[] getChecksum(byte[] in) {
return getChecksum(in, 0, in.length);
}
private static final byte[] getChecksum(byte[] in, int offset, int len) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException nsae) {
throw new RuntimeException("SHA1 message digest not available");
}
md.update(in, offset, len);
byte[] cks = new byte[CHECKSUM_LEN];
System.arraycopy(md.digest(), 0, cks, 0, cks.length);
return cks;
}
}

View File

@@ -0,0 +1,435 @@
/*
* Copyright (c) 1997, 2017, 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.*;
import java.lang.*;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.PrivilegedAction;
import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.KeyAgreementSpi;
import javax.crypto.ShortBufferException;
import javax.crypto.SecretKey;
import javax.crypto.spec.*;
import sun.security.util.KeyUtil;
/**
* This class implements the Diffie-Hellman key agreement protocol between
* any number of parties.
*
* @author Jan Luehe
*
*/
public final class DHKeyAgreement
extends KeyAgreementSpi {
private boolean generateSecret = false;
private BigInteger init_p = null;
private BigInteger init_g = null;
private BigInteger x = BigInteger.ZERO; // the private value
private BigInteger y = BigInteger.ZERO;
private static class AllowKDF {
private static final boolean VALUE = getValue();
private static boolean getValue() {
return AccessController.doPrivileged(
(PrivilegedAction<Boolean>)
() -> Boolean.getBoolean("jdk.crypto.KeyAgreement.legacyKDF"));
}
}
/**
* Empty constructor
*/
public DHKeyAgreement() {
}
/**
* Initializes this key agreement with the given key and source of
* randomness. The given key is required to contain all the algorithm
* parameters required for this key agreement.
*
* <p> If the key agreement algorithm requires random bytes, it gets them
* from the given source of randomness, <code>random</code>.
* However, if the underlying
* algorithm implementation does not require any random bytes,
* <code>random</code> is ignored.
*
* @param key the party's private information. For example, in the case
* of the Diffie-Hellman key agreement, this would be the party's own
* Diffie-Hellman private key.
* @param random the source of randomness
*
* @exception InvalidKeyException if the given key is
* inappropriate for this key agreement, e.g., is of the wrong type or
* has an incompatible algorithm type.
*/
protected void engineInit(Key key, SecureRandom random)
throws InvalidKeyException
{
try {
engineInit(key, null, random);
} catch (InvalidAlgorithmParameterException e) {
// never happens, because we did not pass any parameters
}
}
/**
* Initializes this key agreement with the given key, set of
* algorithm parameters, and source of randomness.
*
* @param key the party's private information. For example, in the case
* of the Diffie-Hellman key agreement, this would be the party's own
* Diffie-Hellman private key.
* @param params the key agreement parameters
* @param random the source of randomness
*
* @exception InvalidKeyException if the given key is
* inappropriate for this key agreement, e.g., is of the wrong type or
* has an incompatible algorithm type.
* @exception InvalidAlgorithmParameterException if the given parameters
* are inappropriate for this key agreement.
*/
protected void engineInit(Key key, AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
// ignore "random" parameter, because our implementation does not
// require any source of randomness
generateSecret = false;
init_p = null;
init_g = null;
if ((params != null) && !(params instanceof DHParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Diffie-Hellman parameters expected");
}
if (!(key instanceof javax.crypto.interfaces.DHPrivateKey)) {
throw new InvalidKeyException("Diffie-Hellman private key "
+ "expected");
}
javax.crypto.interfaces.DHPrivateKey dhPrivKey;
dhPrivKey = (javax.crypto.interfaces.DHPrivateKey)key;
// check if private key parameters are compatible with
// initialized ones
if (params != null) {
init_p = ((DHParameterSpec)params).getP();
init_g = ((DHParameterSpec)params).getG();
}
BigInteger priv_p = dhPrivKey.getParams().getP();
BigInteger priv_g = dhPrivKey.getParams().getG();
if (init_p != null && priv_p != null && !(init_p.equals(priv_p))) {
throw new InvalidKeyException("Incompatible parameters");
}
if (init_g != null && priv_g != null && !(init_g.equals(priv_g))) {
throw new InvalidKeyException("Incompatible parameters");
}
if ((init_p == null && priv_p == null)
|| (init_g == null && priv_g == null)) {
throw new InvalidKeyException("Missing parameters");
}
init_p = priv_p;
init_g = priv_g;
// store the x value
this.x = dhPrivKey.getX();
}
/**
* Executes the next phase of this key agreement with the given
* key that was received from one of the other parties involved in this key
* agreement.
*
* @param key the key for this phase. For example, in the case of
* Diffie-Hellman between 2 parties, this would be the other party's
* Diffie-Hellman public key.
* @param lastPhase flag which indicates whether or not this is the last
* phase of this key agreement.
*
* @return the (intermediate) key resulting from this phase, or null if
* this phase does not yield a key
*
* @exception InvalidKeyException if the given key is inappropriate for
* this phase.
* @exception IllegalStateException if this key agreement has not been
* initialized.
*/
protected Key engineDoPhase(Key key, boolean lastPhase)
throws InvalidKeyException, IllegalStateException
{
if (!(key instanceof javax.crypto.interfaces.DHPublicKey)) {
throw new InvalidKeyException("Diffie-Hellman public key "
+ "expected");
}
javax.crypto.interfaces.DHPublicKey dhPubKey;
dhPubKey = (javax.crypto.interfaces.DHPublicKey)key;
if (init_p == null || init_g == null) {
throw new IllegalStateException("Not initialized");
}
// check if public key parameters are compatible with
// initialized ones
BigInteger pub_p = dhPubKey.getParams().getP();
BigInteger pub_g = dhPubKey.getParams().getG();
if (pub_p != null && !(init_p.equals(pub_p))) {
throw new InvalidKeyException("Incompatible parameters");
}
if (pub_g != null && !(init_g.equals(pub_g))) {
throw new InvalidKeyException("Incompatible parameters");
}
// validate the Diffie-Hellman public key
KeyUtil.validate(dhPubKey);
// store the y value
this.y = dhPubKey.getY();
// we've received a public key (from one of the other parties),
// so we are ready to create the secret, which may be an
// intermediate secret, in which case we wrap it into a
// Diffie-Hellman public key object and return it.
generateSecret = true;
if (lastPhase == false) {
byte[] intermediate = engineGenerateSecret();
return new DHPublicKey(new BigInteger(1, intermediate),
init_p, init_g);
} else {
return null;
}
}
/**
* Generates the shared secret and returns it in a new buffer.
*
* <p>This method resets this <code>KeyAgreementSpi</code> object,
* so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the <code>engineInit</code> methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
*
* @return the new buffer with the shared secret
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
*/
protected byte[] engineGenerateSecret()
throws IllegalStateException
{
int expectedLen = (init_p.bitLength() + 7) >>> 3;
byte[] result = new byte[expectedLen];
try {
engineGenerateSecret(result, 0);
} catch (ShortBufferException sbe) {
// should never happen since length are identical
}
return result;
}
/**
* Generates the shared secret, and places it into the buffer
* <code>sharedSecret</code>, beginning at <code>offset</code>.
*
* <p>If the <code>sharedSecret</code> buffer is too small to hold the
* result, a <code>ShortBufferException</code> is thrown.
* In this case, this call should be repeated with a larger output buffer.
*
* <p>This method resets this <code>KeyAgreementSpi</code> object,
* so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the <code>engineInit</code> methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
*
* @param sharedSecret the buffer for the shared secret
* @param offset the offset in <code>sharedSecret</code> where the
* shared secret will be stored
*
* @return the number of bytes placed into <code>sharedSecret</code>
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
* @exception ShortBufferException if the given output buffer is too small
* to hold the secret
*/
protected int engineGenerateSecret(byte[] sharedSecret, int offset)
throws IllegalStateException, ShortBufferException
{
if (generateSecret == false) {
throw new IllegalStateException
("Key agreement has not been completed yet");
}
if (sharedSecret == null) {
throw new ShortBufferException
("No buffer provided for shared secret");
}
BigInteger modulus = init_p;
int expectedLen = (modulus.bitLength() + 7) >>> 3;
if ((sharedSecret.length - offset) < expectedLen) {
throw new ShortBufferException
("Buffer too short for shared secret");
}
// Reset the key agreement after checking for ShortBufferException
// above, so user can recover w/o losing internal state
generateSecret = false;
/*
* NOTE: BigInteger.toByteArray() returns a byte array containing
* the two's-complement representation of this BigInteger with
* the most significant byte is in the zeroth element. This
* contains the minimum number of bytes required to represent
* this BigInteger, including at least one sign bit whose value
* is always 0.
*
* Keys are always positive, and the above sign bit isn't
* actually used when representing keys. (i.e. key = new
* BigInteger(1, byteArray)) To obtain an array containing
* exactly expectedLen bytes of magnitude, we strip any extra
* leading 0's, or pad with 0's in case of a "short" secret.
*/
byte[] secret = this.y.modPow(this.x, modulus).toByteArray();
if (secret.length == expectedLen) {
System.arraycopy(secret, 0, sharedSecret, offset,
secret.length);
} else {
// Array too short, pad it w/ leading 0s
if (secret.length < expectedLen) {
System.arraycopy(secret, 0, sharedSecret,
offset + (expectedLen - secret.length),
secret.length);
} else {
// Array too long, check and trim off the excess
if ((secret.length == (expectedLen+1)) && secret[0] == 0) {
// ignore the leading sign byte
System.arraycopy(secret, 1, sharedSecret, offset, expectedLen);
} else {
throw new ProviderException("Generated secret is out-of-range");
}
}
}
return expectedLen;
}
/**
* Creates the shared secret and returns it as a secret key object
* of the requested algorithm type.
*
* <p>This method resets this <code>KeyAgreementSpi</code> object,
* so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the <code>engineInit</code> methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
*
* @param algorithm the requested secret key algorithm
*
* @return the shared secret key
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
* @exception NoSuchAlgorithmException if the requested secret key
* algorithm is not available
* @exception InvalidKeyException if the shared secret key material cannot
* be used to generate a secret key of the requested algorithm type (e.g.,
* the key material is too short)
*/
protected SecretKey engineGenerateSecret(String algorithm)
throws IllegalStateException, NoSuchAlgorithmException,
InvalidKeyException
{
if (algorithm == null) {
throw new NoSuchAlgorithmException("null algorithm");
}
if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") &&
!AllowKDF.VALUE) {
throw new NoSuchAlgorithmException("Unsupported secret key "
+ "algorithm: " + algorithm);
}
byte[] secret = engineGenerateSecret();
if (algorithm.equalsIgnoreCase("DES")) {
// DES
return new DESKey(secret);
} else if (algorithm.equalsIgnoreCase("DESede")
|| algorithm.equalsIgnoreCase("TripleDES")) {
// Triple DES
return new DESedeKey(secret);
} else if (algorithm.equalsIgnoreCase("Blowfish")) {
// Blowfish
int keysize = secret.length;
if (keysize >= BlowfishConstants.BLOWFISH_MAX_KEYSIZE)
keysize = BlowfishConstants.BLOWFISH_MAX_KEYSIZE;
SecretKeySpec skey = new SecretKeySpec(secret, 0, keysize,
"Blowfish");
return skey;
} else if (algorithm.equalsIgnoreCase("AES")) {
// AES
int keysize = secret.length;
SecretKeySpec skey = null;
int idx = AESConstants.AES_KEYSIZES.length - 1;
while (skey == null && idx >= 0) {
// Generate the strongest key using the shared secret
// assuming the key sizes in AESConstants class are
// in ascending order
if (keysize >= AESConstants.AES_KEYSIZES[idx]) {
keysize = AESConstants.AES_KEYSIZES[idx];
skey = new SecretKeySpec(secret, 0, keysize, "AES");
}
idx--;
}
if (skey == null) {
throw new InvalidKeyException("Key material is too short");
}
return skey;
} else if (algorithm.equals("TlsPremasterSecret")) {
// remove leading zero bytes per RFC 5246 Section 8.1.2
return new SecretKeySpec(
KeyUtil.trimZeroes(secret), "TlsPremasterSecret");
} else {
throw new NoSuchAlgorithmException("Unsupported secret key "
+ "algorithm: "+ algorithm);
}
}
}

View File

@@ -0,0 +1,233 @@
/*
* Copyright (c) 1997, 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.security.Key;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.KeyFactorySpi;
import java.security.InvalidKeyException;
import java.security.spec.KeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.DHPrivateKeySpec;
import javax.crypto.spec.DHParameterSpec;
/**
* This class implements the Diffie-Hellman key factory of the Sun provider.
*
* @author Jan Luehe
*
*/
public final class DHKeyFactory extends KeyFactorySpi {
/**
* Empty constructor
*/
public DHKeyFactory() {
}
/**
* Generates a public key object from the provided key specification
* (key material).
*
* @param keySpec the specification (key material) of the public key
*
* @return the public key
*
* @exception InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
protected PublicKey engineGeneratePublic(KeySpec keySpec)
throws InvalidKeySpecException
{
try {
if (keySpec instanceof DHPublicKeySpec) {
DHPublicKeySpec dhPubKeySpec = (DHPublicKeySpec)keySpec;
return new DHPublicKey(dhPubKeySpec.getY(),
dhPubKeySpec.getP(),
dhPubKeySpec.getG());
} else if (keySpec instanceof X509EncodedKeySpec) {
return new DHPublicKey
(((X509EncodedKeySpec)keySpec).getEncoded());
} else {
throw new InvalidKeySpecException
("Inappropriate key specification");
}
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException
("Inappropriate key specification", e);
}
}
/**
* Generates a private key object from the provided key specification
* (key material).
*
* @param keySpec the specification (key material) of the private key
*
* @return the private key
*
* @exception InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a private key.
*/
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
throws InvalidKeySpecException
{
try {
if (keySpec instanceof DHPrivateKeySpec) {
DHPrivateKeySpec dhPrivKeySpec = (DHPrivateKeySpec)keySpec;
return new DHPrivateKey(dhPrivKeySpec.getX(),
dhPrivKeySpec.getP(),
dhPrivKeySpec.getG());
} else if (keySpec instanceof PKCS8EncodedKeySpec) {
return new DHPrivateKey
(((PKCS8EncodedKeySpec)keySpec).getEncoded());
} else {
throw new InvalidKeySpecException
("Inappropriate key specification");
}
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException
("Inappropriate key specification", e);
}
}
/**
* Returns a specification (key material) of the given key object
* in the requested format.
*
* @param key the key
*
* @param keySpec the requested format in which the key material shall be
* returned
*
* @return the underlying key specification (key material) in the
* requested format
*
* @exception InvalidKeySpecException if the requested key specification is
* inappropriate for the given key, or the given key cannot be processed
* (e.g., the given key has an unrecognized algorithm or format).
*/
protected <T extends KeySpec>
T engineGetKeySpec(Key key, Class<T> keySpec)
throws InvalidKeySpecException {
DHParameterSpec params;
if (key instanceof javax.crypto.interfaces.DHPublicKey) {
if (keySpec.isAssignableFrom(DHPublicKeySpec.class)) {
javax.crypto.interfaces.DHPublicKey dhPubKey
= (javax.crypto.interfaces.DHPublicKey) key;
params = dhPubKey.getParams();
return keySpec.cast(new DHPublicKeySpec(dhPubKey.getY(),
params.getP(),
params.getG()));
} else if (keySpec.isAssignableFrom(X509EncodedKeySpec.class)) {
return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
} else {
throw new InvalidKeySpecException
("Inappropriate key specification");
}
} else if (key instanceof javax.crypto.interfaces.DHPrivateKey) {
if (keySpec.isAssignableFrom(DHPrivateKeySpec.class)) {
javax.crypto.interfaces.DHPrivateKey dhPrivKey
= (javax.crypto.interfaces.DHPrivateKey)key;
params = dhPrivKey.getParams();
return keySpec.cast(new DHPrivateKeySpec(dhPrivKey.getX(),
params.getP(),
params.getG()));
} else if (keySpec.isAssignableFrom(PKCS8EncodedKeySpec.class)) {
return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded()));
} else {
throw new InvalidKeySpecException
("Inappropriate key specification");
}
} else {
throw new InvalidKeySpecException("Inappropriate key type");
}
}
/**
* Translates a key object, whose provider may be unknown or potentially
* untrusted, into a corresponding key object of this key factory.
*
* @param key the key whose provider is unknown or untrusted
*
* @return the translated key
*
* @exception InvalidKeyException if the given key cannot be processed by
* this key factory.
*/
protected Key engineTranslateKey(Key key)
throws InvalidKeyException
{
try {
if (key instanceof javax.crypto.interfaces.DHPublicKey) {
// Check if key originates from this factory
if (key instanceof com.sun.crypto.provider.DHPublicKey) {
return key;
}
// Convert key to spec
DHPublicKeySpec dhPubKeySpec
= engineGetKeySpec(key, DHPublicKeySpec.class);
// Create key from spec, and return it
return engineGeneratePublic(dhPubKeySpec);
} else if (key instanceof javax.crypto.interfaces.DHPrivateKey) {
// Check if key originates from this factory
if (key instanceof com.sun.crypto.provider.DHPrivateKey) {
return key;
}
// Convert key to spec
DHPrivateKeySpec dhPrivKeySpec
= engineGetKeySpec(key, DHPrivateKeySpec.class);
// Create key from spec, and return it
return engineGeneratePrivate(dhPrivKeySpec);
} else {
throw new InvalidKeyException("Wrong algorithm type");
}
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException("Cannot translate key", e);
}
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 1997, 2017, 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.math.BigInteger;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHGenParameterSpec;
import sun.security.provider.ParameterCache;
import static sun.security.util.SecurityProviderConstants.DEF_DH_KEY_SIZE;
import static sun.security.util.SecurityProviderConstants.getDefDHPrivateExpSize;
/**
* This class represents the key pair generator for Diffie-Hellman key pairs.
*
* <p>This key pair generator may be initialized in two different ways:
*
* <ul>
* <li>By providing the size in bits of the prime modulus -
* This will be used to create a prime modulus and base generator, which will
* then be used to create the Diffie-Hellman key pair.
* <li>By providing a prime modulus and base generator
* </ul>
*
* @author Jan Luehe
*
*
* @see java.security.KeyPairGenerator
*/
public final class DHKeyPairGenerator extends KeyPairGeneratorSpi {
// parameters to use or null if not specified
private DHParameterSpec params;
// The size in bits of the prime modulus
private int pSize;
// The source of randomness
private SecureRandom random;
public DHKeyPairGenerator() {
super();
initialize(DEF_DH_KEY_SIZE, null);
}
// pkg private; used by DHParameterGenerator class as well
static void checkKeySize(int keysize, int expSize)
throws InvalidParameterException {
if ((keysize < 512) || (keysize > 8192) || ((keysize & 0x3F) != 0)) {
throw new InvalidParameterException(
"DH key size must be multiple of 64, and can only range " +
"from 512 to 8192 (inclusive). " +
"The specific key size " + keysize + " is not supported");
}
// optional, could be 0 if not specified
if ((expSize < 0) || (expSize > keysize)) {
throw new InvalidParameterException
("Exponent size must be positive and no larger than" +
" modulus size");
}
}
/**
* Initializes this key pair generator for a certain keysize and source of
* randomness.
* The keysize is specified as the size in bits of the prime modulus.
*
* @param keysize the keysize (size of prime modulus) in bits
* @param random the source of randomness
*/
public void initialize(int keysize, SecureRandom random) {
checkKeySize(keysize, 0);
try {
// Use the built-in parameters (ranging from 512 to 8192)
// when available.
this.params = ParameterCache.getDHParameterSpec(keysize, random);
} catch (GeneralSecurityException e) {
throw new InvalidParameterException(e.getMessage());
}
this.pSize = keysize;
this.random = random;
}
/**
* Initializes this key pair generator for the specified parameter
* set and source of randomness.
*
* <p>The given parameter set contains the prime modulus, the base
* generator, and optionally the requested size in bits of the random
* exponent (private value).
*
* @param algParams the parameter set used to generate the key pair
* @param random the source of randomness
*
* @exception InvalidAlgorithmParameterException if the given parameters
* are inappropriate for this key pair generator
*/
public void initialize(AlgorithmParameterSpec algParams,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (!(algParams instanceof DHParameterSpec)){
throw new InvalidAlgorithmParameterException
("Inappropriate parameter type");
}
params = (DHParameterSpec) algParams;
pSize = params.getP().bitLength();
try {
checkKeySize(pSize, params.getL());
} catch (InvalidParameterException ipe) {
throw new InvalidAlgorithmParameterException(ipe.getMessage());
}
this.random = random;
}
/**
* Generates a key pair.
*
* @return the new key pair
*/
public KeyPair generateKeyPair() {
if (random == null) {
random = SunJCE.getRandom();
}
BigInteger p = params.getP();
BigInteger g = params.getG();
int lSize = params.getL();
if (lSize == 0) { // not specified; use our own default
lSize = getDefDHPrivateExpSize(params);
}
BigInteger x;
BigInteger pMinus2 = p.subtract(BigInteger.valueOf(2));
//
// PKCS#3 section 7.1 "Private-value generation"
// Repeat if either of the followings does not hold:
// 0 < x < p-1
// 2^(lSize-1) <= x < 2^(lSize)
//
do {
// generate random x up to 2^lSize bits long
x = new BigInteger(lSize, random);
} while ((x.compareTo(BigInteger.ONE) < 0) ||
((x.compareTo(pMinus2) > 0)) || (x.bitLength() != lSize));
// calculate public value y
BigInteger y = g.modPow(x, p);
DHPublicKey pubKey = new DHPublicKey(y, p, g, lSize);
DHPrivateKey privKey = new DHPrivateKey(x, p, g, lSize);
return new KeyPair(pubKey, privKey);
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 1997, 2022, 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.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHGenParameterSpec;
import static sun.security.util.SecurityProviderConstants.DEF_DH_KEY_SIZE;
/*
* This class generates parameters for the Diffie-Hellman algorithm.
* The parameters are a prime, a base, and optionally the length in bits of
* the private value.
*
* <p>The Diffie-Hellman parameter generation accepts the size in bits of the
* prime modulus and the size in bits of the random exponent as input.
*
* @author Jan Luehe
*
*
* @see java.security.AlgorithmParameters
* @see java.security.spec.AlgorithmParameterSpec
* @see DHParameters
*/
public final class DHParameterGenerator extends AlgorithmParameterGeneratorSpi {
// The size in bits of the prime modulus
private int primeSize = DEF_DH_KEY_SIZE;
// The size in bits of the random exponent (private value)
private int exponentSize = 0;
// The source of randomness
private SecureRandom random = null;
private static void checkSupport(int keysize, int exponentSize)
throws InvalidParameterException {
boolean supported = ((keysize == 2048) || (keysize == 3072) ||
((keysize >= 512) && (keysize <= 1024) && ((keysize & 0x3F) == 0)));
if (!supported) {
throw new InvalidParameterException(
"Supported DH key size must be multiple of 64 and range " +
"from 512 to 1024 (inclusive), or 2048, 3072. " +
"The specified key size " + keysize + " is not supported");
}
if (exponentSize != 0) {
DHKeyPairGenerator.checkKeySize(keysize, exponentSize);
}
}
/**
* Initializes this parameter generator for a certain keysize
* and source of randomness.
* The keysize is specified as the size in bits of the prime modulus.
*
* @param keysize the keysize (size of prime modulus) in bits
* @param random the source of randomness
*/
@Override
protected void engineInit(int keysize, SecureRandom random) {
checkSupport(keysize, 0);
this.primeSize = keysize;
this.random = random;
}
/**
* Initializes this parameter generator with a set of parameter
* generation values, which specify the size of the prime modulus and
* the size of the random exponent, both in bits.
*
* @param genParamSpec the set of parameter generation values
* @param random the source of randomness
*
* @exception InvalidAlgorithmParameterException if the given parameter
* generation values are inappropriate for this parameter generator
*/
@Override
protected void engineInit(AlgorithmParameterSpec genParamSpec,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (!(genParamSpec instanceof DHGenParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Inappropriate parameter type");
}
DHGenParameterSpec dhParamSpec = (DHGenParameterSpec) genParamSpec;
int primeSize = dhParamSpec.getPrimeSize();
int exponentSize = dhParamSpec.getExponentSize();
try {
checkSupport(primeSize, exponentSize);
} catch (InvalidParameterException ipe) {
throw new InvalidAlgorithmParameterException(ipe.getMessage());
}
this.primeSize = primeSize;
this.exponentSize = exponentSize;
this.random = random;
}
/**
* Generates the parameters.
*
* @return the new AlgorithmParameters object
*/
@Override
protected AlgorithmParameters engineGenerateParameters() {
if (this.exponentSize == 0) {
this.exponentSize = this.primeSize - 1;
}
if (random == null) {
random = SunJCE.getRandom();
}
BigInteger paramP = null;
BigInteger paramG = null;
try {
AlgorithmParameterGenerator dsaParamGen =
AlgorithmParameterGenerator.getInstance("DSA");
dsaParamGen.init(primeSize, random);
AlgorithmParameters dsaParams = dsaParamGen.generateParameters();
DSAParameterSpec dsaParamSpec =
dsaParams.getParameterSpec(DSAParameterSpec.class);
DHParameterSpec dhParamSpec;
if (this.exponentSize > 0) {
dhParamSpec = new DHParameterSpec(dsaParamSpec.getP(),
dsaParamSpec.getG(),
this.exponentSize);
} else {
dhParamSpec = new DHParameterSpec(dsaParamSpec.getP(),
dsaParamSpec.getG());
}
AlgorithmParameters algParams =
AlgorithmParameters.getInstance("DH", SunJCE.getInstance());
algParams.init(dhParamSpec);
return algParams;
} catch (Exception ex) {
throw new ProviderException("Unexpected exception", ex);
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 1997, 2011, 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.io.*;
import sun.security.util.*;
import java.math.BigInteger;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.spec.DHParameterSpec;
/**
* This class implements the parameter set used by the
* Diffie-Hellman key agreement as defined in the PKCS #3 standard.
*
* @author Jan Luehe
*
*/
public final class DHParameters extends AlgorithmParametersSpi {
// The prime (p).
private BigInteger p = BigInteger.ZERO;
// The base (g).
private BigInteger g = BigInteger.ZERO;
// The private-value length (l)
private int l = 0;
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
if (!(paramSpec instanceof DHParameterSpec)) {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
this.p = ((DHParameterSpec)paramSpec).getP();
this.g = ((DHParameterSpec)paramSpec).getG();
this.l = ((DHParameterSpec)paramSpec).getL();
}
protected void engineInit(byte[] params) throws IOException {
try {
DerValue encodedParams = new DerValue(params);
if (encodedParams.tag != DerValue.tag_Sequence) {
throw new IOException("DH params parsing error");
}
encodedParams.data.reset();
this.p = encodedParams.data.getBigInteger();
this.g = encodedParams.data.getBigInteger();
// Private-value length is OPTIONAL
if (encodedParams.data.available() != 0) {
this.l = encodedParams.data.getInteger();
}
if (encodedParams.data.available() != 0) {
throw new IOException
("DH parameter parsing error: Extra data");
}
} catch (NumberFormatException e) {
throw new IOException("Private-value length too big");
}
}
protected void engineInit(byte[] params, String decodingMethod)
throws IOException {
engineInit(params);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException {
if (DHParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast(new DHParameterSpec(this.p, this.g, this.l));
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter Specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
DerOutputStream out = new DerOutputStream();
DerOutputStream bytes = new DerOutputStream();
bytes.putInteger(this.p);
bytes.putInteger(this.g);
// Private-value length is OPTIONAL
if (this.l > 0) {
bytes.putInteger(this.l);
}
out.write(DerValue.tag_Sequence, bytes);
return out.toByteArray();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException {
return engineGetEncoded();
}
/*
* Returns a formatted string describing the parameters.
*/
protected String engineToString() {
String LINE_SEP = System.getProperty("line.separator");
StringBuffer strbuf
= new StringBuffer("SunJCE Diffie-Hellman Parameters:"
+ LINE_SEP + "p:" + LINE_SEP
+ Debug.toHexString(this.p)
+ LINE_SEP + "g:" + LINE_SEP
+ Debug.toHexString(this.g));
if (this.l != 0)
strbuf.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l);
return strbuf.toString();
}
}

View File

@@ -0,0 +1,343 @@
/*
* Copyright (c) 1997, 2023, 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.io.*;
import java.util.Objects;
import java.math.BigInteger;
import java.security.KeyRep;
import java.security.PrivateKey;
import java.security.InvalidKeyException;
import java.security.ProviderException;
import javax.crypto.spec.DHParameterSpec;
import sun.security.util.*;
/**
* A private key in PKCS#8 format for the Diffie-Hellman key agreement
* algorithm.
*
* @author Jan Luehe
* @see DHPublicKey
* @see java.security.KeyAgreement
*/
final class DHPrivateKey implements PrivateKey,
javax.crypto.interfaces.DHPrivateKey, Serializable {
private static final long serialVersionUID = 7565477590005668886L;
// only supported version of PKCS#8 PrivateKeyInfo
private static final BigInteger PKCS8_VERSION = BigInteger.ZERO;
// the private key
private BigInteger x;
// the key bytes, without the algorithm information
private byte[] key;
// the encoded key
private byte[] encodedKey;
// the prime modulus
private final BigInteger p;
// the base generator
private final BigInteger g;
// the private-value length (optional)
private int l;
private int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 };
/**
* Make a DH private key out of a private value <code>x</code>, a prime
* modulus <code>p</code>, and a base generator <code>g</code>.
*
* @param x the private value
* @param p the prime modulus
* @param g the base generator
*
* @exception ProviderException if the key cannot be encoded
*/
DHPrivateKey(BigInteger x, BigInteger p, BigInteger g)
throws InvalidKeyException {
this(x, p, g, 0);
}
/**
* Make a DH private key out of a private value <code>x</code>, a prime
* modulus <code>p</code>, a base generator <code>g</code>, and a
* private-value length <code>l</code>.
*
* @param x the private value
* @param p the prime modulus
* @param g the base generator
* @param l the private-value length
*
* @exception InvalidKeyException if the key cannot be encoded
*/
DHPrivateKey(BigInteger x, BigInteger p, BigInteger g, int l) {
this.x = x;
this.p = p;
this.g = g;
this.l = l;
try {
this.key = new DerValue(DerValue.tag_Integer,
this.x.toByteArray()).toByteArray();
this.encodedKey = getEncoded();
} catch (IOException e) {
throw new ProviderException("Cannot produce ASN.1 encoding", e);
}
}
/**
* Make a DH private key from its DER encoding (PKCS #8).
*
* @param encodedKey the encoded key
*
* @exception InvalidKeyException if the encoded key does not represent
* a Diffie-Hellman private key
*/
DHPrivateKey(byte[] encodedKey) throws InvalidKeyException {
InputStream inStream = new ByteArrayInputStream(encodedKey);
try {
DerValue val = new DerValue(inStream);
if (val.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException ("Key not a SEQUENCE");
}
//
// version
//
BigInteger parsedVersion = val.data.getBigInteger();
if (!parsedVersion.equals(PKCS8_VERSION)) {
throw new IOException("version mismatch: (supported: " +
PKCS8_VERSION + ", parsed: " +
parsedVersion);
}
//
// privateKeyAlgorithm
//
DerValue algid = val.data.getDerValue();
if (algid.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException("AlgId is not a SEQUENCE");
}
DerInputStream derInStream = algid.toDerInputStream();
ObjectIdentifier oid = derInStream.getOID();
if (oid == null) {
throw new InvalidKeyException("Null OID");
}
if (derInStream.available() == 0) {
throw new InvalidKeyException("Parameters missing");
}
// parse the parameters
DerValue params = derInStream.getDerValue();
if (params.tag == DerValue.tag_Null) {
throw new InvalidKeyException("Null parameters");
}
if (params.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException("Parameters not a SEQUENCE");
}
params.data.reset();
this.p = params.data.getBigInteger();
this.g = params.data.getBigInteger();
// Private-value length is OPTIONAL
if (params.data.available() != 0) {
this.l = params.data.getInteger();
}
if (params.data.available() != 0) {
throw new InvalidKeyException("Extra parameter data");
}
//
// privateKey
//
this.key = val.data.getOctetString();
parseKeyBits();
this.encodedKey = encodedKey.clone();
} catch (IOException | NumberFormatException e) {
throw new InvalidKeyException("Error parsing key encoding", e);
}
}
/**
* Returns the encoding format of this key: "PKCS#8"
*/
public String getFormat() {
return "PKCS#8";
}
/**
* Returns the name of the algorithm associated with this key: "DH"
*/
public String getAlgorithm() {
return "DH";
}
/**
* Get the encoding of the key.
*/
public synchronized byte[] getEncoded() {
if (this.encodedKey == null) {
try {
DerOutputStream tmp = new DerOutputStream();
//
// version
//
tmp.putInteger(PKCS8_VERSION);
//
// privateKeyAlgorithm
//
DerOutputStream algid = new DerOutputStream();
// store OID
algid.putOID(new ObjectIdentifier(DH_data));
// encode parameters
DerOutputStream params = new DerOutputStream();
params.putInteger(this.p);
params.putInteger(this.g);
if (this.l != 0) {
params.putInteger(this.l);
}
// wrap parameters into SEQUENCE
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
params.toByteArray());
// store parameter SEQUENCE in algid
algid.putDerValue(paramSequence);
// wrap algid into SEQUENCE
tmp.write(DerValue.tag_Sequence, algid);
// privateKey
tmp.putOctetString(this.key);
// make it a SEQUENCE
DerOutputStream derKey = new DerOutputStream();
derKey.write(DerValue.tag_Sequence, tmp);
this.encodedKey = derKey.toByteArray();
} catch (IOException e) {
return null;
}
}
return this.encodedKey.clone();
}
/**
* Returns the private value, <code>x</code>.
*
* @return the private value, <code>x</code>
*/
public BigInteger getX() {
return this.x;
}
/**
* Returns the key parameters.
*
* @return the key parameters
*/
public DHParameterSpec getParams() {
if (this.l != 0) {
return new DHParameterSpec(this.p, this.g, this.l);
} else {
return new DHParameterSpec(this.p, this.g);
}
}
private void parseKeyBits() throws InvalidKeyException {
try {
DerInputStream in = new DerInputStream(this.key);
this.x = in.getBigInteger();
} catch (IOException e) {
InvalidKeyException ike = new InvalidKeyException(
"Error parsing key encoding: " + e.getMessage());
ike.initCause(e);
throw ike;
}
}
/**
* Calculates a hash code value for the object.
* Objects that are equal will also have the same hashcode.
*/
public int hashCode() {
return Objects.hash(x, p, g);
}
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof javax.crypto.interfaces.DHPrivateKey)) {
return false;
}
javax.crypto.interfaces.DHPrivateKey other =
(javax.crypto.interfaces.DHPrivateKey) obj;
DHParameterSpec otherParams = other.getParams();
return ((this.x.compareTo(other.getX()) == 0) &&
(this.p.compareTo(otherParams.getP()) == 0) &&
(this.g.compareTo(otherParams.getG()) == 0));
}
/**
* Replace the DH private key to be serialized.
*
* @return the standard KeyRep object to be serialized
*
* @throws java.io.ObjectStreamException if a new object representing
* this DH private key could not be created
*/
private Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.PRIVATE,
getAlgorithm(),
getFormat(),
getEncoded());
}
/**
* Restores the state of this object from the stream.
* <p>
* JDK 1.5+ objects use <code>KeyRep</code>s instead.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
if ((key == null) || (key.length == 0)) {
throw new InvalidObjectException("key not deserializable");
}
this.key = key.clone();
if ((encodedKey == null) || (encodedKey.length == 0)) {
throw new InvalidObjectException(
"encoded key not deserializable");
}
this.encodedKey = encodedKey.clone();
}
}

View File

@@ -0,0 +1,344 @@
/*
* Copyright (c) 1997, 2023, 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.io.*;
import java.util.Objects;
import java.math.BigInteger;
import java.security.KeyRep;
import java.security.InvalidKeyException;
import java.security.ProviderException;
import java.security.PublicKey;
import javax.crypto.spec.DHParameterSpec;
import sun.security.util.*;
/**
* A public key in X.509 format for the Diffie-Hellman key agreement algorithm.
*
* @author Jan Luehe
* @see DHPrivateKey
* @see java.security.KeyAgreement
*/
final class DHPublicKey implements PublicKey,
javax.crypto.interfaces.DHPublicKey, Serializable {
private static final long serialVersionUID = 7647557958927458271L;
// the public key
private BigInteger y;
// the key bytes, without the algorithm information
private byte[] key;
// the encoded key
private byte[] encodedKey;
// the prime modulus
private final BigInteger p;
// the base generator
private final BigInteger g;
// the private-value length (optional)
private int l;
private int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 };
/**
* Make a DH public key out of a public value <code>y</code>, a prime
* modulus <code>p</code>, and a base generator <code>g</code>.
*
* @param y the public value
* @param p the prime modulus
* @param g the base generator
*
* @exception InvalidKeyException if the key cannot be encoded
*/
DHPublicKey(BigInteger y, BigInteger p, BigInteger g)
throws InvalidKeyException {
this(y, p, g, 0);
}
/**
* Make a DH public key out of a public value <code>y</code>, a prime
* modulus <code>p</code>, a base generator <code>g</code>, and a
* private-value length <code>l</code>.
*
* @param y the public value
* @param p the prime modulus
* @param g the base generator
* @param l the private-value length
*
* @exception ProviderException if the key cannot be encoded
*/
DHPublicKey(BigInteger y, BigInteger p, BigInteger g, int l) {
this.y = y;
this.p = p;
this.g = g;
this.l = l;
try {
this.key = new DerValue(DerValue.tag_Integer,
this.y.toByteArray()).toByteArray();
this.encodedKey = getEncoded();
} catch (IOException e) {
throw new ProviderException("Cannot produce ASN.1 encoding", e);
}
}
/**
* Make a DH public key from its DER encoding (X.509).
*
* @param encodedKey the encoded key
*
* @exception InvalidKeyException if the encoded key does not represent
* a Diffie-Hellman public key
*/
DHPublicKey(byte[] encodedKey) throws InvalidKeyException {
InputStream inStream = new ByteArrayInputStream(encodedKey);
try {
DerValue derKeyVal = new DerValue(inStream);
if (derKeyVal.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException ("Invalid key format");
}
/*
* Parse the algorithm identifier
*/
DerValue algid = derKeyVal.data.getDerValue();
if (algid.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException("AlgId is not a SEQUENCE");
}
DerInputStream derInStream = algid.toDerInputStream();
ObjectIdentifier oid = derInStream.getOID();
if (oid == null) {
throw new InvalidKeyException("Null OID");
}
if (derInStream.available() == 0) {
throw new InvalidKeyException("Parameters missing");
}
/*
* Parse the parameters
*/
DerValue params = derInStream.getDerValue();
if (params.tag == DerValue.tag_Null) {
throw new InvalidKeyException("Null parameters");
}
if (params.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException("Parameters not a SEQUENCE");
}
params.data.reset();
this.p = params.data.getBigInteger();
this.g = params.data.getBigInteger();
// Private-value length is OPTIONAL
if (params.data.available() != 0) {
this.l = params.data.getInteger();
}
if (params.data.available() != 0) {
throw new InvalidKeyException("Extra parameter data");
}
/*
* Parse the key
*/
this.key = derKeyVal.data.getBitString();
parseKeyBits();
if (derKeyVal.data.available() != 0) {
throw new InvalidKeyException("Excess key data");
}
this.encodedKey = encodedKey.clone();
} catch (IOException | NumberFormatException e) {
throw new InvalidKeyException("Error parsing key encoding", e);
}
}
/**
* Returns the encoding format of this key: "X.509"
*/
public String getFormat() {
return "X.509";
}
/**
* Returns the name of the algorithm associated with this key: "DH"
*/
public String getAlgorithm() {
return "DH";
}
/**
* Get the encoding of the key.
*/
public synchronized byte[] getEncoded() {
if (this.encodedKey == null) {
try {
DerOutputStream algid = new DerOutputStream();
// store oid in algid
algid.putOID(new ObjectIdentifier(DH_data));
// encode parameters
DerOutputStream params = new DerOutputStream();
params.putInteger(this.p);
params.putInteger(this.g);
if (this.l != 0) {
params.putInteger(this.l);
}
// wrap parameters into SEQUENCE
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
params.toByteArray());
// store parameter SEQUENCE in algid
algid.putDerValue(paramSequence);
// wrap algid into SEQUENCE, and store it in key encoding
DerOutputStream tmpDerKey = new DerOutputStream();
tmpDerKey.write(DerValue.tag_Sequence, algid);
// store key data
tmpDerKey.putBitString(this.key);
// wrap algid and key into SEQUENCE
DerOutputStream derKey = new DerOutputStream();
derKey.write(DerValue.tag_Sequence, tmpDerKey);
this.encodedKey = derKey.toByteArray();
} catch (IOException e) {
return null;
}
}
return this.encodedKey.clone();
}
/**
* Returns the public value, <code>y</code>.
*
* @return the public value, <code>y</code>
*/
public BigInteger getY() {
return this.y;
}
/**
* Returns the key parameters.
*
* @return the key parameters
*/
public DHParameterSpec getParams() {
if (this.l != 0) {
return new DHParameterSpec(this.p, this.g, this.l);
} else {
return new DHParameterSpec(this.p, this.g);
}
}
public String toString() {
String LINE_SEP = System.getProperty("line.separator");
StringBuffer strbuf
= new StringBuffer("SunJCE Diffie-Hellman Public Key:"
+ LINE_SEP + "y:" + LINE_SEP
+ Debug.toHexString(this.y)
+ LINE_SEP + "p:" + LINE_SEP
+ Debug.toHexString(this.p)
+ LINE_SEP + "g:" + LINE_SEP
+ Debug.toHexString(this.g));
if (this.l != 0)
strbuf.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l);
return strbuf.toString();
}
private void parseKeyBits() throws InvalidKeyException {
try {
DerInputStream in = new DerInputStream(this.key);
this.y = in.getBigInteger();
} catch (IOException e) {
throw new InvalidKeyException(
"Error parsing key encoding: " + e.toString());
}
}
/**
* Calculates a hash code value for the object.
* Objects that are equal will also have the same hashcode.
*/
public int hashCode() {
return Objects.hash(y, p, g);
}
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof javax.crypto.interfaces.DHPublicKey)) {
return false;
}
javax.crypto.interfaces.DHPublicKey other =
(javax.crypto.interfaces.DHPublicKey) obj;
DHParameterSpec otherParams = other.getParams();
return ((this.y.compareTo(other.getY()) == 0) &&
(this.p.compareTo(otherParams.getP()) == 0) &&
(this.g.compareTo(otherParams.getG()) == 0));
}
/**
* Replace the DH public key to be serialized.
*
* @return the standard KeyRep object to be serialized
*
* @throws java.io.ObjectStreamException if a new object representing
* this DH public key could not be created
*/
private Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.PUBLIC,
getAlgorithm(),
getFormat(),
getEncoded());
}
/**
* Restores the state of this object from the stream.
* <p>
* JDK 1.5+ objects use <code>KeyRep</code>s instead.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
if ((key == null) || (key.length == 0)) {
throw new InvalidObjectException("key not deserializable");
}
this.key = key.clone();
if ((encodedKey == null) || (encodedKey.length == 0)) {
throw new InvalidObjectException(
"encoded key not deserializable");
}
this.encodedKey = encodedKey.clone();
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (c) 1997, 2018, 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.security.InvalidKeyException;
import java.security.ProviderException;
/**
* This class represents ciphers in electronic codebook (ECB) mode.
*
* <p>This mode is implemented independently of a particular cipher.
* Ciphers to which this mode should apply (e.g., DES) must be
* <i>plugged-in</i> using the constructor.
*
* <p>NOTE: This class does not deal with buffering or padding.
*
* @author Gigi Ankeny
*/
final class ElectronicCodeBook extends FeedbackCipher {
ElectronicCodeBook(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
}
/**
* Gets the name of the feedback mechanism
*
* @return the name of the feedback mechanism
*/
String getFeedback() {
return "ECB";
}
/**
* 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).
*/
void reset() {
// empty
}
/**
* Save the current content of this cipher.
*/
void save() {}
/**
* Restores the content of this cipher to the previous saved one.
*/
void restore() {}
/**
* 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
*/
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException {
if ((key == null) || (iv != null)) {
throw new InvalidKeyException("Internal error");
}
embeddedCipher.init(decrypting, algorithm, key);
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>in</code>, starting at
* <code>inOff</code> and ending at * <code>(inOff + len - 1)</code>,
* is encrypted. The result is stored in <code>out</code>, starting at
* <code>outOff</code>.
*
* @param in the buffer with the input data to be encrypted
* @param inOff the offset in <code>plain</code>
* @param len the length of the input data
* @param out the buffer for the result
* @param outOff the offset in <code>cipher</code>
* @exception ProviderException if <code>len</code> is not
* a multiple of the block size
* @return the length of the encrypted data
*/
int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
RangeUtil.blockSizeCheck(len, blockSize);
RangeUtil.nullAndBoundsCheck(in, inOff, len);
RangeUtil.nullAndBoundsCheck(out, outOff, len);
for (int i = len; i >= blockSize; i -= blockSize) {
embeddedCipher.encryptBlock(in, inOff, out, outOff);
inOff += blockSize;
outOff += blockSize;
}
return len;
}
/**
* Performs decryption operation.
*
* <p>The input cipher text <code>in</code>, starting at
* <code>inOff</code> and ending at * <code>(inOff + len - 1)</code>,
* is decrypted.The result is stored in <code>out</code>, starting at
* <code>outOff</code>.
*
* @param in the buffer with the input data to be decrypted
* @param inOff the offset in <code>cipherOffset</code>
* @param len the length of the input data
* @param out the buffer for the result
* @param outOff the offset in <code>plain</code>
* @exception ProviderException if <code>len</code> is not
* a multiple of the block size
* @return the length of the decrypted data
*/
int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
RangeUtil.blockSizeCheck(len, blockSize);
RangeUtil.nullAndBoundsCheck(in, inOff, len);
RangeUtil.nullAndBoundsCheck(out, outOff, len);
for (int i = len; i >= blockSize; i -= blockSize) {
embeddedCipher.decryptBlock(in, inOff, out, outOff);
inOff += blockSize;
outOff += blockSize;
}
return len;
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 1998, 2011, 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.io.*;
import sun.security.x509.AlgorithmId;
import sun.security.util.*;
/**
* This class implements the <code>EncryptedPrivateKeyInfo</code> type,
* which is defined in PKCS #8 as follows:
*
* <pre>
* EncryptedPrivateKeyInfo ::= SEQUENCE {
* encryptionAlgorithm AlgorithmIdentifier,
* encryptedData OCTET STRING }
* </pre>
*
* @author Jan Luehe
*/
final class EncryptedPrivateKeyInfo {
// the "encryptionAlgorithm" field
private AlgorithmId algid;
// the "encryptedData" field
private byte[] encryptedData;
// the ASN.1 encoded contents of this class
private byte[] encoded;
/**
* Constructs (i.e., parses) an <code>EncryptedPrivateKeyInfo</code> from
* its encoding.
*/
EncryptedPrivateKeyInfo(byte[] encoded) throws IOException {
DerValue val = new DerValue(encoded);
DerValue[] seq = new DerValue[2];
seq[0] = val.data.getDerValue();
seq[1] = val.data.getDerValue();
if (val.data.available() != 0) {
throw new IOException("overrun, bytes = " + val.data.available());
}
this.algid = AlgorithmId.parse(seq[0]);
if (seq[0].data.available() != 0) {
throw new IOException("encryptionAlgorithm field overrun");
}
this.encryptedData = seq[1].getOctetString();
if (seq[1].data.available() != 0)
throw new IOException("encryptedData field overrun");
this.encoded = encoded.clone();
}
/**
* Constructs an <code>EncryptedPrivateKeyInfo</code> from the
* encryption algorithm and the encrypted data.
*/
EncryptedPrivateKeyInfo(AlgorithmId algid, byte[] encryptedData) {
this.algid = algid;
this.encryptedData = encryptedData.clone();
this.encoded = null; // lazy generation of encoding
}
/**
* Returns the encryption algorithm.
*/
AlgorithmId getAlgorithm() {
return this.algid;
}
/**
* Returns the encrypted data.
*/
byte[] getEncryptedData() {
return this.encryptedData.clone();
}
/**
* Returns the ASN.1 encoding of this class.
*/
byte[] getEncoded()
throws IOException
{
if (this.encoded != null) return this.encoded.clone();
DerOutputStream out = new DerOutputStream();
DerOutputStream tmp = new DerOutputStream();
// encode encryption algorithm
algid.encode(tmp);
// encode encrypted data
tmp.putOctetString(encryptedData);
// wrap everything into a SEQUENCE
out.write(DerValue.tag_Sequence, tmp);
this.encoded = out.toByteArray();
return this.encoded.clone();
}
}

View File

@@ -0,0 +1,245 @@
/*
* Copyright (c) 1997, 2017, 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.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
import javax.crypto.*;
/**
* This class represents a block cipher in one of its modes. It wraps
* a SymmetricCipher maintaining the mode state and providing
* the capability to encrypt amounts of data larger than a single block.
*
* @author Jan Luehe
* @see ElectronicCodeBook
* @see CipherBlockChaining
* @see CipherFeedback
* @see OutputFeedback
* @see PCBC
*/
abstract class FeedbackCipher {
// the embedded block cipher
final SymmetricCipher embeddedCipher;
// the block size of the embedded block cipher
final int blockSize;
// the initialization vector
byte[] iv;
FeedbackCipher(SymmetricCipher embeddedCipher) {
this.embeddedCipher = embeddedCipher;
blockSize = embeddedCipher.getBlockSize();
}
final SymmetricCipher getEmbeddedCipher() {
return embeddedCipher;
}
/**
* Gets the block size of the embedded cipher.
*
* @return the block size of the embedded cipher
*/
final int getBlockSize() {
return blockSize;
}
/**
* Gets the name of the feedback mechanism
*
* @return the name of the feedback mechanism
*/
abstract String getFeedback();
/**
* Save the current content of this cipher.
*/
abstract void save();
/**
* Restores the content of this cipher to the previous saved one.
*/
abstract void restore();
/**
* Initializes the cipher in the specified mode with the given key
* and iv.
*
* @param decrypting flag indicating encryption or decryption mode
* @param algorithm the algorithm name (never null)
* @param key the key (never null)
* @param iv the iv (either null or blockSize bytes long)
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
*/
abstract void init(boolean decrypting, String algorithm, byte[] key,
byte[] iv) throws InvalidKeyException,
InvalidAlgorithmParameterException;
/**
* Gets the initialization vector.
*
* @return the initialization vector
*/
final byte[] getIV() {
return 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).
*/
abstract void reset();
/**
* Performs encryption operation.
*
* <p>The input <code>plain</code>, starting at <code>plainOffset</code>
* and ending at <code>(plainOffset+plainLen-1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param plain the input buffer with the data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the encryption result
* @param cipherOffset the offset in <code>cipher</code>
* @return the number of bytes placed into <code>cipher</code>
*/
abstract int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset);
/**
* Performs encryption operation for the last time.
*
* <p>NOTE: For cipher feedback modes which does not perform
* special handling for the last few blocks, this is essentially
* the same as <code>encrypt(...)</code>. Given most modes do
* not do special handling, the default impl for this method is
* to simply call <code>encrypt(...)</code>.
*
* @param plain the input buffer with the data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the encryption result
* @param cipherOffset the offset in <code>cipher</code>
* @return the number of bytes placed into <code>cipher</code>
*/
int encryptFinal(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
throws IllegalBlockSizeException, ShortBufferException {
return encrypt(plain, plainOffset, plainLen, cipher, cipherOffset);
}
/**
* Performs decryption operation.
*
* <p>The input <code>cipher</code>, starting at <code>cipherOffset</code>
* and ending at <code>(cipherOffset+cipherLen-1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param cipher the input buffer with the data to be decrypted
* @param cipherOffset the offset in <code>cipher</code>
* @param cipherLen the length of the input data
* @param plain the buffer for the decryption result
* @param plainOffset the offset in <code>plain</code>
* @return the number of bytes placed into <code>plain</code>
*/
abstract int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset);
/**
* Performs decryption operation for the last time.
*
* <p>NOTE: For cipher feedback modes which does not perform
* special handling for the last few blocks, this is essentially
* the same as <code>encrypt(...)</code>. Given most modes do
* not do special handling, the default impl for this method is
* to simply call <code>decrypt(...)</code>.
*
* @param cipher the input buffer with the data to be decrypted
* @param cipherOffset the offset in <code>cipher</code>
* @param cipherLen the length of the input data
* @param plain the buffer for the decryption result
* @param plainOffset the offset in <code>plain</code>
* @return the number of bytes placed into <code>plain</code>
*/
int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
throws IllegalBlockSizeException, AEADBadTagException,
ShortBufferException {
return decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
}
/**
* Continues a multi-part update of the Additional Authentication
* Data (AAD), using a subset of the provided buffer. If this
* cipher is operating in either GCM or CCM mode, all AAD must be
* supplied before beginning operations on the ciphertext (via the
* {@code update} and {@code doFinal} methods).
* <p>
* NOTE: Given most modes do not accept AAD, default impl for this
* method throws IllegalStateException.
*
* @param src the buffer containing the AAD
* @param offset the offset in {@code src} where the AAD input starts
* @param len the number of AAD bytes
*
* @throws IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized), does not accept AAD, or if
* operating in either GCM or CCM mode and one of the {@code update}
* methods has already been called for the active
* encryption/decryption operation
* @throws UnsupportedOperationException if this method
* has not been overridden by an implementation
*
* @since 1.8
*/
void updateAAD(byte[] src, int offset, int len) {
throw new IllegalStateException("No AAD accepted");
}
/**
* @return the number of bytes that are buffered internally inside
* this FeedbackCipher instance.
* @since 1.8
*/
int getBufferedLength() {
// Currently only AEAD cipher impl, e.g. GCM, buffers data
// internally during decryption mode
return 0;
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright (c) 2013, 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.io.IOException;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.spec.GCMParameterSpec;
import sun.misc.HexDumpEncoder;
import sun.security.util.*;
/**
* This class implements the parameter set used with
* GCM encryption, which is defined in RFC 5084 as follows:
*
* <pre>
* GCMParameters ::= SEQUENCE {
* aes-iv OCTET STRING, -- recommended size is 12 octets
* aes-tLen AES-GCM-ICVlen DEFAULT 12 }
*
* AES-GCM-ICVlen ::= INTEGER (12 | 13 | 14 | 15 | 16)
*
* </pre>
*
* @author Valerie Peng
* @since 1.8
*/
public final class GCMParameters extends AlgorithmParametersSpi {
// the iv
private byte[] iv;
// the tag length in bytes
private int tLen;
public GCMParameters() {}
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
if (!(paramSpec instanceof GCMParameterSpec)) {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
GCMParameterSpec gps = (GCMParameterSpec) paramSpec;
// need to convert from bits to bytes for ASN.1 encoding
this.tLen = gps.getTLen()/8;
this.iv = gps.getIV();
}
protected void engineInit(byte[] encoded) throws IOException {
DerValue val = new DerValue(encoded);
// check if IV or params
if (val.tag == DerValue.tag_Sequence) {
byte[] iv = val.data.getOctetString();
int tLen;
if (val.data.available() != 0) {
tLen = val.data.getInteger();
if (tLen < 12 || tLen > 16 ) {
throw new IOException
("GCM parameter parsing error: unsupported tag len: " +
tLen);
}
if (val.data.available() != 0) {
throw new IOException
("GCM parameter parsing error: extra data");
}
} else {
tLen = 12;
}
this.iv = iv.clone();
this.tLen = tLen;
} else {
throw new IOException("GCM parameter parsing error: no SEQ tag");
}
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException {
engineInit(encoded);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException {
if (GCMParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast(new GCMParameterSpec(tLen * 8, iv));
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
DerOutputStream out = new DerOutputStream();
DerOutputStream bytes = new DerOutputStream();
bytes.putOctetString(iv);
bytes.putInteger(tLen);
out.write(DerValue.tag_Sequence, bytes);
return out.toByteArray();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException {
return engineGetEncoded();
}
/*
* Returns a formatted string describing the parameters.
*/
protected String engineToString() {
String LINE_SEP = System.getProperty("line.separator");
HexDumpEncoder encoder = new HexDumpEncoder();
StringBuilder sb
= new StringBuilder(LINE_SEP + " iv:" + LINE_SEP + "["
+ encoder.encodeBuffer(iv) + "]");
sb.append(LINE_SEP + "tLen(bits):" + LINE_SEP + tLen*8 + LINE_SEP);
return sb.toString();
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (c) 2013, 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.
*/
/*
* (C) Copyright IBM Corp. 2013
*/
package com.sun.crypto.provider;
import java.security.*;
import javax.crypto.*;
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
/**
* This class represents the GCTR function defined in NIST 800-38D
* under section 6.5. It needs to be constructed w/ an initialized
* cipher object, and initial counter block(ICB). Given an input X
* of arbitrary length, it processes and returns an output which has
* the same length as X. The invariants of this class are:
*
* (1) The length of intialCounterBlk (and also of its clones, e.g.,
* fields counter and counterSave) is equal to AES_BLOCK_SIZE.
*
* (2) After construction, the field counter never becomes null, it
* always contains a byte array of length AES_BLOCK_SIZE.
*
* If any invariant is broken, failures can occur because the
* AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM
* (see JDK-8067648 for details).
*
* <p>This function is used in the implementation of GCM mode.
*
* @since 1.8
*/
final class GCTR {
// these fields should not change after the object has been constructed
private final SymmetricCipher aes;
private final byte[] icb;
// the current counter value
private byte[] counter;
// needed for save/restore calls
private byte[] counterSave = null;
// NOTE: cipher should already be initialized
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
this.aes = cipher;
if (initialCounterBlk.length != AES_BLOCK_SIZE) {
throw new RuntimeException("length of initial counter block (" + initialCounterBlk.length +
") not equal to AES_BLOCK_SIZE (" + AES_BLOCK_SIZE + ")");
}
this.icb = initialCounterBlk;
this.counter = icb.clone();
}
// input must be multiples of 128-bit blocks when calling update
int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {
if (inLen - inOfs > in.length) {
throw new RuntimeException("input length out of bound");
}
if (inLen < 0 || inLen % AES_BLOCK_SIZE != 0) {
throw new RuntimeException("input length unsupported");
}
if (out.length - outOfs < inLen) {
throw new RuntimeException("output buffer too small");
}
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE;
for (int i = 0; i < numOfCompleteBlocks; i++) {
aes.encryptBlock(counter, 0, encryptedCntr, 0);
for (int n = 0; n < AES_BLOCK_SIZE; n++) {
int index = (i * AES_BLOCK_SIZE + n);
out[outOfs + index] =
(byte) ((in[inOfs + index] ^ encryptedCntr[n]));
}
GaloisCounterMode.increment32(counter);
}
return inLen;
}
// input can be arbitrary size when calling doFinal
protected int doFinal(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws IllegalBlockSizeException {
try {
if (inLen < 0) {
throw new IllegalBlockSizeException("Negative input size!");
} else if (inLen > 0) {
int lastBlockSize = inLen % AES_BLOCK_SIZE;
int completeBlkLen = inLen - lastBlockSize;
// process the complete blocks first
update(in, inOfs, completeBlkLen, out, outOfs);
if (lastBlockSize != 0) {
// do the last partial block
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
aes.encryptBlock(counter, 0, encryptedCntr, 0);
for (int n = 0; n < lastBlockSize; n++) {
out[outOfs + completeBlkLen + n] =
(byte) ((in[inOfs + completeBlkLen + n] ^
encryptedCntr[n]));
}
}
}
} finally {
reset();
}
return inLen;
}
/**
* Resets the content of this object to when it's first constructed.
*/
void reset() {
System.arraycopy(icb, 0, counter, 0, icb.length);
counterSave = null;
}
/**
* Save the current content of this object.
*/
void save() {
this.counterSave = this.counter.clone();
}
/**
* Restores the content of this object to the previous saved one.
*/
void restore() {
if (this.counterSave != null) {
this.counter = this.counterSave;
}
}
}

View File

@@ -0,0 +1,246 @@
/*
* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015 Red Hat, Inc.
* 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.
*/
/*
* (C) Copyright IBM Corp. 2013
*/
package com.sun.crypto.provider;
import java.security.ProviderException;
/**
* This class represents the GHASH function defined in NIST 800-38D
* under section 6.4. It needs to be constructed w/ a hash subkey, i.e.
* block H. Given input of 128-bit blocks, it will process and output
* a 128-bit block.
*
* <p>This function is used in the implementation of GCM mode.
*
* @since 1.8
*/
final class GHASH {
private static long getLong(byte[] buffer, int offset) {
long result = 0;
int end = offset + 8;
for (int i = offset; i < end; ++i) {
result = (result << 8) + (buffer[i] & 0xFF);
}
return result;
}
private static void putLong(byte[] buffer, int offset, long value) {
int end = offset + 8;
for (int i = end - 1; i >= offset; --i) {
buffer[i] = (byte) value;
value >>= 8;
}
}
private static final int AES_BLOCK_SIZE = 16;
// Multiplies state[0], state[1] by subkeyH[0], subkeyH[1].
private static void blockMult(long[] st, long[] subH) {
long Z0 = 0;
long Z1 = 0;
long V0 = subH[0];
long V1 = subH[1];
long X;
// Separate loops for processing state[0] and state[1].
X = st[0];
for (int i = 0; i < 64; i++) {
// Zi+1 = Zi if bit i of x is 0
long mask = X >> 63;
Z0 ^= V0 & mask;
Z1 ^= V1 & mask;
// Save mask for conditional reduction below.
mask = (V1 << 63) >> 63;
// V = rightshift(V)
long carry = V0 & 1;
V0 = V0 >>> 1;
V1 = (V1 >>> 1) | (carry << 63);
// Conditional reduction modulo P128.
V0 ^= 0xe100000000000000L & mask;
X <<= 1;
}
X = st[1];
for (int i = 64; i < 127; i++) {
// Zi+1 = Zi if bit i of x is 0
long mask = X >> 63;
Z0 ^= V0 & mask;
Z1 ^= V1 & mask;
// Save mask for conditional reduction below.
mask = (V1 << 63) >> 63;
// V = rightshift(V)
long carry = V0 & 1;
V0 = V0 >>> 1;
V1 = (V1 >>> 1) | (carry << 63);
// Conditional reduction.
V0 ^= 0xe100000000000000L & mask;
X <<= 1;
}
// calculate Z128
long mask = X >> 63;
Z0 ^= V0 & mask;
Z1 ^= V1 & mask;
// Save result.
st[0] = Z0;
st[1] = Z1;
}
/* subkeyH and state are stored in long[] for GHASH intrinsic use */
// hash subkey H; should not change after the object has been constructed
private final long[] subkeyH;
// buffer for storing hash
private final long[] state;
// variables for save/restore calls
private long stateSave0, stateSave1;
/**
* Initializes the cipher in the specified mode with the given key
* and iv.
*
* @param subkeyH the hash subkey
*
* @exception ProviderException if the given key is inappropriate for
* initializing this digest
*/
GHASH(byte[] subkeyH) throws ProviderException {
if ((subkeyH == null) || subkeyH.length != AES_BLOCK_SIZE) {
throw new ProviderException("Internal error");
}
state = new long[2];
this.subkeyH = new long[2];
this.subkeyH[0] = getLong(subkeyH, 0);
this.subkeyH[1] = getLong(subkeyH, 8);
}
/**
* Resets the GHASH object to its original state, i.e. blank w/
* the same subkey H. Used after digest() is called and to re-use
* this object for different data w/ the same H.
*/
void reset() {
state[0] = 0;
state[1] = 0;
}
/**
* Save the current snapshot of this GHASH object.
*/
void save() {
stateSave0 = state[0];
stateSave1 = state[1];
}
/**
* Restores this object using the saved snapshot.
*/
void restore() {
state[0] = stateSave0;
state[1] = stateSave1;
}
private static void processBlock(byte[] data, int ofs, long[] st, long[] subH) {
st[0] ^= getLong(data, ofs);
st[1] ^= getLong(data, ofs + 8);
blockMult(st, subH);
}
void update(byte[] in) {
update(in, 0, in.length);
}
void update(byte[] in, int inOfs, int inLen) {
if (inLen == 0) {
return;
}
ghashRangeCheck(in, inOfs, inLen, state, subkeyH);
processBlocks(in, inOfs, inLen/AES_BLOCK_SIZE, state, subkeyH);
}
private static void ghashRangeCheck(byte[] in, int inOfs, int inLen, long[] st, long[] subH) {
if (inLen < 0) {
throw new RuntimeException("invalid input length: " + inLen);
}
if (inOfs < 0) {
throw new RuntimeException("invalid offset: " + inOfs);
}
if (inLen > in.length - inOfs) {
throw new RuntimeException("input length out of bound: " +
inLen + " > " + (in.length - inOfs));
}
if (inLen % AES_BLOCK_SIZE != 0) {
throw new RuntimeException("input length/block size mismatch: " +
inLen);
}
// These two checks are for C2 checking
if (st.length != 2) {
throw new RuntimeException("internal state has invalid length: " +
st.length);
}
if (subH.length != 2) {
throw new RuntimeException("internal subkeyH has invalid length: " +
subH.length);
}
}
/*
* This is an intrinsified method. The method's argument list must match
* the hotspot signature. This method and methods called by it, cannot
* throw exceptions or allocate arrays as it will breaking intrinsics
*/
private static void processBlocks(byte[] data, int inOfs, int blocks, long[] st, long[] subH) {
int offset = inOfs;
while (blocks > 0) {
processBlock(data, offset, st, subH);
blocks--;
offset += AES_BLOCK_SIZE;
}
}
byte[] digest() {
byte[] result = new byte[AES_BLOCK_SIZE];
putLong(result, 0, state[0]);
putLong(result, 8, state[1]);
reset();
return result;
}
}

View File

@@ -0,0 +1,638 @@
/*
* Copyright (c) 2013, 2019, 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.io.*;
import java.security.*;
import javax.crypto.*;
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
/**
* This class represents ciphers in GaloisCounter (GCM) mode.
*
* <p>This mode currently should only be used w/ AES cipher.
* Although no checking is done, caller should only pass AES
* Cipher to the constructor.
*
* <p>NOTE: Unlike other modes, when used for decryption, this class
* will buffer all processed outputs internally and won't return them
* until the tag has been successfully verified.
*
* @since 1.8
*/
final class GaloisCounterMode extends FeedbackCipher {
static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;
static int DEFAULT_IV_LEN = 12; // in bytes
// In NIST SP 800-38D, GCM input size is limited to be no longer
// than (2^36 - 32) bytes. Otherwise, the counter will wrap
// around and lead to a leak of plaintext.
// However, given the current GCM 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
// can only be returned by the doFinal(...) call.
private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;
// data size when buffer is divided up to aid in intrinsics
private static final int TRIGGERLEN = 65536; // 64k
// buffer for AAD data; if null, meaning update has been called
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
private int sizeOfAAD = 0;
// buffer for storing input in decryption, not used for encryption
private ByteArrayOutputStream ibuffer = null;
// in bytes; need to convert to bits (default value 128) when needed
private int tagLenBytes = DEFAULT_TAG_LEN;
// these following 2 fields can only be initialized after init() is
// called, e.g. after cipher key k is set, and STAY UNCHANGED
private byte[] subkeyH = null;
private byte[] preCounterBlock = null;
private GCTR gctrPAndC = null;
private GHASH ghashAllToS = null;
// length of total data, i.e. len(C)
private int processed = 0;
// additional variables for save/restore calls
private byte[] aadBufferSave = null;
private int sizeOfAADSave = 0;
private byte[] ibufferSave = null;
private int processedSave = 0;
// value must be 16-byte long; used by GCTR and GHASH as well
static void increment32(byte[] value) {
if (value.length != AES_BLOCK_SIZE) {
// should never happen
throw new ProviderException("Illegal counter block length");
}
// start from last byte and only go over 4 bytes, i.e. total 32 bits
int n = value.length - 1;
while ((n >= value.length - 4) && (++value[n] == 0)) {
n--;
}
}
private static byte[] getLengthBlock(int ivLenInBytes) {
long ivLen = ((long)ivLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE];
out[8] = (byte)(ivLen >>> 56);
out[9] = (byte)(ivLen >>> 48);
out[10] = (byte)(ivLen >>> 40);
out[11] = (byte)(ivLen >>> 32);
out[12] = (byte)(ivLen >>> 24);
out[13] = (byte)(ivLen >>> 16);
out[14] = (byte)(ivLen >>> 8);
out[15] = (byte)ivLen;
return out;
}
private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {
long aLen = ((long)aLenInBytes) << 3;
long cLen = ((long)cLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE];
out[0] = (byte)(aLen >>> 56);
out[1] = (byte)(aLen >>> 48);
out[2] = (byte)(aLen >>> 40);
out[3] = (byte)(aLen >>> 32);
out[4] = (byte)(aLen >>> 24);
out[5] = (byte)(aLen >>> 16);
out[6] = (byte)(aLen >>> 8);
out[7] = (byte)aLen;
out[8] = (byte)(cLen >>> 56);
out[9] = (byte)(cLen >>> 48);
out[10] = (byte)(cLen >>> 40);
out[11] = (byte)(cLen >>> 32);
out[12] = (byte)(cLen >>> 24);
out[13] = (byte)(cLen >>> 16);
out[14] = (byte)(cLen >>> 8);
out[15] = (byte)cLen;
return out;
}
private static byte[] expandToOneBlock(byte[] in, int inOfs, int len) {
if (len > AES_BLOCK_SIZE) {
throw new ProviderException("input " + len + " too long");
}
if (len == AES_BLOCK_SIZE && inOfs == 0) {
return in;
} else {
byte[] paddedIn = new byte[AES_BLOCK_SIZE];
System.arraycopy(in, inOfs, paddedIn, 0, len);
return paddedIn;
}
}
private static byte[] getJ0(byte[] iv, byte[] subkeyH) {
byte[] j0;
if (iv.length == 12) { // 96 bits
j0 = expandToOneBlock(iv, 0, iv.length);
j0[AES_BLOCK_SIZE - 1] = 1;
} else {
GHASH g = new GHASH(subkeyH);
int lastLen = iv.length % AES_BLOCK_SIZE;
if (lastLen != 0) {
g.update(iv, 0, iv.length - lastLen);
byte[] padded =
expandToOneBlock(iv, iv.length - lastLen, lastLen);
g.update(padded);
} else {
g.update(iv);
}
byte[] lengthBlock = getLengthBlock(iv.length);
g.update(lengthBlock);
j0 = g.digest();
}
return j0;
}
private static void checkDataLength(int processed, int len) {
if (processed > MAX_BUF_SIZE - len) {
throw new ProviderException("SunJCE provider only supports " +
"input size up to " + MAX_BUF_SIZE + " bytes");
}
}
GaloisCounterMode(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
aadBuffer = new ByteArrayOutputStream();
}
/**
* Gets the name of the feedback mechanism
*
* @return the name of the feedback mechanism
*/
String getFeedback() {
return "GCM";
}
/**
* Resets the cipher object to its original state.
* This is used when doFinal is called in the Cipher class, so that the
* cipher can be reused (with its original key and iv).
*/
void reset() {
if (aadBuffer == null) {
aadBuffer = new ByteArrayOutputStream();
} else {
aadBuffer.reset();
}
if (gctrPAndC != null) gctrPAndC.reset();
if (ghashAllToS != null) ghashAllToS.reset();
processed = 0;
sizeOfAAD = 0;
if (ibuffer != null) {
ibuffer.reset();
}
}
/**
* Save the current content of this cipher.
*/
void save() {
processedSave = processed;
sizeOfAADSave = sizeOfAAD;
aadBufferSave =
((aadBuffer == null || aadBuffer.size() == 0)?
null : aadBuffer.toByteArray());
if (gctrPAndC != null) gctrPAndC.save();
if (ghashAllToS != null) ghashAllToS.save();
if (ibuffer != null) {
ibufferSave = ibuffer.toByteArray();
}
}
/**
* Restores the content of this cipher to the previous saved one.
*/
void restore() {
processed = processedSave;
sizeOfAAD = sizeOfAADSave;
if (aadBuffer != null) {
aadBuffer.reset();
if (aadBufferSave != null) {
aadBuffer.write(aadBufferSave, 0, aadBufferSave.length);
}
}
if (gctrPAndC != null) gctrPAndC.restore();
if (ghashAllToS != null) ghashAllToS.restore();
if (ibuffer != null) {
ibuffer.reset();
ibuffer.write(ibufferSave, 0, ibufferSave.length);
}
}
/**
* 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
* @param tagLenBytes the length of tag in bytes
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
*/
@Override
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException, InvalidAlgorithmParameterException {
init(decrypting, algorithm, key, iv, DEFAULT_TAG_LEN);
}
/**
* 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
* @param tagLenBytes the length of tag in bytes
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
*/
void init(boolean decrypting, String algorithm, byte[] keyValue,
byte[] ivValue, int tagLenBytes)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (keyValue == null) {
throw new InvalidKeyException("Internal error");
}
if (ivValue == null) {
throw new InvalidAlgorithmParameterException("Internal error");
}
if (ivValue.length == 0) {
throw new InvalidAlgorithmParameterException("IV is empty");
}
// always encrypt mode for embedded cipher
this.embeddedCipher.init(false, algorithm, keyValue);
this.subkeyH = new byte[AES_BLOCK_SIZE];
this.embeddedCipher.encryptBlock(new byte[AES_BLOCK_SIZE], 0,
this.subkeyH, 0);
this.iv = ivValue.clone();
preCounterBlock = getJ0(iv, subkeyH);
byte[] j0Plus1 = preCounterBlock.clone();
increment32(j0Plus1);
gctrPAndC = new GCTR(embeddedCipher, j0Plus1);
ghashAllToS = new GHASH(subkeyH);
this.tagLenBytes = tagLenBytes;
if (aadBuffer == null) {
aadBuffer = new ByteArrayOutputStream();
} else {
aadBuffer.reset();
}
processed = 0;
sizeOfAAD = 0;
if (decrypting) {
ibuffer = new ByteArrayOutputStream();
}
}
/**
* Continues a multi-part update of the Additional Authentication
* Data (AAD), using a subset of the provided buffer. If this
* cipher is operating in either GCM or CCM mode, all AAD must be
* supplied before beginning operations on the ciphertext (via the
* {@code update} and {@code doFinal} methods).
* <p>
* NOTE: Given most modes do not accept AAD, default impl for this
* method throws IllegalStateException.
*
* @param src the buffer containing the AAD
* @param offset the offset in {@code src} where the AAD input starts
* @param len the number of AAD bytes
*
* @throws IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized), does not accept AAD, or if
* operating in either GCM or CCM mode and one of the {@code update}
* methods has already been called for the active
* encryption/decryption operation
* @throws UnsupportedOperationException if this method
* has not been overridden by an implementation
*
* @since 1.8
*/
void updateAAD(byte[] src, int offset, int len) {
if (aadBuffer != null) {
aadBuffer.write(src, offset, len);
} else {
// update has already been called
throw new IllegalStateException
("Update has been called; no more AAD data");
}
}
// Feed the AAD data to GHASH, pad if necessary
void processAAD() {
if (aadBuffer != null) {
if (aadBuffer.size() > 0) {
byte[] aad = aadBuffer.toByteArray();
sizeOfAAD = aad.length;
int lastLen = aad.length % AES_BLOCK_SIZE;
if (lastLen != 0) {
ghashAllToS.update(aad, 0, aad.length - lastLen);
byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
lastLen);
ghashAllToS.update(padded);
} else {
ghashAllToS.update(aad);
}
}
aadBuffer = null;
}
}
// Utility to process the last block; used by encryptFinal and decryptFinal
void doLastBlock(byte[] in, int inOfs, int len, byte[] out, int outOfs,
boolean isEncrypt) throws IllegalBlockSizeException {
byte[] ct;
int ctOfs;
int ilen = len; // internal length
if (isEncrypt) {
ct = out;
ctOfs = outOfs;
} else {
ct = in;
ctOfs = inOfs;
}
// Divide up larger data sizes to trigger CTR & GHASH intrinsic quicker
if (len > TRIGGERLEN) {
int i = 0;
int tlen; // incremental lengths
final int plen = AES_BLOCK_SIZE * 6;
// arbitrary formula to aid intrinsic without reaching buffer end
final int count = len / 1024;
while (count > i) {
tlen = gctrPAndC.update(in, inOfs, plen, out, outOfs);
ghashAllToS.update(ct, ctOfs, tlen);
inOfs += tlen;
outOfs += tlen;
ctOfs += tlen;
i++;
}
ilen -= count * plen;
processed += count * plen;
}
gctrPAndC.doFinal(in, inOfs, ilen, out, outOfs);
processed += ilen;
int lastLen = ilen % AES_BLOCK_SIZE;
if (lastLen != 0) {
ghashAllToS.update(ct, ctOfs, ilen - lastLen);
ghashAllToS.update(
expandToOneBlock(ct, (ctOfs + ilen - lastLen), lastLen));
} else {
ghashAllToS.update(ct, ctOfs, ilen);
}
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>in</code>, starting at <code>inOfs</code>
* and ending at <code>(inOfs + len - 1)</code>, is encrypted. The result
* is stored in <code>out</code>, starting at <code>outOfs</code>.
*
* @param in the buffer with the input data to be encrypted
* @param inOfs the offset in <code>in</code>
* @param len the length of the input data
* @param out the buffer for the result
* @param outOfs the offset in <code>out</code>
* @exception ProviderException if <code>len</code> is not
* a multiple of the block size
* @return the number of bytes placed into the <code>out</code> buffer
*/
int encrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
checkDataLength(processed, len);
RangeUtil.blockSizeCheck(len, blockSize);
processAAD();
if (len > 0) {
RangeUtil.nullAndBoundsCheck(in, inOfs, len);
RangeUtil.nullAndBoundsCheck(out, outOfs, len);
gctrPAndC.update(in, inOfs, len, out, outOfs);
processed += len;
ghashAllToS.update(out, outOfs, len);
}
return len;
}
/**
* Performs encryption operation for the last time.
*
* @param in the input buffer with the data to be encrypted
* @param inOfs the offset in <code>in</code>
* @param len the length of the input data
* @param out the buffer for the encryption result
* @param outOfs the offset in <code>out</code>
* @return the number of bytes placed into the <code>out</code> buffer
*/
int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
throws IllegalBlockSizeException, ShortBufferException {
if (len > MAX_BUF_SIZE - tagLenBytes) {
throw new ShortBufferException
("Can't fit both data and tag into one buffer");
}
try {
RangeUtil.nullAndBoundsCheck(out, outOfs,
(len + tagLenBytes));
} catch (ArrayIndexOutOfBoundsException aiobe) {
throw new ShortBufferException("Output buffer too small");
}
checkDataLength(processed, len);
processAAD();
if (len > 0) {
RangeUtil.nullAndBoundsCheck(in, inOfs, len);
doLastBlock(in, inOfs, len, out, outOfs, true);
}
byte[] lengthBlock =
getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest();
byte[] sOut = new byte[s.length];
GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);
gctrForSToTag.doFinal(s, 0, s.length, sOut, 0);
System.arraycopy(sOut, 0, out, (outOfs + len), tagLenBytes);
return (len + tagLenBytes);
}
/**
* Performs decryption operation.
*
* <p>The input cipher text <code>in</code>, starting at
* <code>inOfs</code> and ending at <code>(inOfs + len - 1)</code>,
* is decrypted. The result is stored in <code>out</code>, starting at
* <code>outOfs</code>.
*
* @param in the buffer with the input data to be decrypted
* @param inOfs the offset in <code>in</code>
* @param len the length of the input data
* @param out the buffer for the result
* @param outOfs the offset in <code>out</code>
* @exception ProviderException if <code>len</code> is not
* a multiple of the block size
* @return the number of bytes placed into the <code>out</code> buffer
*/
int decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
checkDataLength(ibuffer.size(), len);
RangeUtil.blockSizeCheck(len, blockSize);
processAAD();
if (len > 0) {
// store internally until decryptFinal is called because
// spec mentioned that only return recovered data after tag
// is successfully verified
RangeUtil.nullAndBoundsCheck(in, inOfs, len);
ibuffer.write(in, inOfs, len);
}
return 0;
}
/**
* Performs decryption operation for the last time.
*
* <p>NOTE: For cipher feedback modes which does not perform
* special handling for the last few blocks, this is essentially
* the same as <code>encrypt(...)</code>. Given most modes do
* not do special handling, the default impl for this method is
* to simply call <code>decrypt(...)</code>.
*
* @param in the input buffer with the data to be decrypted
* @param inOfs the offset in <code>cipher</code>
* @param len the length of the input data
* @param out the buffer for the decryption result
* @param outOfs the offset in <code>plain</code>
* @return the number of bytes placed into the <code>out</code> buffer
*/
int decryptFinal(byte[] in, int inOfs, int len,
byte[] out, int outOfs)
throws IllegalBlockSizeException, AEADBadTagException,
ShortBufferException {
if (len < tagLenBytes) {
throw new AEADBadTagException("Input too short - need tag");
}
// do this check here can also catch the potential integer overflow
// scenario for the subsequent output buffer capacity check.
checkDataLength(ibuffer.size(), (len - tagLenBytes));
try {
RangeUtil.nullAndBoundsCheck(out, outOfs,
(ibuffer.size() + len) - tagLenBytes);
} catch (ArrayIndexOutOfBoundsException aiobe) {
throw new ShortBufferException("Output buffer too small");
}
processAAD();
RangeUtil.nullAndBoundsCheck(in, inOfs, len);
// get the trailing tag bytes from 'in'
byte[] tag = new byte[tagLenBytes];
System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
len -= tagLenBytes;
// If decryption is in-place or there is buffered "ibuffer" data, copy
// the "in" byte array into the ibuffer before proceeding.
if (in == out || ibuffer.size() > 0) {
if (len > 0) {
ibuffer.write(in, inOfs, len);
}
// refresh 'in' to all buffered-up bytes
in = ibuffer.toByteArray();
inOfs = 0;
len = in.length;
ibuffer.reset();
}
if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, false);
}
byte[] lengthBlock =
getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest();
byte[] sOut = new byte[s.length];
GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);
gctrForSToTag.doFinal(s, 0, s.length, sOut, 0);
// check entire authentication tag for time-consistency
int mismatch = 0;
for (int i = 0; i < tagLenBytes; i++) {
mismatch |= tag[i] ^ sOut[i];
}
if (mismatch != 0) {
throw new AEADBadTagException("Tag mismatch!");
}
return len;
}
// return tag length in bytes
int getTagLen() {
return this.tagLenBytes;
}
int getBufferedLength() {
if (ibuffer == null) {
return 0;
} else {
return ibuffer.size();
}
}
}

View File

@@ -0,0 +1,265 @@
/*
* Copyright (c) 2002, 2012, 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.nio.ByteBuffer;
import javax.crypto.MacSpi;
import javax.crypto.SecretKey;
import java.security.*;
import java.security.spec.*;
/**
* This class constitutes the core of HMAC-<MD> algorithms, where
* <MD> can be SHA1 or MD5, etc. See RFC 2104 for spec.
*
* It also contains the implementation classes for SHA-224, SHA-256,
* SHA-384, and SHA-512 HMACs.
*
* @author Jan Luehe
*/
abstract class HmacCore extends MacSpi implements Cloneable {
private MessageDigest md;
private byte[] k_ipad; // inner padding - key XORd with ipad
private byte[] k_opad; // outer padding - key XORd with opad
private boolean first; // Is this the first data to be processed?
private final int blockLen;
/**
* Standard constructor, creates a new HmacCore instance using the
* specified MessageDigest object.
*/
HmacCore(MessageDigest md, int bl) {
this.md = md;
this.blockLen = bl;
this.k_ipad = new byte[blockLen];
this.k_opad = new byte[blockLen];
first = true;
}
/**
* Standard constructor, creates a new HmacCore instance instantiating
* a MessageDigest of the specified name.
*/
HmacCore(String digestAlgorithm, int bl) throws NoSuchAlgorithmException {
this(MessageDigest.getInstance(digestAlgorithm), bl);
}
/**
* Returns the length of the HMAC in bytes.
*
* @return the HMAC length in bytes.
*/
protected int engineGetMacLength() {
return this.md.getDigestLength();
}
/**
* Initializes the HMAC with the given secret key and algorithm parameters.
*
* @param key the secret key.
* @param params the algorithm parameters.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this MAC.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this MAC.
*/
protected void engineInit(Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("HMAC does not use parameters");
}
if (!(key instanceof SecretKey)) {
throw new InvalidKeyException("Secret key expected");
}
byte[] secret = key.getEncoded();
if (secret == null) {
throw new InvalidKeyException("Missing key data");
}
// if key is longer than the block length, reset it using
// the message digest object.
if (secret.length > blockLen) {
byte[] tmp = md.digest(secret);
// now erase the secret
Arrays.fill(secret, (byte)0);
secret = tmp;
}
// XOR k with ipad and opad, respectively
for (int i = 0; i < blockLen; i++) {
int si = (i < secret.length) ? secret[i] : 0;
k_ipad[i] = (byte)(si ^ 0x36);
k_opad[i] = (byte)(si ^ 0x5c);
}
// now erase the secret
Arrays.fill(secret, (byte)0);
secret = null;
engineReset();
}
/**
* Processes the given byte.
*
* @param input the input byte to be processed.
*/
protected void engineUpdate(byte input) {
if (first == true) {
// compute digest for 1st pass; start with inner pad
md.update(k_ipad);
first = false;
}
// add the passed byte to the inner digest
md.update(input);
}
/**
* Processes the first <code>len</code> bytes in <code>input</code>,
* starting at <code>offset</code>.
*
* @param input the input buffer.
* @param offset the offset in <code>input</code> where the input starts.
* @param len the number of bytes to process.
*/
protected void engineUpdate(byte input[], int offset, int len) {
if (first == true) {
// compute digest for 1st pass; start with inner pad
md.update(k_ipad);
first = false;
}
// add the selected part of an array of bytes to the inner digest
md.update(input, offset, len);
}
/**
* Processes the <code>input.remaining()</code> bytes in the ByteBuffer
* <code>input</code>.
*
* @param input the input byte buffer.
*/
protected void engineUpdate(ByteBuffer input) {
if (first == true) {
// compute digest for 1st pass; start with inner pad
md.update(k_ipad);
first = false;
}
md.update(input);
}
/**
* Completes the HMAC computation and resets the HMAC for further use,
* maintaining the secret key that the HMAC was initialized with.
*
* @return the HMAC result.
*/
protected byte[] engineDoFinal() {
if (first == true) {
// compute digest for 1st pass; start with inner pad
md.update(k_ipad);
} else {
first = true;
}
try {
// finish the inner digest
byte[] tmp = md.digest();
// compute digest for 2nd pass; start with outer pad
md.update(k_opad);
// add result of 1st hash
md.update(tmp);
md.digest(tmp, 0, tmp.length);
return tmp;
} catch (DigestException e) {
// should never occur
throw new ProviderException(e);
}
}
/**
* Resets the HMAC for further use, maintaining the secret key that the
* HMAC was initialized with.
*/
protected void engineReset() {
if (first == false) {
md.reset();
first = true;
}
}
/*
* Clones this object.
*/
public Object clone() throws CloneNotSupportedException {
HmacCore copy = (HmacCore) super.clone();
copy.md = (MessageDigest) md.clone();
copy.k_ipad = k_ipad.clone();
copy.k_opad = k_opad.clone();
return copy;
}
// nested static class for the HmacSHA224 implementation
public static final class HmacSHA224 extends HmacCore {
public HmacSHA224() throws NoSuchAlgorithmException {
super("SHA-224", 64);
}
}
// nested static class for the HmacSHA256 implementation
public static final class HmacSHA256 extends HmacCore {
public HmacSHA256() throws NoSuchAlgorithmException {
super("SHA-256", 64);
}
}
// nested static class for the HmacSHA384 implementation
public static final class HmacSHA384 extends HmacCore {
public HmacSHA384() throws NoSuchAlgorithmException {
super("SHA-384", 128);
}
}
// nested static class for the HmacSHA512 implementation
public static final class HmacSHA512 extends HmacCore {
public HmacSHA512() throws NoSuchAlgorithmException {
super("SHA-512", 128);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 1998, 2012, 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.nio.ByteBuffer;
import javax.crypto.MacSpi;
import javax.crypto.SecretKey;
import java.security.*;
import java.security.spec.*;
/**
* This is an implementation of the HMAC-MD5 algorithm.
*
* @author Jan Luehe
*/
public final class HmacMD5 extends HmacCore {
/**
* Standard constructor, creates a new HmacMD5 instance.
*/
public HmacMD5() throws NoSuchAlgorithmException {
super("MD5", 64);
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 1999, 2013, 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.security.SecureRandom;
import java.security.InvalidParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KeyGeneratorSpi;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* This class generates a secret key for use with the HMAC-MD5 algorithm.
*
* @author Jan Luehe
*
*/
public final class HmacMD5KeyGenerator extends KeyGeneratorSpi {
private SecureRandom random = null;
private int keysize = 64; // default keysize (in number of bytes)
/**
* Empty constructor
*/
public HmacMD5KeyGenerator() {
}
/**
* Initializes this key generator.
*
* @param random the source of randomness for this generator
*/
protected void engineInit(SecureRandom random) {
this.random = random;
}
/**
* Initializes this key generator with the specified parameter
* set and a user-provided source of randomness.
*
* @param params the key generation parameters
* @param random the source of randomness for this key generator
*
* @exception InvalidAlgorithmParameterException if <code>params</code> is
* inappropriate for this key generator
*/
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidAlgorithmParameterException
{
throw new InvalidAlgorithmParameterException
("HMAC-MD5 key generation does not take any parameters");
}
/**
* Initializes this key generator for a certain keysize, using the given
* source of randomness.
*
* @param keysize the keysize. This is an algorithm-specific
* metric specified in number of bits.
* @param random the source of randomness for this key generator
*/
protected void engineInit(int keysize, SecureRandom random) {
this.keysize = (keysize+7) / 8;
this.engineInit(random);
}
/**
* Generates an HMAC-MD5 key.
*
* @return the new HMAC-MD5 key
*/
protected SecretKey engineGenerateKey() {
if (this.random == null) {
this.random = SunJCE.getRandom();
}
byte[] keyBytes = new byte[this.keysize];
this.random.nextBytes(keyBytes);
return new SecretKeySpec(keyBytes, "HmacMD5");
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2003, 2018, 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 javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.*;
import java.security.spec.*;
/**
* This is an implementation of the HMAC algorithms as defined
* in PKCS#12 v1.1 standard (see RFC 7292 Appendix B.4).
*
* @author Valerie Peng
*/
abstract class HmacPKCS12PBECore extends HmacCore {
public static final class HmacPKCS12PBE_SHA1 extends HmacPKCS12PBECore {
public HmacPKCS12PBE_SHA1() throws NoSuchAlgorithmException {
super("SHA1", 64);
}
}
public static final class HmacPKCS12PBE_SHA224 extends HmacPKCS12PBECore {
public HmacPKCS12PBE_SHA224() throws NoSuchAlgorithmException {
super("SHA-224", 64);
}
}
public static final class HmacPKCS12PBE_SHA256 extends HmacPKCS12PBECore {
public HmacPKCS12PBE_SHA256() throws NoSuchAlgorithmException {
super("SHA-256", 64);
}
}
public static final class HmacPKCS12PBE_SHA384 extends HmacPKCS12PBECore {
public HmacPKCS12PBE_SHA384() throws NoSuchAlgorithmException {
super("SHA-384", 128);
}
}
public static final class HmacPKCS12PBE_SHA512 extends HmacPKCS12PBECore {
public HmacPKCS12PBE_SHA512() throws NoSuchAlgorithmException {
super("SHA-512", 128);
}
}
public static final class HmacPKCS12PBE_SHA512_224 extends HmacPKCS12PBECore {
public HmacPKCS12PBE_SHA512_224() throws NoSuchAlgorithmException {
super("SHA-512/224", 128);
}
}
public static final class HmacPKCS12PBE_SHA512_256 extends HmacPKCS12PBECore {
public HmacPKCS12PBE_SHA512_256() throws NoSuchAlgorithmException {
super("SHA-512/256", 128);
}
}
private final String algorithm;
private final int bl;
/**
* Standard constructor, creates a new HmacSHA1 instance.
*/
public HmacPKCS12PBECore(String algorithm, int bl) throws NoSuchAlgorithmException {
super(algorithm, bl);
this.algorithm = algorithm;
this.bl = bl;
}
/**
* Initializes the HMAC with the given secret key and algorithm parameters.
*
* @param key the secret key.
* @param params the algorithm parameters.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this MAC.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this MAC.
*/
protected void engineInit(Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
char[] passwdChars;
byte[] salt = null;
int iCount = 0;
if (key instanceof javax.crypto.interfaces.PBEKey) {
javax.crypto.interfaces.PBEKey pbeKey =
(javax.crypto.interfaces.PBEKey) key;
passwdChars = pbeKey.getPassword();
salt = pbeKey.getSalt(); // maybe null if unspecified
iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified
} else if (key instanceof SecretKey) {
byte[] passwdBytes;
if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) ||
(passwdBytes = key.getEncoded()) == null) {
throw new InvalidKeyException("Missing password");
}
passwdChars = new char[passwdBytes.length];
for (int i=0; i<passwdChars.length; i++) {
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
}
Arrays.fill(passwdBytes, (byte)0x00);
} else {
throw new InvalidKeyException("SecretKey of PBE type required");
}
byte[] derivedKey;
try {
if (params == null) {
// should not auto-generate default values since current
// javax.crypto.Mac api does not have any method for caller to
// retrieve the generated defaults.
if ((salt == null) || (iCount == 0)) {
throw new InvalidAlgorithmParameterException
("PBEParameterSpec required for salt and iteration count");
}
} else if (!(params instanceof PBEParameterSpec)) {
throw new InvalidAlgorithmParameterException
("PBEParameterSpec type required");
} else {
PBEParameterSpec pbeParams = (PBEParameterSpec) params;
// make sure the parameter values are consistent
if (salt != null) {
if (!Arrays.equals(salt, pbeParams.getSalt())) {
throw new InvalidAlgorithmParameterException
("Inconsistent value of salt between key and params");
}
} else {
salt = pbeParams.getSalt();
}
if (iCount != 0) {
if (iCount != pbeParams.getIterationCount()) {
throw new InvalidAlgorithmParameterException
("Different iteration count between key and params");
}
} else {
iCount = pbeParams.getIterationCount();
}
}
// For security purpose, we need to enforce a minimum length
// for salt; just require the minimum salt length to be 8-byte
// which is what PKCS#5 recommends and openssl does.
if (salt.length < 8) {
throw new InvalidAlgorithmParameterException
("Salt must be at least 8 bytes long");
}
if (iCount <= 0) {
throw new InvalidAlgorithmParameterException
("IterationCount must be a positive number");
}
derivedKey = PKCS12PBECipherCore.derive(passwdChars, salt,
iCount, engineGetMacLength(), PKCS12PBECipherCore.MAC_KEY,
algorithm, bl);
} finally {
Arrays.fill(passwdChars, '\0');
}
SecretKey cipherKey = new SecretKeySpec(derivedKey, "HmacSHA1");
super.engineInit(cipherKey, null);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 1998, 2012, 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.nio.ByteBuffer;
import javax.crypto.MacSpi;
import javax.crypto.SecretKey;
import java.security.*;
import java.security.spec.*;
/**
* This is an implementation of the HMAC-SHA1 algorithm.
*
* @author Jan Luehe
*/
public final class HmacSHA1 extends HmacCore {
/**
* Standard constructor, creates a new HmacSHA1 instance.
*/
public HmacSHA1() throws NoSuchAlgorithmException {
super("SHA1", 64);
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 1999, 2013, 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.security.SecureRandom;
import java.security.InvalidParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KeyGeneratorSpi;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* This class generates a secret key for use with the HMAC-SHA1 algorithm.
*
* @author Jan Luehe
*
*/
public final class HmacSHA1KeyGenerator extends KeyGeneratorSpi {
private SecureRandom random = null;
private int keysize = 64; // default keysize (in number of bytes)
/**
* Empty constructor
*/
public HmacSHA1KeyGenerator() {
}
/**
* Initializes this key generator.
*
* @param random the source of randomness for this generator
*/
protected void engineInit(SecureRandom random) {
this.random = random;
}
/**
* Initializes this key generator with the specified parameter
* set and a user-provided source of randomness.
*
* @param params the key generation parameters
* @param random the source of randomness for this key generator
*
* @exception InvalidAlgorithmParameterException if <code>params</code> is
* inappropriate for this key generator
*/
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidAlgorithmParameterException
{
throw new InvalidAlgorithmParameterException
("HMAC-SHA1 key generation does not take any parameters");
}
/**
* Initializes this key generator for a certain keysize, using the given
* source of randomness.
*
* @param keysize the keysize. This is an algorithm-specific
* metric specified in number of bits.
* @param random the source of randomness for this key generator
*/
protected void engineInit(int keysize, SecureRandom random) {
this.keysize = (keysize+7) / 8;
this.engineInit(random);
}
/**
* Generates an HMAC-SHA1 key.
*
* @return the new HMAC-SHA1 key
*/
protected SecretKey engineGenerateKey() {
if (this.random == null) {
this.random = SunJCE.getRandom();
}
byte[] keyBytes = new byte[this.keysize];
this.random.nextBytes(keyBytes);
return new SecretKeySpec(keyBytes, "HmacSHA1");
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright (c) 2003, 2017, 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 javax.crypto.ShortBufferException;
/**
* This class implements padding as specified in the W3 XML ENC standard.
*
* @author Valerie Peng
*
*
* @see Padding
*/
final class ISO10126Padding implements Padding {
private int blockSize;
ISO10126Padding(int blockSize) {
this.blockSize = blockSize;
}
/**
* Adds the given number of padding bytes to the data input.
* The value of the padding bytes is determined
* by the specific padding mechanism that implements this
* interface.
*
* @param in the input buffer with the data to pad
* @param off the offset in <code>in</code> where the padding bytes
* are appended
* @param len the number of padding bytes to add
*
* @exception ShortBufferException if <code>in</code> is too small to hold
* the padding bytes
*/
public void padWithLen(byte[] in, int off, int len)
throws ShortBufferException
{
if (in == null)
return;
int idx = Math.addExact(off, len);
if (idx > in.length) {
throw new ShortBufferException("Buffer too small to hold padding");
}
byte paddingOctet = (byte) (len & 0xff);
byte[] padding = new byte[len - 1];
SunJCE.getRandom().nextBytes(padding);
System.arraycopy(padding, 0, in, off, len - 1);
in[idx - 1] = paddingOctet;
return;
}
/**
* Returns the index where the padding starts.
*
* <p>Given a buffer with padded data, this method returns the
* index where the padding starts.
*
* @param in the buffer with the padded data
* @param off the offset in <code>in</code> where the padded data starts
* @param len the length of the padded data
*
* @return the index where the padding starts, or -1 if the input is
* not properly padded
*/
public int unpad(byte[] in, int off, int len) {
if ((in == null) ||
(len == 0)) { // this can happen if input is really a padded buffer
return 0;
}
int idx = Math.addExact(off, len);
byte lastByte = in[idx - 1];
int padValue = (int)lastByte & 0x0ff;
if ((padValue < 0x01)
|| (padValue > blockSize)) {
return -1;
}
int start = idx - padValue;
if (start < off) {
return -1;
}
return start;
}
/**
* Determines how long the padding will be for a given input length.
*
* @param len the length of the data to pad
*
* @return the length of the padding
*/
public int padLength(int len) {
int paddingOctet = blockSize - (len % blockSize);
return paddingOctet;
}
}

View File

@@ -0,0 +1,971 @@
/*
* Copyright (c) 1998, 2020, 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 sun.security.util.Debug;
import java.io.*;
import java.util.*;
import java.security.AccessController;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PrivilegedAction;
import java.security.KeyStoreSpi;
import java.security.KeyStoreException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateException;
import javax.crypto.SealedObject;
import sun.misc.IOUtils;
import sun.misc.ObjectInputFilter;
/**
* This class provides the keystore implementation referred to as "jceks".
* This implementation strongly protects the keystore private keys using
* triple-DES, where the triple-DES encryption/decryption key is derived from
* the user's password.
* The encrypted private keys are stored in the keystore in a standard format,
* namely the <code>EncryptedPrivateKeyInfo</code> format defined in PKCS #8.
*
* @author Jan Luehe
*
*
* @see java.security.KeyStoreSpi
*/
public final class JceKeyStore extends KeyStoreSpi {
private static final Debug debug = Debug.getInstance("keystore");
private static final int JCEKS_MAGIC = 0xcececece;
private static final int JKS_MAGIC = 0xfeedfeed;
private static final int VERSION_1 = 0x01;
private static final int VERSION_2 = 0x02;
// Private key and supporting certificate chain
private static final class PrivateKeyEntry {
Date date; // the creation date of this entry
byte[] protectedKey;
Certificate[] chain;
};
// Secret key
private static final class SecretKeyEntry {
Date date; // the creation date of this entry
SealedObject sealedKey;
// Maximum possible length of sealedKey. Used to detect malicious
// input data. This field is set to the file length of the keystore
// at loading. It is useless when creating a new SecretKeyEntry
// to be store in a keystore.
int maxLength;
}
// Trusted certificate
private static final class TrustedCertEntry {
Date date; // the creation date of this entry
Certificate cert;
};
/**
* Private keys and certificates are stored in a hashtable.
* Hash entries are keyed by alias names.
*/
private Hashtable<String, Object> entries = new Hashtable<String, Object>();
/**
* Returns the key associated with the given alias, using the given
* password to recover it.
*
* @param alias the alias name
* @param password the password for recovering the key
*
* @return the requested key, or null if the given alias does not exist
* or does not identify a <i>key entry</i>.
*
* @exception NoSuchAlgorithmException if the algorithm for recovering the
* key cannot be found
* @exception UnrecoverableKeyException if the key cannot be recovered
* (e.g., the given password is wrong).
*/
public Key engineGetKey(String alias, char[] password)
throws NoSuchAlgorithmException, UnrecoverableKeyException
{
Key key = null;
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (!((entry instanceof PrivateKeyEntry) ||
(entry instanceof SecretKeyEntry))) {
return null;
}
KeyProtector keyProtector = new KeyProtector(password);
if (entry instanceof PrivateKeyEntry) {
byte[] encrBytes = ((PrivateKeyEntry)entry).protectedKey;
EncryptedPrivateKeyInfo encrInfo;
try {
encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
} catch (IOException ioe) {
throw new UnrecoverableKeyException("Private key not stored "
+ "as PKCS #8 " +
"EncryptedPrivateKeyInfo");
}
key = keyProtector.recover(encrInfo);
} else {
SecretKeyEntry ske = ((SecretKeyEntry)entry);
key = keyProtector.unseal(ske.sealedKey, ske.maxLength);
}
return key;
}
/**
* Returns the certificate chain associated with the given alias.
*
* @param alias the alias name
*
* @return the certificate chain (ordered with the user's certificate first
* and the root certificate authority last), or null if the given alias
* does not exist or does not contain a certificate chain (i.e., the given
* alias identifies either a <i>trusted certificate entry</i> or a
* <i>key entry</i> without a certificate chain).
*/
public Certificate[] engineGetCertificateChain(String alias)
{
Certificate[] chain = null;
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if ((entry instanceof PrivateKeyEntry)
&& (((PrivateKeyEntry)entry).chain != null)) {
chain = ((PrivateKeyEntry)entry).chain.clone();
}
return chain;
}
/**
* Returns the certificate associated with the given alias.
*
* <p>If the given alias name identifies a
* <i>trusted certificate entry</i>, the certificate associated with that
* entry is returned. If the given alias name identifies a
* <i>key entry</i>, the first element of the certificate chain of that
* entry is returned, or null if that entry does not have a certificate
* chain.
*
* @param alias the alias name
*
* @return the certificate, or null if the given alias does not exist or
* does not contain a certificate.
*/
public Certificate engineGetCertificate(String alias) {
Certificate cert = null;
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
if (entry instanceof TrustedCertEntry) {
cert = ((TrustedCertEntry)entry).cert;
} else if ((entry instanceof PrivateKeyEntry) &&
(((PrivateKeyEntry)entry).chain != null)) {
cert = ((PrivateKeyEntry)entry).chain[0];
}
}
return cert;
}
/**
* Returns the creation date of the entry identified by the given alias.
*
* @param alias the alias name
*
* @return the creation date of this entry, or null if the given alias does
* not exist
*/
public Date engineGetCreationDate(String alias) {
Date date = null;
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
// We have to create a new instance of java.util.Date because
// dates are not immutable
if (entry instanceof TrustedCertEntry) {
date = new Date(((TrustedCertEntry)entry).date.getTime());
} else if (entry instanceof PrivateKeyEntry) {
date = new Date(((PrivateKeyEntry)entry).date.getTime());
} else {
date = new Date(((SecretKeyEntry)entry).date.getTime());
}
}
return date;
}
/**
* Assigns the given key to the given alias, protecting it with the given
* password.
*
* <p>If the given key is of type <code>java.security.PrivateKey</code>,
* it must be accompanied by a certificate chain certifying the
* corresponding public key.
*
* <p>If the given alias already exists, the keystore information
* associated with it is overridden by the given key (and possibly
* certificate chain).
*
* @param alias the alias name
* @param key the key to be associated with the alias
* @param password the password to protect the key
* @param chain the certificate chain for the corresponding public
* key (only required if the given key is of type
* <code>java.security.PrivateKey</code>).
*
* @exception KeyStoreException if the given key cannot be protected, or
* this operation fails for some other reason
*/
public void engineSetKeyEntry(String alias, Key key, char[] password,
Certificate[] chain)
throws KeyStoreException
{
synchronized(entries) {
try {
KeyProtector keyProtector = new KeyProtector(password);
if (key instanceof PrivateKey) {
PrivateKeyEntry entry = new PrivateKeyEntry();
entry.date = new Date();
// protect the private key
entry.protectedKey = keyProtector.protect((PrivateKey)key);
// clone the chain
if ((chain != null) &&
(chain.length !=0)) {
entry.chain = chain.clone();
} else {
entry.chain = null;
}
// store the entry
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
} else {
SecretKeyEntry entry = new SecretKeyEntry();
entry.date = new Date();
// seal and store the key
entry.sealedKey = keyProtector.seal(key);
entry.maxLength = Integer.MAX_VALUE;
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
}
} catch (Exception e) {
throw new KeyStoreException(e.getMessage());
}
}
}
/**
* Assigns the given key (that has already been protected) to the given
* alias.
*
* <p>If the protected key is of type
* <code>java.security.PrivateKey</code>,
* it must be accompanied by a certificate chain certifying the
* corresponding public key.
*
* <p>If the given alias already exists, the keystore information
* associated with it is overridden by the given key (and possibly
* certificate chain).
*
* @param alias the alias name
* @param key the key (in protected format) to be associated with the alias
* @param chain the certificate chain for the corresponding public
* key (only useful if the protected key is of type
* <code>java.security.PrivateKey</code>).
*
* @exception KeyStoreException if this operation fails.
*/
public void engineSetKeyEntry(String alias, byte[] key,
Certificate[] chain)
throws KeyStoreException
{
synchronized(entries) {
// We assume it's a private key, because there is no standard
// (ASN.1) encoding format for wrapped secret keys
PrivateKeyEntry entry = new PrivateKeyEntry();
entry.date = new Date();
entry.protectedKey = key.clone();
if ((chain != null) &&
(chain.length != 0)) {
entry.chain = chain.clone();
} else {
entry.chain = null;
}
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
}
}
/**
* Assigns the given certificate to the given alias.
*
* <p>If the given alias already exists in this keystore and identifies a
* <i>trusted certificate entry</i>, the certificate associated with it is
* overridden by the given certificate.
*
* @param alias the alias name
* @param cert the certificate
*
* @exception KeyStoreException if the given alias already exists and does
* not identify a <i>trusted certificate entry</i>, or this operation
* fails for some other reason.
*/
public void engineSetCertificateEntry(String alias, Certificate cert)
throws KeyStoreException
{
synchronized(entries) {
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
if (entry instanceof PrivateKeyEntry) {
throw new KeyStoreException("Cannot overwrite own "
+ "certificate");
} else if (entry instanceof SecretKeyEntry) {
throw new KeyStoreException("Cannot overwrite secret key");
}
}
TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
trustedCertEntry.cert = cert;
trustedCertEntry.date = new Date();
entries.put(alias.toLowerCase(Locale.ENGLISH), trustedCertEntry);
}
}
/**
* Deletes the entry identified by the given alias from this keystore.
*
* @param alias the alias name
*
* @exception KeyStoreException if the entry cannot be removed.
*/
public void engineDeleteEntry(String alias)
throws KeyStoreException
{
synchronized(entries) {
entries.remove(alias.toLowerCase(Locale.ENGLISH));
}
}
/**
* Lists all the alias names of this keystore.
*
* @return enumeration of the alias names
*/
public Enumeration<String> engineAliases() {
return entries.keys();
}
/**
* Checks if the given alias exists in this keystore.
*
* @param alias the alias name
*
* @return true if the alias exists, false otherwise
*/
public boolean engineContainsAlias(String alias) {
return entries.containsKey(alias.toLowerCase(Locale.ENGLISH));
}
/**
* Retrieves the number of entries in this keystore.
*
* @return the number of entries in this keystore
*/
public int engineSize() {
return entries.size();
}
/**
* Returns true if the entry identified by the given alias is a
* <i>key entry</i>, and false otherwise.
*
* @return true if the entry identified by the given alias is a
* <i>key entry</i>, false otherwise.
*/
public boolean engineIsKeyEntry(String alias) {
boolean isKey = false;
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if ((entry instanceof PrivateKeyEntry)
|| (entry instanceof SecretKeyEntry)) {
isKey = true;
}
return isKey;
}
/**
* Returns true if the entry identified by the given alias is a
* <i>trusted certificate entry</i>, and false otherwise.
*
* @return true if the entry identified by the given alias is a
* <i>trusted certificate entry</i>, false otherwise.
*/
public boolean engineIsCertificateEntry(String alias) {
boolean isCert = false;
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry instanceof TrustedCertEntry) {
isCert = true;
}
return isCert;
}
/**
* Returns the (alias) name of the first keystore entry whose certificate
* matches the given certificate.
*
* <p>This method attempts to match the given certificate with each
* keystore entry. If the entry being considered
* is a <i>trusted certificate entry</i>, the given certificate is
* compared to that entry's certificate. If the entry being considered is
* a <i>key entry</i>, the given certificate is compared to the first
* element of that entry's certificate chain (if a chain exists).
*
* @param cert the certificate to match with.
*
* @return the (alias) name of the first entry with matching certificate,
* or null if no such entry exists in this keystore.
*/
public String engineGetCertificateAlias(Certificate cert) {
Certificate certElem;
Enumeration<String> e = entries.keys();
while (e.hasMoreElements()) {
String alias = e.nextElement();
Object entry = entries.get(alias);
if (entry instanceof TrustedCertEntry) {
certElem = ((TrustedCertEntry)entry).cert;
} else if ((entry instanceof PrivateKeyEntry) &&
(((PrivateKeyEntry)entry).chain != null)) {
certElem = ((PrivateKeyEntry)entry).chain[0];
} else {
continue;
}
if (certElem.equals(cert)) {
return alias;
}
}
return null;
}
/**
* Stores this keystore to the given output stream, and protects its
* integrity with the given password.
*
* @param stream the output stream to which this keystore is written.
* @param password the password to generate the keystore integrity check
*
* @exception IOException if there was an I/O problem with data
* @exception NoSuchAlgorithmException if the appropriate data integrity
* algorithm could not be found
* @exception CertificateException if any of the certificates included in
* the keystore data could not be stored
*/
public void engineStore(OutputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
synchronized(entries) {
/*
* KEYSTORE FORMAT:
*
* Magic number (big-endian integer),
* Version of this file format (big-endian integer),
*
* Count (big-endian integer),
* followed by "count" instances of either:
*
* {
* tag=1 (big-endian integer)
* alias (UTF string)
* timestamp
* encrypted private-key info according to PKCS #8
* (integer length followed by encoding)
* cert chain (integer count followed by certs;
* for each cert: type UTF string, followed by integer
* length, followed by encoding)
* }
*
* or:
*
* {
* tag=2 (big-endian integer)
* alias (UTF string)
* timestamp
* cert (type UTF string, followed by integer length,
* followed by encoding)
* }
*
* or:
*
* {
* tag=3 (big-endian integer)
* alias (UTF string)
* timestamp
* sealed secret key (in serialized form)
* }
*
* ended by a keyed SHA1 hash (bytes only) of
* { password + whitener + preceding body }
*/
// password is mandatory when storing
if (password == null) {
throw new IllegalArgumentException("password can't be null");
}
byte[] encoded; // the certificate encoding
MessageDigest md = getPreKeyedHash(password);
DataOutputStream dos
= new DataOutputStream(new DigestOutputStream(stream, md));
// NOTE: don't pass dos to oos at this point or it'll corrupt
// the keystore!!!
ObjectOutputStream oos = null;
try {
dos.writeInt(JCEKS_MAGIC);
dos.writeInt(VERSION_2); // always write the latest version
dos.writeInt(entries.size());
Enumeration<String> e = entries.keys();
while (e.hasMoreElements()) {
String alias = e.nextElement();
Object entry = entries.get(alias);
if (entry instanceof PrivateKeyEntry) {
PrivateKeyEntry pentry = (PrivateKeyEntry)entry;
// write PrivateKeyEntry tag
dos.writeInt(1);
// write the alias
dos.writeUTF(alias);
// write the (entry creation) date
dos.writeLong(pentry.date.getTime());
// write the protected private key
dos.writeInt(pentry.protectedKey.length);
dos.write(pentry.protectedKey);
// write the certificate chain
int chainLen;
if (pentry.chain == null) {
chainLen = 0;
} else {
chainLen = pentry.chain.length;
}
dos.writeInt(chainLen);
for (int i = 0; i < chainLen; i++) {
encoded = pentry.chain[i].getEncoded();
dos.writeUTF(pentry.chain[i].getType());
dos.writeInt(encoded.length);
dos.write(encoded);
}
} else if (entry instanceof TrustedCertEntry) {
// write TrustedCertEntry tag
dos.writeInt(2);
// write the alias
dos.writeUTF(alias);
// write the (entry creation) date
dos.writeLong(((TrustedCertEntry)entry).date.getTime());
// write the trusted certificate
encoded = ((TrustedCertEntry)entry).cert.getEncoded();
dos.writeUTF(((TrustedCertEntry)entry).cert.getType());
dos.writeInt(encoded.length);
dos.write(encoded);
} else {
// write SecretKeyEntry tag
dos.writeInt(3);
// write the alias
dos.writeUTF(alias);
// write the (entry creation) date
dos.writeLong(((SecretKeyEntry)entry).date.getTime());
// write the sealed key
oos = new ObjectOutputStream(dos);
oos.writeObject(((SecretKeyEntry)entry).sealedKey);
// NOTE: don't close oos here since we are still
// using dos!!!
}
}
/*
* Write the keyed hash which is used to detect tampering with
* the keystore (such as deleting or modifying key or
* certificate entries).
*/
byte digest[] = md.digest();
dos.write(digest);
dos.flush();
} finally {
if (oos != null) {
oos.close();
} else {
dos.close();
}
}
}
}
/**
* Loads the keystore from the given input stream.
*
* <p>If a password is given, it is used to check the integrity of the
* keystore data. Otherwise, the integrity of the keystore is not checked.
*
* @param stream the input stream from which the keystore is loaded
* @param password the (optional) password used to check the integrity of
* the keystore.
*
* @exception IOException if there is an I/O or format problem with the
* keystore data
* @exception NoSuchAlgorithmException if the algorithm used to check
* the integrity of the keystore cannot be found
* @exception CertificateException if any of the certificates in the
* keystore could not be loaded
*/
public void engineLoad(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
synchronized(entries) {
DataInputStream dis;
MessageDigest md = null;
CertificateFactory cf = null;
Hashtable<String, CertificateFactory> cfs = null;
ByteArrayInputStream bais = null;
byte[] encoded = null;
int trustedKeyCount = 0, privateKeyCount = 0, secretKeyCount = 0;
if (stream == null)
return;
byte[] allData = IOUtils.readAllBytes(stream);
int fullLength = allData.length;
stream = new ByteArrayInputStream(allData);
if (password != null) {
md = getPreKeyedHash(password);
dis = new DataInputStream(new DigestInputStream(stream, md));
} else {
dis = new DataInputStream(stream);
}
// NOTE: don't pass dis to ois at this point or it'll fail to load
// the keystore!!!
ObjectInputStream ois = null;
try {
// Body format: see store method
int xMagic = dis.readInt();
int xVersion = dis.readInt();
// Accept the following keystore implementations:
// - JCEKS (this implementation), versions 1 and 2
// - JKS (Sun's keystore implementation in JDK 1.2),
// versions 1 and 2
if (((xMagic != JCEKS_MAGIC) && (xMagic != JKS_MAGIC)) ||
((xVersion != VERSION_1) && (xVersion != VERSION_2))) {
throw new IOException("Invalid keystore format");
}
if (xVersion == VERSION_1) {
cf = CertificateFactory.getInstance("X509");
} else {
// version 2
cfs = new Hashtable<String, CertificateFactory>(3);
}
entries.clear();
int count = dis.readInt();
for (int i = 0; i < count; i++) {
int tag;
String alias;
tag = dis.readInt();
if (tag == 1) { // private-key entry
privateKeyCount++;
PrivateKeyEntry entry = new PrivateKeyEntry();
// read the alias
alias = dis.readUTF();
// read the (entry creation) date
entry.date = new Date(dis.readLong());
// read the private key
entry.protectedKey = IOUtils.readExactlyNBytes(dis, dis.readInt());
// read the certificate chain
int numOfCerts = dis.readInt();
List<Certificate> tmpCerts = new ArrayList<>();
for (int j = 0; j < numOfCerts; j++) {
if (xVersion == 2) {
// read the certificate type, and instantiate a
// certificate factory of that type (reuse
// existing factory if possible)
String certType = dis.readUTF();
if (cfs.containsKey(certType)) {
// reuse certificate factory
cf = cfs.get(certType);
} else {
// create new certificate factory
cf = CertificateFactory.getInstance(
certType);
// store the certificate factory so we can
// reuse it later
cfs.put(certType, cf);
}
}
// instantiate the certificate
encoded = IOUtils.readExactlyNBytes(dis, dis.readInt());
bais = new ByteArrayInputStream(encoded);
tmpCerts.add(cf.generateCertificate(bais));
}
entry.chain = tmpCerts.toArray(
new Certificate[numOfCerts]);
// Add the entry to the list
entries.put(alias, entry);
} else if (tag == 2) { // trusted certificate entry
trustedKeyCount++;
TrustedCertEntry entry = new TrustedCertEntry();
// read the alias
alias = dis.readUTF();
// read the (entry creation) date
entry.date = new Date(dis.readLong());
// read the trusted certificate
if (xVersion == 2) {
// read the certificate type, and instantiate a
// certificate factory of that type (reuse
// existing factory if possible)
String certType = dis.readUTF();
if (cfs.containsKey(certType)) {
// reuse certificate factory
cf = cfs.get(certType);
} else {
// create new certificate factory
cf = CertificateFactory.getInstance(certType);
// store the certificate factory so we can
// reuse it later
cfs.put(certType, cf);
}
}
encoded = IOUtils.readExactlyNBytes(dis, dis.readInt());
bais = new ByteArrayInputStream(encoded);
entry.cert = cf.generateCertificate(bais);
// Add the entry to the list
entries.put(alias, entry);
} else if (tag == 3) { // secret-key entry
secretKeyCount++;
SecretKeyEntry entry = new SecretKeyEntry();
// read the alias
alias = dis.readUTF();
// read the (entry creation) date
entry.date = new Date(dis.readLong());
// read the sealed key
try {
ois = new ObjectInputStream(dis);
final ObjectInputStream ois2 = ois;
// Set a deserialization checker
AccessController.doPrivileged(
(PrivilegedAction<Void>)() -> {
ObjectInputFilter.Config.setObjectInputFilter(
ois2, new DeserializationChecker(fullLength));
return null;
});
entry.sealedKey = (SealedObject)ois.readObject();
entry.maxLength = fullLength;
// NOTE: don't close ois here since we are still
// using dis!!!
} catch (ClassNotFoundException cnfe) {
throw new IOException(cnfe.getMessage());
} catch (InvalidClassException ice) {
throw new IOException("Invalid secret key format");
}
// Add the entry to the list
entries.put(alias, entry);
} else {
throw new IOException("Unrecognized keystore entry: " +
tag);
}
}
if (debug != null) {
debug.println("JceKeyStore load: private key count: " +
privateKeyCount + ". trusted key count: " +
trustedKeyCount + ". secret key count: " +
secretKeyCount);
}
/*
* If a password has been provided, we check the keyed digest
* at the end. If this check fails, the store has been tampered
* with
*/
if (password != null) {
byte[] computed = md.digest();
byte[] actual = IOUtils.readExactlyNBytes(dis, computed.length);
if (!MessageDigest.isEqual(computed, actual)) {
throw new IOException(
"Keystore was tampered with, or "
+ "password was incorrect",
new UnrecoverableKeyException(
"Password verification failed"));
}
}
} finally {
if (ois != null) {
ois.close();
} else {
dis.close();
}
}
}
}
/**
* To guard against tampering with the keystore, we append a keyed
* hash with a bit of whitener.
*/
private MessageDigest getPreKeyedHash(char[] password)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
int i, j;
MessageDigest md = MessageDigest.getInstance("SHA");
byte[] passwdBytes = new byte[password.length * 2];
for (i=0, j=0; i<password.length; i++) {
passwdBytes[j++] = (byte)(password[i] >> 8);
passwdBytes[j++] = (byte)password[i];
}
md.update(passwdBytes);
for (i=0; i<passwdBytes.length; i++)
passwdBytes[i] = 0;
md.update("Mighty Aphrodite".getBytes("UTF8"));
return md;
}
/*
* An ObjectInputFilter that checks the format of the secret key being
* deserialized.
*/
private static class DeserializationChecker implements ObjectInputFilter {
// Full length of keystore, anything inside a SecretKeyEntry should not
// be bigger. Otherwise, must be illegal.
private final int fullLength;
public DeserializationChecker(int fullLength) {
this.fullLength = fullLength;
}
@Override
public ObjectInputFilter.Status
checkInput(ObjectInputFilter.FilterInfo info) {
if (info.arrayLength() > fullLength) {
return Status.REJECTED;
}
// First run a custom filter
Class<?> clazz = info.serialClass();
switch((int)info.depth()) {
case 1:
if (clazz != SealedObjectForKeyProtector.class) {
return Status.REJECTED;
}
break;
case 2:
if (clazz != null && clazz != SealedObject.class
&& clazz != byte[].class) {
return Status.REJECTED;
}
break;
default:
if (clazz != null && clazz != Object.class) {
return Status.REJECTED;
}
break;
}
// Next run the default filter, if available
ObjectInputFilter defaultFilter =
ObjectInputFilter.Config.getSerialFilter();
if (defaultFilter != null) {
return defaultFilter.checkInput(info);
}
return Status.UNDECIDED;
}
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright (c) 2003, 2013, 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.security.*;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
/**
* KeyGeneratore core implementation and individual key generator
* implementations. Because of US export regulations, we cannot use
* subclassing to achieve code sharing between the key generator
* implementations for our various algorithms. Instead, we have the
* core implementation in this KeyGeneratorCore class, which is used
* by the individual implementations. See those further down in this
* file.
*
* @since 1.5
* @author Andreas Sterbenz
*/
final class KeyGeneratorCore {
// algorithm name to use for the generator keys
private final String name;
// default key size in bits
private final int defaultKeySize;
// current key size in bits
private int keySize;
// PRNG to use
private SecureRandom random;
/**
* Construct a new KeyGeneratorCore object with the specified name
* and defaultKeySize. Initialize to default key size in case the
* application does not call any of the init() methods.
*/
KeyGeneratorCore(String name, int defaultKeySize) {
this.name = name;
this.defaultKeySize = defaultKeySize;
implInit(null);
}
// implementation for engineInit(), see JCE doc
// reset keySize to default
void implInit(SecureRandom random) {
this.keySize = defaultKeySize;
this.random = random;
}
// implementation for engineInit(), see JCE doc
// we do not support any parameters
void implInit(AlgorithmParameterSpec params, SecureRandom random)
throws InvalidAlgorithmParameterException {
throw new InvalidAlgorithmParameterException
(name + " key generation does not take any parameters");
}
// implementation for engineInit(), see JCE doc
// we enforce a general 40 bit minimum key size for security
void implInit(int keysize, SecureRandom random) {
if (keysize < 40) {
throw new InvalidParameterException
("Key length must be at least 40 bits");
}
this.keySize = keysize;
this.random = random;
}
// implementation for engineInit(), see JCE doc
// generate the key
SecretKey implGenerateKey() {
if (random == null) {
random = SunJCE.getRandom();
}
byte[] b = new byte[(keySize + 7) >> 3];
random.nextBytes(b);
return new SecretKeySpec(b, name);
}
// nested static classes for the HmacSHA-2 family of key generator
abstract static class HmacSHA2KG extends KeyGeneratorSpi {
private final KeyGeneratorCore core;
protected HmacSHA2KG(String algoName, int len) {
core = new KeyGeneratorCore(algoName, len);
}
protected void engineInit(SecureRandom random) {
core.implInit(random);
}
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
core.implInit(params, random);
}
protected void engineInit(int keySize, SecureRandom random) {
core.implInit(keySize, random);
}
protected SecretKey engineGenerateKey() {
return core.implGenerateKey();
}
public static final class SHA224 extends HmacSHA2KG {
public SHA224() {
super("HmacSHA224", 224);
}
}
public static final class SHA256 extends HmacSHA2KG {
public SHA256() {
super("HmacSHA256", 256);
}
}
public static final class SHA384 extends HmacSHA2KG {
public SHA384() {
super("HmacSHA384", 384);
}
}
public static final class SHA512 extends HmacSHA2KG {
public SHA512() {
super("HmacSHA512", 512);
}
}
}
// nested static class for the RC2 key generator
public static final class RC2KeyGenerator extends KeyGeneratorSpi {
private final KeyGeneratorCore core;
public RC2KeyGenerator() {
core = new KeyGeneratorCore("RC2", 128);
}
protected void engineInit(SecureRandom random) {
core.implInit(random);
}
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
core.implInit(params, random);
}
protected void engineInit(int keySize, SecureRandom random) {
if ((keySize < 40) || (keySize > 1024)) {
throw new InvalidParameterException("Key length for RC2"
+ " must be between 40 and 1024 bits");
}
core.implInit(keySize, random);
}
protected SecretKey engineGenerateKey() {
return core.implGenerateKey();
}
}
// nested static class for the ARCFOUR (RC4) key generator
public static final class ARCFOURKeyGenerator extends KeyGeneratorSpi {
private final KeyGeneratorCore core;
public ARCFOURKeyGenerator() {
core = new KeyGeneratorCore("ARCFOUR", 128);
}
protected void engineInit(SecureRandom random) {
core.implInit(random);
}
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
core.implInit(params, random);
}
protected void engineInit(int keySize, SecureRandom random) {
if ((keySize < 40) || (keySize > 1024)) {
throw new InvalidParameterException("Key length for ARCFOUR"
+ " must be between 40 and 1024 bits");
}
core.implInit(keySize, random);
}
protected SecretKey engineGenerateKey() {
return core.implGenerateKey();
}
}
}

View File

@@ -0,0 +1,435 @@
/*
* Copyright (c) 1998, 2019, 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.io.IOException;
import java.security.Key;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.AlgorithmParameters;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.SecretKey;
import javax.crypto.SealedObject;
import javax.crypto.spec.*;
import javax.security.auth.DestroyFailedException;
import sun.security.x509.AlgorithmId;
import sun.security.util.ObjectIdentifier;
import sun.security.util.SecurityProperties;
/**
* This class implements a protection mechanism for private keys. In JCE, we
* use a stronger protection mechanism than in the JDK, because we can use
* the <code>Cipher</code> class.
* Private keys are protected using the JCE mechanism, and are recovered using
* either the JDK or JCE mechanism, depending on how the key has been
* protected. This allows us to parse Sun's keystore implementation that ships
* with JDK 1.2.
*
* @author Jan Luehe
*
*
* @see JceKeyStore
*/
final class KeyProtector {
// defined by SunSoft (SKI project)
private static final String PBE_WITH_MD5_AND_DES3_CBC_OID
= "1.3.6.1.4.1.42.2.19.1";
// JavaSoft proprietary key-protection algorithm (used to protect private
// keys in the keystore implementation that comes with JDK 1.2)
private static final String KEY_PROTECTOR_OID = "1.3.6.1.4.1.42.2.17.1.1";
private static final int MAX_ITERATION_COUNT = 5000000;
private static final int MIN_ITERATION_COUNT = 10000;
private static final int DEFAULT_ITERATION_COUNT = 200000;
private static final int SALT_LEN = 20; // the salt length
private static final int DIGEST_LEN = 20;
private static final int ITERATION_COUNT;
// the password used for protecting/recovering keys passed through this
// key protector
private char[] password;
/**
* {@systemProperty jdk.jceks.iterationCount} property indicating the
* number of iterations for password-based encryption (PBE) in JCEKS
* keystores. Values in the range 10000 to 5000000 are considered valid.
* If the value is out of this range, or is not a number, or is
* unspecified; a default of 200000 is used.
*/
static {
int iterationCount = DEFAULT_ITERATION_COUNT;
String ic = SecurityProperties.privilegedGetOverridable(
"jdk.jceks.iterationCount");
if (ic != null && !ic.isEmpty()) {
try {
iterationCount = Integer.parseInt(ic);
if (iterationCount < MIN_ITERATION_COUNT ||
iterationCount > MAX_ITERATION_COUNT) {
iterationCount = DEFAULT_ITERATION_COUNT;
}
} catch (NumberFormatException e) {}
}
ITERATION_COUNT = iterationCount;
}
KeyProtector(char[] password) {
if (password == null) {
throw new IllegalArgumentException("password can't be null");
}
this.password = password;
}
/**
* Protects the given cleartext private key, using the password provided at
* construction time.
*/
byte[] protect(PrivateKey key)
throws Exception
{
// create a random salt (8 bytes)
byte[] salt = new byte[8];
SunJCE.getRandom().nextBytes(salt);
// create PBE parameters from salt and iteration count
PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, ITERATION_COUNT);
// create PBE key from password
PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
SecretKey sKey = null;
PBEWithMD5AndTripleDESCipher cipher;
try {
sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
// encrypt private key
cipher = new PBEWithMD5AndTripleDESCipher();
cipher.engineInit(Cipher.ENCRYPT_MODE, sKey, pbeSpec, null);
} finally {
pbeKeySpec.clearPassword();
if (sKey != null) sKey.destroy();
}
byte[] plain = key.getEncoded();
byte[] encrKey = cipher.engineDoFinal(plain, 0, plain.length);
Arrays.fill(plain, (byte)0x00);
// wrap encrypted private key in EncryptedPrivateKeyInfo
// (as defined in PKCS#8)
AlgorithmParameters pbeParams =
AlgorithmParameters.getInstance("PBE", SunJCE.getInstance());
pbeParams.init(pbeSpec);
AlgorithmId encrAlg = new AlgorithmId
(new ObjectIdentifier(PBE_WITH_MD5_AND_DES3_CBC_OID), pbeParams);
return new EncryptedPrivateKeyInfo(encrAlg,encrKey).getEncoded();
}
/*
* Recovers the cleartext version of the given key (in protected format),
* using the password provided at construction time.
*/
Key recover(EncryptedPrivateKeyInfo encrInfo)
throws UnrecoverableKeyException, NoSuchAlgorithmException
{
byte[] plain = null;
SecretKey sKey = null;
try {
String encrAlg = encrInfo.getAlgorithm().getOID().toString();
if (!encrAlg.equals(PBE_WITH_MD5_AND_DES3_CBC_OID)
&& !encrAlg.equals(KEY_PROTECTOR_OID)) {
throw new UnrecoverableKeyException("Unsupported encryption "
+ "algorithm");
}
if (encrAlg.equals(KEY_PROTECTOR_OID)) {
// JDK 1.2 style recovery
plain = recover(encrInfo.getEncryptedData());
} else {
byte[] encodedParams =
encrInfo.getAlgorithm().getEncodedParams();
if (encodedParams == null) {
throw new IOException("Missing PBE parameters");
}
// parse the PBE parameters into the corresponding spec
AlgorithmParameters pbeParams =
AlgorithmParameters.getInstance("PBE");
pbeParams.init(encodedParams);
PBEParameterSpec pbeSpec =
pbeParams.getParameterSpec(PBEParameterSpec.class);
if (pbeSpec.getIterationCount() > MAX_ITERATION_COUNT) {
throw new IOException("PBE iteration count too large");
}
// create PBE key from password
PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
pbeKeySpec.clearPassword();
// decrypt private key
PBEWithMD5AndTripleDESCipher cipher;
cipher = new PBEWithMD5AndTripleDESCipher();
cipher.engineInit(Cipher.DECRYPT_MODE, sKey, pbeSpec, null);
plain=cipher.engineDoFinal(encrInfo.getEncryptedData(), 0,
encrInfo.getEncryptedData().length);
}
// determine the private-key algorithm, and parse private key
// using the appropriate key factory
String oidName = new AlgorithmId
(new PrivateKeyInfo(plain).getAlgorithm().getOID()).getName();
KeyFactory kFac = KeyFactory.getInstance(oidName);
return kFac.generatePrivate(new PKCS8EncodedKeySpec(plain));
} catch (NoSuchAlgorithmException ex) {
// Note: this catch needed to be here because of the
// later catch of GeneralSecurityException
throw ex;
} catch (IOException ioe) {
throw new UnrecoverableKeyException(ioe.getMessage());
} catch (GeneralSecurityException gse) {
throw new UnrecoverableKeyException(gse.getMessage());
} finally {
if (plain != null) Arrays.fill(plain, (byte)0x00);
if (sKey != null) {
try {
sKey.destroy();
} catch (DestroyFailedException e) {
//shouldn't happen
}
}
}
}
/*
* Recovers the cleartext version of the given key (in protected format),
* using the password provided at construction time. This method implements
* the recovery algorithm used by Sun's keystore implementation in
* JDK 1.2.
*/
private byte[] recover(byte[] protectedKey)
throws UnrecoverableKeyException, NoSuchAlgorithmException
{
int i, j;
byte[] digest;
int numRounds;
int xorOffset; // offset in xorKey where next digest will be stored
int encrKeyLen; // the length of the encrpyted key
MessageDigest md = MessageDigest.getInstance("SHA");
// Get the salt associated with this key (the first SALT_LEN bytes of
// <code>protectedKey</code>)
byte[] salt = new byte[SALT_LEN];
System.arraycopy(protectedKey, 0, salt, 0, SALT_LEN);
// Determine the number of digest rounds
encrKeyLen = protectedKey.length - SALT_LEN - DIGEST_LEN;
numRounds = encrKeyLen / DIGEST_LEN;
if ((encrKeyLen % DIGEST_LEN) != 0)
numRounds++;
// Get the encrypted key portion and store it in "encrKey"
byte[] encrKey = new byte[encrKeyLen];
System.arraycopy(protectedKey, SALT_LEN, encrKey, 0, encrKeyLen);
// Set up the byte array which will be XORed with "encrKey"
byte[] xorKey = new byte[encrKey.length];
// Convert password to byte array, so that it can be digested
byte[] passwdBytes = new byte[password.length * 2];
for (i=0, j=0; i<password.length; i++) {
passwdBytes[j++] = (byte)(password[i] >> 8);
passwdBytes[j++] = (byte)password[i];
}
// Compute the digests, and store them in "xorKey"
for (i = 0, xorOffset = 0, digest = salt;
i < numRounds;
i++, xorOffset += DIGEST_LEN) {
md.update(passwdBytes);
md.update(digest);
digest = md.digest();
md.reset();
// Copy the digest into "xorKey"
if (i < numRounds - 1) {
System.arraycopy(digest, 0, xorKey, xorOffset,
digest.length);
} else {
System.arraycopy(digest, 0, xorKey, xorOffset,
xorKey.length - xorOffset);
}
}
// XOR "encrKey" with "xorKey", and store the result in "plainKey"
byte[] plainKey = new byte[encrKey.length];
for (i = 0; i < plainKey.length; i++) {
plainKey[i] = (byte)(encrKey[i] ^ xorKey[i]);
}
// Check the integrity of the recovered key by concatenating it with
// the password, digesting the concatenation, and comparing the
// result of the digest operation with the digest provided at the end
// of <code>protectedKey</code>. If the two digest values are
// different, throw an exception.
md.update(passwdBytes);
Arrays.fill(passwdBytes, (byte)0x00);
passwdBytes = null;
md.update(plainKey);
digest = md.digest();
md.reset();
for (i = 0; i < digest.length; i++) {
if (digest[i] != protectedKey[SALT_LEN + encrKeyLen + i]) {
throw new UnrecoverableKeyException("Cannot recover key");
}
}
return plainKey;
}
/**
* Seals the given cleartext key, using the password provided at
* construction time
*/
SealedObject seal(Key key)
throws Exception
{
// create a random salt (8 bytes)
byte[] salt = new byte[8];
SunJCE.getRandom().nextBytes(salt);
// create PBE parameters from salt and iteration count
PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, ITERATION_COUNT);
// create PBE key from password
PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
SecretKey sKey = null;
Cipher cipher;
try {
sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
pbeKeySpec.clearPassword();
// seal key
PBEWithMD5AndTripleDESCipher cipherSpi;
cipherSpi = new PBEWithMD5AndTripleDESCipher();
cipher = new CipherForKeyProtector(cipherSpi, SunJCE.getInstance(),
"PBEWithMD5AndTripleDES");
cipher.init(Cipher.ENCRYPT_MODE, sKey, pbeSpec);
} finally {
if (sKey != null) sKey.destroy();
}
return new SealedObjectForKeyProtector(key, cipher);
}
/**
* Unseals the sealed key.
*
* @param maxLength Maximum possible length of so.
* If bigger, must be illegal.
*/
Key unseal(SealedObject so, int maxLength)
throws NoSuchAlgorithmException, UnrecoverableKeyException {
SecretKey sKey = null;
try {
// create PBE key from password
PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
pbeKeySpec.clearPassword();
SealedObjectForKeyProtector soForKeyProtector = null;
if (!(so instanceof SealedObjectForKeyProtector)) {
soForKeyProtector = new SealedObjectForKeyProtector(so);
} else {
soForKeyProtector = (SealedObjectForKeyProtector)so;
}
AlgorithmParameters params = soForKeyProtector.getParameters();
if (params == null) {
throw new UnrecoverableKeyException("Cannot get " +
"algorithm parameters");
}
PBEParameterSpec pbeSpec;
try {
pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
} catch (InvalidParameterSpecException ipse) {
throw new IOException("Invalid PBE algorithm parameters");
}
if (pbeSpec.getIterationCount() > MAX_ITERATION_COUNT) {
throw new IOException("PBE iteration count too large");
}
PBEWithMD5AndTripleDESCipher cipherSpi;
cipherSpi = new PBEWithMD5AndTripleDESCipher();
Cipher cipher = new CipherForKeyProtector(cipherSpi,
SunJCE.getInstance(),
"PBEWithMD5AndTripleDES");
cipher.init(Cipher.DECRYPT_MODE, sKey, params);
return soForKeyProtector.getKey(cipher, maxLength);
} catch (NoSuchAlgorithmException ex) {
// Note: this catch needed to be here because of the
// later catch of GeneralSecurityException
throw ex;
} catch (IOException ioe) {
throw new UnrecoverableKeyException(ioe.getMessage());
} catch (ClassNotFoundException cnfe) {
throw new UnrecoverableKeyException(cnfe.getMessage());
} catch (GeneralSecurityException gse) {
throw new UnrecoverableKeyException(gse.getMessage());
} finally {
if (sKey != null) {
try {
sKey.destroy();
} catch (DestroyFailedException e) {
//shouldn't happen
}
}
}
}
}
final class CipherForKeyProtector extends javax.crypto.Cipher {
/**
* Creates a Cipher object.
*
* @param cipherSpi the delegate
* @param provider the provider
* @param transformation the transformation
*/
protected CipherForKeyProtector(CipherSpi cipherSpi,
Provider provider,
String transformation) {
super(cipherSpi, provider, transformation);
}
}

View File

@@ -0,0 +1,261 @@
/*
* Copyright (c) 2003, 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.math.BigInteger;
import java.io.*;
import sun.security.util.*;
import sun.security.x509.*;
import java.security.AlgorithmParametersSpi;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.MGF1ParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.OAEPParameterSpec;
/**
* This class implements the OAEP parameters used with the RSA
* algorithm in OAEP padding. Here is its ASN.1 definition:
* RSAES-OAEP-params ::= SEQUENCE {
* hashAlgorithm [0] HashAlgorithm DEFAULT sha1,
* maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
* pSourceAlgorithm [2] PSourceAlgorithm DEFAULT pSpecifiedEmpty
* }
*
* @author Valerie Peng
*
*/
public final class OAEPParameters extends AlgorithmParametersSpi {
private String mdName;
private MGF1ParameterSpec mgfSpec;
private byte[] p;
private static ObjectIdentifier OID_MGF1;
private static ObjectIdentifier OID_PSpecified;
static {
try {
OID_MGF1 = new ObjectIdentifier(new int[] {1,2,840,113549,1,1,8});
} catch (IOException ioe) {
// should not happen
OID_MGF1 = null;
}
try {
OID_PSpecified =
new ObjectIdentifier(new int[] {1,2,840,113549,1,1,9});
} catch (IOException ioe) {
// should not happen
OID_PSpecified = null;
}
}
public OAEPParameters() {
}
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
if (!(paramSpec instanceof OAEPParameterSpec)) {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
OAEPParameterSpec spec = (OAEPParameterSpec) paramSpec;
mdName = spec.getDigestAlgorithm();
String mgfName = spec.getMGFAlgorithm();
if (!mgfName.equalsIgnoreCase("MGF1")) {
throw new InvalidParameterSpecException("Unsupported mgf " +
mgfName + "; MGF1 only");
}
AlgorithmParameterSpec mgfSpec = spec.getMGFParameters();
if (!(mgfSpec instanceof MGF1ParameterSpec)) {
throw new InvalidParameterSpecException("Inappropriate mgf " +
"parameters; non-null MGF1ParameterSpec only");
}
this.mgfSpec = (MGF1ParameterSpec) mgfSpec;
PSource pSrc = spec.getPSource();
if (pSrc.getAlgorithm().equals("PSpecified")) {
p = ((PSource.PSpecified) pSrc).getValue();
} else {
throw new InvalidParameterSpecException("Unsupported pSource " +
pSrc.getAlgorithm() + "; PSpecified only");
}
}
protected void engineInit(byte[] encoded)
throws IOException {
DerInputStream der = new DerInputStream(encoded);
mdName = "SHA-1";
mgfSpec = MGF1ParameterSpec.SHA1;
p = new byte[0];
DerValue[] datum = der.getSequence(3);
for (int i=0; i<datum.length; i++) {
DerValue data = datum[i];
if (data.isContextSpecific((byte) 0x00)) {
// hash algid
mdName = AlgorithmId.parse
(data.data.getDerValue()).getName();
} else if (data.isContextSpecific((byte) 0x01)) {
// mgf algid
AlgorithmId val = AlgorithmId.parse(data.data.getDerValue());
if (!val.getOID().equals((Object) OID_MGF1)) {
throw new IOException("Only MGF1 mgf is supported");
}
byte[] encodedParams = val.getEncodedParams();
if (encodedParams == null) {
throw new IOException("Missing MGF1 parameters");
}
AlgorithmId params = AlgorithmId.parse(
new DerValue(encodedParams));
String mgfDigestName = params.getName();
if (mgfDigestName.equals("SHA-1")) {
mgfSpec = MGF1ParameterSpec.SHA1;
} else if (mgfDigestName.equals("SHA-224")) {
mgfSpec = MGF1ParameterSpec.SHA224;
} else if (mgfDigestName.equals("SHA-256")) {
mgfSpec = MGF1ParameterSpec.SHA256;
} else if (mgfDigestName.equals("SHA-384")) {
mgfSpec = MGF1ParameterSpec.SHA384;
} else if (mgfDigestName.equals("SHA-512")) {
mgfSpec = MGF1ParameterSpec.SHA512;
} else if (mgfDigestName.equals("SHA-512/224")) {
mgfSpec = MGF1ParameterSpec.SHA512_224;
} else if (mgfDigestName.equals("SHA-512/256")) {
mgfSpec = MGF1ParameterSpec.SHA512_256;
} else {
throw new IOException(
"Unrecognized message digest algorithm");
}
} else if (data.isContextSpecific((byte) 0x02)) {
// pSource algid
AlgorithmId val = AlgorithmId.parse(data.data.getDerValue());
if (!val.getOID().equals((Object) OID_PSpecified)) {
throw new IOException("Wrong OID for pSpecified");
}
byte[] encodedParams = val.getEncodedParams();
if (encodedParams == null) {
throw new IOException("Missing pSpecified label");
}
DerInputStream dis = new DerInputStream(encodedParams);
p = dis.getOctetString();
if (dis.available() != 0) {
throw new IOException("Extra data for pSpecified");
}
} else {
throw new IOException("Invalid encoded OAEPParameters");
}
}
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException {
if ((decodingMethod != null) &&
(!decodingMethod.equalsIgnoreCase("ASN.1"))) {
throw new IllegalArgumentException("Only support ASN.1 format");
}
engineInit(encoded);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException {
if (OAEPParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast(
new OAEPParameterSpec(mdName, "MGF1", mgfSpec,
new PSource.PSpecified(p)));
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
DerOutputStream tmp = new DerOutputStream();
DerOutputStream tmp2, tmp3;
// MD
AlgorithmId mdAlgId;
try {
mdAlgId = AlgorithmId.get(mdName);
} catch (NoSuchAlgorithmException nsae) {
throw new IOException("AlgorithmId " + mdName +
" impl not found");
}
tmp2 = new DerOutputStream();
mdAlgId.derEncode(tmp2);
tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0),
tmp2);
// MGF
tmp2 = new DerOutputStream();
tmp2.putOID(OID_MGF1);
AlgorithmId mgfDigestId;
try {
mgfDigestId = AlgorithmId.get(mgfSpec.getDigestAlgorithm());
} catch (NoSuchAlgorithmException nase) {
throw new IOException("AlgorithmId " +
mgfSpec.getDigestAlgorithm() + " impl not found");
}
mgfDigestId.encode(tmp2);
tmp3 = new DerOutputStream();
tmp3.write(DerValue.tag_Sequence, tmp2);
tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)1),
tmp3);
// PSource
tmp2 = new DerOutputStream();
tmp2.putOID(OID_PSpecified);
tmp2.putOctetString(p);
tmp3 = new DerOutputStream();
tmp3.write(DerValue.tag_Sequence, tmp2);
tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)2),
tmp3);
// Put all together under a SEQUENCE tag
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, tmp);
return out.toByteArray();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException {
if ((encodingMethod != null) &&
(!encodingMethod.equalsIgnoreCase("ASN.1"))) {
throw new IllegalArgumentException("Only support ASN.1 format");
}
return engineGetEncoded();
}
protected String engineToString() {
StringBuffer sb = new StringBuffer();
sb.append("MD: " + mdName + "\n");
sb.append("MGF: MGF1" + mgfSpec.getDigestAlgorithm() + "\n");
sb.append("PSource: PSpecified " +
(p.length==0? "":Debug.toHexString(new BigInteger(p))) + "\n");
return sb.toString();
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright (c) 1997, 2018, 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.security.InvalidKeyException;
import java.security.ProviderException;
/**
* This class represents ciphers in output-feedback (OFB) mode.
*
* <p>This mode is implemented independently of a particular cipher.
* Ciphers to which this mode should apply (e.g., DES) must be
* <i>plugged-in</i> using the constructor.
*
* <p>NOTE: This class does not deal with buffering or padding.
*
* @author Gigi Ankeny
*/
final class OutputFeedback extends FeedbackCipher {
/*
* output buffer
*/
private byte[] k = null;
/*
* register buffer
*/
private byte[] register = null;
/*
* number of bytes for each stream unit, defaults to the blocksize
* of the embedded cipher
*/
private int numBytes;
// variables for save/restore calls
private byte[] registerSave = null;
OutputFeedback(SymmetricCipher embeddedCipher, int numBytes) {
super(embeddedCipher);
if (numBytes > blockSize) {
numBytes = blockSize;
}
this.numBytes = numBytes;
k = new byte[blockSize];
register = new byte[blockSize];
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>OFB</code>
*/
String getFeedback() {
return "OFB";
}
/**
* 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
*/
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException {
if ((key == null) || (iv == null) || (iv.length != blockSize)) {
throw new InvalidKeyException("Internal error");
}
this.iv = iv;
reset();
// always encrypt mode for embedded cipher
embeddedCipher.init(false, algorithm, key);
}
/**
* 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).
*/
void reset() {
System.arraycopy(iv, 0, register, 0, blockSize);
}
/**
* Save the current content of this cipher.
*/
void save() {
if (registerSave == null) {
registerSave = new byte[blockSize];
}
System.arraycopy(register, 0, registerSave, 0, blockSize);
}
/**
* Restores the content of this cipher to the previous saved one.
*/
void restore() {
System.arraycopy(registerSave, 0, register, 0, blockSize);
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + plainLen - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @exception ProviderException if <code>plainLen</code> is not
* a multiple of the <code>numBytes</code>
* @return the length of the encrypted data
*/
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) {
RangeUtil.blockSizeCheck(plainLen, numBytes);
RangeUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
RangeUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
int nShift = blockSize - numBytes;
int loopCount = plainLen / numBytes;
for (; loopCount > 0;
plainOffset += numBytes, cipherOffset += numBytes,
loopCount--) {
embeddedCipher.encryptBlock(register, 0, k, 0);
for (int i = 0; i < numBytes; i++) {
cipher[i + cipherOffset] =
(byte)(k[i] ^ plain[i + plainOffset]);
}
if (nShift != 0) {
System.arraycopy(register, numBytes, register, 0, nShift);
}
System.arraycopy(k, 0, register, nShift, numBytes);
}
return plainLen;
}
/**
* Performs last encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + plainLen - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @return the length of the encrypted data
*/
int encryptFinal(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) {
RangeUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
RangeUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
int oddBytes = plainLen % numBytes;
int len = encrypt(plain, plainOffset, (plainLen - oddBytes),
cipher, cipherOffset);
plainOffset += len;
cipherOffset += len;
if (oddBytes != 0) {
embeddedCipher.encryptBlock(register, 0, k, 0);
for (int i = 0; i < oddBytes; i++) {
cipher[i + cipherOffset] =
(byte)(k[i] ^ plain[ i + plainOffset]);
}
}
return plainLen;
}
// OFB encrypt and decrypt are identical
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) {
return encrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
}
// OFB encrypt and decrypt are identical
int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) {
// OFB encrypt and decrypt are identical
return encryptFinal(cipher, cipherOffset, cipherLen, plain, plainOffset);
}
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (c) 1997, 2023, 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.io.IOException;
import java.io.InvalidObjectException;
import java.security.MessageDigest;
import java.security.KeyRep;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Locale;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEKeySpec;
/**
* This class represents a PBE key.
*
* @author Jan Luehe
*
*/
final class PBEKey implements SecretKey {
private static final long serialVersionUID = -2234768909660948176L;
private byte[] key;
private final String type;
/**
* Creates a PBE key from a given PBE key specification.
*
* @param key the given PBE key specification
*/
PBEKey(PBEKeySpec keySpec, String keytype) throws InvalidKeySpecException {
char[] passwd = keySpec.getPassword();
if (passwd == null) {
// Should allow an empty password.
passwd = new char[0];
}
// Accept "\0" to signify "zero-length password with no terminator".
if (!(passwd.length == 1 && passwd[0] == 0)) {
for (int i=0; i<passwd.length; i++) {
if ((passwd[i] < '\u0020') || (passwd[i] > '\u007E')) {
throw new InvalidKeySpecException("Password is not ASCII");
}
}
}
this.key = new byte[passwd.length];
for (int i=0; i<passwd.length; i++)
this.key[i] = (byte) (passwd[i] & 0x7f);
Arrays.fill(passwd, '\0');
type = keytype;
}
public synchronized byte[] getEncoded() {
return this.key.clone();
}
public String getAlgorithm() {
return type;
}
public String getFormat() {
return "RAW";
}
/**
* Calculates a hash code value for the object.
* Objects that are equal will also have the same hashcode.
*/
public int hashCode() {
int retval = 0;
for (int i = 1; i < this.key.length; i++) {
retval += this.key[i] * i;
}
return(retval ^ getAlgorithm().toLowerCase(Locale.ENGLISH).hashCode());
}
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof SecretKey))
return false;
SecretKey that = (SecretKey)obj;
if (!(that.getAlgorithm().equalsIgnoreCase(type)))
return false;
byte[] thatEncoded = that.getEncoded();
boolean ret = MessageDigest.isEqual(this.key, thatEncoded);
Arrays.fill(thatEncoded, (byte)0x00);
return ret;
}
/**
* Clears the internal copy of the key.
*
*/
@Override
public void destroy() {
if (key != null) {
Arrays.fill(key, (byte)0x00);
key = null;
}
}
/**
* Restores the state of this object from the stream.
*
* @param s the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject();
if (key == null) {
throw new InvalidObjectException(
"PBEKey couldn't be deserialized");
}
key = key.clone();
// Accept "\0" to signify "zero-length password with no terminator".
if (!(key.length == 1 && key[0] == 0)) {
for (int i = 0; i < key.length; i++) {
if ((key[i] < '\u0020') || (key[i] > '\u007E')) {
throw new InvalidObjectException(
"PBEKey had non-ASCII chars");
}
}
}
}
/**
* Replace the PBE key to be serialized.
*
* @return the standard KeyRep object to be serialized
*
* @throws java.io.ObjectStreamException if a new object representing
* this PBE key could not be created
*/
private Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.SECRET,
getAlgorithm(),
getFormat(),
getEncoded());
}
/**
* Ensures that the password bytes of this key are
* set to zero when there are no more references to it.
*/
protected void finalize() throws Throwable {
try {
synchronized (this) {
if (this.key != null) {
java.util.Arrays.fill(this.key, (byte)0x00);
this.key = null;
}
}
} finally {
super.finalize();
}
}
}

View File

@@ -0,0 +1,306 @@
/*
* Copyright (c) 1997, 2014, 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.security.InvalidKeyException;
import java.security.spec.KeySpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.PBEKeySpec;
import java.util.HashSet;
import java.util.Locale;
/**
* This class implements a key factory for PBE keys according to PKCS#5,
* meaning that the password must consist of printable ASCII characters
* (values 32 to 126 decimal inclusive) and only the low order 8 bits
* of each password character are used.
*
* @author Jan Luehe
*
*/
abstract class PBEKeyFactory extends SecretKeyFactorySpi {
private String type;
private static HashSet<String> validTypes;
/**
* Simple constructor
*/
private PBEKeyFactory(String keytype) {
type = keytype;
}
static {
validTypes = new HashSet<String>(17);
validTypes.add("PBEWithMD5AndDES".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithSHA1AndDESede".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithSHA1AndRC2_40".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithSHA1AndRC2_128".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithSHA1AndRC4_40".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithSHA1AndRC4_128".toUpperCase(Locale.ENGLISH));
// Proprietary algorithm.
validTypes.add("PBEWithMD5AndTripleDES".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA1AndAES_128".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA224AndAES_128".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA256AndAES_128".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA384AndAES_128".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA512AndAES_128".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA1AndAES_256".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA224AndAES_256".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA256AndAES_256".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA384AndAES_256".toUpperCase(Locale.ENGLISH));
validTypes.add("PBEWithHmacSHA512AndAES_256".toUpperCase(Locale.ENGLISH));
}
public static final class PBEWithMD5AndDES
extends PBEKeyFactory {
public PBEWithMD5AndDES() {
super("PBEWithMD5AndDES");
}
}
public static final class PBEWithSHA1AndDESede
extends PBEKeyFactory {
public PBEWithSHA1AndDESede() {
super("PBEWithSHA1AndDESede");
}
}
public static final class PBEWithSHA1AndRC2_40
extends PBEKeyFactory {
public PBEWithSHA1AndRC2_40() {
super("PBEWithSHA1AndRC2_40");
}
}
public static final class PBEWithSHA1AndRC2_128
extends PBEKeyFactory {
public PBEWithSHA1AndRC2_128() {
super("PBEWithSHA1AndRC2_128");
}
}
public static final class PBEWithSHA1AndRC4_40
extends PBEKeyFactory {
public PBEWithSHA1AndRC4_40() {
super("PBEWithSHA1AndRC4_40");
}
}
public static final class PBEWithSHA1AndRC4_128
extends PBEKeyFactory {
public PBEWithSHA1AndRC4_128() {
super("PBEWithSHA1AndRC4_128");
}
}
/*
* Private proprietary algorithm for supporting JCEKS.
*/
public static final class PBEWithMD5AndTripleDES
extends PBEKeyFactory {
public PBEWithMD5AndTripleDES() {
super("PBEWithMD5AndTripleDES");
}
}
public static final class PBEWithHmacSHA1AndAES_128
extends PBEKeyFactory {
public PBEWithHmacSHA1AndAES_128() {
super("PBEWithHmacSHA1AndAES_128");
}
}
public static final class PBEWithHmacSHA224AndAES_128
extends PBEKeyFactory {
public PBEWithHmacSHA224AndAES_128() {
super("PBEWithHmacSHA224AndAES_128");
}
}
public static final class PBEWithHmacSHA256AndAES_128
extends PBEKeyFactory {
public PBEWithHmacSHA256AndAES_128() {
super("PBEWithHmacSHA256AndAES_128");
}
}
public static final class PBEWithHmacSHA384AndAES_128
extends PBEKeyFactory {
public PBEWithHmacSHA384AndAES_128() {
super("PBEWithHmacSHA384AndAES_128");
}
}
public static final class PBEWithHmacSHA512AndAES_128
extends PBEKeyFactory {
public PBEWithHmacSHA512AndAES_128() {
super("PBEWithHmacSHA512AndAES_128");
}
}
public static final class PBEWithHmacSHA1AndAES_256
extends PBEKeyFactory {
public PBEWithHmacSHA1AndAES_256() {
super("PBEWithHmacSHA1AndAES_256");
}
}
public static final class PBEWithHmacSHA224AndAES_256
extends PBEKeyFactory {
public PBEWithHmacSHA224AndAES_256() {
super("PBEWithHmacSHA224AndAES_256");
}
}
public static final class PBEWithHmacSHA256AndAES_256
extends PBEKeyFactory {
public PBEWithHmacSHA256AndAES_256() {
super("PBEWithHmacSHA256AndAES_256");
}
}
public static final class PBEWithHmacSHA384AndAES_256
extends PBEKeyFactory {
public PBEWithHmacSHA384AndAES_256() {
super("PBEWithHmacSHA384AndAES_256");
}
}
public static final class PBEWithHmacSHA512AndAES_256
extends PBEKeyFactory {
public PBEWithHmacSHA512AndAES_256() {
super("PBEWithHmacSHA512AndAES_256");
}
}
/**
* Generates a <code>SecretKey</code> object from the provided key
* specification (key material).
*
* @param keySpec the specification (key material) of the secret key
*
* @return the secret key
*
* @exception InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
protected SecretKey engineGenerateSecret(KeySpec keySpec)
throws InvalidKeySpecException
{
if (!(keySpec instanceof PBEKeySpec)) {
throw new InvalidKeySpecException("Invalid key spec");
}
return new PBEKey((PBEKeySpec)keySpec, type);
}
/**
* Returns a specification (key material) of the given key
* in the requested format.
*
* @param key the key
*
* @param keySpec the requested format in which the key material shall be
* returned
*
* @return the underlying key specification (key material) in the
* requested format
*
* @exception InvalidKeySpecException if the requested key specification is
* inappropriate for the given key, or the given key cannot be processed
* (e.g., the given key has an unrecognized algorithm or format).
*/
protected KeySpec engineGetKeySpec(SecretKey key, Class<?> keySpecCl)
throws InvalidKeySpecException {
if ((key instanceof SecretKey)
&& (validTypes.contains(key.getAlgorithm().toUpperCase(Locale.ENGLISH)))
&& (key.getFormat().equalsIgnoreCase("RAW"))) {
// Check if requested key spec is amongst the valid ones
if ((keySpecCl != null)
&& PBEKeySpec.class.isAssignableFrom(keySpecCl)) {
byte[] passwdBytes = key.getEncoded();
char[] passwdChars = new char[passwdBytes.length];
for (int i=0; i<passwdChars.length; i++)
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
PBEKeySpec ret = new PBEKeySpec(passwdChars);
// password char[] was cloned in PBEKeySpec constructor,
// so we can zero it out here
java.util.Arrays.fill(passwdChars, ' ');
java.util.Arrays.fill(passwdBytes, (byte)0x00);
return ret;
} else {
throw new InvalidKeySpecException("Invalid key spec");
}
} else {
throw new InvalidKeySpecException("Invalid key "
+ "format/algorithm");
}
}
/**
* Translates a <code>SecretKey</code> object, whose provider may be
* unknown or potentially untrusted, into a corresponding
* <code>SecretKey</code> object of this key factory.
*
* @param key the key whose provider is unknown or untrusted
*
* @return the translated key
*
* @exception InvalidKeyException if the given key cannot be processed by
* this key factory.
*/
protected SecretKey engineTranslateKey(SecretKey key)
throws InvalidKeyException
{
try {
if ((key != null) &&
(validTypes.contains(key.getAlgorithm().toUpperCase(Locale.ENGLISH))) &&
(key.getFormat().equalsIgnoreCase("RAW"))) {
// Check if key originates from this factory
if (key instanceof com.sun.crypto.provider.PBEKey) {
return key;
}
// Convert key to spec
PBEKeySpec pbeKeySpec = (PBEKeySpec)engineGetKeySpec
(key, PBEKeySpec.class);
// Create key from spec, and return it
return engineGenerateSecret(pbeKeySpec);
} else {
throw new InvalidKeyException("Invalid key format/algorithm");
}
} catch (InvalidKeySpecException ikse) {
throw new InvalidKeyException("Cannot translate key: "
+ ikse.getMessage());
}
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 1998, 2012, 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.io.*;
import java.math.BigInteger;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.spec.PBEParameterSpec;
import sun.misc.HexDumpEncoder;
import sun.security.util.*;
/**
* This class implements the parameter set used with password-based
* encryption, which is defined in PKCS#5 as follows:
*
* <pre>
* PBEParameter ::= SEQUENCE {
* salt OCTET STRING SIZE(8),
* iterationCount INTEGER }
* </pre>
*
* @author Jan Luehe
*
*/
public final class PBEParameters extends AlgorithmParametersSpi {
// the salt
private byte[] salt = null;
// the iteration count
private int iCount = 0;
// the cipher parameter
private AlgorithmParameterSpec cipherParam = null;
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException
{
if (!(paramSpec instanceof PBEParameterSpec)) {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
this.salt = ((PBEParameterSpec)paramSpec).getSalt().clone();
this.iCount = ((PBEParameterSpec)paramSpec).getIterationCount();
this.cipherParam = ((PBEParameterSpec)paramSpec).getParameterSpec();
}
protected void engineInit(byte[] encoded)
throws IOException
{
try {
DerValue val = new DerValue(encoded);
if (val.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not a sequence");
}
val.data.reset();
this.salt = val.data.getOctetString();
this.iCount = val.data.getInteger();
if (val.data.available() != 0) {
throw new IOException
("PBE parameter parsing error: extra data");
}
} catch (NumberFormatException e) {
throw new IOException("iteration count too big");
}
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException
{
engineInit(encoded);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException
{
if (PBEParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast(
new PBEParameterSpec(this.salt, this.iCount, this.cipherParam));
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
DerOutputStream out = new DerOutputStream();
DerOutputStream bytes = new DerOutputStream();
bytes.putOctetString(this.salt);
bytes.putInteger(this.iCount);
out.write(DerValue.tag_Sequence, bytes);
return out.toByteArray();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException
{
return engineGetEncoded();
}
/*
* Returns a formatted string describing the parameters.
*/
protected String engineToString() {
String LINE_SEP = System.getProperty("line.separator");
String saltString = LINE_SEP + " salt:" + LINE_SEP + "[";
HexDumpEncoder encoder = new HexDumpEncoder();
saltString += encoder.encodeBuffer(salt);
saltString += "]";
return saltString + LINE_SEP + " iterationCount:"
+ LINE_SEP + Debug.toHexString(BigInteger.valueOf(iCount))
+ LINE_SEP;
}
}

View File

@@ -0,0 +1,553 @@
/*
* Copyright (c) 2002, 2019, 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.security.*;
import java.security.spec.*;
import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class represents password-based encryption as defined by the PKCS #5
* standard.
*
* @author Jan Luehe
*
*
* @see javax.crypto.Cipher
*/
final class PBES1Core {
// the encapsulated DES cipher
private CipherCore cipher;
private MessageDigest md;
private int blkSize;
private String algo = null;
private byte[] salt = null;
private int iCount = 10;
/**
* Creates an instance of PBE Cipher using the specified CipherSpi
* instance.
*
*/
PBES1Core(String cipherAlg) throws NoSuchAlgorithmException,
NoSuchPaddingException {
algo = cipherAlg;
if (algo.equals("DES")) {
cipher = new CipherCore(new DESCrypt(),
DESConstants.DES_BLOCK_SIZE);
} else if (algo.equals("DESede")) {
cipher = new CipherCore(new DESedeCrypt(),
DESConstants.DES_BLOCK_SIZE);
} else {
throw new NoSuchAlgorithmException("No Cipher implementation " +
"for PBEWithMD5And" + algo);
}
cipher.setMode("CBC");
cipher.setPadding("PKCS5Padding");
// get instance of MD5
md = MessageDigest.getInstance("MD5");
}
/**
* Sets the mode of this cipher. This algorithm can only be run in CBC
* mode.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode is
* invalid
*/
void setMode(String mode) throws NoSuchAlgorithmException {
cipher.setMode(mode);
}
/**
* Sets the padding mechanism of this cipher. This algorithm only uses
* PKCS #5 padding.
*
* @param padding the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* is invalid
*/
void setPadding(String paddingScheme) throws NoSuchPaddingException {
cipher.setPadding(paddingScheme);
}
/**
* Returns the block size (in bytes).
*
* @return the block size (in bytes)
*/
int getBlockSize() {
return DESConstants.DES_BLOCK_SIZE;
}
/**
* Returns the length in bytes that an output buffer would need to be in
* order to hold the result of the next <code>update</code> or
* <code>doFinal</code> operation, given the input length
* <code>inputLen</code> (in bytes).
*
* <p>This call takes into account any unprocessed (buffered) data from a
* previous <code>update</code> call, and padding.
*
* <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)
*
*/
int getOutputSize(int inputLen) {
return cipher.getOutputSize(inputLen);
}
/**
* Returns the initialization vector (IV) in a new buffer.
*
* <p> This is useful in the case where a random IV has been created
* (see <a href = "#init">init</a>),
* or in the context of password-based encryption or
* decryption, where the IV is derived from a user-supplied password.
*
* @return the initialization vector in a new buffer, or null if the
* underlying algorithm does not use an IV, or if the IV has not yet
* been set.
*/
byte[] getIV() {
return cipher.getIV();
}
/**
* Returns the parameters used with this cipher.
*
* <p>The returned parameters may be the same that were used to initialize
* this cipher, or may contain the default set of parameters or a set of
* randomly generated parameters used by the underlying cipher
* implementation (provided that the underlying cipher implementation
* uses a default set of parameters or creates new parameters if it needs
* parameters but was not initialized with any).
*
* @return the parameters used with this cipher, or null if this cipher
* does not use any parameters.
*/
AlgorithmParameters getParameters() {
AlgorithmParameters params = null;
if (salt == null) {
salt = new byte[8];
SunJCE.getRandom().nextBytes(salt);
}
PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount);
try {
params = AlgorithmParameters.getInstance("PBEWithMD5And" +
(algo.equalsIgnoreCase("DES")? "DES":"TripleDES"),
SunJCE.getInstance());
params.init(pbeSpec);
} catch (NoSuchAlgorithmException nsae) {
// should never happen
throw new RuntimeException("SunJCE called, but not configured");
} catch (InvalidParameterSpecException ipse) {
// should never happen
throw new RuntimeException("PBEParameterSpec not supported");
}
return params;
}
/**
* Initializes this cipher with a key, a set of
* algorithm parameters, and a source of randomness.
* The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @param key the encryption key
* @param params the algorithm parameters
* @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 are inappropriate for this cipher
*/
void init(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (((opmode == Cipher.DECRYPT_MODE) ||
(opmode == Cipher.UNWRAP_MODE)) && (params == null)) {
throw new InvalidAlgorithmParameterException("Parameters "
+ "missing");
}
if (key == null) {
throw new InvalidKeyException("Null key");
}
byte[] derivedKey;
byte[] passwdBytes = key.getEncoded();
try {
if ((passwdBytes == null) ||
!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
throw new InvalidKeyException("Missing password");
}
if (params == null) {
// create random salt and use default iteration count
salt = new byte[8];
random.nextBytes(salt);
} else {
if (!(params instanceof PBEParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Wrong parameter type: PBE expected");
}
salt = ((PBEParameterSpec) params).getSalt();
// salt must be 8 bytes long (by definition)
if (salt.length != 8) {
throw new InvalidAlgorithmParameterException
("Salt must be 8 bytes long");
}
iCount = ((PBEParameterSpec) params).getIterationCount();
if (iCount <= 0) {
throw new InvalidAlgorithmParameterException
("IterationCount must be a positive number");
}
}
derivedKey = deriveCipherKey(passwdBytes);
} finally {
if (passwdBytes != null) Arrays.fill(passwdBytes, (byte) 0x00);
}
// use all but the last 8 bytes as the key value
SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, 0,
derivedKey.length-8, algo);
// use the last 8 bytes as the IV
IvParameterSpec ivSpec = new IvParameterSpec(derivedKey,
derivedKey.length-8,
8);
// initialize the underlying cipher
cipher.init(opmode, cipherKey, ivSpec, random);
}
private byte[] deriveCipherKey(byte[] passwdBytes) {
byte[] result = null;
if (algo.equals("DES")) {
// P || S (password concatenated with salt)
md.update(passwdBytes);
md.update(salt);
// digest P || S with iCount iterations
// first iteration
byte[] toBeHashed = md.digest(); // this resets the digest
// remaining (iCount - 1) iterations
for (int i = 1; i < iCount; ++i) {
md.update(toBeHashed);
try {
md.digest(toBeHashed, 0, toBeHashed.length);
} catch (DigestException e) {
throw new ProviderException("Internal error", e);
}
}
result = toBeHashed;
} else if (algo.equals("DESede")) {
// if the 2 salt halves are the same, invert one of them
int i;
for (i=0; i<4; i++) {
if (salt[i] != salt[i+4])
break;
}
if (i==4) { // same, invert 1st half
for (i=0; i<2; i++) {
byte tmp = salt[i];
salt[i] = salt[3-i];
salt[3-i] = tmp;
}
}
// Now digest each half (concatenated with password). For each
// half, go through the loop as many times as specified by the
// iteration count parameter (inner for loop).
// Concatenate the output from each digest round with the
// password, and use the result as the input to the next digest
// operation.
byte[] toBeHashed = null;
result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN +
DESConstants.DES_BLOCK_SIZE];
for (i = 0; i < 2; i++) {
// first iteration
md.update(salt, i * (salt.length / 2), salt.length / 2);
md.update(passwdBytes);
toBeHashed = md.digest();
// remaining (iCount - 1) iterations
for (int j = 1; j < iCount; ++j) {
md.update(toBeHashed);
md.update(passwdBytes);
try {
md.digest(toBeHashed, 0, toBeHashed.length);
} catch (DigestException e) {
throw new ProviderException("Internal error", e);
}
}
System.arraycopy(toBeHashed, 0, result, i*16,
toBeHashed.length);
}
}
// clear data used in message
md.reset();
return result;
}
void init(int opmode, Key key, AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
PBEParameterSpec pbeSpec = null;
if (params != null) {
try {
pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
} catch (InvalidParameterSpecException ipse) {
throw new InvalidAlgorithmParameterException("Wrong parameter "
+ "type: PBE "
+ "expected");
}
}
init(opmode, key, pbeSpec, random);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in a new buffer.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
*/
byte[] update(byte[] input, int inputOffset, int inputLen) {
return cipher.update(input, inputOffset, inputLen);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
*/
int update(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
return cipher.update(input, inputOffset, inputLen,
output, outputOffset);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in a new buffer.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception BadPaddingException if decrypting and padding is chosen,
* but the last input data does not have proper padding bytes.
*/
byte[] doFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
return cipher.doFinal(input, inputOffset, inputLen);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
* @exception BadPaddingException if decrypting and padding is chosen,
* but the last input data does not have proper padding bytes.
*/
int doFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return cipher.doFinal(input, inputOffset, inputLen,
output, outputOffset);
}
/**
* 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).
*/
byte[] wrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
byte[] result = null;
byte[] encodedKey = null;
try {
encodedKey = key.getEncoded();
if ((encodedKey == null) || (encodedKey.length == 0)) {
throw new InvalidKeyException("Cannot get an encoding of " +
"the key to be wrapped");
}
result = doFinal(encodedKey, 0, encodedKey.length);
} catch (BadPaddingException e) {
// Should never happen
} finally {
if (encodedKey != null) Arrays.fill(encodedKey, (byte)0x00);
}
return result;
}
/**
* 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>.
*/
Key unwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
byte[] encodedKey;
try {
encodedKey = doFinal(wrappedKey, 0, wrappedKey.length);
} catch (BadPaddingException ePadding) {
throw new InvalidKeyException("The wrapped key is not padded " +
"correctly");
} catch (IllegalBlockSizeException eBlockSize) {
throw new InvalidKeyException("The wrapped key does not have " +
"the correct length");
}
return ConstructKeys.constructKey(encodedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
}

View File

@@ -0,0 +1,420 @@
/*
* Copyright (c) 2012, 2015, 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.security.*;
import java.security.spec.*;
import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class represents password-based encryption as defined by the PKCS #5
* standard.
* These algorithms implement PBE with HmacSHA1/HmacSHA2-family and AES-CBC.
* Padding is done as described in PKCS #5.
*
* @author Jan Luehe
*
*
* @see javax.crypto.Cipher
*/
abstract class PBES2Core extends CipherSpi {
private static final int DEFAULT_SALT_LENGTH = 20;
private static final int DEFAULT_COUNT = 4096;
// the encapsulated cipher
private final CipherCore cipher;
private final int keyLength; // in bits
private final int blkSize; // in bits
private final PBKDF2Core kdf;
private final String pbeAlgo;
private final String cipherAlgo;
private int iCount = DEFAULT_COUNT;
private byte[] salt = null;
private IvParameterSpec ivSpec = null;
/**
* Creates an instance of PBE Scheme 2 according to the selected
* password-based key derivation function and encryption scheme.
*/
PBES2Core(String kdfAlgo, String cipherAlgo, int keySize)
throws NoSuchAlgorithmException, NoSuchPaddingException {
this.cipherAlgo = cipherAlgo;
keyLength = keySize * 8;
pbeAlgo = "PBEWith" + kdfAlgo + "And" + cipherAlgo + "_" + keyLength;
if (cipherAlgo.equals("AES")) {
blkSize = AESConstants.AES_BLOCK_SIZE;
cipher = new CipherCore(new AESCrypt(), blkSize);
switch(kdfAlgo) {
case "HmacSHA1":
kdf = new PBKDF2Core.HmacSHA1();
break;
case "HmacSHA224":
kdf = new PBKDF2Core.HmacSHA224();
break;
case "HmacSHA256":
kdf = new PBKDF2Core.HmacSHA256();
break;
case "HmacSHA384":
kdf = new PBKDF2Core.HmacSHA384();
break;
case "HmacSHA512":
kdf = new PBKDF2Core.HmacSHA512();
break;
default:
throw new NoSuchAlgorithmException(
"No Cipher implementation for " + kdfAlgo);
}
} else {
throw new NoSuchAlgorithmException("No Cipher implementation for " +
pbeAlgo);
}
cipher.setMode("CBC");
cipher.setPadding("PKCS5Padding");
}
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode);
}
}
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
if ((paddingScheme != null) &&
(!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) {
throw new NoSuchPaddingException("Invalid padding scheme: " +
paddingScheme);
}
}
protected int engineGetBlockSize() {
return blkSize;
}
protected int engineGetOutputSize(int inputLen) {
return cipher.getOutputSize(inputLen);
}
protected byte[] engineGetIV() {
return cipher.getIV();
}
protected AlgorithmParameters engineGetParameters() {
AlgorithmParameters params = null;
if (salt == null) {
// generate random salt and use default iteration count
salt = new byte[DEFAULT_SALT_LENGTH];
SunJCE.getRandom().nextBytes(salt);
iCount = DEFAULT_COUNT;
}
if (ivSpec == null) {
// generate random IV
byte[] ivBytes = new byte[blkSize];
SunJCE.getRandom().nextBytes(ivBytes);
ivSpec = new IvParameterSpec(ivBytes);
}
PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount, ivSpec);
try {
params = AlgorithmParameters.getInstance(pbeAlgo,
SunJCE.getInstance());
params.init(pbeSpec);
} catch (NoSuchAlgorithmException nsae) {
// should never happen
throw new RuntimeException("SunJCE called, but not configured");
} catch (InvalidParameterSpecException ipse) {
// should never happen
throw new RuntimeException("PBEParameterSpec not supported");
}
return params;
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
try {
engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
} catch (InvalidAlgorithmParameterException ie) {
InvalidKeyException ike =
new InvalidKeyException("requires PBE parameters");
ike.initCause(ie);
throw ike;
}
}
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (key == null) {
throw new InvalidKeyException("Null key");
}
byte[] passwdBytes = key.getEncoded();
char[] passwdChars = null;
PBEKeySpec pbeSpec;
try {
if ((passwdBytes == null) ||
!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
throw new InvalidKeyException("Missing password");
}
// TBD: consolidate the salt, ic and IV parameter checks below
// Extract salt and iteration count from the key, if present
if (key instanceof javax.crypto.interfaces.PBEKey) {
salt = ((javax.crypto.interfaces.PBEKey)key).getSalt();
if (salt != null && salt.length < 8) {
throw new InvalidAlgorithmParameterException(
"Salt must be at least 8 bytes long");
}
iCount = ((javax.crypto.interfaces.PBEKey)key).getIterationCount();
if (iCount == 0) {
iCount = DEFAULT_COUNT;
} else if (iCount < 0) {
throw new InvalidAlgorithmParameterException(
"Iteration count must be a positive number");
}
}
// Extract salt, iteration count and IV from the params, if present
if (params == null) {
if (salt == null) {
// generate random salt and use default iteration count
salt = new byte[DEFAULT_SALT_LENGTH];
random.nextBytes(salt);
iCount = DEFAULT_COUNT;
}
if ((opmode == Cipher.ENCRYPT_MODE) ||
(opmode == Cipher.WRAP_MODE)) {
// generate random IV
byte[] ivBytes = new byte[blkSize];
random.nextBytes(ivBytes);
ivSpec = new IvParameterSpec(ivBytes);
}
} else {
if (!(params instanceof PBEParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Wrong parameter type: PBE expected");
}
// salt and iteration count from the params take precedence
byte[] specSalt = ((PBEParameterSpec) params).getSalt();
if (specSalt != null && specSalt.length < 8) {
throw new InvalidAlgorithmParameterException(
"Salt must be at least 8 bytes long");
}
salt = specSalt;
int specICount = ((PBEParameterSpec) params).getIterationCount();
if (specICount == 0) {
specICount = DEFAULT_COUNT;
} else if (specICount < 0) {
throw new InvalidAlgorithmParameterException(
"Iteration count must be a positive number");
}
iCount = specICount;
AlgorithmParameterSpec specParams =
((PBEParameterSpec) params).getParameterSpec();
if (specParams != null) {
if (specParams instanceof IvParameterSpec) {
ivSpec = (IvParameterSpec)specParams;
} else {
throw new InvalidAlgorithmParameterException(
"Wrong parameter type: IV expected");
}
} else if ((opmode == Cipher.ENCRYPT_MODE) ||
(opmode == Cipher.WRAP_MODE)) {
// generate random IV
byte[] ivBytes = new byte[blkSize];
random.nextBytes(ivBytes);
ivSpec = new IvParameterSpec(ivBytes);
} else {
throw new InvalidAlgorithmParameterException(
"Missing parameter type: IV expected");
}
}
passwdChars = new char[passwdBytes.length];
for (int i = 0; i < passwdChars.length; i++)
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
pbeSpec = new PBEKeySpec(passwdChars, salt, iCount, keyLength);
// password char[] was cloned in PBEKeySpec constructor,
// so we can zero it out here
} finally {
if (passwdChars != null) Arrays.fill(passwdChars, '\0');
if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00);
}
SecretKey s = null;
try {
s = kdf.engineGenerateSecret(pbeSpec);
} catch (InvalidKeySpecException ikse) {
InvalidKeyException ike =
new InvalidKeyException("Cannot construct PBE key");
ike.initCause(ikse);
throw ike;
}
byte[] derivedKey = s.getEncoded();
SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, cipherAlgo);
// initialize the underlying cipher
cipher.init(opmode, cipherKey, ivSpec, random);
}
protected void engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
AlgorithmParameterSpec pbeSpec = null;
if (params != null) {
try {
pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
} catch (InvalidParameterSpecException ipse) {
throw new InvalidAlgorithmParameterException(
"Wrong parameter type: PBE expected");
}
}
engineInit(opmode, key, pbeSpec, random);
}
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
return cipher.update(input, inputOffset, inputLen);
}
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
return cipher.update(input, inputOffset, inputLen,
output, outputOffset);
}
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
return cipher.doFinal(input, inputOffset, inputLen);
}
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return cipher.doFinal(input, inputOffset, inputLen,
output, outputOffset);
}
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return keyLength;
}
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
return cipher.wrap(key);
}
protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
byte[] encodedKey;
return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
public static final class HmacSHA1AndAES_128 extends PBES2Core {
public HmacSHA1AndAES_128()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA1", "AES", 16);
}
}
public static final class HmacSHA224AndAES_128 extends PBES2Core {
public HmacSHA224AndAES_128()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA224", "AES", 16);
}
}
public static final class HmacSHA256AndAES_128 extends PBES2Core {
public HmacSHA256AndAES_128()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA256", "AES", 16);
}
}
public static final class HmacSHA384AndAES_128 extends PBES2Core {
public HmacSHA384AndAES_128()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA384", "AES", 16);
}
}
public static final class HmacSHA512AndAES_128 extends PBES2Core {
public HmacSHA512AndAES_128()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA512", "AES", 16);
}
}
public static final class HmacSHA1AndAES_256 extends PBES2Core {
public HmacSHA1AndAES_256()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA1", "AES", 32);
}
}
public static final class HmacSHA224AndAES_256 extends PBES2Core {
public HmacSHA224AndAES_256()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA224", "AES", 32);
}
}
public static final class HmacSHA256AndAES_256 extends PBES2Core {
public HmacSHA256AndAES_256()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA256", "AES", 32);
}
}
public static final class HmacSHA384AndAES_256 extends PBES2Core {
public HmacSHA384AndAES_256()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA384", "AES", 32);
}
}
public static final class HmacSHA512AndAES_256 extends PBES2Core {
public HmacSHA512AndAES_256()
throws NoSuchAlgorithmException, NoSuchPaddingException {
super("HmacSHA512", "AES", 32);
}
}
}

View File

@@ -0,0 +1,532 @@
/*
* Copyright (c) 2012, 2018, 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.io.*;
import java.security.NoSuchAlgorithmException;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEParameterSpec;
import sun.security.util.*;
/**
* This class implements the parameter set used with password-based
* encryption scheme 2 (PBES2), which is defined in PKCS#5 as follows:
*
* <pre>
* -- PBES2
*
* PBES2Algorithms ALGORITHM-IDENTIFIER ::=
* { {PBES2-params IDENTIFIED BY id-PBES2}, ...}
*
* id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
*
* PBES2-params ::= SEQUENCE {
* keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
*
* PBES2-KDFs ALGORITHM-IDENTIFIER ::=
* { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
*
* PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
*
* -- PBKDF2
*
* PBKDF2Algorithms ALGORITHM-IDENTIFIER ::=
* { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ...}
*
* id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12}
*
* PBKDF2-params ::= SEQUENCE {
* salt CHOICE {
* specified OCTET STRING,
* otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
* },
* iterationCount INTEGER (1..MAX),
* keyLength INTEGER (1..MAX) OPTIONAL,
* prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
* }
*
* PBKDF2-SaltSources ALGORITHM-IDENTIFIER ::= { ... }
*
* PBKDF2-PRFs ALGORITHM-IDENTIFIER ::= {
* {NULL IDENTIFIED BY id-hmacWithSHA1} |
* {NULL IDENTIFIED BY id-hmacWithSHA224} |
* {NULL IDENTIFIED BY id-hmacWithSHA256} |
* {NULL IDENTIFIED BY id-hmacWithSHA384} |
* {NULL IDENTIFIED BY id-hmacWithSHA512}, ... }
*
* algid-hmacWithSHA1 AlgorithmIdentifier {{PBKDF2-PRFs}} ::=
* {algorithm id-hmacWithSHA1, parameters NULL : NULL}
*
* id-hmacWithSHA1 OBJECT IDENTIFIER ::= {digestAlgorithm 7}
*
* PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
*
* </pre>
*/
abstract class PBES2Parameters extends AlgorithmParametersSpi {
private static final int pkcs5PBKDF2[] =
{1, 2, 840, 113549, 1, 5, 12};
private static final int pkcs5PBES2[] =
{1, 2, 840, 113549, 1, 5, 13};
private static final int hmacWithSHA1[] =
{1, 2, 840, 113549, 2, 7};
private static final int hmacWithSHA224[] =
{1, 2, 840, 113549, 2, 8};
private static final int hmacWithSHA256[] =
{1, 2, 840, 113549, 2, 9};
private static final int hmacWithSHA384[] =
{1, 2, 840, 113549, 2, 10};
private static final int hmacWithSHA512[] =
{1, 2, 840, 113549, 2, 11};
private static final int aes128CBC[] =
{2, 16, 840, 1, 101, 3, 4, 1, 2};
private static final int aes192CBC[] =
{2, 16, 840, 1, 101, 3, 4, 1, 22};
private static final int aes256CBC[] =
{2, 16, 840, 1, 101, 3, 4, 1, 42};
private static ObjectIdentifier pkcs5PBKDF2_OID;
private static ObjectIdentifier pkcs5PBES2_OID;
private static ObjectIdentifier hmacWithSHA1_OID;
private static ObjectIdentifier hmacWithSHA224_OID;
private static ObjectIdentifier hmacWithSHA256_OID;
private static ObjectIdentifier hmacWithSHA384_OID;
private static ObjectIdentifier hmacWithSHA512_OID;
private static ObjectIdentifier aes128CBC_OID;
private static ObjectIdentifier aes192CBC_OID;
private static ObjectIdentifier aes256CBC_OID;
static {
try {
pkcs5PBKDF2_OID = new ObjectIdentifier(pkcs5PBKDF2);
pkcs5PBES2_OID = new ObjectIdentifier(pkcs5PBES2);
hmacWithSHA1_OID = new ObjectIdentifier(hmacWithSHA1);
hmacWithSHA224_OID = new ObjectIdentifier(hmacWithSHA224);
hmacWithSHA256_OID = new ObjectIdentifier(hmacWithSHA256);
hmacWithSHA384_OID = new ObjectIdentifier(hmacWithSHA384);
hmacWithSHA512_OID = new ObjectIdentifier(hmacWithSHA512);
aes128CBC_OID = new ObjectIdentifier(aes128CBC);
aes192CBC_OID = new ObjectIdentifier(aes192CBC);
aes256CBC_OID = new ObjectIdentifier(aes256CBC);
} catch (IOException ioe) {
// should not happen
}
}
// the PBES2 algorithm name
private String pbes2AlgorithmName = null;
// the salt
private byte[] salt = null;
// the iteration count
private int iCount = 0;
// the cipher parameter
private AlgorithmParameterSpec cipherParam = null;
// the key derivation function (default is HmacSHA1)
private ObjectIdentifier kdfAlgo_OID = hmacWithSHA1_OID;
// the encryption function
private ObjectIdentifier cipherAlgo_OID = null;
// the cipher keysize (in bits)
private int keysize = -1;
PBES2Parameters() {
// KDF, encryption & keysize values are set later, in engineInit(byte[])
}
PBES2Parameters(String pbes2AlgorithmName) throws NoSuchAlgorithmException {
int and;
String kdfAlgo = null;
String cipherAlgo = null;
// Extract the KDF and encryption algorithm names
this.pbes2AlgorithmName = pbes2AlgorithmName;
if (pbes2AlgorithmName.startsWith("PBEWith") &&
(and = pbes2AlgorithmName.indexOf("And", 7 + 1)) > 0) {
kdfAlgo = pbes2AlgorithmName.substring(7, and);
cipherAlgo = pbes2AlgorithmName.substring(and + 3);
// Check for keysize
int underscore;
if ((underscore = cipherAlgo.indexOf('_')) > 0) {
int slash;
if ((slash = cipherAlgo.indexOf('/', underscore + 1)) > 0) {
keysize =
Integer.parseInt(cipherAlgo.substring(underscore + 1,
slash));
} else {
keysize =
Integer.parseInt(cipherAlgo.substring(underscore + 1));
}
cipherAlgo = cipherAlgo.substring(0, underscore);
}
} else {
throw new NoSuchAlgorithmException("No crypto implementation for " +
pbes2AlgorithmName);
}
switch (kdfAlgo) {
case "HmacSHA1":
kdfAlgo_OID = hmacWithSHA1_OID;
break;
case "HmacSHA224":
kdfAlgo_OID = hmacWithSHA224_OID;
break;
case "HmacSHA256":
kdfAlgo_OID = hmacWithSHA256_OID;
break;
case "HmacSHA384":
kdfAlgo_OID = hmacWithSHA384_OID;
break;
case "HmacSHA512":
kdfAlgo_OID = hmacWithSHA512_OID;
break;
default:
throw new NoSuchAlgorithmException(
"No crypto implementation for " + kdfAlgo);
}
if (cipherAlgo.equals("AES")) {
this.keysize = keysize;
switch (keysize) {
case 128:
cipherAlgo_OID = aes128CBC_OID;
break;
case 256:
cipherAlgo_OID = aes256CBC_OID;
break;
default:
throw new NoSuchAlgorithmException(
"No Cipher implementation for " + keysize + "-bit " +
cipherAlgo);
}
} else {
throw new NoSuchAlgorithmException("No Cipher implementation for " +
cipherAlgo);
}
}
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException
{
if (!(paramSpec instanceof PBEParameterSpec)) {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
this.salt = ((PBEParameterSpec)paramSpec).getSalt().clone();
this.iCount = ((PBEParameterSpec)paramSpec).getIterationCount();
this.cipherParam = ((PBEParameterSpec)paramSpec).getParameterSpec();
}
protected void engineInit(byte[] encoded)
throws IOException
{
String kdfAlgo = null;
String cipherAlgo = null;
DerValue pBES2_params = new DerValue(encoded);
if (pBES2_params.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 SEQUENCE tag");
}
DerValue kdf = pBES2_params.data.getDerValue();
// Before JDK-8202837, PBES2-params was mistakenly encoded like
// an AlgorithmId which is a sequence of its own OID and the real
// PBES2-params. If the first DerValue is an OID instead of a
// PBES2-KDFs (which should be a SEQUENCE), we are likely to be
// dealing with this buggy encoding. Skip the OID and treat the
// next DerValue as the real PBES2-params.
if (kdf.getTag() == DerValue.tag_ObjectId) {
pBES2_params = pBES2_params.data.getDerValue();
kdf = pBES2_params.data.getDerValue();
}
kdfAlgo = parseKDF(kdf);
if (pBES2_params.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 SEQUENCE tag");
}
cipherAlgo = parseES(pBES2_params.data.getDerValue());
pbes2AlgorithmName = new StringBuilder().append("PBEWith")
.append(kdfAlgo).append("And").append(cipherAlgo).toString();
}
private String parseKDF(DerValue keyDerivationFunc) throws IOException {
if (!pkcs5PBKDF2_OID.equals(keyDerivationFunc.data.getOID())) {
throw new IOException("PBE parameter parsing error: "
+ "expecting the object identifier for PBKDF2");
}
if (keyDerivationFunc.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 SEQUENCE tag");
}
DerValue pBKDF2_params = keyDerivationFunc.data.getDerValue();
if (pBKDF2_params.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 SEQUENCE tag");
}
DerValue specified = pBKDF2_params.data.getDerValue();
// the 'specified' ASN.1 CHOICE for 'salt' is supported
if (specified.tag == DerValue.tag_OctetString) {
salt = specified.getOctetString();
} else {
// the 'otherSource' ASN.1 CHOICE for 'salt' is not supported
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 OCTET STRING tag");
}
iCount = pBKDF2_params.data.getInteger();
DerValue prf = null;
// keyLength INTEGER (1..MAX) OPTIONAL,
if (pBKDF2_params.data.available() > 0) {
DerValue keyLength = pBKDF2_params.data.getDerValue();
if (keyLength.tag == DerValue.tag_Integer) {
keysize = keyLength.getInteger() * 8; // keysize (in bits)
} else {
// Should be the prf
prf = keyLength;
}
}
// prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
String kdfAlgo = "HmacSHA1";
if (prf == null) {
if (pBKDF2_params.data.available() > 0) {
prf = pBKDF2_params.data.getDerValue();
}
}
if (prf != null) {
kdfAlgo_OID = prf.data.getOID();
if (hmacWithSHA1_OID.equals(kdfAlgo_OID)) {
kdfAlgo = "HmacSHA1";
} else if (hmacWithSHA224_OID.equals(kdfAlgo_OID)) {
kdfAlgo = "HmacSHA224";
} else if (hmacWithSHA256_OID.equals(kdfAlgo_OID)) {
kdfAlgo = "HmacSHA256";
} else if (hmacWithSHA384_OID.equals(kdfAlgo_OID)) {
kdfAlgo = "HmacSHA384";
} else if (hmacWithSHA512_OID.equals(kdfAlgo_OID)) {
kdfAlgo = "HmacSHA512";
} else {
throw new IOException("PBE parameter parsing error: "
+ "expecting the object identifier for a HmacSHA key "
+ "derivation function");
}
if (prf.data.available() != 0) {
// parameter is 'NULL' for all HmacSHA KDFs
DerValue parameter = prf.data.getDerValue();
if (parameter.tag != DerValue.tag_Null) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 NULL tag");
}
}
}
return kdfAlgo;
}
private String parseES(DerValue encryptionScheme) throws IOException {
String cipherAlgo = null;
cipherAlgo_OID = encryptionScheme.data.getOID();
if (aes128CBC_OID.equals(cipherAlgo_OID)) {
cipherAlgo = "AES_128";
// parameter is AES-IV 'OCTET STRING (SIZE(16))'
cipherParam =
new IvParameterSpec(encryptionScheme.data.getOctetString());
keysize = 128;
} else if (aes256CBC_OID.equals(cipherAlgo_OID)) {
cipherAlgo = "AES_256";
// parameter is AES-IV 'OCTET STRING (SIZE(16))'
cipherParam =
new IvParameterSpec(encryptionScheme.data.getOctetString());
keysize = 256;
} else {
throw new IOException("PBE parameter parsing error: "
+ "expecting the object identifier for AES cipher");
}
return cipherAlgo;
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException
{
engineInit(encoded);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException
{
if (PBEParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast(
new PBEParameterSpec(this.salt, this.iCount, this.cipherParam));
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
DerOutputStream out = new DerOutputStream();
DerOutputStream pBES2_params = new DerOutputStream();
DerOutputStream keyDerivationFunc = new DerOutputStream();
keyDerivationFunc.putOID(pkcs5PBKDF2_OID);
DerOutputStream pBKDF2_params = new DerOutputStream();
pBKDF2_params.putOctetString(salt); // choice: 'specified OCTET STRING'
pBKDF2_params.putInteger(iCount);
if (keysize > 0) {
pBKDF2_params.putInteger(keysize / 8); // derived key length (in octets)
}
DerOutputStream prf = new DerOutputStream();
// algorithm is id-hmacWithSHA1/SHA224/SHA256/SHA384/SHA512
prf.putOID(kdfAlgo_OID);
// parameters is 'NULL'
prf.putNull();
pBKDF2_params.write(DerValue.tag_Sequence, prf);
keyDerivationFunc.write(DerValue.tag_Sequence, pBKDF2_params);
pBES2_params.write(DerValue.tag_Sequence, keyDerivationFunc);
DerOutputStream encryptionScheme = new DerOutputStream();
// algorithm is id-aes128-CBC or id-aes256-CBC
encryptionScheme.putOID(cipherAlgo_OID);
// parameters is 'AES-IV ::= OCTET STRING (SIZE(16))'
if (cipherParam != null && cipherParam instanceof IvParameterSpec) {
encryptionScheme.putOctetString(
((IvParameterSpec)cipherParam).getIV());
} else {
throw new IOException("Wrong parameter type: IV expected");
}
pBES2_params.write(DerValue.tag_Sequence, encryptionScheme);
out.write(DerValue.tag_Sequence, pBES2_params);
return out.toByteArray();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException
{
return engineGetEncoded();
}
/*
* Returns a formatted string describing the parameters.
*
* The algorithn name pattern is: "PBEWith<prf>And<encryption>"
* where <prf> is one of: HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA384,
* or HmacSHA512, and <encryption> is AES with a keysize suffix.
*/
protected String engineToString() {
return pbes2AlgorithmName;
}
public static final class General extends PBES2Parameters {
public General() throws NoSuchAlgorithmException {
super();
}
}
public static final class HmacSHA1AndAES_128 extends PBES2Parameters {
public HmacSHA1AndAES_128() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA1AndAES_128");
}
}
public static final class HmacSHA224AndAES_128 extends PBES2Parameters {
public HmacSHA224AndAES_128() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA224AndAES_128");
}
}
public static final class HmacSHA256AndAES_128 extends PBES2Parameters {
public HmacSHA256AndAES_128() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA256AndAES_128");
}
}
public static final class HmacSHA384AndAES_128 extends PBES2Parameters {
public HmacSHA384AndAES_128() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA384AndAES_128");
}
}
public static final class HmacSHA512AndAES_128 extends PBES2Parameters {
public HmacSHA512AndAES_128() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA512AndAES_128");
}
}
public static final class HmacSHA1AndAES_256 extends PBES2Parameters {
public HmacSHA1AndAES_256() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA1AndAES_256");
}
}
public static final class HmacSHA224AndAES_256 extends PBES2Parameters {
public HmacSHA224AndAES_256() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA224AndAES_256");
}
}
public static final class HmacSHA256AndAES_256 extends PBES2Parameters {
public HmacSHA256AndAES_256() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA256AndAES_256");
}
}
public static final class HmacSHA384AndAES_256 extends PBES2Parameters {
public HmacSHA384AndAES_256() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA384AndAES_256");
}
}
public static final class HmacSHA512AndAES_256 extends PBES2Parameters {
public HmacSHA512AndAES_256() throws NoSuchAlgorithmException {
super("PBEWithHmacSHA512AndAES_256");
}
}
}

View File

@@ -0,0 +1,429 @@
/*
* Copyright (c) 1997, 2013, 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.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class represents password-based encryption as defined by the PKCS #5
* standard.
* The particular algorithm implemented is pbeWithMD5AndDES-CBC.
* Padding is done as described in PKCS #5.
*
* @author Jan Luehe
*
*
* @see javax.crypto.Cipher
*/
public final class PBEWithMD5AndDESCipher extends CipherSpi {
// the encapsulated DES cipher
private PBES1Core core;
/**
* Creates an instance of this cipher, and initializes its mode (CBC) and
* padding (PKCS5).
*
* @exception NoSuchAlgorithmException if the required cipher mode (CBC) is
* unavailable
* @exception NoSuchPaddingException if the required padding mechanism
* (PKCS5Padding) is unavailable
*/
public PBEWithMD5AndDESCipher()
throws NoSuchAlgorithmException, NoSuchPaddingException {
core = new PBES1Core("DES");
}
/**
* Sets the mode of this cipher. This algorithm can only be run in CBC
* mode.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode is
* invalid
*/
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode);
}
}
/**
* Sets the padding mechanism of this cipher. This algorithm only uses
* PKCS #5 padding.
*
* @param paddingScheme the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* is invalid
*/
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException
{
if ((paddingScheme != null) &&
(!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) {
throw new NoSuchPaddingException("Invalid padding scheme: " +
paddingScheme);
}
}
/**
* Returns the block size (in bytes).
*
* @return the block size (in bytes)
*/
protected int engineGetBlockSize() {
return core.getBlockSize();
}
/**
* Returns the length in bytes that an output buffer would need to be in
* order to hold the result of the next <code>update</code> or
* <code>doFinal</code> operation, given the input length
* <code>inputLen</code> (in bytes).
*
* <p>This call takes into account any unprocessed (buffered) data from a
* previous <code>update</code> call, and padding.
*
* <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) {
return core.getOutputSize(inputLen);
}
/**
* Returns the initialization vector (IV) in a new buffer.
*
* <p> This is useful in the case where a random IV has been created
* (see <a href = "#init">init</a>),
* or in the context of password-based encryption or
* decryption, where the IV is derived from a user-supplied password.
*
* @return the initialization vector in a new buffer, or null if the
* underlying algorithm does not use an IV, or if the IV has not yet
* been set.
*/
protected byte[] engineGetIV() {
return core.getIV();
}
/**
* Returns the parameters used with this cipher.
*
* <p>The returned parameters may be the same that were used to initialize
* this cipher, or may contain the default set of parameters or a set of
* randomly generated parameters used by the underlying cipher
* implementation (provided that the underlying cipher implementation
* uses a default set of parameters or creates new parameters if it needs
* parameters but was not initialized with any).
*
* @return the parameters used with this cipher, or null if this cipher
* does not use any parameters.
*/
protected AlgorithmParameters engineGetParameters() {
return core.getParameters();
}
/**
* Initializes this cipher with a key and a source
* of randomness.
* The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @param key the encryption 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 {
try {
engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
} catch (InvalidAlgorithmParameterException ie) {
InvalidKeyException ike =
new InvalidKeyException("requires PBE parameters");
ike.initCause(ie);
throw ike;
}
}
/**
* Initializes this cipher with a key, a set of
* algorithm parameters, and a source of randomness.
* The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @param key the encryption key
* @param params the algorithm parameters
* @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 are inappropriate for this cipher
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in a new buffer.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
*/
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen)
{
return core.update(input, inputOffset, inputLen);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
*/
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException
{
return core.update(input, inputOffset, inputLen,
output, outputOffset);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in a new buffer.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception BadPaddingException if decrypting and padding is chosen,
* but the last input data does not have proper padding bytes.
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException
{
return core.doFinal(input, inputOffset, inputLen);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
* @exception BadPaddingException if decrypting and padding is chosen,
* but the last input data does not have proper padding bytes.
*/
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException
{
return core.doFinal(input, inputOffset, inputLen,
output, outputOffset);
}
/**
* Returns the key size of the given key object.
*
* @param key the key object.
*
* @return the key size of the given key object.
*
* @exception InvalidKeyException if <code>key</code> is invalid.
*/
protected int engineGetKeySize(Key key) throws InvalidKeyException {
// Always returns 56 since the encryption key
// is a DES key.
return 56;
}
/**
* 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 {
return core.wrap(key);
}
/**
* Unwrap a previously wrapped key.
*
* @param wrappedKey the key to be unwrapped.
*
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
*
* @param wrappedKeyType the type of the wrapped key.
* This is one of <code>Cipher.SECRET_KEY</code>,
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
*
* @return the unwrapped key.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys of type <code>wrappedKeyType</code> for the
* <code>wrappedKeyAlgorithm</code>.
*
* @exception InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key of type <code>wrappedKeyType</code> for
* the <code>wrappedKeyAlgorithm</code>.
*/
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
byte[] encodedKey;
return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
}

View File

@@ -0,0 +1,435 @@
/*
* Copyright (c) 1998, 2013, 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.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class implements a proprietary password-based encryption algorithm.
* It is based on password-based encryption as defined by the PKCS #5
* standard, except that is uses triple DES instead of DES.
*
* Here's how this algorithm works:
*
* 1. Create random salt and split it in two halves. If the two halves are
* identical, invert one of them.
* 2. Concatenate password with each of the halves.
* 3. Digest each concatenation with c iterations, where c is the
* iterationCount. Concatenate the output from each digest round with the
* password, and use the result as the input to the next digest operation.
* The digest algorithm is MD5.
* 4. After c iterations, use the 2 resulting digests as follows:
* The 16 bytes of the first digest and the 1st 8 bytes of the 2nd digest
* form the triple DES key, and the last 8 bytes of the 2nd digest form the
* IV.
*
* @author Jan Luehe
* @see javax.crypto.Cipher
*/
public final class PBEWithMD5AndTripleDESCipher extends CipherSpi {
private PBES1Core core;
/**
* Creates an instance of this cipher, and initializes its mode (CBC) and
* padding (PKCS5).
*
* @exception NoSuchAlgorithmException if the required cipher mode (CBC) is
* unavailable
* @exception NoSuchPaddingException if the required padding mechanism
* (PKCS5Padding) is unavailable
*/
public PBEWithMD5AndTripleDESCipher()
throws NoSuchAlgorithmException, NoSuchPaddingException
{
// set the encapsulated cipher to do triple DES
core = new PBES1Core("DESede");
}
/**
* Sets the mode of this cipher. This algorithm can only be run in CBC
* mode.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode is
* invalid
*/
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode);
}
}
/**
* Sets the padding mechanism of this cipher. This algorithm only uses
* PKCS #5 padding.
*
* @param paddingScheme the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* is invalid
*/
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException
{
if ((paddingScheme != null) &&
(!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) {
throw new NoSuchPaddingException("Invalid padding scheme: " +
paddingScheme);
}
}
/**
* Returns the block size (in bytes).
*
* @return the block size (in bytes)
*/
protected int engineGetBlockSize() {
return core.getBlockSize();
}
/**
* Returns the length in bytes that an output buffer would need to be in
* order to hold the result of the next <code>update</code> or
* <code>doFinal</code> operation, given the input length
* <code>inputLen</code> (in bytes).
*
* <p>This call takes into account any unprocessed (buffered) data from a
* previous <code>update</code> call, and padding.
*
* <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) {
return core.getOutputSize(inputLen);
}
/**
* Returns the initialization vector (IV) in a new buffer.
*
* <p> This is useful in the case where a random IV has been created
* (see <a href = "#init">init</a>),
* or in the context of password-based encryption or
* decryption, where the IV is derived from a user-supplied password.
*
* @return the initialization vector in a new buffer, or null if the
* underlying algorithm does not use an IV, or if the IV has not yet
* been set.
*/
protected byte[] engineGetIV() {
return core.getIV();
}
/**
* Returns the parameters used with this cipher.
*
* <p>The returned parameters may be the same that were used to initialize
* this cipher, or may contain the default set of parameters or a set of
* randomly generated parameters used by the underlying cipher
* implementation (provided that the underlying cipher implementation
* uses a default set of parameters or creates new parameters if it needs
* parameters but was not initialized with any).
*
* @return the parameters used with this cipher, or null if this cipher
* does not use any parameters.
*/
protected AlgorithmParameters engineGetParameters() {
return core.getParameters();
}
/**
* Initializes this cipher with a key and a source
* of randomness.
* The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is one of
* the following:
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
* @param key the encryption 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 {
try {
core.init(opmode, key, (AlgorithmParameterSpec) null, random);
} catch (InvalidAlgorithmParameterException ie) {
InvalidKeyException ike =
new InvalidKeyException("requires PBE parameters");
ike.initCause(ie);
throw ike;
}
}
/**
* Initializes this cipher with a key, a set of
* algorithm parameters, and a source of randomness.
* The cipher is initialized for encryption or decryption, depending on
* the value of <code>opmode</code>.
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes, it will get them from <code>random</code>.
*
* @param opmode the operation mode of this cipher (this is either
* <code>ENCRYPT_MODE</code> or <code>DECRYPT_MODE</code>)
* @param key the encryption key
* @param params the algorithm parameters
* @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 are inappropriate for this cipher
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
core.init(opmode, key, params, random);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in a new buffer.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
*/
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen)
{
return core.update(input, inputOffset, inputLen);
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, are processed, and the
* result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
*/
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException
{
return core.update(input, inputOffset, inputLen,
output, outputOffset);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in a new buffer.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
*
* @return the new buffer with the result
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception BadPaddingException if decrypting and padding is chosen,
* but the last input data does not have proper padding bytes.
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException
{
return core.doFinal(input, inputOffset, inputLen);
}
/**
* Encrypts or decrypts data in a single-part operation,
* or finishes a multiple-part operation.
* The data is encrypted or decrypted, depending on how this cipher was
* initialized.
*
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
* buffer, starting at <code>inputOffset</code>, and any input bytes that
* may have been buffered during a previous <code>update</code> operation,
* are processed, with padding (if requested) being applied.
* The result is stored in the <code>output</code> buffer, starting at
* <code>outputOffset</code>.
*
* <p>The cipher is reset to its initial state (uninitialized) after this
* call.
*
* @param input the input buffer
* @param inputOffset the offset in <code>input</code> where the input
* starts
* @param inputLen the input length
* @param output the buffer for the result
* @param outputOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
* @exception BadPaddingException if decrypting and padding is chosen,
* but the last input data does not have proper padding bytes.
*/
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException
{
return core.doFinal(input, inputOffset, inputLen,
output, outputOffset);
}
/**
* Returns the key size of the given key object.
*
* @param key the key object.
*
* @return the key size of the given key object.
*
* @exception InvalidKeyException if <code>key</code> is invalid.
*/
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return 168;
}
/**
* 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 {
return core.wrap(key);
}
/**
* Unwrap a previously wrapped key.
*
* @param wrappedKey the key to be unwrapped.
*
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
*
* @param wrappedKeyType the type of the wrapped key.
* This is one of <code>Cipher.SECRET_KEY</code>,
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
*
* @return the unwrapped key.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys of type <code>wrappedKeyType</code> for the
* <code>wrappedKeyAlgorithm</code>.
*
* @exception InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key of type <code>wrappedKeyType</code> for
* the <code>wrappedKeyAlgorithm</code>.
*/
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright (c) 2005, 2012, 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.security.InvalidKeyException;
import java.security.spec.KeySpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.PBEKeySpec;
/**
* This class implements a key factory for PBE keys derived using
* PBKDF2 with HmacSHA1/HmacSHA224/HmacSHA256/HmacSHA384/HmacSHA512
* pseudo random function (PRF) as defined in PKCS#5 v2.1.
*
* @author Valerie Peng
*
*/
abstract class PBKDF2Core extends SecretKeyFactorySpi {
private final String prfAlgo;
PBKDF2Core(String prfAlgo) {
this.prfAlgo = prfAlgo;
}
/**
* Generates a <code>SecretKey</code> object from the provided key
* specification (key material).
*
* @param keySpec the specification (key material) of the secret key
*
* @return the secret key
*
* @exception InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
protected SecretKey engineGenerateSecret(KeySpec keySpec)
throws InvalidKeySpecException
{
if (!(keySpec instanceof PBEKeySpec)) {
throw new InvalidKeySpecException("Invalid key spec");
}
PBEKeySpec ks = (PBEKeySpec) keySpec;
return new PBKDF2KeyImpl(ks, prfAlgo);
}
/**
* Returns a specification (key material) of the given key
* in the requested format.
*
* @param key the key
*
* @param keySpec the requested format in which the key material shall be
* returned
*
* @return the underlying key specification (key material) in the
* requested format
*
* @exception InvalidKeySpecException if the requested key
* specification is inappropriate for the given key, or the
* given key cannot be processed (e.g., the given key has an
* unrecognized algorithm or format).
*/
protected KeySpec engineGetKeySpec(SecretKey key, Class<?> keySpecCl)
throws InvalidKeySpecException {
if (key instanceof javax.crypto.interfaces.PBEKey) {
// Check if requested key spec is amongst the valid ones
if ((keySpecCl != null)
&& PBEKeySpec.class.isAssignableFrom(keySpecCl)) {
javax.crypto.interfaces.PBEKey pKey =
(javax.crypto.interfaces.PBEKey) key;
return new PBEKeySpec
(pKey.getPassword(), pKey.getSalt(),
pKey.getIterationCount(), pKey.getEncoded().length*8);
} else {
throw new InvalidKeySpecException("Invalid key spec");
}
} else {
throw new InvalidKeySpecException("Invalid key " +
"format/algorithm");
}
}
/**
* Translates a <code>SecretKey</code> object, whose provider may be
* unknown or potentially untrusted, into a corresponding
* <code>SecretKey</code> object of this key factory.
*
* @param key the key whose provider is unknown or untrusted
*
* @return the translated key
*
* @exception InvalidKeyException if the given key cannot be processed by
* this key factory.
*/
protected SecretKey engineTranslateKey(SecretKey key)
throws InvalidKeyException {
if ((key != null) &&
(key.getAlgorithm().equalsIgnoreCase("PBKDF2With" + prfAlgo)) &&
(key.getFormat().equalsIgnoreCase("RAW"))) {
// Check if key originates from this factory
if (key instanceof com.sun.crypto.provider.PBKDF2KeyImpl) {
return key;
}
// Check if key implements the PBEKey
if (key instanceof javax.crypto.interfaces.PBEKey) {
javax.crypto.interfaces.PBEKey pKey =
(javax.crypto.interfaces.PBEKey) key;
try {
PBEKeySpec spec =
new PBEKeySpec(pKey.getPassword(),
pKey.getSalt(),
pKey.getIterationCount(),
pKey.getEncoded().length*8);
return new PBKDF2KeyImpl(spec, prfAlgo);
} catch (InvalidKeySpecException re) {
InvalidKeyException ike = new InvalidKeyException
("Invalid key component(s)");
ike.initCause(re);
throw ike;
}
}
}
throw new InvalidKeyException("Invalid key format/algorithm");
}
public static final class HmacSHA1 extends PBKDF2Core {
public HmacSHA1() {
super("HmacSHA1");
}
}
public static final class HmacSHA224 extends PBKDF2Core {
public HmacSHA224() {
super("HmacSHA224");
}
}
public static final class HmacSHA256 extends PBKDF2Core {
public HmacSHA256() {
super("HmacSHA256");
}
}
public static final class HmacSHA384 extends PBKDF2Core {
public HmacSHA384() {
super("HmacSHA384");
}
}
public static final class HmacSHA512 extends PBKDF2Core {
public HmacSHA512() {
super("HmacSHA512");
}
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2005, 2011, 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.security.InvalidKeyException;
import java.security.spec.KeySpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.PBEKeySpec;
/**
* This class implements a key factory for PBE keys derived using
* PBKDF2 with HmacSHA1 psuedo random function(PRF) as defined in
* PKCS#5 v2.0.
*
* @author Valerie Peng
*
*/
public final class PBKDF2HmacSHA1Factory extends SecretKeyFactorySpi {
/**
* Empty constructor
*/
public PBKDF2HmacSHA1Factory() {
}
/**
* Generates a <code>SecretKey</code> object from the provided key
* specification (key material).
*
* @param keySpec the specification (key material) of the secret key
*
* @return the secret key
*
* @exception InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
protected SecretKey engineGenerateSecret(KeySpec keySpec)
throws InvalidKeySpecException
{
if (!(keySpec instanceof PBEKeySpec)) {
throw new InvalidKeySpecException("Invalid key spec");
}
PBEKeySpec ks = (PBEKeySpec) keySpec;
return new PBKDF2KeyImpl(ks, "HmacSHA1");
}
/**
* Returns a specification (key material) of the given key
* in the requested format.
*
* @param key the key
*
* @param keySpecCl the requested format in which the key material shall be
* returned
*
* @return the underlying key specification (key material) in the
* requested format
*
* @exception InvalidKeySpecException if the requested key
* specification is inappropriate for the given key, or the
* given key cannot be processed (e.g., the given key has an
* unrecognized algorithm or format).
*/
protected KeySpec engineGetKeySpec(SecretKey key, Class<?> keySpecCl)
throws InvalidKeySpecException {
if (key instanceof javax.crypto.interfaces.PBEKey) {
// Check if requested key spec is amongst the valid ones
if ((keySpecCl != null)
&& PBEKeySpec.class.isAssignableFrom(keySpecCl)) {
javax.crypto.interfaces.PBEKey pKey =
(javax.crypto.interfaces.PBEKey) key;
return new PBEKeySpec
(pKey.getPassword(), pKey.getSalt(),
pKey.getIterationCount(), pKey.getEncoded().length*8);
} else {
throw new InvalidKeySpecException("Invalid key spec");
}
} else {
throw new InvalidKeySpecException("Invalid key " +
"format/algorithm");
}
}
/**
* Translates a <code>SecretKey</code> object, whose provider may be
* unknown or potentially untrusted, into a corresponding
* <code>SecretKey</code> object of this key factory.
*
* @param key the key whose provider is unknown or untrusted
*
* @return the translated key
*
* @exception InvalidKeyException if the given key cannot be processed by
* this key factory.
*/
protected SecretKey engineTranslateKey(SecretKey key)
throws InvalidKeyException {
if ((key != null) &&
(key.getAlgorithm().equalsIgnoreCase("PBKDF2WithHmacSHA1")) &&
(key.getFormat().equalsIgnoreCase("RAW"))) {
// Check if key originates from this factory
if (key instanceof com.sun.crypto.provider.PBKDF2KeyImpl) {
return key;
}
// Check if key implements the PBEKey
if (key instanceof javax.crypto.interfaces.PBEKey) {
javax.crypto.interfaces.PBEKey pKey =
(javax.crypto.interfaces.PBEKey) key;
try {
PBEKeySpec spec =
new PBEKeySpec(pKey.getPassword(),
pKey.getSalt(),
pKey.getIterationCount(),
pKey.getEncoded().length*8);
return new PBKDF2KeyImpl(spec, "HmacSHA1");
} catch (InvalidKeySpecException re) {
InvalidKeyException ike = new InvalidKeyException
("Invalid key component(s)");
ike.initCause(re);
throw ike;
}
}
}
throw new InvalidKeyException("Invalid key format/algorithm");
}
}

View File

@@ -0,0 +1,309 @@
/*
* Copyright (c) 2005, 2015, 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.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.security.MessageDigest;
import java.util.Locale;
import java.security.KeyRep;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEKeySpec;
/**
* This class represents a PBE key derived using PBKDF2 defined
* in PKCS#5 v2.0. meaning that
* 1) the password must consist of characters which will be converted
* to bytes using UTF-8 character encoding.
* 2) salt, iteration count, and to be derived key length are supplied
*
* @author Valerie Peng
*
*/
final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
private static final long serialVersionUID = -2234868909660948157L;
private char[] passwd;
private final byte[] salt;
private final int iterCount;
private byte[] key;
private final Mac prf;
private static byte[] getPasswordBytes(char[] passwd) {
Charset utf8 = Charset.forName("UTF-8");
CharBuffer cb = CharBuffer.wrap(passwd);
ByteBuffer bb = utf8.encode(cb);
int len = bb.limit();
byte[] passwdBytes = new byte[len];
bb.get(passwdBytes, 0, len);
return passwdBytes;
}
/**
* Creates a PBE key from a given PBE key specification.
*
* @param key the given PBE key specification
*/
PBKDF2KeyImpl(PBEKeySpec keySpec, String prfAlgo)
throws InvalidKeySpecException {
char[] passwd = keySpec.getPassword();
if (passwd == null) {
// Should allow an empty password.
this.passwd = new char[0];
} else {
this.passwd = passwd.clone();
}
// Convert the password from char[] to byte[]
byte[] passwdBytes = getPasswordBytes(this.passwd);
// remove local copy
if (passwd != null) Arrays.fill(passwd, '\0');
this.salt = keySpec.getSalt();
if (salt == null) {
throw new InvalidKeySpecException("Salt not found");
}
this.iterCount = keySpec.getIterationCount();
if (iterCount == 0) {
throw new InvalidKeySpecException("Iteration count not found");
} else if (iterCount < 0) {
throw new InvalidKeySpecException("Iteration count is negative");
}
int keyLength = keySpec.getKeyLength();
if (keyLength == 0) {
throw new InvalidKeySpecException("Key length not found");
} else if (keyLength < 0) {
throw new InvalidKeySpecException("Key length is negative");
}
try {
this.prf = Mac.getInstance(prfAlgo, SunJCE.getInstance());
this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength);
} catch (NoSuchAlgorithmException nsae) {
// not gonna happen; re-throw just in case
InvalidKeySpecException ike = new InvalidKeySpecException();
ike.initCause(nsae);
throw ike;
} finally {
Arrays.fill(passwdBytes, (byte)0x00);
}
}
private static byte[] deriveKey(final Mac prf, final byte[] password,
byte[] salt, int iterCount, int keyLengthInBit) {
int keyLength = keyLengthInBit/8;
byte[] key = new byte[keyLength];
try {
int hlen = prf.getMacLength();
int intL = (keyLength + hlen - 1)/hlen; // ceiling
int intR = keyLength - (intL - 1)*hlen; // residue
byte[] ui = new byte[hlen];
byte[] ti = new byte[hlen];
String algName = prf.getAlgorithm();
// SecretKeySpec cannot be used, since password can be empty here.
SecretKey macKey = new SecretKey() {
private static final long serialVersionUID = 7874493593505141603L;
@Override
public String getAlgorithm() {
return algName;
}
@Override
public String getFormat() {
return "RAW";
}
@Override
public byte[] getEncoded() {
return password;
}
@Override
public int hashCode() {
return Arrays.hashCode(password) * 41 +
algName.toLowerCase(Locale.ENGLISH).hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (this.getClass() != obj.getClass()) return false;
SecretKey sk = (SecretKey)obj;
return algName.equalsIgnoreCase(
sk.getAlgorithm()) &&
MessageDigest.isEqual(password, sk.getEncoded());
}
// This derived key can't be deserialized.
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException(
"PBKDF2KeyImpl SecretKeys are not " +
"directly deserializable");
}
};
prf.init(macKey);
byte[] ibytes = new byte[4];
for (int i = 1; i <= intL; i++) {
prf.update(salt);
ibytes[3] = (byte) i;
ibytes[2] = (byte) ((i >> 8) & 0xff);
ibytes[1] = (byte) ((i >> 16) & 0xff);
ibytes[0] = (byte) ((i >> 24) & 0xff);
prf.update(ibytes);
prf.doFinal(ui, 0);
System.arraycopy(ui, 0, ti, 0, ui.length);
for (int j = 2; j <= iterCount; j++) {
prf.update(ui);
prf.doFinal(ui, 0);
// XOR the intermediate Ui's together.
for (int k = 0; k < ui.length; k++) {
ti[k] ^= ui[k];
}
}
if (i == intL) {
System.arraycopy(ti, 0, key, (i-1)*hlen, intR);
} else {
System.arraycopy(ti, 0, key, (i-1)*hlen, hlen);
}
}
} catch (GeneralSecurityException gse) {
throw new RuntimeException("Error deriving PBKDF2 keys");
}
return key;
}
public synchronized byte[] getEncoded() {
return key.clone();
}
public String getAlgorithm() {
return "PBKDF2With" + prf.getAlgorithm();
}
public int getIterationCount() {
return iterCount;
}
public synchronized char[] getPassword() {
return passwd.clone();
}
public byte[] getSalt() {
return salt.clone();
}
public String getFormat() {
return "RAW";
}
/**
* Calculates a hash code value for the object.
* Objects that are equal will also have the same hashcode.
*/
public int hashCode() {
int retval = 0;
for (int i = 1; i < this.key.length; i++) {
retval += this.key[i] * i;
}
return(retval ^= getAlgorithm().toLowerCase(Locale.ENGLISH).hashCode());
}
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof SecretKey))
return false;
SecretKey that = (SecretKey) obj;
if (!(that.getAlgorithm().equalsIgnoreCase(getAlgorithm())))
return false;
if (!(that.getFormat().equalsIgnoreCase("RAW")))
return false;
byte[] thatEncoded = that.getEncoded();
boolean ret = MessageDigest.isEqual(key, thatEncoded);
Arrays.fill(thatEncoded, (byte)0x00);
return ret;
}
/**
* Replace the PBE key to be serialized.
*
* @return the standard KeyRep object to be serialized
*
* @throws ObjectStreamException if a new object representing
* this PBE key could not be created
*/
private Object writeReplace() throws ObjectStreamException {
return new KeyRep(KeyRep.Type.SECRET, getAlgorithm(),
getFormat(), getEncoded());
}
/**
* Ensures that the password bytes of this key are
* erased when there are no more references to it.
*/
protected void finalize() throws Throwable {
try {
synchronized (this) {
if (this.passwd != null) {
java.util.Arrays.fill(this.passwd, '\0');
this.passwd = null;
}
if (this.key != null) {
java.util.Arrays.fill(this.key, (byte)0x00);
this.key = null;
}
}
} finally {
super.finalize();
}
}
/**
* Restores the state of this object from the stream.
* <p>
* Deserialization of this class is not supported.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException(
"PBKDF2KeyImpl keys are not directly deserializable");
}
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright (c) 2003, 2013, 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.nio.ByteBuffer;
import javax.crypto.MacSpi;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.*;
import java.security.spec.*;
/**
* This is an implementation of the PBMAC1 algorithms as defined
* in PKCS#5 v2.1 standard.
*/
abstract class PBMAC1Core extends HmacCore {
// NOTE: this class inherits the Cloneable interface from HmacCore
// Need to override clone() if mutable fields are added.
private final String kdfAlgo;
private final String hashAlgo;
private final int blockLength; // in octets
/**
* Creates an instance of PBMAC1 according to the selected
* password-based key derivation function.
*/
PBMAC1Core(String kdfAlgo, String hashAlgo, int blockLength)
throws NoSuchAlgorithmException {
super(hashAlgo, blockLength);
this.kdfAlgo = kdfAlgo;
this.hashAlgo = hashAlgo;
this.blockLength = blockLength;
}
private static PBKDF2Core getKDFImpl(String algo) {
PBKDF2Core kdf = null;
switch(algo) {
case "HmacSHA1":
kdf = new PBKDF2Core.HmacSHA1();
break;
case "HmacSHA224":
kdf = new PBKDF2Core.HmacSHA224();
break;
case "HmacSHA256":
kdf = new PBKDF2Core.HmacSHA256();
break;
case "HmacSHA384":
kdf = new PBKDF2Core.HmacSHA384();
break;
case "HmacSHA512":
kdf = new PBKDF2Core.HmacSHA512();
break;
default:
throw new ProviderException(
"No MAC implementation for " + algo);
}
return kdf;
}
/**
* Initializes the HMAC with the given secret key and algorithm parameters.
*
* @param key the secret key.
* @param params the algorithm parameters.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this MAC.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this MAC.
*/
protected void engineInit(Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
char[] passwdChars;
byte[] salt = null;
int iCount = 0;
if (key instanceof javax.crypto.interfaces.PBEKey) {
javax.crypto.interfaces.PBEKey pbeKey =
(javax.crypto.interfaces.PBEKey) key;
passwdChars = pbeKey.getPassword();
salt = pbeKey.getSalt(); // maybe null if unspecified
iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified
} else if (key instanceof SecretKey) {
byte[] passwdBytes;
if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) ||
(passwdBytes = key.getEncoded()) == null) {
throw new InvalidKeyException("Missing password");
}
passwdChars = new char[passwdBytes.length];
for (int i=0; i<passwdChars.length; i++) {
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
}
Arrays.fill(passwdBytes, (byte)0x00);
} else {
throw new InvalidKeyException("SecretKey of PBE type required");
}
PBEKeySpec pbeSpec;
try {
if (params == null) {
// should not auto-generate default values since current
// javax.crypto.Mac api does not have any method for caller to
// retrieve the generated defaults.
if ((salt == null) || (iCount == 0)) {
throw new InvalidAlgorithmParameterException
("PBEParameterSpec required for salt and iteration count");
}
} else if (!(params instanceof PBEParameterSpec)) {
throw new InvalidAlgorithmParameterException
("PBEParameterSpec type required");
} else {
PBEParameterSpec pbeParams = (PBEParameterSpec) params;
// make sure the parameter values are consistent
if (salt != null) {
if (!Arrays.equals(salt, pbeParams.getSalt())) {
throw new InvalidAlgorithmParameterException
("Inconsistent value of salt between key and params");
}
} else {
salt = pbeParams.getSalt();
}
if (iCount != 0) {
if (iCount != pbeParams.getIterationCount()) {
throw new InvalidAlgorithmParameterException
("Different iteration count between key and params");
}
} else {
iCount = pbeParams.getIterationCount();
}
}
// For security purpose, we need to enforce a minimum length
// for salt; just require the minimum salt length to be 8-byte
// which is what PKCS#5 recommends and openssl does.
if (salt.length < 8) {
throw new InvalidAlgorithmParameterException
("Salt must be at least 8 bytes long");
}
if (iCount <= 0) {
throw new InvalidAlgorithmParameterException
("IterationCount must be a positive number");
}
pbeSpec = new PBEKeySpec(passwdChars, salt, iCount, blockLength);
// password char[] was cloned in PBEKeySpec constructor,
// so we can zero it out here
} finally {
Arrays.fill(passwdChars, '\0');
}
SecretKey s;
PBKDF2Core kdf = getKDFImpl(kdfAlgo);
try {
s = kdf.engineGenerateSecret(pbeSpec);
} catch (InvalidKeySpecException ikse) {
InvalidKeyException ike =
new InvalidKeyException("Cannot construct PBE key");
ike.initCause(ikse);
throw ike;
}
byte[] derivedKey = s.getEncoded();
SecretKey cipherKey = new SecretKeySpec(derivedKey, kdfAlgo);
super.engineInit(cipherKey, null);
}
public static final class HmacSHA1 extends PBMAC1Core {
public HmacSHA1() throws NoSuchAlgorithmException {
super("HmacSHA1", "SHA1", 64);
}
}
public static final class HmacSHA224 extends PBMAC1Core {
public HmacSHA224() throws NoSuchAlgorithmException {
super("HmacSHA224", "SHA-224", 64);
}
}
public static final class HmacSHA256 extends PBMAC1Core {
public HmacSHA256() throws NoSuchAlgorithmException {
super("HmacSHA256", "SHA-256", 64);
}
}
public static final class HmacSHA384 extends PBMAC1Core {
public HmacSHA384() throws NoSuchAlgorithmException {
super("HmacSHA384", "SHA-384", 128);
}
}
public static final class HmacSHA512 extends PBMAC1Core {
public HmacSHA512() throws NoSuchAlgorithmException {
super("HmacSHA512", "SHA-512", 128);
}
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright (c) 1997, 2018, 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.security.InvalidKeyException;
import java.security.ProviderException;
/**
* This class represents ciphers in Plaintext Cipher Block Chaining (PCBC)
* mode.
*
* <p>This mode is implemented independently of a particular cipher.
* Ciphers to which this mode should apply (e.g., DES) must be
* <i>plugged-in</i> using the constructor.
*
* <p>NOTE: This class does not deal with buffering or padding.
*
* @author Gigi Ankeny
*/
final class PCBC extends FeedbackCipher {
/*
* output buffer
*/
private final byte[] k;
// variables for save/restore calls
private byte[] kSave = null;
PCBC(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
k = new byte[blockSize];
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>PCBC</code>
*/
String getFeedback() {
return "PCBC";
}
/**
* 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
*/
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException {
if ((key == null) || (iv == null) || (iv.length != blockSize)) {
throw new InvalidKeyException("Internal error");
}
this.iv = iv;
reset();
embeddedCipher.init(decrypting, algorithm, key);
}
/**
* 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).
*/
void reset() {
System.arraycopy(iv, 0, k, 0, blockSize);
}
/**
* Save the current content of this cipher.
*/
void save() {
if (kSave == null) {
kSave = new byte[blockSize];
}
System.arraycopy(k, 0, kSave, 0, blockSize);
}
/**
* Restores the content of this cipher to the previous saved one.
*/
void restore() {
System.arraycopy(kSave, 0, k, 0, blockSize);
}
/**
* Performs encryption operation.
*
* <p>The input plain text <code>plain</code>, starting at
* <code>plainOffset</code> and ending at
* <code>(plainOffset + plainLen - 1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param plain the buffer with the input data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @exception ProviderException if <code>plainLen</code> is not
* a multiple of the block size
* @return the length of the encrypted data
*/
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
{
RangeUtil.blockSizeCheck(plainLen, blockSize);
RangeUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
RangeUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
int i;
int endIndex = plainOffset + plainLen;
for (; plainOffset < endIndex;
plainOffset += blockSize, cipherOffset += blockSize) {
for (i = 0; i < blockSize; i++) {
k[i] ^= plain[i + plainOffset];
}
embeddedCipher.encryptBlock(k, 0, cipher, cipherOffset);
for (i = 0; i < blockSize; i++) {
k[i] = (byte)(plain[i + plainOffset] ^ cipher[i + cipherOffset]);
}
}
return plainLen;
}
/**
* Performs decryption operation.
*
* <p>The input cipher text <code>cipher</code>, starting at
* <code>cipherOffset</code> and ending at
* <code>(cipherOffset + cipherLen - 1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* @param cipher the buffer with the input data to be decrypted
* @param cipherOffset the offset in <code>cipherOffset</code>
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
* @exception ProviderException if <code>cipherLen</code> is not
* a multiple of the block size
* @return the length of the decrypted data
*/
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
{
RangeUtil.blockSizeCheck(cipherLen, blockSize);
RangeUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen);
RangeUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen);
int i;
int endIndex = cipherOffset + cipherLen;
for (; cipherOffset < endIndex;
plainOffset += blockSize, cipherOffset += blockSize) {
embeddedCipher.decryptBlock(cipher, cipherOffset,
plain, plainOffset);
for (i = 0; i < blockSize; i++) {
plain[i + plainOffset] ^= k[i];
}
for (i = 0; i < blockSize; i++) {
k[i] = (byte)(plain[i + plainOffset] ^ cipher[i + cipherOffset]);
}
}
return cipherLen;
}
}

View File

@@ -0,0 +1,820 @@
/*
* Copyright (c) 2003, 2013, 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.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class implements password-base encryption algorithm with
* SHA1 digest and the following Ciphers (in CBC mode, where applicable):
* - DESede cipher and
* - RC2 Cipher with 40-bit or 128-bit effective key length and
* - RC4 Cipher with 40-bit or 128-bit effective key length
* as defined by PKCS #12 version 1.0 standard.
*
* @author Valerie Peng
* @see javax.crypto.CipherSpi
*/
final class PKCS12PBECipherCore {
// TBD: replace CipherCore with a CipherSpi object to simplify maintenance
private CipherCore cipher;
private int blockSize;
private int keySize;
private String algo = null;
private String pbeAlgo = null;
private byte[] salt = null;
private int iCount = 0;
private static final int DEFAULT_SALT_LENGTH = 20;
private static final int DEFAULT_COUNT = 1024;
static final int CIPHER_KEY = 1;
static final int CIPHER_IV = 2;
static final int MAC_KEY = 3;
// Uses default hash algorithm (SHA-1)
static byte[] derive(char[] chars, byte[] salt,
int ic, int n, int type) {
return derive(chars, salt, ic, n, type, "SHA-1", 64);
}
// Uses supplied hash algorithm
static byte[] derive(char[] chars, byte[] salt, int ic, int n, int type,
String hashAlgo, int blockLength) {
// Add in trailing NULL terminator. Special case:
// no terminator if password is "\0".
int length = chars.length*2;
if (length == 2 && chars[0] == 0) {
chars = new char[0];
length = 0;
} else {
length += 2;
}
byte[] passwd = new byte[length];
for (int i = 0, j = 0; i < chars.length; i++, j+=2) {
passwd[j] = (byte) ((chars[i] >>> 8) & 0xFF);
passwd[j+1] = (byte) (chars[i] & 0xFF);
}
byte[] key = new byte[n];
try {
MessageDigest sha = MessageDigest.getInstance(hashAlgo);
int v = blockLength;
int u = sha.getDigestLength();
int c = roundup(n, u) / u;
byte[] D = new byte[v];
int s = roundup(salt.length, v);
int p = roundup(passwd.length, v);
byte[] I = new byte[s + p];
Arrays.fill(D, (byte)type);
concat(salt, I, 0, s);
concat(passwd, I, s, p);
Arrays.fill(passwd, (byte)0x00);
byte[] Ai;
byte[] B = new byte[v];
byte[] tmp = new byte[v];
int i = 0;
for (; ; i++, n -= u) {
sha.update(D);
sha.update(I);
Ai = sha.digest();
for (int r = 1; r < ic; r++)
Ai = sha.digest(Ai);
System.arraycopy(Ai, 0, key, u * i, Math.min(n, u));
if (i + 1 == c)
break;
concat(Ai, B, 0, B.length);
BigInteger B1;
B1 = new BigInteger(1, B).add(BigInteger.ONE);
for (int j = 0; j < I.length; j += v) {
BigInteger Ij;
int trunc;
if (tmp.length != v)
tmp = new byte[v];
System.arraycopy(I, j, tmp, 0, v);
Ij = new BigInteger(1, tmp);
Ij = Ij.add(B1);
tmp = Ij.toByteArray();
trunc = tmp.length - v;
if (trunc >= 0) {
System.arraycopy(tmp, trunc, I, j, v);
} else if (trunc < 0) {
Arrays.fill(I, j, j + (-trunc), (byte)0);
System.arraycopy(tmp, 0, I, j + (-trunc), tmp.length);
}
}
}
} catch (Exception e) {
throw new RuntimeException("internal error: " + e);
}
return key;
}
private static int roundup(int x, int y) {
return ((x + (y - 1)) / y) * y;
}
private static void concat(byte[] src, byte[] dst, int start, int len) {
if (src.length == 0) {
return;
}
int loop = len / src.length;
int off, i;
for (i = 0, off = 0; i < loop; i++, off += src.length)
System.arraycopy(src, 0, dst, off + start, src.length);
System.arraycopy(src, 0, dst, off + start, len - off);
}
PKCS12PBECipherCore(String symmCipherAlg, int defKeySize)
throws NoSuchAlgorithmException {
algo = symmCipherAlg;
if (algo.equals("RC4")) {
pbeAlgo = "PBEWithSHA1AndRC4_" + defKeySize * 8;
} else {
SymmetricCipher symmCipher = null;
if (algo.equals("DESede")) {
symmCipher = new DESedeCrypt();
pbeAlgo = "PBEWithSHA1AndDESede";
} else if (algo.equals("RC2")) {
symmCipher = new RC2Crypt();
pbeAlgo = "PBEWithSHA1AndRC2_" + defKeySize * 8;
} else {
throw new NoSuchAlgorithmException("No Cipher implementation " +
"for PBEWithSHA1And" + algo);
}
blockSize = symmCipher.getBlockSize();
cipher = new CipherCore(symmCipher, blockSize);
cipher.setMode("CBC");
try {
cipher.setPadding("PKCS5Padding");
} catch (NoSuchPaddingException nspe) {
// should not happen
}
}
keySize = defKeySize;
}
void implSetMode(String mode) throws NoSuchAlgorithmException {
if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
throw new NoSuchAlgorithmException("Invalid cipher mode: "
+ mode);
}
}
void implSetPadding(String padding) throws NoSuchPaddingException {
if ((padding != null) &&
(!padding.equalsIgnoreCase("PKCS5Padding"))) {
throw new NoSuchPaddingException("Invalid padding scheme: " +
padding);
}
}
int implGetBlockSize() {
return blockSize;
}
int implGetOutputSize(int inLen) {
return cipher.getOutputSize(inLen);
}
byte[] implGetIV() {
return cipher.getIV();
}
AlgorithmParameters implGetParameters() {
AlgorithmParameters params = null;
if (salt == null) {
// Cipher is not initialized with parameters;
// follow the recommendation in PKCS12 v1.0
// section B.4 to generate salt and iCount.
salt = new byte[DEFAULT_SALT_LENGTH];
SunJCE.getRandom().nextBytes(salt);
iCount = DEFAULT_COUNT;
}
PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount);
try {
params = AlgorithmParameters.getInstance(pbeAlgo,
SunJCE.getInstance());
params.init(pbeSpec);
} catch (NoSuchAlgorithmException nsae) {
// should never happen
throw new RuntimeException(
"SunJCE provider is not configured properly");
} catch (InvalidParameterSpecException ipse) {
// should never happen
throw new RuntimeException("PBEParameterSpec not supported");
}
return params;
}
void implInit(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
implInit(opmode, key, params, random, null);
}
void implInit(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random, CipherSpi cipherImpl)
throws InvalidKeyException,
InvalidAlgorithmParameterException {
char[] passwdChars = null;
salt = null;
iCount = 0;
if (key instanceof javax.crypto.interfaces.PBEKey) {
javax.crypto.interfaces.PBEKey pbeKey =
(javax.crypto.interfaces.PBEKey) key;
passwdChars = pbeKey.getPassword();
salt = pbeKey.getSalt(); // maybe null if unspecified
iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified
} else if (key instanceof SecretKey) {
byte[] passwdBytes;
if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) ||
(passwdBytes = key.getEncoded()) == null) {
throw new InvalidKeyException("Missing password");
}
passwdChars = new char[passwdBytes.length];
for (int i=0; i<passwdChars.length; i++) {
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
}
Arrays.fill(passwdBytes, (byte)0x00);
} else {
throw new InvalidKeyException("SecretKey of PBE type required");
}
try {
if (((opmode == Cipher.DECRYPT_MODE) ||
(opmode == Cipher.UNWRAP_MODE)) &&
((params == null) && ((salt == null) || (iCount == 0)))) {
throw new InvalidAlgorithmParameterException
("Parameters missing");
}
if (params == null) {
// generate default for salt and iteration count if necessary
if (salt == null) {
salt = new byte[DEFAULT_SALT_LENGTH];
if (random != null) {
random.nextBytes(salt);
} else {
SunJCE.getRandom().nextBytes(salt);
}
}
if (iCount == 0) iCount = DEFAULT_COUNT;
} else if (!(params instanceof PBEParameterSpec)) {
throw new InvalidAlgorithmParameterException
("PBEParameterSpec type required");
} else {
PBEParameterSpec pbeParams = (PBEParameterSpec) params;
// make sure the parameter values are consistent
if (salt != null) {
if (!Arrays.equals(salt, pbeParams.getSalt())) {
throw new InvalidAlgorithmParameterException
("Inconsistent value of salt between key and params");
}
} else {
salt = pbeParams.getSalt();
}
if (iCount != 0) {
if (iCount != pbeParams.getIterationCount()) {
throw new InvalidAlgorithmParameterException
("Different iteration count between key and params");
}
} else {
iCount = pbeParams.getIterationCount();
}
}
// salt is recommended to be ideally as long as the output
// of the hash function. However, it may be too strict to
// force this; so instead, we'll just require the minimum
// salt length to be 8-byte which is what PKCS#5 recommends
// and openssl does.
if (salt.length < 8) {
throw new InvalidAlgorithmParameterException
("Salt must be at least 8 bytes long");
}
if (iCount <= 0) {
throw new InvalidAlgorithmParameterException
("IterationCount must be a positive number");
}
byte[] derivedKey = derive(passwdChars, salt, iCount,
keySize, CIPHER_KEY);
SecretKey cipherKey = new SecretKeySpec(derivedKey, algo);
if (cipherImpl != null && cipherImpl instanceof ARCFOURCipher) {
((ARCFOURCipher)cipherImpl).engineInit(opmode, cipherKey, random);
} else {
byte[] derivedIv = derive(passwdChars, salt, iCount, 8,
CIPHER_IV);
IvParameterSpec ivSpec = new IvParameterSpec(derivedIv, 0, 8);
// initialize the underlying cipher
cipher.init(opmode, cipherKey, ivSpec, random);
}
} finally {
Arrays.fill(passwdChars, '\0');
}
}
void implInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
implInit(opmode, key, params, random, null);
}
void implInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom random, CipherSpi cipherImpl)
throws InvalidKeyException, InvalidAlgorithmParameterException {
AlgorithmParameterSpec paramSpec = null;
if (params != null) {
try {
paramSpec = params.getParameterSpec(PBEParameterSpec.class);
} catch (InvalidParameterSpecException ipse) {
throw new InvalidAlgorithmParameterException(
"requires PBE parameters");
}
}
implInit(opmode, key, paramSpec, random, cipherImpl);
}
void implInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
implInit(opmode, key, random, null);
}
void implInit(int opmode, Key key, SecureRandom random,
CipherSpi cipherImpl) throws InvalidKeyException {
try {
implInit(opmode, key, (AlgorithmParameterSpec) null, random,
cipherImpl);
} catch (InvalidAlgorithmParameterException iape) {
throw new InvalidKeyException("requires PBE parameters");
}
}
byte[] implUpdate(byte[] in, int inOff, int inLen) {
return cipher.update(in, inOff, inLen);
}
int implUpdate(byte[] in, int inOff, int inLen, byte[] out, int outOff)
throws ShortBufferException {
return cipher.update(in, inOff, inLen, out, outOff);
}
byte[] implDoFinal(byte[] in, int inOff, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
return cipher.doFinal(in, inOff, inLen);
}
int implDoFinal(byte[] in, int inOff, int inLen, byte[] out, int outOff)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return cipher.doFinal(in, inOff, inLen, out, outOff);
}
int implGetKeySize(Key key) throws InvalidKeyException {
return keySize;
}
byte[] implWrap(Key key) throws IllegalBlockSizeException,
InvalidKeyException {
return cipher.wrap(key);
}
Key implUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
public static final class PBEWithSHA1AndDESede extends CipherSpi {
private final PKCS12PBECipherCore core;
public PBEWithSHA1AndDESede() throws NoSuchAlgorithmException {
core = new PKCS12PBECipherCore("DESede", 24);
}
protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
return core.implDoFinal(in, inOff, inLen);
}
protected int engineDoFinal(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return core.implDoFinal(in, inOff, inLen, out, outOff);
}
protected int engineGetBlockSize() {
return core.implGetBlockSize();
}
protected byte[] engineGetIV() {
return core.implGetIV();
}
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return core.implGetKeySize(key);
}
protected int engineGetOutputSize(int inLen) {
return core.implGetOutputSize(inLen);
}
protected AlgorithmParameters engineGetParameters() {
return core.implGetParameters();
}
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
core.implInit(opmode, key, random);
}
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
core.implSetMode(mode);
}
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
core.implSetPadding(paddingScheme);
}
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return core.implUnwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
return core.implUpdate(in, inOff, inLen);
}
protected int engineUpdate(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException {
return core.implUpdate(in, inOff, inLen, out, outOff);
}
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
return core.implWrap(key);
}
}
public static final class PBEWithSHA1AndRC2_40 extends CipherSpi {
private final PKCS12PBECipherCore core;
public PBEWithSHA1AndRC2_40() throws NoSuchAlgorithmException {
core = new PKCS12PBECipherCore("RC2", 5);
}
protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
return core.implDoFinal(in, inOff, inLen);
}
protected int engineDoFinal(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return core.implDoFinal(in, inOff, inLen, out, outOff);
}
protected int engineGetBlockSize() {
return core.implGetBlockSize();
}
protected byte[] engineGetIV() {
return core.implGetIV();
}
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return core.implGetKeySize(key);
}
protected int engineGetOutputSize(int inLen) {
return core.implGetOutputSize(inLen);
}
protected AlgorithmParameters engineGetParameters() {
return core.implGetParameters();
}
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
core.implInit(opmode, key, random);
}
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
core.implSetMode(mode);
}
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
core.implSetPadding(paddingScheme);
}
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return core.implUnwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
return core.implUpdate(in, inOff, inLen);
}
protected int engineUpdate(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException {
return core.implUpdate(in, inOff, inLen, out, outOff);
}
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
return core.implWrap(key);
}
}
public static final class PBEWithSHA1AndRC2_128 extends CipherSpi {
private final PKCS12PBECipherCore core;
public PBEWithSHA1AndRC2_128() throws NoSuchAlgorithmException {
core = new PKCS12PBECipherCore("RC2", 16);
}
protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
return core.implDoFinal(in, inOff, inLen);
}
protected int engineDoFinal(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return core.implDoFinal(in, inOff, inLen, out, outOff);
}
protected int engineGetBlockSize() {
return core.implGetBlockSize();
}
protected byte[] engineGetIV() {
return core.implGetIV();
}
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return core.implGetKeySize(key);
}
protected int engineGetOutputSize(int inLen) {
return core.implGetOutputSize(inLen);
}
protected AlgorithmParameters engineGetParameters() {
return core.implGetParameters();
}
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
core.implInit(opmode, key, random);
}
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
core.implSetMode(mode);
}
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
core.implSetPadding(paddingScheme);
}
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return core.implUnwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
return core.implUpdate(in, inOff, inLen);
}
protected int engineUpdate(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException {
return core.implUpdate(in, inOff, inLen, out, outOff);
}
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
return core.implWrap(key);
}
}
public static final class PBEWithSHA1AndRC4_40 extends CipherSpi {
private static final int RC4_KEYSIZE = 5;
private final PKCS12PBECipherCore core;
private final ARCFOURCipher cipher;
public PBEWithSHA1AndRC4_40() throws NoSuchAlgorithmException {
core = new PKCS12PBECipherCore("RC4", RC4_KEYSIZE);
cipher = new ARCFOURCipher();
}
protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
return cipher.engineDoFinal(in, inOff, inLen);
}
protected int engineDoFinal(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return cipher.engineDoFinal(in, inOff, inLen, out, outOff);
}
protected int engineGetBlockSize() {
return cipher.engineGetBlockSize();
}
protected byte[] engineGetIV() {
return cipher.engineGetIV();
}
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return RC4_KEYSIZE;
}
protected int engineGetOutputSize(int inLen) {
return cipher.engineGetOutputSize(inLen);
}
protected AlgorithmParameters engineGetParameters() {
return core.implGetParameters();
}
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random, cipher);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random, cipher);
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
core.implInit(opmode, key, random, cipher);
}
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
if (mode.equalsIgnoreCase("ECB") == false) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
}
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
if (paddingScheme.equalsIgnoreCase("NoPadding") == false) {
throw new NoSuchPaddingException("Padding must be NoPadding");
}
}
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return cipher.engineUnwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
return cipher.engineUpdate(in, inOff, inLen);
}
protected int engineUpdate(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException {
return cipher.engineUpdate(in, inOff, inLen, out, outOff);
}
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
return cipher.engineWrap(key);
}
}
public static final class PBEWithSHA1AndRC4_128 extends CipherSpi {
private static final int RC4_KEYSIZE = 16;
private final PKCS12PBECipherCore core;
private final ARCFOURCipher cipher;
public PBEWithSHA1AndRC4_128() throws NoSuchAlgorithmException {
core = new PKCS12PBECipherCore("RC4", RC4_KEYSIZE);
cipher = new ARCFOURCipher();
}
protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
return cipher.engineDoFinal(in, inOff, inLen);
}
protected int engineDoFinal(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return cipher.engineDoFinal(in, inOff, inLen, out, outOff);
}
protected int engineGetBlockSize() {
return cipher.engineGetBlockSize();
}
protected byte[] engineGetIV() {
return cipher.engineGetIV();
}
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return RC4_KEYSIZE;
}
protected int engineGetOutputSize(int inLen) {
return cipher.engineGetOutputSize(inLen);
}
protected AlgorithmParameters engineGetParameters() {
return core.implGetParameters();
}
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random, cipher);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.implInit(opmode, key, params, random, cipher);
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
core.implInit(opmode, key, random, cipher);
}
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
if (mode.equalsIgnoreCase("ECB") == false) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
}
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
if (paddingScheme.equalsIgnoreCase("NoPadding") == false) {
throw new NoSuchPaddingException("Padding must be NoPadding");
}
}
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
return cipher.engineUnwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
return cipher.engineUpdate(in, inOff, inLen);
}
protected int engineUpdate(byte[] in, int inOff, int inLen,
byte[] out, int outOff)
throws ShortBufferException {
return cipher.engineUpdate(in, inOff, inLen, out, outOff);
}
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
return cipher.engineWrap(key);
}
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 1997, 2017, 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 javax.crypto.ShortBufferException;
import java.util.Arrays;
/**
* This class implements padding as specified in the PKCS#5 standard.
*
* @author Gigi Ankeny
*
*
* @see Padding
*/
final class PKCS5Padding implements Padding {
private int blockSize;
PKCS5Padding(int blockSize) {
this.blockSize = blockSize;
}
/**
* Adds the given number of padding bytes to the data input.
* The value of the padding bytes is determined
* by the specific padding mechanism that implements this
* interface.
*
* @param in the input buffer with the data to pad
* @param off the offset in <code>in</code> where the padding bytes
* are appended
* @param len the number of padding bytes to add
*
* @exception ShortBufferException if <code>in</code> is too small to hold
* the padding bytes
*/
public void padWithLen(byte[] in, int off, int len)
throws ShortBufferException
{
if (in == null)
return;
int idx = Math.addExact(off, len);
if (idx > in.length) {
throw new ShortBufferException("Buffer too small to hold padding");
}
byte paddingOctet = (byte) (len & 0xff);
Arrays.fill(in, off, idx, paddingOctet);
return;
}
/**
* Returns the index where the padding starts.
*
* <p>Given a buffer with padded data, this method returns the
* index where the padding starts.
*
* @param in the buffer with the padded data
* @param off the offset in <code>in</code> where the padded data starts
* @param len the length of the padded data
*
* @return the index where the padding starts, or -1 if the input is
* not properly padded
*/
public int unpad(byte[] in, int off, int len) {
if ((in == null) ||
(len == 0)) { // this can happen if input is really a padded buffer
return 0;
}
int idx = Math.addExact(off, len);
byte lastByte = in[idx - 1];
int padValue = (int)lastByte & 0x0ff;
if ((padValue < 0x01)
|| (padValue > blockSize)) {
return -1;
}
int start = idx - padValue;
if (start < off) {
return -1;
}
for (int i = start; i < idx; i++) {
if (in[i] != lastByte) {
return -1;
}
}
return start;
}
/**
* Determines how long the padding will be for a given input length.
*
* @param len the length of the data to pad
*
* @return the length of the padding
*/
public int padLength(int len) {
int paddingOctet = blockSize - (len % blockSize);
return paddingOctet;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 1997, 2007, 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 javax.crypto.ShortBufferException;
/**
* Padding interface.
*
* This interface is implemented by general-purpose padding schemes, such as
* the one described in PKCS#5.
*
* @author Jan Luehe
* @author Gigi Ankeny
*
*
* @see PKCS5Padding
*/
interface Padding {
/**
* Adds the given number of padding bytes to the data input.
* The value of the padding bytes is determined
* by the specific padding mechanism that implements this
* interface.
*
* @param in the input buffer with the data to pad
* @param the offset in <code>in</code> where the padding bytes
* are appended
* @param len the number of padding bytes to add
*
* @exception ShortBufferException if <code>in</code> is too small to hold
* the padding bytes
*/
void padWithLen(byte[] in, int off, int len) throws ShortBufferException;
/**
* Returns the index where padding starts.
*
* <p>Given a buffer with data and their padding, this method returns the
* index where the padding starts.
*
* @param in the buffer with the data and their padding
* @param off the offset in <code>in</code> where the data starts
* @param len the length of the data and their padding
*
* @return the index where the padding starts, or -1 if the input is
* not properly padded
*/
int unpad(byte[] in, int off, int len);
/**
* Determines how long the padding will be for a given input length.
*
* @param len the length of the data to pad
*
* @return the length of the padding
*/
int padLength(int len);
}

View File

@@ -0,0 +1,345 @@
/*
* Copyright (c) 2016, 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.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Utility methods to check if state or arguments are correct.
*
*/
public class Preconditions {
/**
* Maps out-of-bounds values to a runtime exception.
*
* @param checkKind the kind of bounds check, whose name may correspond
* to the name of one of the range check methods, checkIndex,
* checkFromToIndex, checkFromIndexSize
* @param args the out-of-bounds arguments that failed the range check.
* If the checkKind corresponds a the name of a range check method
* then the bounds arguments are those that can be passed in order
* to the method.
* @param oobef the exception formatter that when applied with a checkKind
* and a list out-of-bounds arguments returns a runtime exception.
* If {@code null} then, it is as if an exception formatter was
* supplied that returns {@link IndexOutOfBoundsException} for any
* given arguments.
* @return the runtime exception
*/
private static RuntimeException outOfBounds(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobef,
String checkKind,
Integer... args) {
List<Integer> largs = Collections.unmodifiableList(Arrays.asList(args));
RuntimeException e = oobef == null
? null : oobef.apply(checkKind, largs);
return e == null
? new IndexOutOfBoundsException(outOfBoundsMessage(checkKind, largs)) : e;
}
private static RuntimeException outOfBoundsCheckIndex(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
int index, int length) {
return outOfBounds(oobe, "checkIndex", index, length);
}
private static RuntimeException outOfBoundsCheckFromToIndex(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
int fromIndex, int toIndex, int length) {
return outOfBounds(oobe, "checkFromToIndex", fromIndex, toIndex, length);
}
private static RuntimeException outOfBoundsCheckFromIndexSize(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
int fromIndex, int size, int length) {
return outOfBounds(oobe, "checkFromIndexSize", fromIndex, size, length);
}
/**
* Returns an out-of-bounds exception formatter from an given exception
* factory. The exception formatter is a function that formats an
* out-of-bounds message from its arguments and applies that message to the
* given exception factory to produce and relay an exception.
*
* <p>The exception formatter accepts two arguments: a {@code String}
* describing the out-of-bounds range check that failed, referred to as the
* <em>check kind</em>; and a {@code List<Integer>} containing the
* out-of-bound integer values that failed the check. The list of
* out-of-bound values is not modified.
*
* <p>Three check kinds are supported {@code checkIndex},
* {@code checkFromToIndex} and {@code checkFromIndexSize} corresponding
* respectively to the specified application of an exception formatter as an
* argument to the out-of-bounds range check methods
* {@link #checkIndex(int, int, BiFunction) checkIndex},
* {@link #checkFromToIndex(int, int, int, BiFunction) checkFromToIndex}, and
* {@link #checkFromIndexSize(int, int, int, BiFunction) checkFromIndexSize}.
* Thus a supported check kind corresponds to a method name and the
* out-of-bound integer values correspond to method argument values, in
* order, preceding the exception formatter argument (similar in many
* respects to the form of arguments required for a reflective invocation of
* such a range check method).
*
* <p>Formatter arguments conforming to such supported check kinds will
* produce specific exception messages describing failed out-of-bounds
* checks. Otherwise, more generic exception messages will be produced in
* any of the following cases: the check kind is supported but fewer
* or more out-of-bounds values are supplied, the check kind is not
* supported, the check kind is {@code null}, or the list of out-of-bound
* values is {@code null}.
*
* @apiNote
* This method produces an out-of-bounds exception formatter that can be
* passed as an argument to any of the supported out-of-bounds range check
* methods declared by {@code Objects}. For example, a formatter producing
* an {@code ArrayIndexOutOfBoundsException} may be produced and stored on a
* {@code static final} field as follows:
* <pre>{@code
* static final
* BiFunction<String, List<Integer>, ArrayIndexOutOfBoundsException> AIOOBEF =
* outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new);
* }</pre>
* The formatter instance {@code AIOOBEF} may be passed as an argument to an
* out-of-bounds range check method, such as checking if an {@code index}
* is within the bounds of a {@code limit}:
* <pre>{@code
* checkIndex(index, limit, AIOOBEF);
* }</pre>
* If the bounds check fails then the range check method will throw an
* {@code ArrayIndexOutOfBoundsException} with an appropriate exception
* message that is a produced from {@code AIOOBEF} as follows:
* <pre>{@code
* AIOOBEF.apply("checkIndex", List.of(index, limit));
* }</pre>
*
* @param f the exception factory, that produces an exception from a message
* where the message is produced and formatted by the returned
* exception formatter. If this factory is stateless and side-effect
* free then so is the returned formatter.
* Exceptions thrown by the factory are relayed to the caller
* of the returned formatter.
* @param <X> the type of runtime exception to be returned by the given
* exception factory and relayed by the exception formatter
* @return the out-of-bounds exception formatter
*/
public static <X extends RuntimeException>
BiFunction<String, List<Integer>, X> outOfBoundsExceptionFormatter(Function<String, X> f) {
// Use anonymous class to avoid bootstrap issues if this method is
// used early in startup
return new BiFunction<String, List<Integer>, X>() {
@Override
public X apply(String checkKind, List<Integer> args) {
return f.apply(outOfBoundsMessage(checkKind, args));
}
};
}
private static String outOfBoundsMessage(String checkKind, List<Integer> args) {
if (checkKind == null && args == null) {
return String.format("Range check failed");
} else if (checkKind == null) {
return String.format("Range check failed: %s", args);
} else if (args == null) {
return String.format("Range check failed: %s", checkKind);
}
int argSize = 0;
switch (checkKind) {
case "checkIndex":
argSize = 2;
break;
case "checkFromToIndex":
case "checkFromIndexSize":
argSize = 3;
break;
default:
}
// Switch to default if fewer or more arguments than required are supplied
switch ((args.size() != argSize) ? "" : checkKind) {
case "checkIndex":
return String.format("Index %d out-of-bounds for length %d",
args.get(0), args.get(1));
case "checkFromToIndex":
return String.format("Range [%d, %d) out-of-bounds for length %d",
args.get(0), args.get(1), args.get(2));
case "checkFromIndexSize":
return String.format("Range [%d, %<d + %d) out-of-bounds for length %d",
args.get(0), args.get(1), args.get(2));
default:
return String.format("Range check failed: %s %s", checkKind, args);
}
}
/**
* Checks if the {@code index} is within the bounds of the range from
* {@code 0} (inclusive) to {@code length} (exclusive).
*
* <p>The {@code index} is defined to be out-of-bounds if any of the
* following inequalities is true:
* <ul>
* <li>{@code index < 0}</li>
* <li>{@code index >= length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the {@code index} is out-of-bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkIndex};
* and an unmodifiable list integers whose values are, in order, the
* out-of-bounds arguments {@code index} and {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out-of-bounds
* @param index the index
* @param length the upper-bound (exclusive) of the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code index} if it is within bounds of the range
* @throws X if the {@code index} is out-of-bounds and the exception
* formatter is non-{@code null}
* @throws IndexOutOfBoundsException if the {@code index} is out-of-bounds
* and the exception formatter is {@code null}
* @since 9
*
* @implNote
* This method is made intrinsic in optimizing compilers to guide them to
* perform unsigned comparisons of the index and length when it is known the
* length is a non-negative value (such as that of an array length or from
* the upper bound of a loop)
*/
public static <X extends RuntimeException>
int checkIndex(int index, int length,
BiFunction<String, List<Integer>, X> oobef) {
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
}
/**
* Checks if the sub-range from {@code fromIndex} (inclusive) to
* {@code toIndex} (exclusive) is within the bounds of range from {@code 0}
* (inclusive) to {@code length} (exclusive).
*
* <p>The sub-range is defined to be out-of-bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code fromIndex > toIndex}</li>
* <li>{@code toIndex > length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the sub-range is out-of-bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkFromToIndex};
* and an unmodifiable list integers whose values are, in order, the
* out-of-bounds arguments {@code fromIndex}, {@code toIndex}, and {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out-of-bounds
* @param fromIndex the lower-bound (inclusive) of the sub-range
* @param toIndex the upper-bound (exclusive) of the sub-range
* @param length the upper-bound (exclusive) the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code fromIndex} if the sub-range within bounds of the range
* @throws X if the sub-range is out-of-bounds and the exception factory
* function is non-{@code null}
* @throws IndexOutOfBoundsException if the sub-range is out-of-bounds and
* the exception factory function is {@code null}
* @since 9
*/
public static <X extends RuntimeException>
int checkFromToIndex(int fromIndex, int toIndex, int length,
BiFunction<String, List<Integer>, X> oobef) {
if (fromIndex < 0 || fromIndex > toIndex || toIndex > length)
throw outOfBoundsCheckFromToIndex(oobef, fromIndex, toIndex, length);
return fromIndex;
}
/**
* Checks if the sub-range from {@code fromIndex} (inclusive) to
* {@code fromIndex + size} (exclusive) is within the bounds of range from
* {@code 0} (inclusive) to {@code length} (exclusive).
*
* <p>The sub-range is defined to be out-of-bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code size < 0}</li>
* <li>{@code fromIndex + size > length}, taking into account integer overflow</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the sub-range is out-of-bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkFromIndexSize};
* and an unmodifiable list integers whose values are, in order, the
* out-of-bounds arguments {@code fromIndex}, {@code size}, and
* {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out-of-bounds
* @param fromIndex the lower-bound (inclusive) of the sub-interval
* @param size the size of the sub-range
* @param length the upper-bound (exclusive) of the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code fromIndex} if the sub-range within bounds of the range
* @throws X if the sub-range is out-of-bounds and the exception factory
* function is non-{@code null}
* @throws IndexOutOfBoundsException if the sub-range is out-of-bounds and
* the exception factory function is {@code null}
* @since 9
*/
public static <X extends RuntimeException>
int checkFromIndexSize(int fromIndex, int size, int length,
BiFunction<String, List<Integer>, X> oobef) {
if ((length | fromIndex | size) < 0 || size > length - fromIndex)
throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length);
return fromIndex;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 1998, 2007, 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.math.*;
import java.io.*;
import sun.security.x509.AlgorithmId;
import sun.security.util.*;
/**
* This class implements the <code>PrivateKeyInfo</code> type,
* which is defined in PKCS #8 as follows:
*
* <pre>
* PrivateKeyInfo ::= SEQUENCE {
* version INTEGER,
* privateKeyAlgorithm AlgorithmIdentifier,
* privateKey OCTET STRING,
* attributes [0] IMPLICIT Attributes OPTIONAL }
* </pre>
*
* @author Jan Luehe
*/
final class PrivateKeyInfo {
// the version number defined by the PKCS #8 standard
private static final BigInteger VERSION = BigInteger.ZERO;
// the private-key algorithm
private AlgorithmId algid;
// the private-key value
private byte[] privkey;
/**
* Constructs a PKCS#8 PrivateKeyInfo from its ASN.1 encoding.
*/
PrivateKeyInfo(byte[] encoded) throws IOException {
DerValue val = new DerValue(encoded);
if (val.tag != DerValue.tag_Sequence)
throw new IOException("private key parse error: not a sequence");
// version
BigInteger parsedVersion = val.data.getBigInteger();
if (!parsedVersion.equals(VERSION)) {
throw new IOException("version mismatch: (supported: " +
VERSION + ", parsed: " + parsedVersion);
}
// privateKeyAlgorithm
this.algid = AlgorithmId.parse(val.data.getDerValue());
// privateKey
this.privkey = val.data.getOctetString();
// OPTIONAL attributes not supported yet
}
/**
* Returns the private-key algorithm.
*/
AlgorithmId getAlgorithm() {
return this.algid;
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 2003, 2011, 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.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.RC2ParameterSpec;
/**
* JCE CipherSpi for the RC2(tm) algorithm as described in RFC 2268.
* The real code is in CipherCore and RC2Crypt.
*
* @since 1.5
* @author Andreas Sterbenz
*/
public final class RC2Cipher extends CipherSpi {
// internal CipherCore & RC2Crypt objects which do the real work.
private final CipherCore core;
private final RC2Crypt embeddedCipher;
public RC2Cipher() {
embeddedCipher = new RC2Crypt();
core = new CipherCore(embeddedCipher, 8);
}
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
core.setMode(mode);
}
protected void engineSetPadding(String paddingScheme)
throws NoSuchPaddingException {
core.setPadding(paddingScheme);
}
protected int engineGetBlockSize() {
return 8;
}
protected int engineGetOutputSize(int inputLen) {
return core.getOutputSize(inputLen);
}
protected byte[] engineGetIV() {
return core.getIV();
}
protected AlgorithmParameters engineGetParameters() {
return core.getParameters("RC2");
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
embeddedCipher.initEffectiveKeyBits(0);
core.init(opmode, key, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null && params instanceof RC2ParameterSpec) {
embeddedCipher.initEffectiveKeyBits
(((RC2ParameterSpec)params).getEffectiveKeyBits());
} else {
embeddedCipher.initEffectiveKeyBits(0);
}
core.init(opmode, key, params, random);
}
protected void engineInit(int opmode, Key key,
AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null && params.getAlgorithm().equals("RC2")) {
try {
RC2ParameterSpec rc2Params =
params.getParameterSpec(RC2ParameterSpec.class);
engineInit(opmode, key, rc2Params, random);
} catch (InvalidParameterSpecException ipse) {
throw new InvalidAlgorithmParameterException
("Wrong parameter type: RC2 expected");
}
} else {
embeddedCipher.initEffectiveKeyBits(0);
core.init(opmode, key, params, random);
}
}
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
return core.update(in, inOfs, inLen);
}
protected int engineUpdate(byte[] in, int inOfs, int inLen,
byte[] out, int outOfs) throws ShortBufferException {
return core.update(in, inOfs, inLen, out, outOfs);
}
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
return core.doFinal(in, inOfs, inLen);
}
protected int engineDoFinal(byte[] in, int inOfs, int inLen,
byte[] out, int outOfs) throws IllegalBlockSizeException,
ShortBufferException, BadPaddingException {
return core.doFinal(in, inOfs, inLen, out, outOfs);
}
protected int engineGetKeySize(Key key) throws InvalidKeyException {
byte[] keyBytes = CipherCore.getKeyBytes(key);
RC2Crypt.checkKey(key.getAlgorithm(), keyBytes.length);
return keyBytes.length << 3;
}
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
return core.wrap(key);
}
protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
int wrappedKeyType) throws InvalidKeyException,
NoSuchAlgorithmException {
return core.unwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType);
}
}

View File

@@ -0,0 +1,320 @@
/*
* Copyright (c) 2003, 2007, 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.security.InvalidKeyException;
/**
* Implementation of the RC2(tm) algorithm as described in RFC 2268.
*
* RC2 is a 16-bit based algorithm and not particularly fast on 32/64 bit
* architectures. Also, note that although the JVM has a 16-bit integer
* type (short), all expressions are evaluated either in 32 or 64 bit
* (int or long). Expression such as "s1 = s2 + s3" are implemented by
* first promoting s2 and s3 to int, performing an int addition, and
* then demoting the result back to short to store in s1. To avoid this
* fairly slow process, we use the int type throughout and manually insert
* "& 0xffff" where necessary.
*
* @since 1.5
* @author Andreas Sterbenz
*/
final class RC2Crypt extends SymmetricCipher {
// PITABLE from the RFC, used in key setup
private final static int[] PI_TABLE = new int[] {
0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed,
0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e,
0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13,
0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b,
0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c,
0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1,
0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57,
0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7,
0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7,
0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74,
0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc,
0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a,
0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae,
0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c,
0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0,
0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77,
0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad,
};
// expanded key, 64 times 16-bit words
private final int[] expandedKey;
// effective key bits
private int effectiveKeyBits;
RC2Crypt() {
expandedKey = new int[64];
}
int getBlockSize() {
return 8;
}
int getEffectiveKeyBits() {
return effectiveKeyBits;
}
/**
* Initializes the effective key bit size. This method is a hook to
* allow RC2Cipher to initialize the effective key size.
*/
void initEffectiveKeyBits(int effectiveKeyBits) {
this.effectiveKeyBits = effectiveKeyBits;
}
static void checkKey(String algorithm, int keyLength)
throws InvalidKeyException {
if (algorithm.equals("RC2") == false) {
throw new InvalidKeyException("Key algorithm must be RC2");
}
if ((keyLength < 5) || (keyLength > 128)) {
throw new InvalidKeyException
("RC2 key length must be between 40 and 1024 bit");
}
}
void init(boolean decrypting, String algorithm, byte[] key)
throws InvalidKeyException {
int keyLength = key.length;
if (effectiveKeyBits == 0) {
effectiveKeyBits = keyLength << 3;
}
checkKey(algorithm, keyLength);
// key buffer, the L[] byte array from the spec
byte[] expandedKeyBytes = new byte[128];
// place key into key buffer
System.arraycopy(key, 0, expandedKeyBytes, 0, keyLength);
// first loop
int t = expandedKeyBytes[keyLength - 1];
for (int i = keyLength; i < 128; i++) {
t = PI_TABLE[(t + expandedKeyBytes[i - keyLength]) & 0xff];
expandedKeyBytes[i] = (byte)t;
}
int t8 = (effectiveKeyBits + 7) >> 3;
int tm = 0xff >> (-effectiveKeyBits & 7);
// second loop, reduce search space to effective key bits
t = PI_TABLE[expandedKeyBytes[128 - t8] & tm];
expandedKeyBytes[128 - t8] = (byte)t;
for (int i = 127 - t8; i >= 0; i--) {
t = PI_TABLE[t ^ (expandedKeyBytes[i + t8] & 0xff)];
expandedKeyBytes[i] = (byte)t;
}
// byte to short conversion, little endian (copy into K[])
for (int i = 0, j = 0; i < 64; i++, j += 2) {
t = (expandedKeyBytes[j ] & 0xff)
+ ((expandedKeyBytes[j + 1] & 0xff) << 8);
expandedKey[i] = t;
}
}
/**
* Encrypt a single block. Note that in a few places we omit a "& 0xffff"
* and allow variables to become larger than 16 bit. This still works
* because there is never a 32 bit overflow.
*/
void encryptBlock(byte[] in, int inOfs, byte[] out, int outOfs) {
int R0 = (in[inOfs ] & 0xff)
+ ((in[inOfs + 1] & 0xff) << 8);
int R1 = (in[inOfs + 2] & 0xff)
+ ((in[inOfs + 3] & 0xff) << 8);
int R2 = (in[inOfs + 4] & 0xff)
+ ((in[inOfs + 5] & 0xff) << 8);
int R3 = (in[inOfs + 6] & 0xff)
+ ((in[inOfs + 7] & 0xff) << 8);
// 5 mixing rounds
for (int i = 0; i < 20; i += 4) {
R0 = (R0 + expandedKey[i ] + (R3 & R2) + (~R3 & R1)) & 0xffff;
R0 = (R0 << 1) | (R0 >>> 15);
R1 = (R1 + expandedKey[i + 1] + (R0 & R3) + (~R0 & R2)) & 0xffff;
R1 = (R1 << 2) | (R1 >>> 14);
R2 = (R2 + expandedKey[i + 2] + (R1 & R0) + (~R1 & R3)) & 0xffff;
R2 = (R2 << 3) | (R2 >>> 13);
R3 = (R3 + expandedKey[i + 3] + (R2 & R1) + (~R2 & R0)) & 0xffff;
R3 = (R3 << 5) | (R3 >>> 11);
}
// 1 mashing round
R0 += expandedKey[R3 & 0x3f];
R1 += expandedKey[R0 & 0x3f];
R2 += expandedKey[R1 & 0x3f];
R3 += expandedKey[R2 & 0x3f];
// 6 mixing rounds
for (int i = 20; i < 44; i += 4) {
R0 = (R0 + expandedKey[i ] + (R3 & R2) + (~R3 & R1)) & 0xffff;
R0 = (R0 << 1) | (R0 >>> 15);
R1 = (R1 + expandedKey[i + 1] + (R0 & R3) + (~R0 & R2)) & 0xffff;
R1 = (R1 << 2) | (R1 >>> 14);
R2 = (R2 + expandedKey[i + 2] + (R1 & R0) + (~R1 & R3)) & 0xffff;
R2 = (R2 << 3) | (R2 >>> 13);
R3 = (R3 + expandedKey[i + 3] + (R2 & R1) + (~R2 & R0)) & 0xffff;
R3 = (R3 << 5) | (R3 >>> 11);
}
// 1 mashing round
R0 += expandedKey[R3 & 0x3f];
R1 += expandedKey[R0 & 0x3f];
R2 += expandedKey[R1 & 0x3f];
R3 += expandedKey[R2 & 0x3f];
// 5 mixing rounds
for (int i = 44; i < 64; i += 4) {
R0 = (R0 + expandedKey[i ] + (R3 & R2) + (~R3 & R1)) & 0xffff;
R0 = (R0 << 1) | (R0 >>> 15);
R1 = (R1 + expandedKey[i + 1] + (R0 & R3) + (~R0 & R2)) & 0xffff;
R1 = (R1 << 2) | (R1 >>> 14);
R2 = (R2 + expandedKey[i + 2] + (R1 & R0) + (~R1 & R3)) & 0xffff;
R2 = (R2 << 3) | (R2 >>> 13);
R3 = (R3 + expandedKey[i + 3] + (R2 & R1) + (~R2 & R0)) & 0xffff;
R3 = (R3 << 5) | (R3 >>> 11);
}
out[outOfs ] = (byte)R0;
out[outOfs + 1] = (byte)(R0 >> 8);
out[outOfs + 2] = (byte)R1;
out[outOfs + 3] = (byte)(R1 >> 8);
out[outOfs + 4] = (byte)R2;
out[outOfs + 5] = (byte)(R2 >> 8);
out[outOfs + 6] = (byte)R3;
out[outOfs + 7] = (byte)(R3 >> 8);
}
void decryptBlock(byte[] in, int inOfs, byte[] out, int outOfs) {
int R0 = (in[inOfs ] & 0xff)
+ ((in[inOfs + 1] & 0xff) << 8);
int R1 = (in[inOfs + 2] & 0xff)
+ ((in[inOfs + 3] & 0xff) << 8);
int R2 = (in[inOfs + 4] & 0xff)
+ ((in[inOfs + 5] & 0xff) << 8);
int R3 = (in[inOfs + 6] & 0xff)
+ ((in[inOfs + 7] & 0xff) << 8);
// 5 r-mixing rounds
for(int i = 64; i > 44; i -= 4) {
R3 = ((R3 << 11) | (R3 >>> 5)) & 0xffff;
R3 = (R3 - expandedKey[i - 1] - (R2 & R1) - (~R2 & R0)) & 0xffff;
R2 = ((R2 << 13) | (R2 >>> 3)) & 0xffff;
R2 = (R2 - expandedKey[i - 2] - (R1 & R0) - (~R1 & R3)) & 0xffff;
R1 = ((R1 << 14) | (R1 >>> 2)) & 0xffff;
R1 = (R1 - expandedKey[i - 3] - (R0 & R3) - (~R0 & R2)) & 0xffff;
R0 = ((R0 << 15) | (R0 >>> 1)) & 0xffff;
R0 = (R0 - expandedKey[i - 4] - (R3 & R2) - (~R3 & R1)) & 0xffff;
}
// 1 r-mashing round
R3 = (R3 - expandedKey[R2 & 0x3f]) & 0xffff;
R2 = (R2 - expandedKey[R1 & 0x3f]) & 0xffff;
R1 = (R1 - expandedKey[R0 & 0x3f]) & 0xffff;
R0 = (R0 - expandedKey[R3 & 0x3f]) & 0xffff;
// 6 r-mixing rounds
for(int i = 44; i > 20; i -= 4) {
R3 = ((R3 << 11) | (R3 >>> 5)) & 0xffff;
R3 = (R3 - expandedKey[i - 1] - (R2 & R1) - (~R2 & R0)) & 0xffff;
R2 = ((R2 << 13) | (R2 >>> 3)) & 0xffff;
R2 = (R2 - expandedKey[i - 2] - (R1 & R0) - (~R1 & R3)) & 0xffff;
R1 = ((R1 << 14) | (R1 >>> 2)) & 0xffff;
R1 = (R1 - expandedKey[i - 3] - (R0 & R3) - (~R0 & R2)) & 0xffff;
R0 = ((R0 << 15) | (R0 >>> 1)) & 0xffff;
R0 = (R0 - expandedKey[i - 4] - (R3 & R2) - (~R3 & R1)) & 0xffff;
}
// 1 r-mashing round
R3 = (R3 - expandedKey[R2 & 0x3f]) & 0xffff;
R2 = (R2 - expandedKey[R1 & 0x3f]) & 0xffff;
R1 = (R1 - expandedKey[R0 & 0x3f]) & 0xffff;
R0 = (R0 - expandedKey[R3 & 0x3f]) & 0xffff;
// 5 r-mixing rounds
for(int i = 20; i > 0; i -= 4) {
R3 = ((R3 << 11) | (R3 >>> 5)) & 0xffff;
R3 = (R3 - expandedKey[i - 1] - (R2 & R1) - (~R2 & R0)) & 0xffff;
R2 = ((R2 << 13) | (R2 >>> 3)) & 0xffff;
R2 = (R2 - expandedKey[i - 2] - (R1 & R0) - (~R1 & R3)) & 0xffff;
R1 = ((R1 << 14) | (R1 >>> 2)) & 0xffff;
R1 = (R1 - expandedKey[i - 3] - (R0 & R3) - (~R0 & R2)) & 0xffff;
R0 = ((R0 << 15) | (R0 >>> 1)) & 0xffff;
R0 = (R0 - expandedKey[i - 4] - (R3 & R2) - (~R3 & R1)) & 0xffff;
}
out[outOfs ] = (byte)R0;
out[outOfs + 1] = (byte)(R0 >> 8);
out[outOfs + 2] = (byte)R1;
out[outOfs + 3] = (byte)(R1 >> 8);
out[outOfs + 4] = (byte)R2;
out[outOfs + 5] = (byte)(R2 >> 8);
out[outOfs + 6] = (byte)R3;
out[outOfs + 7] = (byte)(R3 >> 8);
}
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright (c) 2003, 2011, 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.io.IOException;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.spec.RC2ParameterSpec;
import sun.misc.HexDumpEncoder;
import sun.security.util.*;
/**
* This class implements the parameter set used with RC2
* encryption, which is defined in RFC2268 as follows:
*
* <pre>
* RC2-CBCParameter ::= CHOICE {
* iv IV,
* params SEQUENCE {
* version RC2Version,
* iv IV
* }
* }
*
* where
*
* IV ::= OCTET STRING -- 8 octets
* RC2Version ::= INTEGER -- 1-1024
* </pre>
*
* @author Sean Mullan
* @since 1.5
*/
public final class RC2Parameters extends AlgorithmParametersSpi {
// TABLE[EKB] from section 6 of RFC 2268, used to convert effective key
// size to/from encoded version number
private final static int[] EKB_TABLE = new int[] {
0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a,
0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0,
0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b,
0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a,
0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda,
0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36,
0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8,
0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c,
0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17,
0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60,
0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72,
0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa,
0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd,
0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e,
0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b,
0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf,
0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77,
0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6,
0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3,
0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3,
0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e,
0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c,
0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d,
0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2,
0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46,
0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5,
0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97,
0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5,
0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef,
0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f,
0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf,
0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab
};
// the iv
private byte[] iv;
// the version number
private int version = 0;
// the effective key size
private int effectiveKeySize = 0;
public RC2Parameters() {}
protected void engineInit(AlgorithmParameterSpec paramSpec)
throws InvalidParameterSpecException {
if (!(paramSpec instanceof RC2ParameterSpec)) {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
RC2ParameterSpec rps = (RC2ParameterSpec) paramSpec;
// check effective key size (a value of 0 means it is unspecified)
effectiveKeySize = rps.getEffectiveKeyBits();
if (effectiveKeySize != 0) {
if (effectiveKeySize < 1 || effectiveKeySize > 1024) {
throw new InvalidParameterSpecException("RC2 effective key " +
"size must be between 1 and 1024 bits");
}
if (effectiveKeySize < 256) {
version = EKB_TABLE[effectiveKeySize];
} else {
version = effectiveKeySize;
}
}
this.iv = rps.getIV();
}
protected void engineInit(byte[] encoded) throws IOException {
DerValue val = new DerValue(encoded);
// check if IV or params
if (val.tag == DerValue.tag_Sequence) {
val.data.reset();
version = val.data.getInteger();
if (version < 0 || version > 1024) {
throw new IOException("RC2 parameter parsing error: " +
"version number out of legal range (0-1024): " + version);
}
if (version > 255) {
effectiveKeySize = version;
} else {
// search table for index containing version
for (int i = 0; i < EKB_TABLE.length; i++) {
if (version == EKB_TABLE[i]) {
effectiveKeySize = i;
break;
}
}
}
iv = val.data.getOctetString();
} else {
val.data.reset();
iv = val.getOctetString();
version = 0;
effectiveKeySize = 0;
}
if (iv.length != 8) {
throw new IOException("RC2 parameter parsing error: iv length " +
"must be 8 bits, actual: " + iv.length);
}
if (val.data.available() != 0) {
throw new IOException("RC2 parameter parsing error: extra data");
}
}
protected void engineInit(byte[] encoded, String decodingMethod)
throws IOException {
engineInit(encoded);
}
protected <T extends AlgorithmParameterSpec>
T engineGetParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException {
if (RC2ParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast((iv == null ?
new RC2ParameterSpec(effectiveKeySize) :
new RC2ParameterSpec(effectiveKeySize, iv)));
} else {
throw new InvalidParameterSpecException
("Inappropriate parameter specification");
}
}
protected byte[] engineGetEncoded() throws IOException {
DerOutputStream out = new DerOutputStream();
DerOutputStream bytes = new DerOutputStream();
if (this.effectiveKeySize != 0) {
bytes.putInteger(version);
bytes.putOctetString(iv);
out.write(DerValue.tag_Sequence, bytes);
} else {
out.putOctetString(iv);
}
return out.toByteArray();
}
protected byte[] engineGetEncoded(String encodingMethod)
throws IOException {
return engineGetEncoded();
}
/*
* Returns a formatted string describing the parameters.
*/
protected String engineToString() {
String LINE_SEP = System.getProperty("line.separator");
HexDumpEncoder encoder = new HexDumpEncoder();
StringBuilder sb
= new StringBuilder(LINE_SEP + " iv:" + LINE_SEP + "["
+ encoder.encodeBuffer(iv) + "]");
if (version != 0) {
sb.append(LINE_SEP + "version:" + LINE_SEP
+ version + LINE_SEP);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,538 @@
/*
* Copyright (c) 2003, 2023, 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.util.Locale;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.MGF1ParameterSpec;
import javax.crypto.*;
import javax.crypto.spec.PSource;
import javax.crypto.spec.OAEPParameterSpec;
import sun.security.rsa.*;
import sun.security.jca.Providers;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
import sun.security.util.KeyUtil;
/**
* RSA cipher implementation. Supports RSA en/decryption and signing/verifying
* using both PKCS#1 v1.5 and OAEP (v2.2) paddings and without padding (raw RSA).
* Note that raw RSA is supported mostly for completeness and should only be
* used in rare cases.
*
* Objects should be instantiated by calling Cipher.getInstance() using the
* following algorithm names:
* . "RSA/ECB/PKCS1Padding" (or "RSA") for PKCS#1 v1.5 padding.
* . "RSA/ECB/OAEPwith<hash>andMGF1Padding" (or "RSA/ECB/OAEPPadding") for
* PKCS#1 v2.2 padding.
* . "RSA/ECB/NoPadding" for rsa RSA.
*
* We only do one RSA operation per doFinal() call. If the application passes
* more data via calls to update() or doFinal(), we throw an
* IllegalBlockSizeException when doFinal() is called (see JCE API spec).
* Bulk encryption using RSA does not make sense and is not standardized.
*
* Note: RSA keys should be at least 512 bits long
*
* @since 1.5
* @author Andreas Sterbenz
*/
public final class RSACipher extends CipherSpi {
// constant for an empty byte array
private final static byte[] B0 = new byte[0];
// mode constant for public key encryption
private final static int MODE_ENCRYPT = 1;
// mode constant for private key decryption
private final static int MODE_DECRYPT = 2;
// mode constant for private key encryption (signing)
private final static int MODE_SIGN = 3;
// mode constant for public key decryption (verifying)
private final static int MODE_VERIFY = 4;
// constant for raw RSA
private final static String PAD_NONE = "NoPadding";
// constant for PKCS#1 v1.5 RSA
private final static String PAD_PKCS1 = "PKCS1Padding";
// constant for PKCS#2 v2.2 OAEP with MGF1
private final static String PAD_OAEP_MGF1 = "OAEP";
// current mode, one of MODE_* above. Set when init() is called
private int mode;
// active padding type, one of PAD_* above. Set by setPadding()
private String paddingType;
// padding object
private RSAPadding padding;
// cipher parameter for OAEP padding and TLS RSA premaster secret
private AlgorithmParameterSpec spec = null;
private boolean forTlsPremasterSecret = false;
// buffer for the data
private byte[] buffer;
// offset into the buffer (number of bytes buffered)
private int bufOfs;
// size of the output
private int outputSize;
// the public key, if we were initialized using a public key
private RSAPublicKey publicKey;
// the private key, if we were initialized using a private key
private RSAPrivateKey privateKey;
// hash algorithm for OAEP
private String oaepHashAlgorithm = "SHA-1";
// the source of randomness
private SecureRandom random;
public RSACipher() {
paddingType = PAD_PKCS1;
}
// modes do not make sense for RSA, but allow ECB
// see JCE spec
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if (mode.equalsIgnoreCase("ECB") == false) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
}
// set the padding type
// see JCE spec
protected void engineSetPadding(String paddingName)
throws NoSuchPaddingException {
if (paddingName.equalsIgnoreCase(PAD_NONE)) {
paddingType = PAD_NONE;
} else if (paddingName.equalsIgnoreCase(PAD_PKCS1)) {
paddingType = PAD_PKCS1;
} else {
String lowerPadding = paddingName.toLowerCase(Locale.ENGLISH);
if (lowerPadding.equals("oaeppadding")) {
paddingType = PAD_OAEP_MGF1;
} else if (lowerPadding.startsWith("oaepwith") &&
lowerPadding.endsWith("andmgf1padding")) {
paddingType = PAD_OAEP_MGF1;
// "oaepwith".length() == 8
// "andmgf1padding".length() == 14
oaepHashAlgorithm =
paddingName.substring(8, paddingName.length() - 14);
// check if MessageDigest appears to be available
// avoid getInstance() call here
if (Providers.getProviderList().getService
("MessageDigest", oaepHashAlgorithm) == null) {
throw new NoSuchPaddingException
("MessageDigest not available for " + paddingName);
}
} else {
throw new NoSuchPaddingException
("Padding " + paddingName + " not supported");
}
}
}
// return 0 as block size, we are not a block cipher
// see JCE spec
protected int engineGetBlockSize() {
return 0;
}
// return the output size
// see JCE spec
protected int engineGetOutputSize(int inputLen) {
return outputSize;
}
// no iv, return null
// see JCE spec
protected byte[] engineGetIV() {
return null;
}
// see JCE spec
protected AlgorithmParameters engineGetParameters() {
if (spec != null && spec instanceof OAEPParameterSpec) {
try {
AlgorithmParameters params =
AlgorithmParameters.getInstance("OAEP",
SunJCE.getInstance());
params.init(spec);
return params;
} catch (NoSuchAlgorithmException nsae) {
// should never happen
throw new RuntimeException("Cannot find OAEP " +
" AlgorithmParameters implementation in SunJCE provider");
} catch (InvalidParameterSpecException ipse) {
// should never happen
throw new RuntimeException("OAEPParameterSpec not supported");
}
} else {
return null;
}
}
// see JCE spec
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
try {
init(opmode, key, random, null);
} catch (InvalidAlgorithmParameterException iape) {
// never thrown when null parameters are used;
// but re-throw it just in case
InvalidKeyException ike =
new InvalidKeyException("Wrong parameters");
ike.initCause(iape);
throw ike;
}
}
// see JCE spec
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
init(opmode, key, random, params);
}
// see JCE spec
protected void engineInit(int opmode, Key key,
AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params == null) {
init(opmode, key, random, null);
} else {
try {
OAEPParameterSpec spec =
params.getParameterSpec(OAEPParameterSpec.class);
init(opmode, key, random, spec);
} catch (InvalidParameterSpecException ipse) {
InvalidAlgorithmParameterException iape =
new InvalidAlgorithmParameterException("Wrong parameter");
iape.initCause(ipse);
throw iape;
}
}
}
// initialize this cipher
private void init(int opmode, Key key, SecureRandom random,
AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
boolean encrypt;
switch (opmode) {
case Cipher.ENCRYPT_MODE:
case Cipher.WRAP_MODE:
encrypt = true;
break;
case Cipher.DECRYPT_MODE:
case Cipher.UNWRAP_MODE:
encrypt = false;
break;
default:
throw new InvalidKeyException("Unknown mode: " + opmode);
}
RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
if (key instanceof RSAPublicKey) {
mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
publicKey = (RSAPublicKey)key;
privateKey = null;
} else { // must be RSAPrivateKey per check in toRSAKey
mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
privateKey = (RSAPrivateKey)key;
publicKey = null;
}
int n = RSACore.getByteLength(rsaKey.getModulus());
outputSize = n;
bufOfs = 0;
if (paddingType == PAD_NONE) {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
}
padding = RSAPadding.getInstance(RSAPadding.PAD_NONE, n, random);
buffer = new byte[n];
} else if (paddingType == PAD_PKCS1) {
if (params != null) {
if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"Parameters not supported");
}
spec = params;
forTlsPremasterSecret = true;
this.random = random; // for TLS RSA premaster secret
}
int blockType = (mode <= MODE_DECRYPT) ? RSAPadding.PAD_BLOCKTYPE_2
: RSAPadding.PAD_BLOCKTYPE_1;
padding = RSAPadding.getInstance(blockType, n, random);
if (encrypt) {
int k = padding.getMaxDataSize();
buffer = new byte[k];
} else {
buffer = new byte[n];
}
} else { // PAD_OAEP_MGF1
if ((mode == MODE_SIGN) || (mode == MODE_VERIFY)) {
throw new InvalidKeyException
("OAEP cannot be used to sign or verify signatures");
}
if (params != null) {
if (!(params instanceof OAEPParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Wrong Parameters for OAEP Padding");
}
spec = params;
} else {
spec = new OAEPParameterSpec(oaepHashAlgorithm, "MGF1",
MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
}
padding = RSAPadding.getInstance(RSAPadding.PAD_OAEP_MGF1, n,
random, (OAEPParameterSpec)spec);
if (encrypt) {
int k = padding.getMaxDataSize();
buffer = new byte[k];
} else {
buffer = new byte[n];
}
}
}
// internal update method
private void update(byte[] in, int inOfs, int inLen) {
if ((inLen == 0) || (in == null)) {
return;
}
if (inLen > (buffer.length - bufOfs)) {
bufOfs = buffer.length + 1;
return;
}
System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
bufOfs += inLen;
}
// internal doFinal() method. Here we perform the actual RSA operation
private byte[] doFinal() throws BadPaddingException,
IllegalBlockSizeException {
if (bufOfs > buffer.length) {
throw new IllegalBlockSizeException("Data must not be longer "
+ "than " + buffer.length + " bytes");
}
byte[] paddingCopy = null;
byte[] result = null;
try {
switch (mode) {
case MODE_SIGN:
paddingCopy = padding.pad(buffer, 0, bufOfs);
if (paddingCopy != null) {
result = RSACore.rsa(paddingCopy, privateKey, true);
} else {
throw new BadPaddingException("Padding error in signing");
}
break;
case MODE_VERIFY:
byte[] verifyBuffer = RSACore.convert(buffer, 0, bufOfs);
paddingCopy = RSACore.rsa(verifyBuffer, publicKey);
result = padding.unpad(paddingCopy);
if (result == null) {
throw new BadPaddingException
("Padding error in verification");
}
break;
case MODE_ENCRYPT:
paddingCopy = padding.pad(buffer, 0, bufOfs);
if (paddingCopy != null) {
result = RSACore.rsa(paddingCopy, publicKey);
} else {
throw new BadPaddingException
("Padding error in encryption");
}
break;
case MODE_DECRYPT:
byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs);
paddingCopy = RSACore.rsa(decryptBuffer, privateKey, false);
result = padding.unpad(paddingCopy);
if (!forTlsPremasterSecret && result == null) {
throw new BadPaddingException
("Padding error in decryption");
}
break;
default:
throw new AssertionError("Internal error");
}
return result;
} finally {
Arrays.fill(buffer, 0, bufOfs, (byte)0);
bufOfs = 0;
if (paddingCopy != null
&& paddingCopy != buffer // already cleaned
&& paddingCopy != result) { // DO NOT CLEAN, THIS IS RESULT
Arrays.fill(paddingCopy, (byte)0);
}
}
}
// TLS master secret decode version of the doFinal() method.
private byte[] doFinalForTls(int clientVersion, int serverVersion)
throws BadPaddingException, IllegalBlockSizeException {
if (bufOfs > buffer.length) {
throw new IllegalBlockSizeException("Data must not be longer "
+ "than " + buffer.length + " bytes");
}
byte[] paddingCopy = null;
byte[] result = null;
try {
byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs);
paddingCopy = RSACore.rsa(decryptBuffer, privateKey, false);
result = padding.unpadForTls(paddingCopy, clientVersion,
serverVersion);
return result;
} finally {
Arrays.fill(buffer, 0, bufOfs, (byte)0);
bufOfs = 0;
if (paddingCopy != null
&& paddingCopy != buffer // already cleaned
&& paddingCopy != result) { // DO NOT CLEAN, THIS IS RESULT
Arrays.fill(paddingCopy, (byte)0);
}
}
}
// see JCE spec
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
update(in, inOfs, inLen);
return B0;
}
// see JCE spec
protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) {
update(in, inOfs, inLen);
return 0;
}
// see JCE spec
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
throws BadPaddingException, IllegalBlockSizeException {
update(in, inOfs, inLen);
return doFinal();
}
// see JCE spec
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException, BadPaddingException,
IllegalBlockSizeException {
if (outputSize > out.length - outOfs) {
throw new ShortBufferException
("Need " + outputSize + " bytes for output");
}
update(in, inOfs, inLen);
byte[] result = doFinal();
int n = result.length;
System.arraycopy(result, 0, out, outOfs, n);
Arrays.fill(result, (byte)0);
return n;
}
// see JCE spec
protected byte[] engineWrap(Key key) throws InvalidKeyException,
IllegalBlockSizeException {
byte[] encoded = key.getEncoded();
if ((encoded == null) || (encoded.length == 0)) {
throw new InvalidKeyException("Could not obtain encoded key");
}
try {
if (encoded.length > buffer.length) {
throw new InvalidKeyException("Key is too long for wrapping");
}
update(encoded, 0, encoded.length);
try {
return doFinal();
} catch (BadPaddingException e) {
// should not occur
throw new InvalidKeyException("Wrapping failed", e);
}
} finally {
Arrays.fill(encoded, (byte)0);
}
}
// see JCE spec
protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
int type) throws InvalidKeyException, NoSuchAlgorithmException {
if (wrappedKey.length > buffer.length) {
throw new InvalidKeyException("Key is too long for unwrapping");
}
boolean isTlsRsaPremasterSecret =
algorithm.equals("TlsRsaPremasterSecret");
byte[] encoded = null;
update(wrappedKey, 0, wrappedKey.length);
try {
if (isTlsRsaPremasterSecret) {
if (!forTlsPremasterSecret) {
throw new IllegalStateException(
"No TlsRsaPremasterSecretParameterSpec specified");
}
TlsRsaPremasterSecretParameterSpec parameterSpec =
(TlsRsaPremasterSecretParameterSpec) spec;
encoded = doFinalForTls(parameterSpec.getClientVersion(),
parameterSpec.getServerVersion());
} else {
encoded = doFinal();
}
return ConstructKeys.constructKey(encoded, algorithm, type);
} catch (BadPaddingException | IllegalBlockSizeException e) {
// BadPaddingException cannot happen for TLS RSA unwrap.
// Neither padding error nor server version error is indicated
// for TLS, but a fake unwrapped value is returned.
// IllegalBlockSizeException cannot happen in any case,
// because of the length check above.
throw new InvalidKeyException("Unwrapping failed", e);
} finally {
if (encoded != null) {
Arrays.fill(encoded, (byte) 0);
}
}
}
// see JCE spec
protected int engineGetKeySize(Key key) throws InvalidKeyException {
RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
return rsaKey.getModulus().bitLength();
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2018, 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.List;
import java.util.function.BiFunction;
import java.security.*;
/**
* This class holds the various utility methods for range checks.
*/
final class RangeUtil {
private static final BiFunction<String, List<Integer>,
ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER =
Preconditions.outOfBoundsExceptionFormatter
(ArrayIndexOutOfBoundsException::new);
public static void blockSizeCheck(int len, int blockSize) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
}
public static void nullAndBoundsCheck(byte[] array, int offset, int len) {
// NPE is thrown when array is null
Preconditions.checkFromIndexSize(offset, len, array.length, AIOOBE_SUPPLIER);
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (c) 1998, 2018, 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 sun.misc.ObjectInputFilter;
import sun.misc.SharedSecrets;
import java.io.*;
import java.security.*;
import javax.crypto.*;
final class SealedObjectForKeyProtector extends SealedObject {
static final long serialVersionUID = -3650226485480866989L;
/**
* The InputStreamFilter for a Key object inside this SealedObject. It can
* be either provided as a {@link Security} property or a system property
* (when provided as latter, it shadows the former). If the result of this
* filter is {@link sun.misc.ObjectInputFilter.Status.UNDECIDED}, the system
* level filter defined by jdk.serialFilter will be consulted. The value
* of this property uses the same format of jdk.serialFilter.
*/
private static final String KEY_SERIAL_FILTER = "jceks.key.serialFilter";
SealedObjectForKeyProtector(Serializable object, Cipher c)
throws IOException, IllegalBlockSizeException {
super(object, c);
}
SealedObjectForKeyProtector(SealedObject so) {
super(so);
}
AlgorithmParameters getParameters() {
AlgorithmParameters params = null;
if (super.encodedParams != null) {
try {
params = AlgorithmParameters.getInstance("PBE",
SunJCE.getInstance());
params.init(super.encodedParams);
} catch (NoSuchAlgorithmException nsae) {
throw new RuntimeException(
"SunJCE provider is not configured properly");
} catch (IOException io) {
throw new RuntimeException("Parameter failure: "+
io.getMessage());
}
}
return params;
}
final Key getKey(Cipher c, int maxLength)
throws IOException, ClassNotFoundException, IllegalBlockSizeException,
BadPaddingException {
try (ObjectInputStream ois = SharedSecrets.getJavaxCryptoSealedObjectAccess()
.getExtObjectInputStream(this, c)) {
AccessController.doPrivileged(
(PrivilegedAction<Void>) () -> {
ObjectInputFilter.Config.setObjectInputFilter(ois,
new DeserializationChecker(maxLength));
return null;
});
try {
@SuppressWarnings("unchecked")
Key t = (Key) ois.readObject();
return t;
} catch (InvalidClassException ice) {
String msg = ice.getMessage();
if (msg.contains("REJECTED")) {
throw new IOException("Rejected by the"
+ " jceks.key.serialFilter or jdk.serialFilter"
+ " property", ice);
} else {
throw ice;
}
}
}
}
/**
* The filter for the content of a SealedObjectForKeyProtector.
*
* First, the jceks.key.serialFilter will be consulted. If the result
* is UNDECIDED, the system level jdk.serialFilter will be consulted.
*/
private static class DeserializationChecker implements ObjectInputFilter {
private static final ObjectInputFilter OWN_FILTER;
static {
String prop = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> {
String tmp = System.getProperty(KEY_SERIAL_FILTER);
if (tmp != null) {
return tmp;
} else {
return Security.getProperty(KEY_SERIAL_FILTER);
}
});
OWN_FILTER = prop == null
? null
: ObjectInputFilter.Config.createFilter(prop);
}
// Maximum possible length of anything inside
private final int maxLength;
private DeserializationChecker(int maxLength) {
this.maxLength = maxLength;
}
@Override
public ObjectInputFilter.Status checkInput(
ObjectInputFilter.FilterInfo info) {
if (info.arrayLength() > maxLength) {
return Status.REJECTED;
}
if (info.serialClass() == Object.class) {
return Status.UNDECIDED;
}
if (OWN_FILTER != null) {
Status result = OWN_FILTER.checkInput(info);
if (result != Status.UNDECIDED) {
return result;
}
}
ObjectInputFilter defaultFilter =
ObjectInputFilter.Config.getSerialFilter();
if (defaultFilter != null) {
return defaultFilter.checkInput(info);
}
return Status.UNDECIDED;
}
}
}

View File

@@ -0,0 +1,264 @@
/*
* Copyright (c) 2005, 2009, 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.nio.ByteBuffer;
import javax.crypto.MacSpi;
import javax.crypto.SecretKey;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import static com.sun.crypto.provider.TlsPrfGenerator.genPad;
/**
* This file contains the code for the SslMacMD5 and SslMacSHA1 implementations.
* The SSL 3.0 MAC is a variation of the HMAC algorithm.
*
* Note that we don't implement Cloneable as that is not needed for SSL.
*
* @author Andreas Sterbenz
* @since 1.6
*/
final class SslMacCore {
private final MessageDigest md;
private final byte[] pad1, pad2;
private boolean first; // Is this the first data to be processed?
private byte[] secret;
/**
* Standard constructor, creates a new SslMacCore instance instantiating
* a MessageDigest of the specified name.
*/
SslMacCore(String digestAlgorithm, byte[] pad1, byte[] pad2)
throws NoSuchAlgorithmException {
md = MessageDigest.getInstance(digestAlgorithm);
this.pad1 = pad1;
this.pad2 = pad2;
first = true;
}
/**
* Returns the length of the Mac in bytes.
*
* @return the Mac length in bytes.
*/
int getDigestLength() {
return md.getDigestLength();
}
/**
* Initializes the Mac with the given secret key and algorithm parameters.
*
* @param key the secret key.
* @param params the algorithm parameters.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this MAC.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this MAC.
*/
void init(Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("SslMac does not use parameters");
}
if (!(key instanceof SecretKey)) {
throw new InvalidKeyException("Secret key expected");
}
secret = key.getEncoded();
if (secret == null || secret.length == 0) {
throw new InvalidKeyException("Missing key data");
}
reset();
}
/**
* Processes the given byte.
*
* @param input the input byte to be processed.
*/
void update(byte input) {
if (first == true) {
// compute digest for 1st pass; start with inner pad
md.update(secret);
md.update(pad1);
first = false;
}
// add the passed byte to the inner digest
md.update(input);
}
/**
* Processes the first <code>len</code> bytes in <code>input</code>,
* starting at <code>offset</code>.
*
* @param input the input buffer.
* @param offset the offset in <code>input</code> where the input starts.
* @param len the number of bytes to process.
*/
void update(byte input[], int offset, int len) {
if (first == true) {
// compute digest for 1st pass; start with inner pad
md.update(secret);
md.update(pad1);
first = false;
}
// add the selected part of an array of bytes to the inner digest
md.update(input, offset, len);
}
void update(ByteBuffer input) {
if (first == true) {
// compute digest for 1st pass; start with inner pad
md.update(secret);
md.update(pad1);
first = false;
}
md.update(input);
}
/**
* Completes the Mac computation and resets the Mac for further use,
* maintaining the secret key that the Mac was initialized with.
*
* @return the Mac result.
*/
byte[] doFinal() {
if (first == true) {
// compute digest for 1st pass; start with inner pad
md.update(secret);
md.update(pad1);
} else {
first = true;
}
try {
// finish the inner digest
byte[] tmp = md.digest();
// compute digest for 2nd pass; start with outer pad
md.update(secret);
md.update(pad2);
// add result of 1st hash
md.update(tmp);
md.digest(tmp, 0, tmp.length);
return tmp;
} catch (DigestException e) {
// should never occur
throw new ProviderException(e);
}
}
/**
* Resets the Mac for further use, maintaining the secret key that the
* Mac was initialized with.
*/
void reset() {
if (first == false) {
md.reset();
first = true;
}
}
// nested static class for the SslMacMD5 implementation
public static final class SslMacMD5 extends MacSpi {
private final SslMacCore core;
public SslMacMD5() throws NoSuchAlgorithmException {
core = new SslMacCore("MD5", md5Pad1, md5Pad2);
}
protected int engineGetMacLength() {
return core.getDigestLength();
}
protected void engineInit(Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(key, params);
}
protected void engineUpdate(byte input) {
core.update(input);
}
protected void engineUpdate(byte input[], int offset, int len) {
core.update(input, offset, len);
}
protected void engineUpdate(ByteBuffer input) {
core.update(input);
}
protected byte[] engineDoFinal() {
return core.doFinal();
}
protected void engineReset() {
core.reset();
}
static final byte[] md5Pad1 = genPad((byte)0x36, 48);
static final byte[] md5Pad2 = genPad((byte)0x5c, 48);
}
// nested static class for the SslMacMD5 implementation
public static final class SslMacSHA1 extends MacSpi {
private final SslMacCore core;
public SslMacSHA1() throws NoSuchAlgorithmException {
core = new SslMacCore("SHA", shaPad1, shaPad2);
}
protected int engineGetMacLength() {
return core.getDigestLength();
}
protected void engineInit(Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
core.init(key, params);
}
protected void engineUpdate(byte input) {
core.update(input);
}
protected void engineUpdate(byte input[], int offset, int len) {
core.update(input, offset, len);
}
protected void engineUpdate(ByteBuffer input) {
core.update(input);
}
protected byte[] engineDoFinal() {
return core.doFinal();
}
protected void engineReset() {
core.reset();
}
static final byte[] shaPad1 = genPad((byte)0x36, 40);
static final byte[] shaPad2 = genPad((byte)0x5c, 40);
}
}

View File

@@ -0,0 +1,814 @@
/*
* Copyright (c) 1997, 2020, 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.security.AccessController;
import java.security.Provider;
import java.security.SecureRandom;
/**
* The "SunJCE" Cryptographic Service Provider.
*
* @author Jan Luehe
* @author Sharon Liu
*/
/**
* Defines the "SunJCE" provider.
*
* Supported algorithms and their names:
*
* - RSA encryption (PKCS#1 v1.5 and raw)
*
* - DES
*
* - DES-EDE
*
* - AES
*
* - Blowfish
*
* - RC2
*
* - ARCFOUR (RC4 compatible)
*
* - Cipher modes ECB, CBC, CFB, OFB, PCBC, CTR, and CTS for all block ciphers
* and mode GCM for AES cipher
*
* - Cipher padding ISO10126Padding for non-PKCS#5 block ciphers and
* NoPadding and PKCS5Padding for all block ciphers
*
* - Password-based Encryption (PBE)
*
* - Diffie-Hellman Key Agreement
*
* - HMAC-MD5, HMAC-SHA1, HMAC-SHA-224, HMAC-SHA-256, HMAC-SHA-384, HMAC-SHA-512
*
*/
public final class SunJCE extends Provider {
private static final long serialVersionUID = 6812507587804302833L;
private static final String info = "SunJCE Provider " +
"(implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, "
+ "Diffie-Hellman, HMAC)";
private static final String OID_PKCS12_RC4_128 = "1.2.840.113549.1.12.1.1";
private static final String OID_PKCS12_RC4_40 = "1.2.840.113549.1.12.1.2";
private static final String OID_PKCS12_DESede = "1.2.840.113549.1.12.1.3";
private static final String OID_PKCS12_RC2_128 = "1.2.840.113549.1.12.1.5";
private static final String OID_PKCS12_RC2_40 = "1.2.840.113549.1.12.1.6";
private static final String OID_PKCS5_MD5_DES = "1.2.840.113549.1.5.3";
private static final String OID_PKCS5_PBKDF2 = "1.2.840.113549.1.5.12";
private static final String OID_PKCS5_PBES2 = "1.2.840.113549.1.5.13";
private static final String OID_PKCS3 = "1.2.840.113549.1.3.1";
/* Are we debugging? -- for developers */
static final boolean debug = false;
// Instance of this provider, so we don't have to call the provider list
// to find ourselves or run the risk of not being in the list.
private static volatile SunJCE instance = null;
// lazy initialize SecureRandom to avoid potential recursion if Sun
// provider has not been installed yet
private static class SecureRandomHolder {
static final SecureRandom RANDOM = new SecureRandom();
}
static SecureRandom getRandom() { return SecureRandomHolder.RANDOM; }
public SunJCE() {
/* We are the "SunJCE" provider */
super("SunJCE", 1.8d, info);
final String BLOCK_MODES = "ECB|CBC|PCBC|CTR|CTS|CFB|OFB" +
"|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" +
"|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64";
final String BLOCK_MODES128 = BLOCK_MODES +
"|GCM|CFB72|CFB80|CFB88|CFB96|CFB104|CFB112|CFB120|CFB128" +
"|OFB72|OFB80|OFB88|OFB96|OFB104|OFB112|OFB120|OFB128";
final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING";
AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
public Object run() {
/*
* Cipher engines
*/
put("Cipher.RSA", "com.sun.crypto.provider.RSACipher");
put("Cipher.RSA SupportedModes", "ECB");
put("Cipher.RSA SupportedPaddings",
"NOPADDING|PKCS1PADDING|OAEPPADDING"
+ "|OAEPWITHMD5ANDMGF1PADDING"
+ "|OAEPWITHSHA1ANDMGF1PADDING"
+ "|OAEPWITHSHA-1ANDMGF1PADDING"
+ "|OAEPWITHSHA-224ANDMGF1PADDING"
+ "|OAEPWITHSHA-256ANDMGF1PADDING"
+ "|OAEPWITHSHA-384ANDMGF1PADDING"
+ "|OAEPWITHSHA-512ANDMGF1PADDING"
+ "|OAEPWITHSHA-512/224ANDMGF1PADDING"
+ "|OAEPWITHSHA-512/256ANDMGF1PADDING");
put("Cipher.RSA SupportedKeyClasses",
"java.security.interfaces.RSAPublicKey" +
"|java.security.interfaces.RSAPrivateKey");
put("Cipher.DES", "com.sun.crypto.provider.DESCipher");
put("Cipher.DES SupportedModes", BLOCK_MODES);
put("Cipher.DES SupportedPaddings", BLOCK_PADS);
put("Cipher.DES SupportedKeyFormats", "RAW");
put("Cipher.DESede", "com.sun.crypto.provider.DESedeCipher");
put("Alg.Alias.Cipher.TripleDES", "DESede");
put("Cipher.DESede SupportedModes", BLOCK_MODES);
put("Cipher.DESede SupportedPaddings", BLOCK_PADS);
put("Cipher.DESede SupportedKeyFormats", "RAW");
put("Cipher.DESedeWrap",
"com.sun.crypto.provider.DESedeWrapCipher");
put("Cipher.DESedeWrap SupportedModes", "CBC");
put("Cipher.DESedeWrap SupportedPaddings", "NOPADDING");
put("Cipher.DESedeWrap SupportedKeyFormats", "RAW");
// PBES1
put("Cipher.PBEWithMD5AndDES",
"com.sun.crypto.provider.PBEWithMD5AndDESCipher");
put("Alg.Alias.Cipher.OID."+OID_PKCS5_MD5_DES,
"PBEWithMD5AndDES");
put("Alg.Alias.Cipher."+OID_PKCS5_MD5_DES,
"PBEWithMD5AndDES");
put("Cipher.PBEWithMD5AndTripleDES",
"com.sun.crypto.provider.PBEWithMD5AndTripleDESCipher");
put("Cipher.PBEWithSHA1AndDESede",
"com.sun.crypto.provider.PKCS12PBECipherCore$" +
"PBEWithSHA1AndDESede");
put("Alg.Alias.Cipher.OID." + OID_PKCS12_DESede,
"PBEWithSHA1AndDESede");
put("Alg.Alias.Cipher." + OID_PKCS12_DESede,
"PBEWithSHA1AndDESede");
put("Cipher.PBEWithSHA1AndRC2_40",
"com.sun.crypto.provider.PKCS12PBECipherCore$" +
"PBEWithSHA1AndRC2_40");
put("Alg.Alias.Cipher.OID." + OID_PKCS12_RC2_40,
"PBEWithSHA1AndRC2_40");
put("Alg.Alias.Cipher." + OID_PKCS12_RC2_40,
"PBEWithSHA1AndRC2_40");
put("Cipher.PBEWithSHA1AndRC2_128",
"com.sun.crypto.provider.PKCS12PBECipherCore$" +
"PBEWithSHA1AndRC2_128");
put("Alg.Alias.Cipher.OID." + OID_PKCS12_RC2_128,
"PBEWithSHA1AndRC2_128");
put("Alg.Alias.Cipher." + OID_PKCS12_RC2_128,
"PBEWithSHA1AndRC2_128");
put("Cipher.PBEWithSHA1AndRC4_40",
"com.sun.crypto.provider.PKCS12PBECipherCore$" +
"PBEWithSHA1AndRC4_40");
put("Alg.Alias.Cipher.OID." + OID_PKCS12_RC4_40,
"PBEWithSHA1AndRC4_40");
put("Alg.Alias.Cipher." + OID_PKCS12_RC4_40,
"PBEWithSHA1AndRC4_40");
put("Cipher.PBEWithSHA1AndRC4_128",
"com.sun.crypto.provider.PKCS12PBECipherCore$" +
"PBEWithSHA1AndRC4_128");
put("Alg.Alias.Cipher.OID." + OID_PKCS12_RC4_128,
"PBEWithSHA1AndRC4_128");
put("Alg.Alias.Cipher." + OID_PKCS12_RC4_128,
"PBEWithSHA1AndRC4_128");
//PBES2
put("Cipher.PBEWithHmacSHA1AndAES_128",
"com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_128");
put("Cipher.PBEWithHmacSHA224AndAES_128",
"com.sun.crypto.provider.PBES2Core$" +
"HmacSHA224AndAES_128");
put("Cipher.PBEWithHmacSHA256AndAES_128",
"com.sun.crypto.provider.PBES2Core$" +
"HmacSHA256AndAES_128");
put("Cipher.PBEWithHmacSHA384AndAES_128",
"com.sun.crypto.provider.PBES2Core$" +
"HmacSHA384AndAES_128");
put("Cipher.PBEWithHmacSHA512AndAES_128",
"com.sun.crypto.provider.PBES2Core$" +
"HmacSHA512AndAES_128");
put("Cipher.PBEWithHmacSHA1AndAES_256",
"com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_256");
put("Cipher.PBEWithHmacSHA224AndAES_256",
"com.sun.crypto.provider.PBES2Core$" +
"HmacSHA224AndAES_256");
put("Cipher.PBEWithHmacSHA256AndAES_256",
"com.sun.crypto.provider.PBES2Core$" +
"HmacSHA256AndAES_256");
put("Cipher.PBEWithHmacSHA384AndAES_256",
"com.sun.crypto.provider.PBES2Core$" +
"HmacSHA384AndAES_256");
put("Cipher.PBEWithHmacSHA512AndAES_256",
"com.sun.crypto.provider.PBES2Core$" +
"HmacSHA512AndAES_256");
put("Cipher.Blowfish",
"com.sun.crypto.provider.BlowfishCipher");
put("Cipher.Blowfish SupportedModes", BLOCK_MODES);
put("Cipher.Blowfish SupportedPaddings", BLOCK_PADS);
put("Cipher.Blowfish SupportedKeyFormats", "RAW");
put("Cipher.AES", "com.sun.crypto.provider.AESCipher$General");
put("Alg.Alias.Cipher.Rijndael", "AES");
put("Cipher.AES SupportedModes", BLOCK_MODES128);
put("Cipher.AES SupportedPaddings", BLOCK_PADS);
put("Cipher.AES SupportedKeyFormats", "RAW");
put("Cipher.AES_128/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.1", "AES_128/ECB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.1", "AES_128/ECB/NoPadding");
put("Cipher.AES_128/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_CBC_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding");
put("Cipher.AES_128/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_OFB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding");
put("Cipher.AES_128/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding");
put("Cipher.AES_128/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding");
put("Cipher.AES_192/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding");
put("Cipher.AES_192/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_CBC_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding");
put("Cipher.AES_192/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_OFB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding");
put("Cipher.AES_192/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding");
put("Cipher.AES_192/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding");
put("Cipher.AES_256/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding");
put("Cipher.AES_256/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_CBC_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding");
put("Cipher.AES_256/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_OFB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding");
put("Cipher.AES_256/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding");
put("Cipher.AES_256/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding");
put("Cipher.AESWrap", "com.sun.crypto.provider.AESWrapCipher$General");
put("Cipher.AESWrap SupportedModes", "ECB");
put("Cipher.AESWrap SupportedPaddings", "NOPADDING");
put("Cipher.AESWrap SupportedKeyFormats", "RAW");
put("Cipher.AESWrap_128", "com.sun.crypto.provider.AESWrapCipher$AES128");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.5", "AESWrap_128");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.5", "AESWrap_128");
put("Cipher.AESWrap_192", "com.sun.crypto.provider.AESWrapCipher$AES192");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.25", "AESWrap_192");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.25", "AESWrap_192");
put("Cipher.AESWrap_256", "com.sun.crypto.provider.AESWrapCipher$AES256");
put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.45", "AESWrap_256");
put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.45", "AESWrap_256");
put("Cipher.RC2",
"com.sun.crypto.provider.RC2Cipher");
put("Cipher.RC2 SupportedModes", BLOCK_MODES);
put("Cipher.RC2 SupportedPaddings", BLOCK_PADS);
put("Cipher.RC2 SupportedKeyFormats", "RAW");
put("Cipher.ARCFOUR",
"com.sun.crypto.provider.ARCFOURCipher");
put("Alg.Alias.Cipher.RC4", "ARCFOUR");
put("Cipher.ARCFOUR SupportedModes", "ECB");
put("Cipher.ARCFOUR SupportedPaddings", "NOPADDING");
put("Cipher.ARCFOUR SupportedKeyFormats", "RAW");
/*
* Key(pair) Generator engines
*/
put("KeyGenerator.DES",
"com.sun.crypto.provider.DESKeyGenerator");
put("KeyGenerator.DESede",
"com.sun.crypto.provider.DESedeKeyGenerator");
put("Alg.Alias.KeyGenerator.TripleDES", "DESede");
put("KeyGenerator.Blowfish",
"com.sun.crypto.provider.BlowfishKeyGenerator");
put("KeyGenerator.AES",
"com.sun.crypto.provider.AESKeyGenerator");
put("Alg.Alias.KeyGenerator.Rijndael", "AES");
put("KeyGenerator.RC2",
"com.sun.crypto.provider.KeyGeneratorCore$" +
"RC2KeyGenerator");
put("KeyGenerator.ARCFOUR",
"com.sun.crypto.provider.KeyGeneratorCore$" +
"ARCFOURKeyGenerator");
put("Alg.Alias.KeyGenerator.RC4", "ARCFOUR");
put("KeyGenerator.HmacMD5",
"com.sun.crypto.provider.HmacMD5KeyGenerator");
put("KeyGenerator.HmacSHA1",
"com.sun.crypto.provider.HmacSHA1KeyGenerator");
put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.7", "HmacSHA1");
put("Alg.Alias.KeyGenerator.1.2.840.113549.2.7", "HmacSHA1");
put("KeyGenerator.HmacSHA224",
"com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA224");
put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.8", "HmacSHA224");
put("Alg.Alias.KeyGenerator.1.2.840.113549.2.8", "HmacSHA224");
put("KeyGenerator.HmacSHA256",
"com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA256");
put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.9", "HmacSHA256");
put("Alg.Alias.KeyGenerator.1.2.840.113549.2.9", "HmacSHA256");
put("KeyGenerator.HmacSHA384",
"com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA384");
put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.10", "HmacSHA384");
put("Alg.Alias.KeyGenerator.1.2.840.113549.2.10", "HmacSHA384");
put("KeyGenerator.HmacSHA512",
"com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA512");
put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.11", "HmacSHA512");
put("Alg.Alias.KeyGenerator.1.2.840.113549.2.11", "HmacSHA512");
put("KeyPairGenerator.DiffieHellman",
"com.sun.crypto.provider.DHKeyPairGenerator");
put("Alg.Alias.KeyPairGenerator.DH", "DiffieHellman");
put("Alg.Alias.KeyPairGenerator.OID."+OID_PKCS3,
"DiffieHellman");
put("Alg.Alias.KeyPairGenerator."+OID_PKCS3,
"DiffieHellman");
/*
* Algorithm parameter generation engines
*/
put("AlgorithmParameterGenerator.DiffieHellman",
"com.sun.crypto.provider.DHParameterGenerator");
put("Alg.Alias.AlgorithmParameterGenerator.DH",
"DiffieHellman");
put("Alg.Alias.AlgorithmParameterGenerator.OID."+OID_PKCS3,
"DiffieHellman");
put("Alg.Alias.AlgorithmParameterGenerator."+OID_PKCS3,
"DiffieHellman");
/*
* Key Agreement engines
*/
put("KeyAgreement.DiffieHellman",
"com.sun.crypto.provider.DHKeyAgreement");
put("Alg.Alias.KeyAgreement.DH", "DiffieHellman");
put("Alg.Alias.KeyAgreement.OID."+OID_PKCS3, "DiffieHellman");
put("Alg.Alias.KeyAgreement."+OID_PKCS3, "DiffieHellman");
put("KeyAgreement.DiffieHellman SupportedKeyClasses",
"javax.crypto.interfaces.DHPublicKey" +
"|javax.crypto.interfaces.DHPrivateKey");
/*
* Algorithm Parameter engines
*/
put("AlgorithmParameters.DiffieHellman",
"com.sun.crypto.provider.DHParameters");
put("Alg.Alias.AlgorithmParameters.DH", "DiffieHellman");
put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS3,
"DiffieHellman");
put("Alg.Alias.AlgorithmParameters."+OID_PKCS3,
"DiffieHellman");
put("AlgorithmParameters.DES",
"com.sun.crypto.provider.DESParameters");
put("AlgorithmParameters.DESede",
"com.sun.crypto.provider.DESedeParameters");
put("Alg.Alias.AlgorithmParameters.TripleDES", "DESede");
put("AlgorithmParameters.PBE",
"com.sun.crypto.provider.PBEParameters");
put("AlgorithmParameters.PBEWithMD5AndDES",
"com.sun.crypto.provider.PBEParameters");
put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS5_MD5_DES,
"PBEWithMD5AndDES");
put("Alg.Alias.AlgorithmParameters."+OID_PKCS5_MD5_DES,
"PBEWithMD5AndDES");
put("AlgorithmParameters.PBEWithMD5AndTripleDES",
"com.sun.crypto.provider.PBEParameters");
put("AlgorithmParameters.PBEWithSHA1AndDESede",
"com.sun.crypto.provider.PBEParameters");
put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_DESede,
"PBEWithSHA1AndDESede");
put("Alg.Alias.AlgorithmParameters."+OID_PKCS12_DESede,
"PBEWithSHA1AndDESede");
put("AlgorithmParameters.PBEWithSHA1AndRC2_40",
"com.sun.crypto.provider.PBEParameters");
put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_RC2_40,
"PBEWithSHA1AndRC2_40");
put("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC2_40,
"PBEWithSHA1AndRC2_40");
put("AlgorithmParameters.PBEWithSHA1AndRC2_128",
"com.sun.crypto.provider.PBEParameters");
put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_RC2_128,
"PBEWithSHA1AndRC2_128");
put("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC2_128,
"PBEWithSHA1AndRC2_128");
put("AlgorithmParameters.PBEWithSHA1AndRC4_40",
"com.sun.crypto.provider.PBEParameters");
put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_RC4_40,
"PBEWithSHA1AndRC4_40");
put("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC4_40,
"PBEWithSHA1AndRC4_40");
put("AlgorithmParameters.PBEWithSHA1AndRC4_128",
"com.sun.crypto.provider.PBEParameters");
put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_RC4_128,
"PBEWithSHA1AndRC4_128");
put("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC4_128,
"PBEWithSHA1AndRC4_128");
put("AlgorithmParameters.PBES2",
"com.sun.crypto.provider.PBES2Parameters$General");
put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS5_PBES2,
"PBES2");
put("Alg.Alias.AlgorithmParameters." + OID_PKCS5_PBES2,
"PBES2");
put("AlgorithmParameters.PBEWithHmacSHA1AndAES_128",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_128");
put("AlgorithmParameters.PBEWithHmacSHA224AndAES_128",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_128");
put("AlgorithmParameters.PBEWithHmacSHA256AndAES_128",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_128");
put("AlgorithmParameters.PBEWithHmacSHA384AndAES_128",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_128");
put("AlgorithmParameters.PBEWithHmacSHA512AndAES_128",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_128");
put("AlgorithmParameters.PBEWithHmacSHA1AndAES_256",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_256");
put("AlgorithmParameters.PBEWithHmacSHA224AndAES_256",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_256");
put("AlgorithmParameters.PBEWithHmacSHA256AndAES_256",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_256");
put("AlgorithmParameters.PBEWithHmacSHA384AndAES_256",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_256");
put("AlgorithmParameters.PBEWithHmacSHA512AndAES_256",
"com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_256");
put("AlgorithmParameters.Blowfish",
"com.sun.crypto.provider.BlowfishParameters");
put("AlgorithmParameters.AES",
"com.sun.crypto.provider.AESParameters");
put("Alg.Alias.AlgorithmParameters.Rijndael", "AES");
put("AlgorithmParameters.GCM",
"com.sun.crypto.provider.GCMParameters");
put("AlgorithmParameters.RC2",
"com.sun.crypto.provider.RC2Parameters");
put("AlgorithmParameters.OAEP",
"com.sun.crypto.provider.OAEPParameters");
/*
* Key factories
*/
put("KeyFactory.DiffieHellman",
"com.sun.crypto.provider.DHKeyFactory");
put("Alg.Alias.KeyFactory.DH", "DiffieHellman");
put("Alg.Alias.KeyFactory.OID."+OID_PKCS3,
"DiffieHellman");
put("Alg.Alias.KeyFactory."+OID_PKCS3, "DiffieHellman");
/*
* Secret-key factories
*/
put("SecretKeyFactory.DES",
"com.sun.crypto.provider.DESKeyFactory");
put("SecretKeyFactory.DESede",
"com.sun.crypto.provider.DESedeKeyFactory");
put("Alg.Alias.SecretKeyFactory.TripleDES", "DESede");
put("SecretKeyFactory.PBEWithMD5AndDES",
"com.sun.crypto.provider.PBEKeyFactory$PBEWithMD5AndDES"
);
put("Alg.Alias.SecretKeyFactory.OID."+OID_PKCS5_MD5_DES,
"PBEWithMD5AndDES");
put("Alg.Alias.SecretKeyFactory."+OID_PKCS5_MD5_DES,
"PBEWithMD5AndDES");
put("Alg.Alias.SecretKeyFactory.PBE",
"PBEWithMD5AndDES");
/*
* Internal in-house crypto algorithm used for
* the JCEKS keystore type. Since this was developed
* internally, there isn't an OID corresponding to this
* algorithm.
*/
put("SecretKeyFactory.PBEWithMD5AndTripleDES",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithMD5AndTripleDES"
);
put("SecretKeyFactory.PBEWithSHA1AndDESede",
"com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndDESede"
);
put("Alg.Alias.SecretKeyFactory.OID."+OID_PKCS12_DESede,
"PBEWithSHA1AndDESede");
put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_DESede,
"PBEWithSHA1AndDESede");
put("SecretKeyFactory.PBEWithSHA1AndRC2_40",
"com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC2_40"
);
put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC2_40,
"PBEWithSHA1AndRC2_40");
put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC2_40,
"PBEWithSHA1AndRC2_40");
put("SecretKeyFactory.PBEWithSHA1AndRC2_128",
"com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC2_128"
);
put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC2_128,
"PBEWithSHA1AndRC2_128");
put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC2_128,
"PBEWithSHA1AndRC2_128");
put("SecretKeyFactory.PBEWithSHA1AndRC4_40",
"com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC4_40"
);
put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC4_40,
"PBEWithSHA1AndRC4_40");
put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC4_40,
"PBEWithSHA1AndRC4_40");
put("SecretKeyFactory.PBEWithSHA1AndRC4_128",
"com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC4_128"
);
put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC4_128,
"PBEWithSHA1AndRC4_128");
put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC4_128,
"PBEWithSHA1AndRC4_128");
put("SecretKeyFactory.PBEWithHmacSHA1AndAES_128",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA1AndAES_128");
put("SecretKeyFactory.PBEWithHmacSHA224AndAES_128",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA224AndAES_128");
put("SecretKeyFactory.PBEWithHmacSHA256AndAES_128",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA256AndAES_128");
put("SecretKeyFactory.PBEWithHmacSHA384AndAES_128",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA384AndAES_128");
put("SecretKeyFactory.PBEWithHmacSHA512AndAES_128",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA512AndAES_128");
put("SecretKeyFactory.PBEWithHmacSHA1AndAES_256",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA1AndAES_256");
put("SecretKeyFactory.PBEWithHmacSHA224AndAES_256",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA224AndAES_256");
put("SecretKeyFactory.PBEWithHmacSHA256AndAES_256",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA256AndAES_256");
put("SecretKeyFactory.PBEWithHmacSHA384AndAES_256",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA384AndAES_256");
put("SecretKeyFactory.PBEWithHmacSHA512AndAES_256",
"com.sun.crypto.provider.PBEKeyFactory$" +
"PBEWithHmacSHA512AndAES_256");
// PBKDF2
put("SecretKeyFactory.PBKDF2WithHmacSHA1",
"com.sun.crypto.provider.PBKDF2Core$HmacSHA1");
put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS5_PBKDF2,
"PBKDF2WithHmacSHA1");
put("Alg.Alias.SecretKeyFactory." + OID_PKCS5_PBKDF2,
"PBKDF2WithHmacSHA1");
put("SecretKeyFactory.PBKDF2WithHmacSHA224",
"com.sun.crypto.provider.PBKDF2Core$HmacSHA224");
put("SecretKeyFactory.PBKDF2WithHmacSHA256",
"com.sun.crypto.provider.PBKDF2Core$HmacSHA256");
put("SecretKeyFactory.PBKDF2WithHmacSHA384",
"com.sun.crypto.provider.PBKDF2Core$HmacSHA384");
put("SecretKeyFactory.PBKDF2WithHmacSHA512",
"com.sun.crypto.provider.PBKDF2Core$HmacSHA512");
/*
* MAC
*/
put("Mac.HmacMD5", "com.sun.crypto.provider.HmacMD5");
put("Mac.HmacSHA1", "com.sun.crypto.provider.HmacSHA1");
put("Alg.Alias.Mac.OID.1.2.840.113549.2.7", "HmacSHA1");
put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1");
put("Mac.HmacSHA224",
"com.sun.crypto.provider.HmacCore$HmacSHA224");
put("Alg.Alias.Mac.OID.1.2.840.113549.2.8", "HmacSHA224");
put("Alg.Alias.Mac.1.2.840.113549.2.8", "HmacSHA224");
put("Mac.HmacSHA256",
"com.sun.crypto.provider.HmacCore$HmacSHA256");
put("Alg.Alias.Mac.OID.1.2.840.113549.2.9", "HmacSHA256");
put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA256");
put("Mac.HmacSHA384",
"com.sun.crypto.provider.HmacCore$HmacSHA384");
put("Alg.Alias.Mac.OID.1.2.840.113549.2.10", "HmacSHA384");
put("Alg.Alias.Mac.1.2.840.113549.2.10", "HmacSHA384");
put("Mac.HmacSHA512",
"com.sun.crypto.provider.HmacCore$HmacSHA512");
put("Alg.Alias.Mac.OID.1.2.840.113549.2.11", "HmacSHA512");
put("Alg.Alias.Mac.1.2.840.113549.2.11", "HmacSHA512");
put("Mac.HmacPBESHA1",
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA1");
put("Mac.HmacPBESHA224",
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA224");
put("Mac.HmacPBESHA256",
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA256");
put("Mac.HmacPBESHA384",
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA384");
put("Mac.HmacPBESHA512",
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512");
put("Mac.HmacPBESHA512/224",
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512_224");
put("Mac.HmacPBESHA512/256",
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512_256");
// PBMAC1
put("Mac.PBEWithHmacSHA1",
"com.sun.crypto.provider.PBMAC1Core$HmacSHA1");
put("Mac.PBEWithHmacSHA224",
"com.sun.crypto.provider.PBMAC1Core$HmacSHA224");
put("Mac.PBEWithHmacSHA256",
"com.sun.crypto.provider.PBMAC1Core$HmacSHA256");
put("Mac.PBEWithHmacSHA384",
"com.sun.crypto.provider.PBMAC1Core$HmacSHA384");
put("Mac.PBEWithHmacSHA512",
"com.sun.crypto.provider.PBMAC1Core$HmacSHA512");
put("Mac.SslMacMD5",
"com.sun.crypto.provider.SslMacCore$SslMacMD5");
put("Mac.SslMacSHA1",
"com.sun.crypto.provider.SslMacCore$SslMacSHA1");
put("Mac.HmacMD5 SupportedKeyFormats", "RAW");
put("Mac.HmacSHA1 SupportedKeyFormats", "RAW");
put("Mac.HmacSHA224 SupportedKeyFormats", "RAW");
put("Mac.HmacSHA256 SupportedKeyFormats", "RAW");
put("Mac.HmacSHA384 SupportedKeyFormats", "RAW");
put("Mac.HmacSHA512 SupportedKeyFormats", "RAW");
put("Mac.HmacPBESHA1 SupportedKeyFormats", "RAW");
put("Mac.HmacPBESHA224 SupportedKeyFormats", "RAW");
put("Mac.HmacPBESHA256 SupportedKeyFormats", "RAW");
put("Mac.HmacPBESHA384 SupportedKeyFormats", "RAW");
put("Mac.HmacPBESHA512 SupportedKeyFormats", "RAW");
put("Mac.HmacPBESHA512/224 SupportedKeyFormats", "RAW");
put("Mac.HmacPBESHA512/256 SupportedKeyFormats", "RAW");
put("Mac.PBEWithHmacSHA1 SupportedKeyFormatS", "RAW");
put("Mac.PBEWithHmacSHA224 SupportedKeyFormats", "RAW");
put("Mac.PBEWithHmacSHA256 SupportedKeyFormats", "RAW");
put("Mac.PBEWithHmacSHA384 SupportedKeyFormats", "RAW");
put("Mac.PBEWithHmacSHA512 SupportedKeyFormats", "RAW");
put("Mac.SslMacMD5 SupportedKeyFormats", "RAW");
put("Mac.SslMacSHA1 SupportedKeyFormats", "RAW");
/*
* KeyStore
*/
put("KeyStore.JCEKS", "com.sun.crypto.provider.JceKeyStore");
/*
* SSL/TLS mechanisms
*
* These are strictly internal implementations and may
* be changed at any time. These names were chosen
* because PKCS11/SunPKCS11 does not yet have TLS1.2
* mechanisms, and it will cause calls to come here.
*/
put("KeyGenerator.SunTlsPrf",
"com.sun.crypto.provider.TlsPrfGenerator$V10");
put("KeyGenerator.SunTls12Prf",
"com.sun.crypto.provider.TlsPrfGenerator$V12");
put("KeyGenerator.SunTlsMasterSecret",
"com.sun.crypto.provider.TlsMasterSecretGenerator");
put("Alg.Alias.KeyGenerator.SunTls12MasterSecret",
"SunTlsMasterSecret");
put("Alg.Alias.KeyGenerator.SunTlsExtendedMasterSecret",
"SunTlsMasterSecret");
put("KeyGenerator.SunTlsKeyMaterial",
"com.sun.crypto.provider.TlsKeyMaterialGenerator");
put("Alg.Alias.KeyGenerator.SunTls12KeyMaterial",
"SunTlsKeyMaterial");
put("KeyGenerator.SunTlsRsaPremasterSecret",
"com.sun.crypto.provider.TlsRsaPremasterSecretGenerator");
put("Alg.Alias.KeyGenerator.SunTls12RsaPremasterSecret",
"SunTlsRsaPremasterSecret");
return null;
}
});
if (instance == null) {
instance = this;
}
}
// Return the instance of this class or create one if needed.
static SunJCE getInstance() {
if (instance == null) {
return new SunJCE();
}
return instance;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (c) 1997, 2007, 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.security.InvalidKeyException;
/**
* This abstract class represents the core of all block ciphers. It allows to
* intialize the cipher and encrypt/decrypt single blocks. Larger quantities
* are handled by modes, which are subclasses of FeedbackCipher.
*
* @author Gigi Ankeny
* @author Jan Luehe
*
*
* @see AESCrypt
* @see DESCrypt
* @see DESedeCrypt
* @see BlowfishCrypt
* @see FeedbackCipher
*/
abstract class SymmetricCipher {
SymmetricCipher() {
// empty
}
/**
* Retrieves this cipher's block size.
*
* @return the block size of this cipher
*/
abstract int getBlockSize();
/**
* Initializes the cipher in the specified mode with the given key.
*
* @param decrypting flag indicating encryption or decryption
* @param algorithm the algorithm name
* @param key the key
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
*/
abstract void init(boolean decrypting, String algorithm, byte[] key)
throws InvalidKeyException;
/**
* Encrypt one cipher block.
*
* <p>The input <code>plain</code>, starting at <code>plainOffset</code>
* and ending at <code>(plainOffset+blockSize-1)</code>, is encrypted.
* The result is stored in <code>cipher</code>, starting at
* <code>cipherOffset</code>.
*
* @param plain the input buffer with the data to be encrypted
* @param plainOffset the offset in <code>plain</code>
* @param cipher the buffer for the encryption result
* @param cipherOffset the offset in <code>cipher</code>
*/
abstract void encryptBlock(byte[] plain, int plainOffset,
byte[] cipher, int cipherOffset);
/**
* Decrypt one cipher block.
*
* <p>The input <code>cipher</code>, starting at <code>cipherOffset</code>
* and ending at <code>(cipherOffset+blockSize-1)</code>, is decrypted.
* The result is stored in <code>plain</code>, starting at
* <code>plainOffset</code>.
*
* @param cipher the input buffer with the data to be decrypted
* @param cipherOffset the offset in <code>cipher</code>
* @param plain the buffer for the decryption result
* @param plainOffset the offset in <code>plain</code>
*/
abstract void decryptBlock(byte[] cipher, int cipherOffset,
byte[] plain, int plainOffset);
}

View File

@@ -0,0 +1,278 @@
/*
* Copyright (c) 2005, 2013, 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.security.*;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.*;
import javax.crypto.spec.*;
import sun.security.internal.spec.*;
import static com.sun.crypto.provider.TlsPrfGenerator.*;
/**
* KeyGenerator implementation for the SSL/TLS master secret derivation.
*
* @author Andreas Sterbenz
* @since 1.6
*/
public final class TlsKeyMaterialGenerator extends KeyGeneratorSpi {
private final static String MSG = "TlsKeyMaterialGenerator must be "
+ "initialized using a TlsKeyMaterialParameterSpec";
private TlsKeyMaterialParameterSpec spec;
private int protocolVersion;
public TlsKeyMaterialGenerator() {
}
protected void engineInit(SecureRandom random) {
throw new InvalidParameterException(MSG);
}
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (params instanceof TlsKeyMaterialParameterSpec == false) {
throw new InvalidAlgorithmParameterException(MSG);
}
this.spec = (TlsKeyMaterialParameterSpec)params;
if ("RAW".equals(spec.getMasterSecret().getFormat()) == false) {
throw new InvalidAlgorithmParameterException(
"Key format must be RAW");
}
protocolVersion = (spec.getMajorVersion() << 8)
| spec.getMinorVersion();
if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) {
throw new InvalidAlgorithmParameterException(
"Only SSL 3.0, TLS 1.0/1.1/1.2 supported");
}
}
protected void engineInit(int keysize, SecureRandom random) {
throw new InvalidParameterException(MSG);
}
protected SecretKey engineGenerateKey() {
if (spec == null) {
throw new IllegalStateException(
"TlsKeyMaterialGenerator must be initialized");
}
try {
return engineGenerateKey0();
} catch (GeneralSecurityException e) {
throw new ProviderException(e);
}
}
private SecretKey engineGenerateKey0() throws GeneralSecurityException {
byte[] masterSecret = spec.getMasterSecret().getEncoded();
byte[] clientRandom = spec.getClientRandom();
byte[] serverRandom = spec.getServerRandom();
SecretKey clientMacKey = null;
SecretKey serverMacKey = null;
SecretKey clientCipherKey = null;
SecretKey serverCipherKey = null;
IvParameterSpec clientIv = null;
IvParameterSpec serverIv = null;
int macLength = spec.getMacKeyLength();
int expandedKeyLength = spec.getExpandedCipherKeyLength();
boolean isExportable = (expandedKeyLength != 0);
int keyLength = spec.getCipherKeyLength();
int ivLength = spec.getIvLength();
int keyBlockLen = macLength + keyLength
+ (isExportable ? 0 : ivLength);
keyBlockLen <<= 1;
byte[] keyBlock = new byte[keyBlockLen];
// These may be used again later for exportable suite calculations.
MessageDigest md5 = null;
MessageDigest sha = null;
// generate key block
if (protocolVersion >= 0x0303) {
// TLS 1.2
byte[] seed = concat(serverRandom, clientRandom);
keyBlock = doTLS12PRF(masterSecret, LABEL_KEY_EXPANSION, seed,
keyBlockLen, spec.getPRFHashAlg(),
spec.getPRFHashLength(), spec.getPRFBlockSize());
} else if (protocolVersion >= 0x0301) {
// TLS 1.0/1.1
md5 = MessageDigest.getInstance("MD5");
sha = MessageDigest.getInstance("SHA1");
byte[] seed = concat(serverRandom, clientRandom);
keyBlock = doTLS10PRF(masterSecret, LABEL_KEY_EXPANSION, seed,
keyBlockLen, md5, sha);
} else {
// SSL
md5 = MessageDigest.getInstance("MD5");
sha = MessageDigest.getInstance("SHA1");
keyBlock = new byte[keyBlockLen];
byte[] tmp = new byte[20];
for (int i = 0, remaining = keyBlockLen;
remaining > 0;
i++, remaining -= 16) {
sha.update(SSL3_CONST[i]);
sha.update(masterSecret);
sha.update(serverRandom);
sha.update(clientRandom);
sha.digest(tmp, 0, 20);
md5.update(masterSecret);
md5.update(tmp);
if (remaining >= 16) {
md5.digest(keyBlock, i << 4, 16);
} else {
md5.digest(tmp, 0, 16);
System.arraycopy(tmp, 0, keyBlock, i << 4, remaining);
}
}
}
// partition keyblock into individual secrets
int ofs = 0;
if (macLength != 0) {
byte[] tmp = new byte[macLength];
// mac keys
System.arraycopy(keyBlock, ofs, tmp, 0, macLength);
ofs += macLength;
clientMacKey = new SecretKeySpec(tmp, "Mac");
System.arraycopy(keyBlock, ofs, tmp, 0, macLength);
ofs += macLength;
serverMacKey = new SecretKeySpec(tmp, "Mac");
}
if (keyLength == 0) { // SSL_RSA_WITH_NULL_* ciphersuites
return new TlsKeyMaterialSpec(clientMacKey, serverMacKey);
}
String alg = spec.getCipherAlgorithm();
// cipher keys
byte[] clientKeyBytes = new byte[keyLength];
System.arraycopy(keyBlock, ofs, clientKeyBytes, 0, keyLength);
ofs += keyLength;
byte[] serverKeyBytes = new byte[keyLength];
System.arraycopy(keyBlock, ofs, serverKeyBytes, 0, keyLength);
ofs += keyLength;
if (isExportable == false) {
// cipher keys
clientCipherKey = new SecretKeySpec(clientKeyBytes, alg);
serverCipherKey = new SecretKeySpec(serverKeyBytes, alg);
// IV keys if needed.
if (ivLength != 0) {
byte[] tmp = new byte[ivLength];
System.arraycopy(keyBlock, ofs, tmp, 0, ivLength);
ofs += ivLength;
clientIv = new IvParameterSpec(tmp);
System.arraycopy(keyBlock, ofs, tmp, 0, ivLength);
ofs += ivLength;
serverIv = new IvParameterSpec(tmp);
}
} else {
// if exportable suites, calculate the alternate
// cipher key expansion and IV generation
if (protocolVersion >= 0x0302) {
// TLS 1.1+
throw new RuntimeException(
"Internal Error: TLS 1.1+ should not be negotiating" +
"exportable ciphersuites");
} else if (protocolVersion == 0x0301) {
// TLS 1.0
byte[] seed = concat(clientRandom, serverRandom);
byte[] tmp = doTLS10PRF(clientKeyBytes,
LABEL_CLIENT_WRITE_KEY, seed, expandedKeyLength, md5, sha);
clientCipherKey = new SecretKeySpec(tmp, alg);
tmp = doTLS10PRF(serverKeyBytes, LABEL_SERVER_WRITE_KEY, seed,
expandedKeyLength, md5, sha);
serverCipherKey = new SecretKeySpec(tmp, alg);
if (ivLength != 0) {
tmp = new byte[ivLength];
byte[] block = doTLS10PRF(null, LABEL_IV_BLOCK, seed,
ivLength << 1, md5, sha);
System.arraycopy(block, 0, tmp, 0, ivLength);
clientIv = new IvParameterSpec(tmp);
System.arraycopy(block, ivLength, tmp, 0, ivLength);
serverIv = new IvParameterSpec(tmp);
}
} else {
// SSLv3
byte[] tmp = new byte[expandedKeyLength];
md5.update(clientKeyBytes);
md5.update(clientRandom);
md5.update(serverRandom);
System.arraycopy(md5.digest(), 0, tmp, 0, expandedKeyLength);
clientCipherKey = new SecretKeySpec(tmp, alg);
md5.update(serverKeyBytes);
md5.update(serverRandom);
md5.update(clientRandom);
System.arraycopy(md5.digest(), 0, tmp, 0, expandedKeyLength);
serverCipherKey = new SecretKeySpec(tmp, alg);
if (ivLength != 0) {
tmp = new byte[ivLength];
md5.update(clientRandom);
md5.update(serverRandom);
System.arraycopy(md5.digest(), 0, tmp, 0, ivLength);
clientIv = new IvParameterSpec(tmp);
md5.update(serverRandom);
md5.update(clientRandom);
System.arraycopy(md5.digest(), 0, tmp, 0, ivLength);
serverIv = new IvParameterSpec(tmp);
}
}
}
return new TlsKeyMaterialSpec(clientMacKey, serverMacKey,
clientCipherKey, clientIv, serverCipherKey, serverIv);
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright (c) 2005, 2023, 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.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.*;
import sun.security.internal.interfaces.TlsMasterSecret;
import sun.security.internal.spec.TlsMasterSecretParameterSpec;
import static com.sun.crypto.provider.TlsPrfGenerator.*;
/**
* KeyGenerator implementation for the SSL/TLS master secret derivation.
*
* @author Andreas Sterbenz
* @since 1.6
*/
public final class TlsMasterSecretGenerator extends KeyGeneratorSpi {
private final static String MSG = "TlsMasterSecretGenerator must be "
+ "initialized using a TlsMasterSecretParameterSpec";
private TlsMasterSecretParameterSpec spec;
private int protocolVersion;
public TlsMasterSecretGenerator() {
}
protected void engineInit(SecureRandom random) {
throw new InvalidParameterException(MSG);
}
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (!(params instanceof TlsMasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(MSG);
}
this.spec = (TlsMasterSecretParameterSpec)params;
if (!"RAW".equals(spec.getPremasterSecret().getFormat())) {
throw new InvalidAlgorithmParameterException(
"Key format must be RAW");
}
protocolVersion = (spec.getMajorVersion() << 8)
| spec.getMinorVersion();
if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) {
throw new InvalidAlgorithmParameterException(
"Only SSL 3.0, TLS 1.0/1.1/1.2 supported");
}
}
protected void engineInit(int keysize, SecureRandom random) {
throw new InvalidParameterException(MSG);
}
protected SecretKey engineGenerateKey() {
if (spec == null) {
throw new IllegalStateException(
"TlsMasterSecretGenerator must be initialized");
}
SecretKey premasterKey = spec.getPremasterSecret();
byte[] premaster = premasterKey.getEncoded();
int premasterMajor, premasterMinor;
if (premasterKey.getAlgorithm().equals("TlsRsaPremasterSecret")) {
// RSA
premasterMajor = premaster[0] & 0xff;
premasterMinor = premaster[1] & 0xff;
} else {
// DH, KRB5, others
premasterMajor = -1;
premasterMinor = -1;
}
try {
byte[] master;
if (protocolVersion >= 0x0301) {
byte[] label;
byte[] seed;
byte[] extendedMasterSecretSessionHash =
spec.getExtendedMasterSecretSessionHash();
if (extendedMasterSecretSessionHash.length != 0) {
label = LABEL_EXTENDED_MASTER_SECRET;
seed = extendedMasterSecretSessionHash;
} else {
byte[] clientRandom = spec.getClientRandom();
byte[] serverRandom = spec.getServerRandom();
label = LABEL_MASTER_SECRET;
seed = concat(clientRandom, serverRandom);
}
master = ((protocolVersion >= 0x0303) ?
doTLS12PRF(premaster, label, seed, 48,
spec.getPRFHashAlg(), spec.getPRFHashLength(),
spec.getPRFBlockSize()) :
doTLS10PRF(premaster, label, seed, 48));
} else {
master = new byte[48];
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest sha = MessageDigest.getInstance("SHA");
byte[] clientRandom = spec.getClientRandom();
byte[] serverRandom = spec.getServerRandom();
byte[] tmp = new byte[20];
for (int i = 0; i < 3; i++) {
sha.update(SSL3_CONST[i]);
sha.update(premaster);
sha.update(clientRandom);
sha.update(serverRandom);
sha.digest(tmp, 0, 20);
md5.update(premaster);
md5.update(tmp);
md5.digest(master, i << 4, 16);
}
}
return new TlsMasterSecretKey(master, premasterMajor,
premasterMinor);
} catch (NoSuchAlgorithmException e) {
throw new ProviderException(e);
} catch (DigestException e) {
throw new ProviderException(e);
}
}
private static final class TlsMasterSecretKey implements TlsMasterSecret {
private static final long serialVersionUID = 1019571680375368880L;
private byte[] key;
private final int majorVersion, minorVersion;
TlsMasterSecretKey(byte[] key, int majorVersion, int minorVersion) {
this.key = key;
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
public int getMajorVersion() {
return majorVersion;
}
public int getMinorVersion() {
return minorVersion;
}
public String getAlgorithm() {
return "TlsMasterSecret";
}
public String getFormat() {
return "RAW";
}
public byte[] getEncoded() {
return key.clone();
}
/**
* Restores the state of this object from the stream.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
if ((key == null) || (key.length == 0)) {
throw new InvalidObjectException("TlsMasterSecretKey is null");
}
key = key.clone();
}
}
}

View File

@@ -0,0 +1,388 @@
/*
* Copyright (c) 2005, 2017, 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.AlgorithmParameterSpec;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import sun.security.internal.spec.TlsPrfParameterSpec;
/**
* KeyGenerator implementation for the TLS PRF function.
* <p>
* This class duplicates the HMAC functionality (RFC 2104) with
* performance optimizations (e.g. XOR'ing keys with padding doesn't
* need to be redone for each HMAC operation).
*
* @author Andreas Sterbenz
* @since 1.6
*/
abstract class TlsPrfGenerator extends KeyGeneratorSpi {
// magic constants and utility functions, also used by other files
// in this package
private final static byte[] B0 = new byte[0];
final static byte[] LABEL_MASTER_SECRET = // "master secret"
{ 109, 97, 115, 116, 101, 114, 32, 115, 101, 99, 114, 101, 116 };
final static byte[] LABEL_EXTENDED_MASTER_SECRET =
// "extended master secret"
{ 101, 120, 116, 101, 110, 100, 101, 100, 32, 109, 97, 115, 116,
101, 114, 32, 115, 101, 99, 114, 101, 116 };
final static byte[] LABEL_KEY_EXPANSION = // "key expansion"
{ 107, 101, 121, 32, 101, 120, 112, 97, 110, 115, 105, 111, 110 };
final static byte[] LABEL_CLIENT_WRITE_KEY = // "client write key"
{ 99, 108, 105, 101, 110, 116, 32, 119, 114, 105, 116, 101, 32,
107, 101, 121 };
final static byte[] LABEL_SERVER_WRITE_KEY = // "server write key"
{ 115, 101, 114, 118, 101, 114, 32, 119, 114, 105, 116, 101, 32,
107, 101, 121 };
final static byte[] LABEL_IV_BLOCK = // "IV block"
{ 73, 86, 32, 98, 108, 111, 99, 107 };
/*
* TLS HMAC "inner" and "outer" padding. This isn't a function
* of the digest algorithm.
*/
private static final byte[] HMAC_ipad64 = genPad((byte)0x36, 64);
private static final byte[] HMAC_ipad128 = genPad((byte)0x36, 128);
private static final byte[] HMAC_opad64 = genPad((byte)0x5c, 64);
private static final byte[] HMAC_opad128 = genPad((byte)0x5c, 128);
// SSL3 magic mix constants ("A", "BB", "CCC", ...)
final static byte[][] SSL3_CONST = genConst();
static byte[] genPad(byte b, int count) {
byte[] padding = new byte[count];
Arrays.fill(padding, b);
return padding;
}
static byte[] concat(byte[] b1, byte[] b2) {
int n1 = b1.length;
int n2 = b2.length;
byte[] b = new byte[n1 + n2];
System.arraycopy(b1, 0, b, 0, n1);
System.arraycopy(b2, 0, b, n1, n2);
return b;
}
private static byte[][] genConst() {
int n = 10;
byte[][] arr = new byte[n][];
for (int i = 0; i < n; i++) {
byte[] b = new byte[i + 1];
Arrays.fill(b, (byte)('A' + i));
arr[i] = b;
}
return arr;
}
// PRF implementation
private final static String MSG = "TlsPrfGenerator must be "
+ "initialized using a TlsPrfParameterSpec";
private TlsPrfParameterSpec spec;
public TlsPrfGenerator() {
}
protected void engineInit(SecureRandom random) {
throw new InvalidParameterException(MSG);
}
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (params instanceof TlsPrfParameterSpec == false) {
throw new InvalidAlgorithmParameterException(MSG);
}
this.spec = (TlsPrfParameterSpec)params;
SecretKey key = spec.getSecret();
if ((key != null) && ("RAW".equals(key.getFormat()) == false)) {
throw new InvalidAlgorithmParameterException(
"Key encoding format must be RAW");
}
}
protected void engineInit(int keysize, SecureRandom random) {
throw new InvalidParameterException(MSG);
}
SecretKey engineGenerateKey0(boolean tls12) {
if (spec == null) {
throw new IllegalStateException(
"TlsPrfGenerator must be initialized");
}
SecretKey key = spec.getSecret();
byte[] secret = (key == null) ? null : key.getEncoded();
try {
byte[] labelBytes = spec.getLabel().getBytes("UTF8");
int n = spec.getOutputLength();
byte[] prfBytes = (tls12 ?
doTLS12PRF(secret, labelBytes, spec.getSeed(), n,
spec.getPRFHashAlg(), spec.getPRFHashLength(),
spec.getPRFBlockSize()) :
doTLS10PRF(secret, labelBytes, spec.getSeed(), n));
return new SecretKeySpec(prfBytes, "TlsPrf");
} catch (GeneralSecurityException e) {
throw new ProviderException("Could not generate PRF", e);
} catch (java.io.UnsupportedEncodingException e) {
throw new ProviderException("Could not generate PRF", e);
}
}
static byte[] doTLS12PRF(byte[] secret, byte[] labelBytes,
byte[] seed, int outputLength,
String prfHash, int prfHashLength, int prfBlockSize)
throws NoSuchAlgorithmException, DigestException {
if (prfHash == null) {
throw new NoSuchAlgorithmException("Unspecified PRF algorithm");
}
MessageDigest prfMD = MessageDigest.getInstance(prfHash);
return doTLS12PRF(secret, labelBytes, seed, outputLength,
prfMD, prfHashLength, prfBlockSize);
}
static byte[] doTLS12PRF(byte[] secret, byte[] labelBytes,
byte[] seed, int outputLength,
MessageDigest mdPRF, int mdPRFLen, int mdPRFBlockSize)
throws DigestException {
if (secret == null) {
secret = B0;
}
// If we have a long secret, digest it first.
if (secret.length > mdPRFBlockSize) {
secret = mdPRF.digest(secret);
}
byte[] output = new byte[outputLength];
byte [] ipad;
byte [] opad;
switch (mdPRFBlockSize) {
case 64:
ipad = HMAC_ipad64.clone();
opad = HMAC_opad64.clone();
break;
case 128:
ipad = HMAC_ipad128.clone();
opad = HMAC_opad128.clone();
break;
default:
throw new DigestException("Unexpected block size.");
}
// P_HASH(Secret, label + seed)
expand(mdPRF, mdPRFLen, secret, 0, secret.length, labelBytes,
seed, output, ipad, opad);
return output;
}
static byte[] doTLS10PRF(byte[] secret, byte[] labelBytes,
byte[] seed, int outputLength) throws NoSuchAlgorithmException,
DigestException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest sha = MessageDigest.getInstance("SHA1");
return doTLS10PRF(secret, labelBytes, seed, outputLength, md5, sha);
}
static byte[] doTLS10PRF(byte[] secret, byte[] labelBytes,
byte[] seed, int outputLength, MessageDigest md5,
MessageDigest sha) throws DigestException {
/*
* Split the secret into two halves S1 and S2 of same length.
* S1 is taken from the first half of the secret, S2 from the
* second half.
* Their length is created by rounding up the length of the
* overall secret divided by two; thus, if the original secret
* is an odd number of bytes long, the last byte of S1 will be
* the same as the first byte of S2.
*
* Note: Instead of creating S1 and S2, we determine the offset into
* the overall secret where S2 starts.
*/
if (secret == null) {
secret = B0;
}
int off = secret.length >> 1;
int seclen = off + (secret.length & 1);
byte[] secKey = secret;
int keyLen = seclen;
byte[] output = new byte[outputLength];
// P_MD5(S1, label + seed)
// If we have a long secret, digest it first.
if (seclen > 64) { // 64: block size of HMAC-MD5
md5.update(secret, 0, seclen);
secKey = md5.digest();
keyLen = secKey.length;
}
expand(md5, 16, secKey, 0, keyLen, labelBytes, seed, output,
HMAC_ipad64.clone(), HMAC_opad64.clone());
// P_SHA-1(S2, label + seed)
// If we have a long secret, digest it first.
if (seclen > 64) { // 64: block size of HMAC-SHA1
sha.update(secret, off, seclen);
secKey = sha.digest();
keyLen = secKey.length;
off = 0;
}
expand(sha, 20, secKey, off, keyLen, labelBytes, seed, output,
HMAC_ipad64.clone(), HMAC_opad64.clone());
return output;
}
/*
* @param digest the MessageDigest to produce the HMAC
* @param hmacSize the HMAC size
* @param secret the secret
* @param secOff the offset into the secret
* @param secLen the secret length
* @param label the label
* @param seed the seed
* @param output the output array
*/
private static void expand(MessageDigest digest, int hmacSize,
byte[] secret, int secOff, int secLen, byte[] label, byte[] seed,
byte[] output, byte[] pad1, byte[] pad2) throws DigestException {
/*
* modify the padding used, by XORing the key into our copy of that
* padding. That's to avoid doing that for each HMAC computation.
*/
for (int i = 0; i < secLen; i++) {
pad1[i] ^= secret[i + secOff];
pad2[i] ^= secret[i + secOff];
}
byte[] tmp = new byte[hmacSize];
byte[] aBytes = null;
/*
* compute:
*
* P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
* HMAC_hash(secret, A(2) + seed) +
* HMAC_hash(secret, A(3) + seed) + ...
* A() is defined as:
*
* A(0) = seed
* A(i) = HMAC_hash(secret, A(i-1))
*/
int remaining = output.length;
int ofs = 0;
while (remaining > 0) {
/*
* compute A() ...
*/
// inner digest
digest.update(pad1);
if (aBytes == null) {
digest.update(label);
digest.update(seed);
} else {
digest.update(aBytes);
}
digest.digest(tmp, 0, hmacSize);
// outer digest
digest.update(pad2);
digest.update(tmp);
if (aBytes == null) {
aBytes = new byte[hmacSize];
}
digest.digest(aBytes, 0, hmacSize);
/*
* compute HMAC_hash() ...
*/
// inner digest
digest.update(pad1);
digest.update(aBytes);
digest.update(label);
digest.update(seed);
digest.digest(tmp, 0, hmacSize);
// outer digest
digest.update(pad2);
digest.update(tmp);
digest.digest(tmp, 0, hmacSize);
int k = Math.min(hmacSize, remaining);
for (int i = 0; i < k; i++) {
output[ofs++] ^= tmp[i];
}
remaining -= k;
}
}
/**
* A KeyGenerator implementation that supports TLS 1.2.
* <p>
* TLS 1.2 uses a different hash algorithm than 1.0/1.1 for the PRF
* calculations. As of 2010, there is no PKCS11-level support for TLS
* 1.2 PRF calculations, and no known OS's have an internal variant
* we could use. Therefore for TLS 1.2, we are updating JSSE to request
* a different provider algorithm: "SunTls12Prf". If we reused the
* name "SunTlsPrf", the PKCS11 provider would need be updated to
* fail correctly when presented with the wrong version number
* (via Provider.Service.supportsParameters()), and add the
* appropriate supportsParamters() checks into KeyGenerators (not
* currently there).
*/
static public class V12 extends TlsPrfGenerator {
protected SecretKey engineGenerateKey() {
return engineGenerateKey0(true);
}
}
/**
* A KeyGenerator implementation that supports TLS 1.0/1.1.
*/
static public class V10 extends TlsPrfGenerator {
protected SecretKey engineGenerateKey() {
return engineGenerateKey0(false);
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2005, 2014, 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.security.*;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
/**
* KeyGenerator implementation for the SSL/TLS RSA premaster secret.
*
* @author Andreas Sterbenz
* @since 1.6
*/
public final class TlsRsaPremasterSecretGenerator extends KeyGeneratorSpi {
private final static String MSG = "TlsRsaPremasterSecretGenerator must be "
+ "initialized using a TlsRsaPremasterSecretParameterSpec";
private TlsRsaPremasterSecretParameterSpec spec;
private SecureRandom random;
public TlsRsaPremasterSecretGenerator() {
}
protected void engineInit(SecureRandom random) {
throw new InvalidParameterException(MSG);
}
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(MSG);
}
this.spec = (TlsRsaPremasterSecretParameterSpec)params;
this.random = random;
}
protected void engineInit(int keysize, SecureRandom random) {
throw new InvalidParameterException(MSG);
}
// Only can be used in client side to generate TLS RSA premaster secret.
protected SecretKey engineGenerateKey() {
if (spec == null) {
throw new IllegalStateException(
"TlsRsaPremasterSecretGenerator must be initialized");
}
byte[] b = spec.getEncodedSecret();
if (b == null) {
if (random == null) {
random = new SecureRandom();
}
b = new byte[48];
random.nextBytes(b);
}
b[0] = (byte)spec.getMajorVersion();
b[1] = (byte)spec.getMinorVersion();
return new SecretKeySpec(b, "TlsRsaPremasterSecret");
}
}