8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,645 @@
/*
* Copyright (c) 2002, 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.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*8 + " bits");
}
}
}
/*
* 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 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 = 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,697 @@
/*
* Copyright (c) 2002, 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.
*/
/* $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;
import java.util.Arrays;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
* 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) {
Objects.checkFromIndexSize(inOffset, AES_BLOCK_SIZE, in.length);
Objects.checkFromIndexSize(outOffset, AES_BLOCK_SIZE, out.length);
implEncryptBlock(in, inOffset, out, outOffset);
}
// Encryption operation. Possibly replaced with a compiler intrinsic.
@HotSpotIntrinsicCandidate
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) {
Objects.checkFromIndexSize(inOffset, AES_BLOCK_SIZE, in.length);
Objects.checkFromIndexSize(outOffset, AES_BLOCK_SIZE, out.length);
implDecryptBlock(in, inOffset, out, outOffset);
}
// Decrypt operation. Possibly replaced with a compiler intrinsic.
@HotSpotIntrinsicCandidate
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, 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.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 = 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 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[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, 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.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 encodedKey.length << 3;
}
}

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.security.util.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, 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 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 (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,240 @@
/*
* 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.ProviderException;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
* 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;
}
cryptBlockSizeCheck(plainLen);
cryptNullAndBoundsCheck(plain, plainOffset, plainLen);
cryptNullAndBoundsCheck(cipher, cipherOffset, plainLen);
return implEncrypt(plain, plainOffset, plainLen,
cipher, cipherOffset);
}
@HotSpotIntrinsicCandidate
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;
}
cryptBlockSizeCheck(cipherLen);
cryptNullAndBoundsCheck(cipher, cipherOffset, cipherLen);
cryptNullAndBoundsCheck(plain, plainOffset, cipherLen);
return implDecrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
}
@HotSpotIntrinsicCandidate
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;
}
private void cryptBlockSizeCheck(int len) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
}
private static void cryptNullAndBoundsCheck(byte[] array, int offset, int len) {
Objects.requireNonNull(array);
if (offset < 0 || offset >= array.length) {
throw new ArrayIndexOutOfBoundsException(offset);
}
int endIndex = offset + len - 1;
if (endIndex < 0 || endIndex >= array.length) {
throw new ArrayIndexOutOfBoundsException(endIndex);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,284 @@
/*
* 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.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) {
if ((plainLen % numBytes) != 0) {
throw new ProviderException("Internal error in input buffering");
}
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) {
if ((cipherLen % numBytes) != 0) {
throw new ProviderException("Internal error in input buffering");
}
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,198 @@
/*
* 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.InvalidKeyException;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
* 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
*/
class CounterMode extends FeedbackCipher {
// current counter value
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 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>
* @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;
}
Objects.checkFromIndexSize(inOff, len, in.length);
Objects.checkFromIndexSize(outOff, len, out.length);
return implCrypt(in, inOff, len, out, outOff);
}
// Implementation of crpyt() method. Possibly replaced with a compiler intrinsic.
@HotSpotIntrinsicCandidate
private int implCrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
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,154 @@
/*
* 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.MessageDigest;
import java.security.KeyRep;
import java.security.InvalidKeyException;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESKeySpec;
import jdk.internal.ref.CleanerFactory;
/**
* This class represents a DES key.
*
* @author Jan Luehe
*
*/
final class DESKey implements SecretKey {
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);
// Use the cleaner to zero the key when no longer referenced
final byte[] k = this.key;
CleanerFactory.cleaner().register(this,
() -> java.util.Arrays.fill(k, (byte)0x00));
}
public 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;
}
/**
* readObject is called to restore the state of this key from
* a stream.
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException
{
s.defaultReadObject();
key = key.clone();
}
/**
* 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());
}
}

View file

@ -0,0 +1,167 @@
/*
* 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.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) &&
DESKeySpec.class.isAssignableFrom(keySpec)) {
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,155 @@
/*
* 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.MessageDigest;
import java.security.KeyRep;
import java.security.InvalidKeyException;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESedeKeySpec;
import jdk.internal.ref.CleanerFactory;
/**
* This class represents a DES-EDE key.
*
* @author Jan Luehe
*
*/
final class DESedeKey implements SecretKey {
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);
// Use the cleaner to zero the key when no longer referenced
final byte[] k = this.key;
CleanerFactory.cleaner().register(this,
() -> java.util.Arrays.fill(k, (byte)0x00));
}
public 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;
}
/**
* readObject is called to restore the state of this key from
* a stream.
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException
{
s.defaultReadObject();
key = key.clone();
}
/**
* 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());
}
}

View file

@ -0,0 +1,162 @@
/*
* 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.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 (DESedeKeySpec.class.isAssignableFrom(keySpec)) {
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,564 @@
/*
* 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 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 = 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[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[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");
}
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");
}
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);
cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(),
iv);
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
cipher.init(decrypting, cipherKey.getAlgorithm(),
cipherKey.getEncoded(), IV2);
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,414 @@
/*
* 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.util.*;
import java.lang.*;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
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;
/**
* 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");
}
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, 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.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 (DHPublicKeySpec.class.isAssignableFrom(keySpec)) {
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 (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
} else {
throw new InvalidKeySpecException
("Inappropriate key specification");
}
} else if (key instanceof javax.crypto.interfaces.DHPrivateKey) {
if (DHPrivateKeySpec.class.isAssignableFrom(keySpec)) {
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 (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
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,204 @@
/*
* Copyright (c) 1997, 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.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;
/**
* 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. The default size of the
* prime modulus is 2048 bits.
* <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 size in bits of the random exponent (private value)
private int lSize;
// The source of randomness
private SecureRandom random;
public DHKeyPairGenerator() {
super();
initialize(2048, null);
}
private static void checkKeySize(int keysize)
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");
}
}
/**
* 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);
// Use the built-in parameters (ranging from 512 to 8192)
// when available.
this.params = ParameterCache.getCachedDHParameterSpec(keysize);
// Due to performance issue, only support DH parameters generation
// up to 1024 bits.
if ((this.params == null) && (keysize > 1024)) {
throw new InvalidParameterException(
"Unsupported " + keysize + "-bit DH parameter generation");
}
this.pSize = keysize;
this.lSize = 0;
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);
} catch (InvalidParameterException ipe) {
throw new InvalidAlgorithmParameterException(ipe.getMessage());
}
// exponent size is optional, could be 0
lSize = params.getL();
// Require exponentSize < primeSize
if ((lSize != 0) && (lSize > pSize)) {
throw new InvalidAlgorithmParameterException
("Exponent size must not be larger than modulus size");
}
this.random = random;
}
/**
* Generates a key pair.
*
* @return the new key pair
*/
public KeyPair generateKeyPair() {
if (random == null) {
random = SunJCE.getRandom();
}
if (params == null) {
try {
params = ParameterCache.getDHParameterSpec(pSize, random);
} catch (GeneralSecurityException e) {
// should never happen
throw new ProviderException(e);
}
}
BigInteger p = params.getP();
BigInteger g = params.getG();
if (lSize <= 0) {
lSize = pSize >> 1;
// use an exponent size of (pSize / 2) but at least 384 bits
if (lSize < 384) {
lSize = 384;
}
}
BigInteger x;
BigInteger pMinus2 = p.subtract(BigInteger.TWO);
//
// 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,168 @@
/*
* Copyright (c) 1997, 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.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHGenParameterSpec;
/*
* 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.
* The size of the prime modulus defaults to 2048 bits.
*
* @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 = 2048;
// 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 checkKeySize(int keysize)
throws InvalidParameterException {
boolean supported = ((keysize == 2048) || (keysize == 3072) ||
((keysize >= 512) && (keysize <= 1024) && ((keysize & 0x3F) == 0)));
if (!supported) {
throw new InvalidParameterException(
"DH key size must be multiple of 64 and range " +
"from 512 to 1024 (inclusive), or 2048, 3072. " +
"The specific key size " + keysize + " is not supported");
}
}
/**
* 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) {
checkKeySize(keysize);
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;
primeSize = dhParamSpec.getPrimeSize();
exponentSize = dhParamSpec.getExponentSize();
if ((exponentSize <= 0) || (exponentSize >= primeSize)) {
throw new InvalidAlgorithmParameterException(
"Exponent size (" + exponentSize +
") must be positive and less than modulus size (" +
primeSize + ")");
}
try {
checkKeySize(primeSize);
} catch (InvalidParameterException ipe) {
throw new InvalidAlgorithmParameterException(ipe.getMessage());
}
this.random = random;
}
/**
* Generates the parameters.
*
* @return the new AlgorithmParameters object
*/
@Override
protected AlgorithmParameters engineGenerateParameters() {
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, 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.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");
StringBuilder sb
= new StringBuilder("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)
sb.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l);
return sb.toString();
}
}

View file

@ -0,0 +1,322 @@
/*
* 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.*;
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 {
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 BigInteger p;
// the base generator
private 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());
}
}

View file

@ -0,0 +1,323 @@
/*
* 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.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 {
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 BigInteger p;
// the base generator
private 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");
StringBuilder sb
= new StringBuilder("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)
sb.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l);
return sb.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());
}
}

View file

@ -0,0 +1,154 @@
/*
* 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.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) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
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) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
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,243 @@
/*
* 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.InvalidKeyException;
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;
/**
* 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.security.util.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,113 @@
/*
* Copyright (c) 2013, 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.
*/
/*
* (C) Copyright IBM Corp. 2013
*/
package com.sun.crypto.provider;
import javax.crypto.IllegalBlockSizeException;
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. With a given cipher object and initial counter
* block, a counter mode operation is performed. Blocksize is limited
* to 16 bytes.
*
* 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).
*
* The counter mode operations can be intrinsified and parallelized
* by using CounterMode.implCrypt() if HotSpot VM supports it on the
* architecture.
*
* <p>This function is used in the implementation of GCM mode.
*
* @since 1.8
*/
final class GCTR extends CounterMode {
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
super(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 + ")");
}
iv = initialCounterBlk;
reset();
}
@Override
String getFeedback() {
return "GCTR";
}
// 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");
}
return encrypt(in, inOfs, inLen, out, outOfs);
}
// input can be arbitrary size when calling doFinal
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];
embeddedCipher.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;
}
}

View file

@ -0,0 +1,249 @@
/*
* 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;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
* 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
*/
@HotSpotIntrinsicCandidate
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,591 @@
/*
* Copyright (c) 2013, 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.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;
// 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
*/
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException {
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 {
if (keyValue == null || ivValue == null) {
throw new InvalidKeyException("Internal error");
}
// 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 {
// process data in 'in'
gctrPAndC.doFinal(in, inOfs, len, out, outOfs);
processed += len;
byte[] ct;
int ctOfs;
if (isEncrypt) {
ct = out;
ctOfs = outOfs;
} else {
ct = in;
ctOfs = inOfs;
}
int lastLen = len % AES_BLOCK_SIZE;
if (lastLen != 0) {
ghashAllToS.update(ct, ctOfs, len - lastLen);
byte[] padded =
expandToOneBlock(ct, (ctOfs + len - lastLen), lastLen);
ghashAllToS.update(padded);
} else {
ghashAllToS.update(ct, ctOfs, len);
}
}
/**
* 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>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) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
checkDataLength(processed, len);
processAAD();
if (len > 0) {
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");
}
if (out.length - outOfs < (len + tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
checkDataLength(processed, len);
processAAD();
if (len > 0) {
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) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
checkDataLength(ibuffer.size(), len);
processAAD();
if (len > 0) {
// store internally until decryptFinal is called because
// spec mentioned that only return recovered data after tag
// is successfully verified
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));
if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
processAAD();
// get the trailing tag bytes from 'in'
byte[] tag = new byte[tagLenBytes];
System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
len -= tagLenBytes;
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,294 @@
/*
* Copyright (c) 2002, 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.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 instantiating
* a MessageDigest of the specified name.
*/
HmacCore(String digestAlgo, int bl) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(digestAlgo);
if (!(md instanceof Cloneable)) {
// use SUN provider if the most preferred one does not support
// cloning
Provider sun = Security.getProvider("SUN");
if (sun != null) {
md = MessageDigest.getInstance(digestAlgo, sun);
} else {
String noCloneProv = md.getProvider().getName();
// if no Sun provider, use provider list
Provider[] provs = Security.getProviders();
for (Provider p : provs) {
try {
if (!p.getName().equals(noCloneProv)) {
MessageDigest md2 =
MessageDigest.getInstance(digestAlgo, p);
if (md2 instanceof Cloneable) {
md = md2;
break;
}
}
} catch (NoSuchAlgorithmException nsae) {
continue;
}
}
}
}
this.md = md;
this.blockLen = bl;
this.k_ipad = new byte[blockLen];
this.k_opad = new byte[blockLen];
first = true;
}
/**
* 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);
}
}
public static final class HmacSHA512_224 extends HmacCore {
public HmacSHA512_224() throws NoSuchAlgorithmException {
super("SHA-512/224", 128);
}
}
public static final class HmacSHA512_256 extends HmacCore {
public HmacSHA512_256() throws NoSuchAlgorithmException {
super("SHA-512/256", 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,135 @@
/*
* 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.PBEParameterSpec;
import java.security.*;
import java.security.spec.*;
/**
* This is an implementation of the HMAC-PBESHA1 algorithm as defined
* in PKCS#12 v1.0 standard.
*
* @author Valerie Peng
*/
public final class HmacPKCS12PBESHA1 extends HmacCore {
/**
* Standard constructor, creates a new HmacSHA1 instance.
*/
public HmacPKCS12PBESHA1() throws NoSuchAlgorithmException {
super("SHA1", 64);
}
/**
* 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 = key.getEncoded();
if ((passwdBytes == null) ||
!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
throw new InvalidKeyException("Missing password");
}
passwdChars = new char[passwdBytes.length];
for (int i=0; i<passwdChars.length; i++) {
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
}
} else {
throw new InvalidKeyException("SecretKey of PBE type required");
}
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");
}
byte[] derivedKey = PKCS12PBECipherCore.derive(passwdChars, salt,
iCount, engineGetMacLength(), PKCS12PBECipherCore.MAC_KEY);
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,123 @@
/*
* Copyright (c) 2003, 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 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;
if ((off + len) > 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[off + len - 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;
}
byte lastByte = in[off + len - 1];
int padValue = (int)lastByte & 0x0ff;
if ((padValue < 0x01)
|| (padValue > blockSize)) {
return -1;
}
int start = off + len - 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,919 @@
/*
* Copyright (c) 1998, 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.util.*;
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.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;
/**
* 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 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;
}
// 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 {
key =
keyProtector.unseal(((SecretKeyEntry)entry).sealedKey);
}
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);
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;
if (stream == null)
return;
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<>(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
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
try {
entry.protectedKey = new byte[dis.readInt()];
} catch (OutOfMemoryError e) {
throw new IOException("Keysize too big");
}
dis.readFully(entry.protectedKey);
// read the certificate chain
int numOfCerts = dis.readInt();
try {
if (numOfCerts > 0) {
entry.chain = new Certificate[numOfCerts];
}
} catch (OutOfMemoryError e) {
throw new IOException("Too many certificates in "
+ "chain");
}
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
try {
encoded = new byte[dis.readInt()];
} catch (OutOfMemoryError e) {
throw new IOException("Certificate too big");
}
dis.readFully(encoded);
bais = new ByteArrayInputStream(encoded);
entry.chain[j] = cf.generateCertificate(bais);
}
// Add the entry to the list
entries.put(alias, entry);
} else if (tag == 2) { // trusted certificate entry
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);
}
}
try {
encoded = new byte[dis.readInt()];
} catch (OutOfMemoryError e) {
throw new IOException("Certificate too big");
}
dis.readFully(encoded);
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
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);
entry.sealedKey = (SealedObject)ois.readObject();
// NOTE: don't close ois here since we are still
// using dis!!!
} catch (ClassNotFoundException cnfe) {
throw new IOException(cnfe.getMessage());
}
// Add the entry to the list
entries.put(alias, entry);
} else {
throw new IOException("Unrecognized keystore entry");
}
}
/*
* 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[], actual[];
computed = md.digest();
actual = new byte[computed.length];
dis.readFully(actual);
for (int i = 0; i < computed.length; i++) {
if (computed[i] != actual[i]) {
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;
}
/**
* Probe the first few bytes of the keystore data stream for a valid
* JCEKS keystore encoding.
*/
@Override
public boolean engineProbe(InputStream stream) throws IOException {
DataInputStream dataStream;
if (stream instanceof DataInputStream) {
dataStream = (DataInputStream)stream;
} else {
dataStream = new DataInputStream(stream);
}
return JCEKS_MAGIC == dataStream.readInt();
}
}

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,364 @@
/*
* 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.IOException;
import java.io.Serializable;
import java.security.Security;
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.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.AlgorithmParameters;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.SecretKey;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SealedObject;
import javax.crypto.spec.*;
import sun.security.x509.AlgorithmId;
import sun.security.util.ObjectIdentifier;
/**
* 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 SALT_LEN = 20; // the salt length
private static final int DIGEST_LEN = 20;
// the password used for protecting/recovering keys passed through this
// key protector
private char[] password;
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, 20);
// create PBE key from password
PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
SecretKey sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
pbeKeySpec.clearPassword();
// encrypt private key
PBEWithMD5AndTripleDESCipher cipher;
cipher = new PBEWithMD5AndTripleDESCipher();
cipher.engineInit(Cipher.ENCRYPT_MODE, sKey, pbeSpec, null);
byte[] plain = key.getEncoded();
byte[] encrKey = cipher.engineDoFinal(plain, 0, plain.length);
// 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;
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();
// parse the PBE parameters into the corresponding spec
AlgorithmParameters pbeParams =
AlgorithmParameters.getInstance("PBE");
pbeParams.init(encodedParams);
PBEParameterSpec pbeSpec =
pbeParams.getParameterSpec(PBEParameterSpec.class);
// create PBE key from password
PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
SecretKey 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());
}
}
/*
* 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);
java.util.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, 20);
// create PBE key from password
PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
SecretKey sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
pbeKeySpec.clearPassword();
// seal key
Cipher cipher;
PBEWithMD5AndTripleDESCipher cipherSpi;
cipherSpi = new PBEWithMD5AndTripleDESCipher();
cipher = new CipherForKeyProtector(cipherSpi, SunJCE.getInstance(),
"PBEWithMD5AndTripleDES");
cipher.init(Cipher.ENCRYPT_MODE, sKey, pbeSpec);
return new SealedObjectForKeyProtector(key, cipher);
}
/**
* Unseals the sealed key.
*/
Key unseal(SealedObject so)
throws NoSuchAlgorithmException, UnrecoverableKeyException
{
try {
// create PBE key from password
PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
SecretKey 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");
}
PBEWithMD5AndTripleDESCipher cipherSpi;
cipherSpi = new PBEWithMD5AndTripleDESCipher();
Cipher cipher = new CipherForKeyProtector(cipherSpi,
SunJCE.getInstance(),
"PBEWithMD5AndTripleDES");
cipher.init(Cipher.DECRYPT_MODE, skey, params);
return (Key)soForKeyProtector.getObject(cipher);
} 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());
}
}
}
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,248 @@
/*
* Copyright (c) 2003, 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.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(OID_MGF1)) {
throw new IOException("Only MGF1 mgf is supported");
}
AlgorithmId params = AlgorithmId.parse(
new DerValue(val.getEncodedParams()));
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 {
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(OID_PSpecified)) {
throw new IOException("Wrong OID for pSpecified");
}
DerInputStream dis = new DerInputStream(val.getEncodedParams());
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() {
StringBuilder sb = new StringBuilder();
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,220 @@
/*
* 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.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) {
if ((plainLen % numBytes) != 0) {
throw new ProviderException("Internal error in input buffering");
}
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) {
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,535 @@
/*
* 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.*;
import java.security.spec.*;
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 PBECipherCore {
// 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.
*
*/
PBECipherCore(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) ||
(key.getEncoded() == 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");
}
}
byte[] derivedKey = deriveCipherKey(key);
// 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(Key key) {
byte[] result = null;
byte[] passwdBytes = key.getEncoded();
if (algo.equals("DES")) {
// P || S (password concatenated with salt)
byte[] concat = new byte[passwdBytes.length + salt.length];
System.arraycopy(passwdBytes, 0, concat, 0, passwdBytes.length);
java.util.Arrays.fill(passwdBytes, (byte)0x00);
System.arraycopy(salt, 0, concat, passwdBytes.length, salt.length);
// digest P || S with c iterations
byte[] toBeHashed = concat;
for (int i = 0; i < iCount; i++) {
md.update(toBeHashed);
toBeHashed = md.digest(); // this resets the digest
}
java.util.Arrays.fill(concat, (byte)0x00);
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-1] = 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[] kBytes = null;
IvParameterSpec iv = null;
byte[] toBeHashed = null;
result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN +
DESConstants.DES_BLOCK_SIZE];
for (i = 0; i < 2; i++) {
toBeHashed = new byte[salt.length/2];
System.arraycopy(salt, i*(salt.length/2), toBeHashed, 0,
toBeHashed.length);
for (int j=0; j < iCount; j++) {
md.update(toBeHashed);
md.update(passwdBytes);
toBeHashed = md.digest(); // this resets the digest
}
System.arraycopy(toBeHashed, 0, result, i*16,
toBeHashed.length);
}
}
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;
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 = doFinal(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 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,150 @@
/*
* 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.MessageDigest;
import java.security.KeyRep;
import java.security.spec.InvalidKeySpecException;
import java.util.Locale;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEKeySpec;
import jdk.internal.ref.CleanerFactory;
/**
* This class represents a PBE key.
*
* @author Jan Luehe
*
*/
final class PBEKey implements SecretKey {
static final long serialVersionUID = -2234768909660948176L;
private byte[] key;
private String type;
/**
* Creates a PBE key from a given PBE key specification.
*
* @param keytype 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);
java.util.Arrays.fill(passwd, ' ');
type = keytype;
// Use the cleaner to zero the key when no longer referenced
final byte[] k = this.key;
CleanerFactory.cleaner().register(this,
() -> java.util.Arrays.fill(k, (byte)0x00));
}
public 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);
java.util.Arrays.fill(thatEncoded, (byte)0x00);
return ret;
}
/**
* readObject is called to restore the state of this key from
* a stream.
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException
{
s.defaultReadObject();
key = key.clone();
}
/**
* 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());
}
}

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<>(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.security.util.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,535 @@
/*
* 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.*;
import java.security.spec.*;
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) ||
(key.getEncoded() == 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");
}
}
byte[] derivedKey = deriveCipherKey(key);
// 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(Key key) {
byte[] result = null;
byte[] passwdBytes = key.getEncoded();
if (algo.equals("DES")) {
// P || S (password concatenated with salt)
byte[] concat = new byte[passwdBytes.length + salt.length];
System.arraycopy(passwdBytes, 0, concat, 0, passwdBytes.length);
java.util.Arrays.fill(passwdBytes, (byte)0x00);
System.arraycopy(salt, 0, concat, passwdBytes.length, salt.length);
// digest P || S with c iterations
byte[] toBeHashed = concat;
for (int i = 0; i < iCount; i++) {
md.update(toBeHashed);
toBeHashed = md.digest(); // this resets the digest
}
java.util.Arrays.fill(concat, (byte)0x00);
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-1] = 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[] kBytes = null;
IvParameterSpec iv = null;
byte[] toBeHashed = null;
result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN +
DESConstants.DES_BLOCK_SIZE];
for (i = 0; i < 2; i++) {
toBeHashed = new byte[salt.length/2];
System.arraycopy(salt, i*(salt.length/2), toBeHashed, 0,
toBeHashed.length);
for (int j=0; j < iCount; j++) {
md.update(toBeHashed);
md.update(passwdBytes);
toBeHashed = md.digest(); // this resets the digest
}
System.arraycopy(toBeHashed, 0, result, i*16,
toBeHashed.length);
}
}
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;
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 = doFinal(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 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,415 @@
/*
* 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 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) ||
(key.getEncoded() == 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");
}
}
SecretKeySpec cipherKey = null;
byte[] derivedKey = null;
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 pbeSpec =
new PBEKeySpec(passwdChars, salt, iCount, keyLength);
// 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);
SecretKey s = null;
try {
s = kdf.engineGenerateSecret(pbeSpec);
} catch (InvalidKeySpecException ikse) {
InvalidKeyException ike =
new InvalidKeyException("Cannot construct PBE key");
ike.initCause(ikse);
throw ike;
}
derivedKey = s.getEncoded();
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,525 @@
/*
* Copyright (c) 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.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.HexDumpEncoder;
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();
}
@SuppressWarnings("deprecation")
protected void engineInit(byte[] encoded)
throws IOException
{
String kdfAlgo = null;
String cipherAlgo = null;
DerValue pBES2Algorithms = new DerValue(encoded);
if (pBES2Algorithms.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 SEQUENCE tag");
}
if (!pkcs5PBES2_OID.equals(pBES2Algorithms.data.getOID())) {
throw new IOException("PBE parameter parsing error: "
+ "expecting the object identifier for PBES2");
}
if (pBES2Algorithms.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 SEQUENCE tag");
}
DerValue pBES2_params = pBES2Algorithms.data.getDerValue();
if (pBES2_params.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 SEQUENCE tag");
}
kdfAlgo = parseKDF(pBES2_params.data.getDerValue());
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();
}
@SuppressWarnings("deprecation")
private String parseKDF(DerValue keyDerivationFunc) throws IOException {
String kdfAlgo = null;
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 keyLength = pBKDF2_params.data.getDerValue();
if (keyLength.tag == DerValue.tag_Integer) {
keysize = keyLength.getInteger() * 8; // keysize (in bits)
}
if (pBKDF2_params.tag == DerValue.tag_Sequence) {
DerValue prf = pBKDF2_params.data.getDerValue();
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;
}
@SuppressWarnings("deprecation")
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 pBES2Algorithms = new DerOutputStream();
pBES2Algorithms.putOID(pkcs5PBES2_OID);
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);
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);
pBES2Algorithms.write(DerValue.tag_Sequence, pBES2_params);
out.write(DerValue.tag_Sequence, pBES2Algorithms);
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,277 @@
/*
* 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.io.ObjectStreamException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Locale;
import java.security.MessageDigest;
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;
import jdk.internal.ref.CleanerFactory;
/**
* 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 {
static final long serialVersionUID = -2234868909660948157L;
private char[] passwd;
private byte[] salt;
private int iterCount;
private byte[] key;
private 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 keySpec the given PBE key specification
* @param prfAlgo the given PBE key algorithm
*/
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);
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);
// SunPKCS11 requires a non-empty PBE password
if (passwdBytes.length == 0 &&
this.prf.getProvider().getName().startsWith("SunPKCS11")) {
this.prf = Mac.getInstance(prfAlgo, SunJCE.getInstance());
}
} catch (NoSuchAlgorithmException nsae) {
// not gonna happen; re-throw just in case
InvalidKeySpecException ike = new InvalidKeySpecException();
ike.initCause(nsae);
throw ike;
}
this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength);
// Use the cleaner to zero the key when no longer referenced
final byte[] k = this.key;
final char[] p = this.passwd;
CleanerFactory.cleaner().register(this,
() -> {
java.util.Arrays.fill(k, (byte)0x00);
java.util.Arrays.fill(p, '0');
});
}
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];
// 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 prf.getAlgorithm();
}
@Override
public String getFormat() {
return "RAW";
}
@Override
public byte[] getEncoded() {
return password;
}
@Override
public int hashCode() {
return Arrays.hashCode(password) * 41 +
prf.getAlgorithm().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 prf.getAlgorithm().equalsIgnoreCase(
sk.getAlgorithm()) &&
MessageDigest.isEqual(password, sk.getEncoded());
}
};
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 byte[] getEncoded() {
return key.clone();
}
public String getAlgorithm() {
return "PBKDF2With" + prf.getAlgorithm();
}
public int getIterationCount() {
return iterCount;
}
public 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, that.getEncoded());
java.util.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());
}
}

View file

@ -0,0 +1,218 @@
/*
* 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 = key.getEncoded();
if ((passwdBytes == null) ||
!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
throw new InvalidKeyException("Missing password");
}
passwdChars = new char[passwdBytes.length];
for (int i=0; i<passwdChars.length; i++) {
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
}
} else {
throw new InvalidKeyException("SecretKey of PBE type required");
}
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");
}
PBEKeySpec pbeSpec =
new PBEKeySpec(passwdChars, salt, iCount, blockLength);
// password char[] was cloned in PBEKeySpec constructor,
// so we can zero it out here
java.util.Arrays.fill(passwdChars, ' ');
SecretKey s = null;
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,198 @@
/*
* 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.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)
{
if ((plainLen % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
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)
{
if ((cipherLen % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
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,817 @@
/*
* Copyright (c) 2003, 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.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 int keyLength; // in bits
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);
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;
keyLength = defKeySize * 8;
if (algo.equals("RC4")) {
pbeAlgo = "PBEWithSHA1AndRC4_" + keyLength;
} else {
SymmetricCipher symmCipher = null;
if (algo.equals("DESede")) {
symmCipher = new DESedeCrypt();
pbeAlgo = "PBEWithSHA1AndDESede";
keyLength = 112; // effective key length
} else if (algo.equals("RC2")) {
symmCipher = new RC2Crypt();
pbeAlgo = "PBEWithSHA1AndRC2_" + keyLength;
} 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 = key.getEncoded();
if ((passwdBytes == null) ||
!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
throw new InvalidKeyException("Missing password");
}
passwdChars = new char[passwdBytes.length];
for (int i=0; i<passwdChars.length; i++) {
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
}
} else {
throw new InvalidKeyException("SecretKey of PBE type required");
}
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);
}
}
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 keyLength;
}
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,128 @@
/*
* 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;
/**
* 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;
if ((off + len) > in.length) {
throw new ShortBufferException("Buffer too small to hold padding");
}
byte paddingOctet = (byte) (len & 0xff);
for (int i = 0; i < len; i++) {
in[i + off] = 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;
}
byte lastByte = in[off + len - 1];
int padValue = (int)lastByte & 0x0ff;
if ((padValue < 0x01)
|| (padValue > blockSize)) {
return -1;
}
int start = off + len - ((int)lastByte & 0x0ff);
if (start < off) {
return -1;
}
for (int i = 0; i < ((int)lastByte & 0x0ff); i++) {
if (in[start+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,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 static final 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.security.util.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 static final 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,475 @@
/*
* Copyright (c) 2003, 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.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 PKCS#1 v1.5 padding 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 padding. The mode (blocktype)
* is selected based on the en/decryption mode and public/private key used
* . "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 static final byte[] B0 = new byte[0];
// mode constant for public key encryption
private static final int MODE_ENCRYPT = 1;
// mode constant for private key decryption
private static final int MODE_DECRYPT = 2;
// mode constant for private key encryption (signing)
private static final int MODE_SIGN = 3;
// mode constant for public key decryption (verifying)
private static final int MODE_VERIFY = 4;
// constant for raw RSA
private static final String PAD_NONE = "NoPadding";
// constant for PKCS#1 v1.5 RSA
private static final String PAD_PKCS1 = "PKCS1Padding";
// constant for PKCS#2 v2.0 OAEP with MGF1
private static final 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;
// 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
@SuppressWarnings("deprecation")
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;
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 (bufOfs + inLen > buffer.length) {
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");
}
try {
byte[] data;
switch (mode) {
case MODE_SIGN:
data = padding.pad(buffer, 0, bufOfs);
return RSACore.rsa(data, privateKey, true);
case MODE_VERIFY:
byte[] verifyBuffer = RSACore.convert(buffer, 0, bufOfs);
data = RSACore.rsa(verifyBuffer, publicKey);
return padding.unpad(data);
case MODE_ENCRYPT:
data = padding.pad(buffer, 0, bufOfs);
return RSACore.rsa(data, publicKey);
case MODE_DECRYPT:
byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs);
data = RSACore.rsa(decryptBuffer, privateKey, false);
return padding.unpad(data);
default:
throw new AssertionError("Internal error");
}
} finally {
bufOfs = 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);
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");
}
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);
}
}
// see JCE spec
@SuppressWarnings("deprecation")
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");
Exception failover = null;
byte[] encoded = null;
update(wrappedKey, 0, wrappedKey.length);
try {
encoded = doFinal();
} catch (BadPaddingException e) {
if (isTlsRsaPremasterSecret) {
failover = e;
} else {
throw new InvalidKeyException("Unwrapping failed", e);
}
} catch (IllegalBlockSizeException e) {
// should not occur, handled with length check above
throw new InvalidKeyException("Unwrapping failed", e);
}
if (isTlsRsaPremasterSecret) {
if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new IllegalStateException(
"No TlsRsaPremasterSecretParameterSpec specified");
}
// polish the TLS premaster secret
encoded = KeyUtil.checkTlsPreMasterSecretKey(
((TlsRsaPremasterSecretParameterSpec)spec).getClientVersion(),
((TlsRsaPremasterSecretParameterSpec)spec).getServerVersion(),
random, encoded, (failover != null));
}
return ConstructKeys.constructKey(encoded, algorithm, type);
}
// see JCE spec
protected int engineGetKeySize(Key key) throws InvalidKeyException {
RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
return rsaKey.getModulus().bitLength();
}
}

View file

@ -0,0 +1,62 @@
/*
* 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.*;
import java.security.*;
import javax.crypto.*;
final class SealedObjectForKeyProtector extends SealedObject {
static final long serialVersionUID = -3650226485480866989L;
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;
}
}

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,799 @@
/*
* Copyright (c) 1997, 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.security.AccessController;
import java.security.Provider;
import java.security.SecureRandom;
import static sun.security.util.SecurityConstants.PROVIDER_VER;
/**
* 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;
// 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", PROVIDER_VER, 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<>() {
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");
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");
// TODO: aliases with OIDs
put("Mac.HmacSHA512/224",
"com.sun.crypto.provider.HmacCore$HmacSHA512_224");
put("Mac.HmacSHA512/256",
"com.sun.crypto.provider.HmacCore$HmacSHA512_256");
put("Mac.HmacPBESHA1",
"com.sun.crypto.provider.HmacPKCS12PBESHA1");
// 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.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("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,281 @@
/*
* 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 static final String MSG = "TlsKeyMaterialGenerator must be "
+ "initialized using a TlsKeyMaterialParameterSpec";
@SuppressWarnings("deprecation")
private TlsKeyMaterialParameterSpec spec;
private int protocolVersion;
public TlsKeyMaterialGenerator() {
}
protected void engineInit(SecureRandom random) {
throw new InvalidParameterException(MSG);
}
@SuppressWarnings("deprecation")
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);
}
}
@SuppressWarnings("deprecation")
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,179 @@
/*
* 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.*;
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 static final String MSG = "TlsMasterSecretGenerator must be "
+ "initialized using a TlsMasterSecretParameterSpec";
@SuppressWarnings("deprecation")
private TlsMasterSecretParameterSpec spec;
private int protocolVersion;
public TlsMasterSecretGenerator() {
}
protected void engineInit(SecureRandom random) {
throw new InvalidParameterException(MSG);
}
@SuppressWarnings("deprecation")
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (params instanceof TlsMasterSecretParameterSpec == false) {
throw new InvalidAlgorithmParameterException(MSG);
}
this.spec = (TlsMasterSecretParameterSpec)params;
if ("RAW".equals(spec.getPremasterSecret().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(
"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;
byte[] clientRandom = spec.getClientRandom();
byte[] serverRandom = spec.getServerRandom();
if (protocolVersion >= 0x0301) {
byte[] seed = concat(clientRandom, serverRandom);
master = ((protocolVersion >= 0x0303) ?
doTLS12PRF(premaster, LABEL_MASTER_SECRET, seed, 48,
spec.getPRFHashAlg(), spec.getPRFHashLength(),
spec.getPRFBlockSize()) :
doTLS10PRF(premaster, LABEL_MASTER_SECRET, seed, 48));
} else {
master = new byte[48];
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest sha = MessageDigest.getInstance("SHA");
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);
}
}
@SuppressWarnings("deprecation")
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();
}
}
}

View file

@ -0,0 +1,385 @@
/*
* 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.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 static final byte[] B0 = new byte[0];
static final byte[] LABEL_MASTER_SECRET = // "master secret"
{ 109, 97, 115, 116, 101, 114, 32, 115, 101, 99, 114, 101, 116 };
static final byte[] LABEL_KEY_EXPANSION = // "key expansion"
{ 107, 101, 121, 32, 101, 120, 112, 97, 110, 115, 105, 111, 110 };
static final byte[] LABEL_CLIENT_WRITE_KEY = // "client write key"
{ 99, 108, 105, 101, 110, 116, 32, 119, 114, 105, 116, 101, 32,
107, 101, 121 };
static final byte[] LABEL_SERVER_WRITE_KEY = // "server write key"
{ 115, 101, 114, 118, 101, 114, 32, 119, 114, 105, 116, 101, 32,
107, 101, 121 };
static final 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", ...)
static final 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 static final String MSG = "TlsPrfGenerator must be "
+ "initialized using a TlsPrfParameterSpec";
@SuppressWarnings("deprecation")
private TlsPrfParameterSpec spec;
public TlsPrfGenerator() {
}
protected void engineInit(SecureRandom random) {
throw new InvalidParameterException(MSG);
}
@SuppressWarnings("deprecation")
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).
*/
public static class V12 extends TlsPrfGenerator {
protected SecretKey engineGenerateKey() {
return engineGenerateKey0(true);
}
}
/**
* A KeyGenerator implementation that supports TLS 1.0/1.1.
*/
public static class V10 extends TlsPrfGenerator {
protected SecretKey engineGenerateKey() {
return engineGenerateKey0(false);
}
}
}

View file

@ -0,0 +1,93 @@
/*
* 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 static final String MSG = "TlsRsaPremasterSecretGenerator must be "
+ "initialized using a TlsRsaPremasterSecretParameterSpec";
@SuppressWarnings("deprecation")
private TlsRsaPremasterSecretParameterSpec spec;
private SecureRandom random;
public TlsRsaPremasterSecretGenerator() {
}
protected void engineInit(SecureRandom random) {
throw new InvalidParameterException(MSG);
}
@SuppressWarnings("deprecation")
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");
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2001, 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.io.IOException;
import java.io.Serializable;
import java.io.ObjectStreamException;
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SealedObject;
import javax.crypto.spec.*;
/**
* This class is introduced to workaround a problem in
* the SunJCE provider shipped in JCE 1.2.1: the class
* SealedObjectForKeyProtector was obfuscated due to a mistake.
*
* In order to retrieve secret keys in a JCEKS KeyStore written
* by the SunJCE provider in JCE 1.2.1, this class will be used.
*
* @author Valerie Peng
*
*
* @see JceKeyStore
*/
final class ai extends javax.crypto.SealedObject {
static final long serialVersionUID = -7051502576727967444L;
ai(SealedObject so) {
super(so);
}
Object readResolve() throws ObjectStreamException {
return new SealedObjectForKeyProtector(this);
}
}

View file

@ -0,0 +1,298 @@
/*
* Copyright (c) 2003, 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.java.util.jar.pack;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Adaptive coding.
* See the section "Adaptive Encodings" in the Pack200 spec.
* @author John Rose
*/
class AdaptiveCoding implements CodingMethod {
CodingMethod headCoding;
int headLength;
CodingMethod tailCoding;
public AdaptiveCoding(int headLength, CodingMethod headCoding, CodingMethod tailCoding) {
assert(isCodableLength(headLength));
this.headLength = headLength;
this.headCoding = headCoding;
this.tailCoding = tailCoding;
}
public void setHeadCoding(CodingMethod headCoding) {
this.headCoding = headCoding;
}
public void setHeadLength(int headLength) {
assert(isCodableLength(headLength));
this.headLength = headLength;
}
public void setTailCoding(CodingMethod tailCoding) {
this.tailCoding = tailCoding;
}
public boolean isTrivial() {
return headCoding == tailCoding;
}
// CodingMethod methods.
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException {
writeArray(this, out, a, start, end);
}
// writeArrayTo must be coded iteratively, not recursively:
private static void writeArray(AdaptiveCoding run, OutputStream out, int[] a, int start, int end) throws IOException {
for (;;) {
int mid = start+run.headLength;
assert(mid <= end);
run.headCoding.writeArrayTo(out, a, start, mid);
start = mid;
if (run.tailCoding instanceof AdaptiveCoding) {
run = (AdaptiveCoding) run.tailCoding;
continue;
}
break;
}
run.tailCoding.writeArrayTo(out, a, start, end);
}
public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException {
readArray(this, in, a, start, end);
}
private static void readArray(AdaptiveCoding run, InputStream in, int[] a, int start, int end) throws IOException {
for (;;) {
int mid = start+run.headLength;
assert(mid <= end);
run.headCoding.readArrayFrom(in, a, start, mid);
start = mid;
if (run.tailCoding instanceof AdaptiveCoding) {
run = (AdaptiveCoding) run.tailCoding;
continue;
}
break;
}
run.tailCoding.readArrayFrom(in, a, start, end);
}
public static final int KX_MIN = 0;
public static final int KX_MAX = 3;
public static final int KX_LG2BASE = 4;
public static final int KX_BASE = 16;
public static final int KB_MIN = 0x00;
public static final int KB_MAX = 0xFF;
public static final int KB_OFFSET = 1;
public static final int KB_DEFAULT = 3;
static int getKXOf(int K) {
for (int KX = KX_MIN; KX <= KX_MAX; KX++) {
if (((K - KB_OFFSET) & ~KB_MAX) == 0)
return KX;
K >>>= KX_LG2BASE;
}
return -1;
}
static int getKBOf(int K) {
int KX = getKXOf(K);
if (KX < 0) return -1;
K >>>= (KX * KX_LG2BASE);
return K-1;
}
static int decodeK(int KX, int KB) {
assert(KX_MIN <= KX && KX <= KX_MAX);
assert(KB_MIN <= KB && KB <= KB_MAX);
return (KB+KB_OFFSET) << (KX * KX_LG2BASE);
}
static int getNextK(int K) {
if (K <= 0) return 1; // 1st K value
int KX = getKXOf(K);
if (KX < 0) return Integer.MAX_VALUE;
// This is the increment we expect to apply:
int unit = 1 << (KX * KX_LG2BASE);
int mask = KB_MAX << (KX * KX_LG2BASE);
int K1 = K + unit;
K1 &= ~(unit-1); // cut off stray low-order bits
if (((K1 - unit) & ~mask) == 0) {
assert(getKXOf(K1) == KX);
return K1;
}
if (KX == KX_MAX) return Integer.MAX_VALUE;
KX += 1;
int mask2 = KB_MAX << (KX * KX_LG2BASE);
K1 |= (mask & ~mask2);
K1 += unit;
assert(getKXOf(K1) == KX);
return K1;
}
// Is K of the form ((KB:[0..255])+1) * 16^(KX:{0..3])?
public static boolean isCodableLength(int K) {
int KX = getKXOf(K);
if (KX < 0) return false;
int unit = 1 << (KX * KX_LG2BASE);
int mask = KB_MAX << (KX * KX_LG2BASE);
return ((K - unit) & ~mask) == 0;
}
public byte[] getMetaCoding(Coding dflt) {
//assert(!isTrivial()); // can happen
// See the isCodableLength restriction in CodingChooser.
ByteArrayOutputStream bytes = new ByteArrayOutputStream(10);
try {
makeMetaCoding(this, dflt, bytes);
} catch (IOException ee) {
throw new RuntimeException(ee);
}
return bytes.toByteArray();
}
private static void makeMetaCoding(AdaptiveCoding run, Coding dflt,
ByteArrayOutputStream bytes)
throws IOException {
for (;;) {
CodingMethod headCoding = run.headCoding;
int headLength = run.headLength;
CodingMethod tailCoding = run.tailCoding;
int K = headLength;
assert(isCodableLength(K));
int ADef = (headCoding == dflt)?1:0;
int BDef = (tailCoding == dflt)?1:0;
if (ADef+BDef > 1) BDef = 0; // arbitrary choice
int ABDef = 1*ADef + 2*BDef;
assert(ABDef < 3);
int KX = getKXOf(K);
int KB = getKBOf(K);
assert(decodeK(KX, KB) == K);
int KBFlag = (KB != KB_DEFAULT)?1:0;
bytes.write(_meta_run + KX + 4*KBFlag + 8*ABDef);
if (KBFlag != 0) bytes.write(KB);
if (ADef == 0) bytes.write(headCoding.getMetaCoding(dflt));
if (tailCoding instanceof AdaptiveCoding) {
run = (AdaptiveCoding) tailCoding;
continue; // tail call, to avoid deep stack recursion
}
if (BDef == 0) bytes.write(tailCoding.getMetaCoding(dflt));
break;
}
}
public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) {
int op = bytes[pos++] & 0xFF;
if (op < _meta_run || op >= _meta_pop) return pos-1; // backup
AdaptiveCoding prevc = null;
for (boolean keepGoing = true; keepGoing; ) {
keepGoing = false;
assert(op >= _meta_run);
op -= _meta_run;
int KX = op % 4;
int KBFlag = (op / 4) % 2;
int ABDef = (op / 8);
assert(ABDef < 3);
int ADef = (ABDef & 1);
int BDef = (ABDef & 2);
CodingMethod[] ACode = {dflt}, BCode = {dflt};
int KB = KB_DEFAULT;
if (KBFlag != 0)
KB = bytes[pos++] & 0xFF;
if (ADef == 0) {
pos = BandStructure.parseMetaCoding(bytes, pos, dflt, ACode);
}
if (BDef == 0 &&
((op = bytes[pos] & 0xFF) >= _meta_run) && op < _meta_pop) {
pos++;
keepGoing = true;
} else if (BDef == 0) {
pos = BandStructure.parseMetaCoding(bytes, pos, dflt, BCode);
}
AdaptiveCoding newc = new AdaptiveCoding(decodeK(KX, KB),
ACode[0], BCode[0]);
if (prevc == null) {
res[0] = newc;
} else {
prevc.tailCoding = newc;
}
prevc = newc;
}
return pos;
}
private String keyString(CodingMethod m) {
if (m instanceof Coding)
return ((Coding)m).keyString();
return m.toString();
}
public String toString() {
StringBuilder res = new StringBuilder(20);
AdaptiveCoding run = this;
res.append("run(");
for (;;) {
res.append(run.headLength).append("*");
res.append(keyString(run.headCoding));
if (run.tailCoding instanceof AdaptiveCoding) {
run = (AdaptiveCoding) run.tailCoding;
res.append(" ");
continue;
}
break;
}
res.append(" **").append(keyString(run.tailCoding));
res.append(")");
return res.toString();
}
/*
public static void main(String av[]) {
int[][] samples = {
{1,2,3,4,5},
{254,255,256,256+1*16,256+2*16},
{0xfd,0xfe,0xff,0x100,0x110,0x120,0x130},
{0xfd0,0xfe0,0xff0,0x1000,0x1100,0x1200,0x1300},
{0xfd00,0xfe00,0xff00,0x10000,0x11000,0x12000,0x13000},
{0xfd000,0xfe000,0xff000,0x100000}
};
for (int i = 0; i < samples.length; i++) {
for (int j = 0; j < samples[i].length; j++) {
int K = samples[i][j];
int KX = getKXOf(K);
int KB = getKBOf(K);
System.out.println("K="+Integer.toHexString(K)+
" KX="+KX+" KB="+KB);
assert(isCodableLength(K));
assert(K == decodeK(KX, KB));
if (j == 0) continue;
int K1 = samples[i][j-1];
assert(K == getNextK(K1));
}
}
}
//*/
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,618 @@
/*
* Copyright (c) 2001, 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.java.util.jar.pack;
import com.sun.java.util.jar.pack.ConstantPool.ClassEntry;
import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry;
import com.sun.java.util.jar.pack.ConstantPool.Entry;
import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry;
import com.sun.java.util.jar.pack.ConstantPool.MemberEntry;
import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry;
import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry;
import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry;
import com.sun.java.util.jar.pack.Package.Class;
import com.sun.java.util.jar.pack.Package.InnerClass;
import java.io.DataInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Reader for a class file that is being incorporated into a package.
* @author John Rose
*/
class ClassReader {
int verbose;
Package pkg;
Class cls;
long inPos;
long constantPoolLimit = -1;
DataInputStream in;
Map<Attribute.Layout, Attribute> attrDefs;
Map<Attribute.Layout, String> attrCommands;
String unknownAttrCommand = "error";;
ClassReader(Class cls, InputStream in) throws IOException {
this.pkg = cls.getPackage();
this.cls = cls;
this.verbose = pkg.verbose;
this.in = new DataInputStream(new FilterInputStream(in) {
public int read(byte b[], int off, int len) throws IOException {
int nr = super.read(b, off, len);
if (nr >= 0) inPos += nr;
return nr;
}
public int read() throws IOException {
int ch = super.read();
if (ch >= 0) inPos += 1;
return ch;
}
public long skip(long n) throws IOException {
long ns = super.skip(n);
if (ns >= 0) inPos += ns;
return ns;
}
});
}
public void setAttrDefs(Map<Attribute.Layout, Attribute> attrDefs) {
this.attrDefs = attrDefs;
}
public void setAttrCommands(Map<Attribute.Layout, String> attrCommands) {
this.attrCommands = attrCommands;
}
private void skip(int n, String what) throws IOException {
Utils.log.warning("skipping "+n+" bytes of "+what);
long skipped = 0;
while (skipped < n) {
long j = in.skip(n - skipped);
assert(j > 0);
skipped += j;
}
assert(skipped == n);
}
private int readUnsignedShort() throws IOException {
return in.readUnsignedShort();
}
private int readInt() throws IOException {
return in.readInt();
}
/** Read a 2-byte int, and return the <em>global</em> CP entry for it. */
private Entry readRef() throws IOException {
int i = in.readUnsignedShort();
return i == 0 ? null : cls.cpMap[i];
}
private Entry readRef(byte tag) throws IOException {
Entry e = readRef();
assert(!(e instanceof UnresolvedEntry));
checkTag(e, tag);
return e;
}
/** Throw a ClassFormatException if the entry does not match the expected tag type. */
private Entry checkTag(Entry e, byte tag) throws ClassFormatException {
if (e == null || !e.tagMatches(tag)) {
String where = (inPos == constantPoolLimit
? " in constant pool"
: " at pos: " + inPos);
String got = (e == null
? "null CP index"
: "type=" + ConstantPool.tagName(e.tag));
throw new ClassFormatException("Bad constant, expected type=" +
ConstantPool.tagName(tag) +
" got "+ got + ", in File: " + cls.file.nameString + where);
}
return e;
}
private Entry checkTag(Entry e, byte tag, boolean nullOK) throws ClassFormatException {
return nullOK && e == null ? null : checkTag(e, tag);
}
private Entry readRefOrNull(byte tag) throws IOException {
Entry e = readRef();
checkTag(e, tag, true);
return e;
}
private Utf8Entry readUtf8Ref() throws IOException {
return (Utf8Entry) readRef(CONSTANT_Utf8);
}
private ClassEntry readClassRef() throws IOException {
return (ClassEntry) readRef(CONSTANT_Class);
}
private ClassEntry readClassRefOrNull() throws IOException {
return (ClassEntry) readRefOrNull(CONSTANT_Class);
}
private SignatureEntry readSignatureRef() throws IOException {
// The class file stores a Utf8, but we want a Signature.
Entry e = readRef(CONSTANT_Signature);
return (e != null && e.getTag() == CONSTANT_Utf8)
? ConstantPool.getSignatureEntry(e.stringValue())
: (SignatureEntry) e;
}
void read() throws IOException {
boolean ok = false;
try {
readMagicNumbers();
readConstantPool();
readHeader();
readMembers(false); // fields
readMembers(true); // methods
readAttributes(ATTR_CONTEXT_CLASS, cls);
fixUnresolvedEntries();
cls.finishReading();
assert(0 >= in.read(new byte[1]));
ok = true;
} finally {
if (!ok) {
if (verbose > 0) Utils.log.warning("Erroneous data at input offset "+inPos+" of "+cls.file);
}
}
}
void readMagicNumbers() throws IOException {
cls.magic = in.readInt();
if (cls.magic != JAVA_MAGIC)
throw new Attribute.FormatException
("Bad magic number in class file "
+Integer.toHexString(cls.magic),
ATTR_CONTEXT_CLASS, "magic-number", "pass");
int minver = (short) readUnsignedShort();
int majver = (short) readUnsignedShort();
cls.version = Package.Version.of(majver, minver);
//System.out.println("ClassFile.version="+cls.majver+"."+cls.minver);
String bad = checkVersion(cls.version);
if (bad != null) {
throw new Attribute.FormatException
("classfile version too "+bad+": "
+cls.version+" in "+cls.file,
ATTR_CONTEXT_CLASS, "version", "pass");
}
}
private String checkVersion(Package.Version ver) {
int majver = ver.major;
int minver = ver.minor;
if (majver < pkg.minClassVersion.major ||
(majver == pkg.minClassVersion.major &&
minver < pkg.minClassVersion.minor)) {
return "small";
}
if (majver > pkg.maxClassVersion.major ||
(majver == pkg.maxClassVersion.major &&
minver > pkg.maxClassVersion.minor)) {
return "large";
}
return null; // OK
}
void readConstantPool() throws IOException {
int length = in.readUnsignedShort();
//System.err.println("reading CP, length="+length);
int[] fixups = new int[length*4];
int fptr = 0;
Entry[] cpMap = new Entry[length];
cpMap[0] = null;
for (int i = 1; i < length; i++) {
//System.err.println("reading CP elt, i="+i);
int tag = in.readByte();
switch (tag) {
case CONSTANT_Utf8:
cpMap[i] = ConstantPool.getUtf8Entry(in.readUTF());
break;
case CONSTANT_Integer:
{
cpMap[i] = ConstantPool.getLiteralEntry(in.readInt());
}
break;
case CONSTANT_Float:
{
cpMap[i] = ConstantPool.getLiteralEntry(in.readFloat());
}
break;
case CONSTANT_Long:
{
cpMap[i] = ConstantPool.getLiteralEntry(in.readLong());
cpMap[++i] = null;
}
break;
case CONSTANT_Double:
{
cpMap[i] = ConstantPool.getLiteralEntry(in.readDouble());
cpMap[++i] = null;
}
break;
// just read the refs; do not attempt to resolve while reading
case CONSTANT_Class:
case CONSTANT_String:
case CONSTANT_MethodType:
fixups[fptr++] = i;
fixups[fptr++] = tag;
fixups[fptr++] = in.readUnsignedShort();
fixups[fptr++] = -1; // empty ref2
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_NameandType:
fixups[fptr++] = i;
fixups[fptr++] = tag;
fixups[fptr++] = in.readUnsignedShort();
fixups[fptr++] = in.readUnsignedShort();
break;
case CONSTANT_InvokeDynamic:
fixups[fptr++] = i;
fixups[fptr++] = tag;
fixups[fptr++] = -1 ^ in.readUnsignedShort(); // not a ref
fixups[fptr++] = in.readUnsignedShort();
break;
case CONSTANT_MethodHandle:
fixups[fptr++] = i;
fixups[fptr++] = tag;
fixups[fptr++] = -1 ^ in.readUnsignedByte();
fixups[fptr++] = in.readUnsignedShort();
break;
default:
throw new ClassFormatException("Bad constant pool tag " +
tag + " in File: " + cls.file.nameString +
" at pos: " + inPos);
}
}
constantPoolLimit = inPos;
// Fix up refs, which might be out of order.
while (fptr > 0) {
if (verbose > 3)
Utils.log.fine("CP fixups ["+fptr/4+"]");
int flimit = fptr;
fptr = 0;
for (int fi = 0; fi < flimit; ) {
int cpi = fixups[fi++];
int tag = fixups[fi++];
int ref = fixups[fi++];
int ref2 = fixups[fi++];
if (verbose > 3)
Utils.log.fine(" cp["+cpi+"] = "+ConstantPool.tagName(tag)+"{"+ref+","+ref2+"}");
if (ref >= 0 && cpMap[ref] == null || ref2 >= 0 && cpMap[ref2] == null) {
// Defer.
fixups[fptr++] = cpi;
fixups[fptr++] = tag;
fixups[fptr++] = ref;
fixups[fptr++] = ref2;
continue;
}
switch (tag) {
case CONSTANT_Class:
cpMap[cpi] = ConstantPool.getClassEntry(cpMap[ref].stringValue());
break;
case CONSTANT_String:
cpMap[cpi] = ConstantPool.getStringEntry(cpMap[ref].stringValue());
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
ClassEntry mclass = (ClassEntry) checkTag(cpMap[ref], CONSTANT_Class);
DescriptorEntry mdescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType);
cpMap[cpi] = ConstantPool.getMemberEntry((byte)tag, mclass, mdescr);
break;
case CONSTANT_NameandType:
Utf8Entry mname = (Utf8Entry) checkTag(cpMap[ref], CONSTANT_Utf8);
Utf8Entry mtype = (Utf8Entry) checkTag(cpMap[ref2], CONSTANT_Signature);
cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype);
break;
case CONSTANT_MethodType:
cpMap[cpi] = ConstantPool.getMethodTypeEntry((Utf8Entry) checkTag(cpMap[ref], CONSTANT_Signature));
break;
case CONSTANT_MethodHandle:
byte refKind = (byte)(-1 ^ ref);
MemberEntry memRef = (MemberEntry) checkTag(cpMap[ref2], CONSTANT_AnyMember);
cpMap[cpi] = ConstantPool.getMethodHandleEntry(refKind, memRef);
break;
case CONSTANT_InvokeDynamic:
DescriptorEntry idescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType);
cpMap[cpi] = new UnresolvedEntry((byte)tag, (-1 ^ ref), idescr);
// Note that ref must be resolved later, using the BootstrapMethods attribute.
break;
default:
assert(false);
}
}
assert(fptr < flimit); // Must make progress.
}
cls.cpMap = cpMap;
}
private /*non-static*/
class UnresolvedEntry extends Entry {
final Object[] refsOrIndexes;
UnresolvedEntry(byte tag, Object... refsOrIndexes) {
super(tag);
this.refsOrIndexes = refsOrIndexes;
ClassReader.this.haveUnresolvedEntry = true;
}
Entry resolve() {
Class cls = ClassReader.this.cls;
Entry res;
switch (tag) {
case CONSTANT_InvokeDynamic:
BootstrapMethodEntry iboots = cls.bootstrapMethods.get((Integer) refsOrIndexes[0]);
DescriptorEntry idescr = (DescriptorEntry) refsOrIndexes[1];
res = ConstantPool.getInvokeDynamicEntry(iboots, idescr);
break;
default:
throw new AssertionError();
}
return res;
}
private void unresolved() { throw new RuntimeException("unresolved entry has no string"); }
public int compareTo(Object x) { unresolved(); return 0; }
public boolean equals(Object x) { unresolved(); return false; }
protected int computeValueHash() { unresolved(); return 0; }
public String stringValue() { unresolved(); return toString(); }
public String toString() { return "(unresolved "+ConstantPool.tagName(tag)+")"; }
}
boolean haveUnresolvedEntry;
private void fixUnresolvedEntries() {
if (!haveUnresolvedEntry) return;
Entry[] cpMap = cls.getCPMap();
for (int i = 0; i < cpMap.length; i++) {
Entry e = cpMap[i];
if (e instanceof UnresolvedEntry) {
cpMap[i] = e = ((UnresolvedEntry)e).resolve();
assert(!(e instanceof UnresolvedEntry));
}
}
haveUnresolvedEntry = false;
}
void readHeader() throws IOException {
cls.flags = readUnsignedShort();
cls.thisClass = readClassRef();
cls.superClass = readClassRefOrNull();
int ni = readUnsignedShort();
cls.interfaces = new ClassEntry[ni];
for (int i = 0; i < ni; i++) {
cls.interfaces[i] = readClassRef();
}
}
void readMembers(boolean doMethods) throws IOException {
int nm = readUnsignedShort();
for (int i = 0; i < nm; i++) {
readMember(doMethods);
}
}
void readMember(boolean doMethod) throws IOException {
int mflags = readUnsignedShort();
Utf8Entry mname = readUtf8Ref();
SignatureEntry mtype = readSignatureRef();
DescriptorEntry descr = ConstantPool.getDescriptorEntry(mname, mtype);
Class.Member m;
if (!doMethod)
m = cls.new Field(mflags, descr);
else
m = cls.new Method(mflags, descr);
readAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD,
m);
}
void readAttributes(int ctype, Attribute.Holder h) throws IOException {
int na = readUnsignedShort();
if (na == 0) return; // nothing to do here
if (verbose > 3)
Utils.log.fine("readAttributes "+h+" ["+na+"]");
for (int i = 0; i < na; i++) {
String name = readUtf8Ref().stringValue();
int length = readInt();
// See if there is a special command that applies.
if (attrCommands != null) {
Attribute.Layout lkey = Attribute.keyForLookup(ctype, name);
String cmd = attrCommands.get(lkey);
if (cmd != null) {
switch (cmd) {
case "pass":
String message1 = "passing attribute bitwise in " + h;
throw new Attribute.FormatException(message1, ctype, name, cmd);
case "error":
String message2 = "attribute not allowed in " + h;
throw new Attribute.FormatException(message2, ctype, name, cmd);
case "strip":
skip(length, name + " attribute in " + h);
continue;
}
}
}
// Find canonical instance of the requested attribute.
Attribute a = Attribute.lookup(Package.attrDefs, ctype, name);
if (verbose > 4 && a != null)
Utils.log.fine("pkg_attribute_lookup "+name+" = "+a);
if (a == null) {
a = Attribute.lookup(this.attrDefs, ctype, name);
if (verbose > 4 && a != null)
Utils.log.fine("this "+name+" = "+a);
}
if (a == null) {
a = Attribute.lookup(null, ctype, name);
if (verbose > 4 && a != null)
Utils.log.fine("null_attribute_lookup "+name+" = "+a);
}
if (a == null && length == 0) {
// Any zero-length attr is "known"...
// We can assume an empty attr. has an empty layout.
// Handles markers like Enum, Bridge, Synthetic, Deprecated.
a = Attribute.find(ctype, name, "");
}
boolean isStackMap = (ctype == ATTR_CONTEXT_CODE
&& (name.equals("StackMap") ||
name.equals("StackMapX")));
if (isStackMap) {
// Known attribute but with a corner case format, "pass" it.
Code code = (Code) h;
final int TOO_BIG = 0x10000;
if (code.max_stack >= TOO_BIG ||
code.max_locals >= TOO_BIG ||
code.getLength() >= TOO_BIG ||
name.endsWith("X")) {
// No, we don't really know what to do with this one.
// Do not compress the rare and strange "u4" and "X" cases.
a = null;
}
}
if (a == null) {
if (isStackMap) {
// Known attribute but w/o a format; pass it.
String message = "unsupported StackMap variant in "+h;
throw new Attribute.FormatException(message, ctype, name,
"pass");
} else if ("strip".equals(unknownAttrCommand)) {
// Skip the unknown attribute.
skip(length, "unknown "+name+" attribute in "+h);
continue;
} else {
String message = " is unknown attribute in class " + h;
throw new Attribute.FormatException(message, ctype, name,
unknownAttrCommand);
}
}
long pos0 = inPos; // in case we want to check it
if (a.layout() == Package.attrCodeEmpty) {
// These are hardwired.
Class.Method m = (Class.Method) h;
m.code = new Code(m);
try {
readCode(m.code);
} catch (Instruction.FormatException iie) {
String message = iie.getMessage() + " in " + h;
throw new ClassReader.ClassFormatException(message, iie);
}
assert(length == inPos - pos0);
// Keep empty attribute a...
} else if (a.layout() == Package.attrBootstrapMethodsEmpty) {
assert(h == cls);
readBootstrapMethods(cls);
assert(length == inPos - pos0);
// Delete the attribute; it is logically part of the constant pool.
continue;
} else if (a.layout() == Package.attrInnerClassesEmpty) {
// These are hardwired also.
assert(h == cls);
readInnerClasses(cls);
assert(length == inPos - pos0);
// Keep empty attribute a...
} else if (length > 0) {
byte[] bytes = new byte[length];
in.readFully(bytes);
a = a.addContent(bytes);
}
if (a.size() == 0 && !a.layout().isEmpty()) {
throw new ClassFormatException(name +
": attribute length cannot be zero, in " + h);
}
h.addAttribute(a);
if (verbose > 2)
Utils.log.fine("read "+a);
}
}
void readCode(Code code) throws IOException {
code.max_stack = readUnsignedShort();
code.max_locals = readUnsignedShort();
code.bytes = new byte[readInt()];
in.readFully(code.bytes);
Entry[] cpMap = cls.getCPMap();
Instruction.opcodeChecker(code.bytes, cpMap, this.cls.version);
int nh = readUnsignedShort();
code.setHandlerCount(nh);
for (int i = 0; i < nh; i++) {
code.handler_start[i] = readUnsignedShort();
code.handler_end[i] = readUnsignedShort();
code.handler_catch[i] = readUnsignedShort();
code.handler_class[i] = readClassRefOrNull();
}
readAttributes(ATTR_CONTEXT_CODE, code);
}
void readBootstrapMethods(Class cls) throws IOException {
BootstrapMethodEntry[] bsms = new BootstrapMethodEntry[readUnsignedShort()];
for (int i = 0; i < bsms.length; i++) {
MethodHandleEntry bsmRef = (MethodHandleEntry) readRef(CONSTANT_MethodHandle);
Entry[] argRefs = new Entry[readUnsignedShort()];
for (int j = 0; j < argRefs.length; j++) {
argRefs[j] = readRef(CONSTANT_LoadableValue);
}
bsms[i] = ConstantPool.getBootstrapMethodEntry(bsmRef, argRefs);
}
cls.setBootstrapMethods(Arrays.asList(bsms));
}
void readInnerClasses(Class cls) throws IOException {
int nc = readUnsignedShort();
ArrayList<InnerClass> ics = new ArrayList<>(nc);
for (int i = 0; i < nc; i++) {
InnerClass ic =
new InnerClass(readClassRef(),
readClassRefOrNull(),
(Utf8Entry)readRefOrNull(CONSTANT_Utf8),
readUnsignedShort());
ics.add(ic);
}
cls.innerClasses = ics; // set directly; do not use setInnerClasses.
// (Later, ics may be transferred to the pkg.)
}
static class ClassFormatException extends IOException {
private static final long serialVersionUID = -3564121733989501833L;
public ClassFormatException(String message) {
super(message);
}
public ClassFormatException(String message, Throwable cause) {
super(message, cause);
}
}
}

View file

@ -0,0 +1,317 @@
/*
* Copyright (c) 2001, 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.java.util.jar.pack;
import com.sun.java.util.jar.pack.ConstantPool.Entry;
import com.sun.java.util.jar.pack.ConstantPool.Index;
import com.sun.java.util.jar.pack.ConstantPool.NumberEntry;
import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry;
import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry;
import com.sun.java.util.jar.pack.Package.Class;
import com.sun.java.util.jar.pack.Package.InnerClass;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Writer for a class file that is incorporated into a package.
* @author John Rose
*/
class ClassWriter {
int verbose;
Package pkg;
Class cls;
DataOutputStream out;
Index cpIndex;
Index bsmIndex;
ClassWriter(Class cls, OutputStream out) throws IOException {
this.pkg = cls.getPackage();
this.cls = cls;
this.verbose = pkg.verbose;
this.out = new DataOutputStream(new BufferedOutputStream(out));
this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap());
this.cpIndex.flattenSigs = true;
if (cls.hasBootstrapMethods()) {
this.bsmIndex = ConstantPool.makeIndex(cpIndex.debugName+".BootstrapMethods",
cls.getBootstrapMethodMap());
}
if (verbose > 1)
Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString()));
}
private void writeShort(int x) throws IOException {
out.writeShort(x);
}
private void writeInt(int x) throws IOException {
out.writeInt(x);
}
/** Write a 2-byte int representing a CP entry, using the local cpIndex. */
private void writeRef(Entry e) throws IOException {
writeRef(e, cpIndex);
}
/** Write a 2-byte int representing a CP entry, using the given cpIndex. */
private void writeRef(Entry e, Index cpIndex) throws IOException {
int i = (e == null) ? 0 : cpIndex.indexOf(e);
writeShort(i);
}
void write() throws IOException {
boolean ok = false;
try {
if (verbose > 1) Utils.log.fine("...writing "+cls);
writeMagicNumbers();
writeConstantPool();
writeHeader();
writeMembers(false); // fields
writeMembers(true); // methods
writeAttributes(ATTR_CONTEXT_CLASS, cls);
/* Closing here will cause all the underlying
streams to close, Causing the jar stream
to close prematurely, instead we just flush.
out.close();
*/
out.flush();
ok = true;
} finally {
if (!ok) {
Utils.log.warning("Error on output of "+cls);
}
}
}
void writeMagicNumbers() throws IOException {
writeInt(cls.magic);
writeShort(cls.version.minor);
writeShort(cls.version.major);
}
void writeConstantPool() throws IOException {
Entry[] cpMap = cls.cpMap;
writeShort(cpMap.length);
for (int i = 0; i < cpMap.length; i++) {
Entry e = cpMap[i];
assert((e == null) == (i == 0 || cpMap[i-1] != null && cpMap[i-1].isDoubleWord()));
if (e == null) continue;
byte tag = e.getTag();
if (verbose > 2) Utils.log.fine(" CP["+i+"] = "+e);
out.write(tag);
switch (tag) {
case CONSTANT_Signature:
throw new AssertionError("CP should have Signatures remapped to Utf8");
case CONSTANT_Utf8:
out.writeUTF(e.stringValue());
break;
case CONSTANT_Integer:
out.writeInt(((NumberEntry)e).numberValue().intValue());
break;
case CONSTANT_Float:
float fval = ((NumberEntry)e).numberValue().floatValue();
out.writeInt(Float.floatToRawIntBits(fval));
break;
case CONSTANT_Long:
out.writeLong(((NumberEntry)e).numberValue().longValue());
break;
case CONSTANT_Double:
double dval = ((NumberEntry)e).numberValue().doubleValue();
out.writeLong(Double.doubleToRawLongBits(dval));
break;
case CONSTANT_Class:
case CONSTANT_String:
case CONSTANT_MethodType:
writeRef(e.getRef(0));
break;
case CONSTANT_MethodHandle:
MethodHandleEntry mhe = (MethodHandleEntry) e;
out.writeByte(mhe.refKind);
writeRef(mhe.getRef(0));
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_NameandType:
writeRef(e.getRef(0));
writeRef(e.getRef(1));
break;
case CONSTANT_InvokeDynamic:
writeRef(e.getRef(0), bsmIndex);
writeRef(e.getRef(1));
break;
case CONSTANT_BootstrapMethod:
throw new AssertionError("CP should have BootstrapMethods moved to side-table");
default:
throw new IOException("Bad constant pool tag "+tag);
}
}
}
void writeHeader() throws IOException {
writeShort(cls.flags);
writeRef(cls.thisClass);
writeRef(cls.superClass);
writeShort(cls.interfaces.length);
for (int i = 0; i < cls.interfaces.length; i++) {
writeRef(cls.interfaces[i]);
}
}
void writeMembers(boolean doMethods) throws IOException {
List<? extends Class.Member> mems;
if (!doMethods)
mems = cls.getFields();
else
mems = cls.getMethods();
writeShort(mems.size());
for (Class.Member m : mems) {
writeMember(m, doMethods);
}
}
void writeMember(Class.Member m, boolean doMethod) throws IOException {
if (verbose > 2) Utils.log.fine("writeMember "+m);
writeShort(m.flags);
writeRef(m.getDescriptor().nameRef);
writeRef(m.getDescriptor().typeRef);
writeAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD,
m);
}
private void reorderBSMandICS(Attribute.Holder h) {
Attribute bsmAttr = h.getAttribute(Package.attrBootstrapMethodsEmpty);
if (bsmAttr == null) return;
Attribute icsAttr = h.getAttribute(Package.attrInnerClassesEmpty);
if (icsAttr == null) return;
int bsmidx = h.attributes.indexOf(bsmAttr);
int icsidx = h.attributes.indexOf(icsAttr);
if (bsmidx > icsidx) {
h.attributes.remove(bsmAttr);
h.attributes.add(icsidx, bsmAttr);
}
return;
}
// handy buffer for collecting attrs
ByteArrayOutputStream buf = new ByteArrayOutputStream();
DataOutputStream bufOut = new DataOutputStream(buf);
void writeAttributes(int ctype, Attribute.Holder h) throws IOException {
if (h.attributes == null) {
writeShort(0); // attribute size
return;
}
// there may be cases if an InnerClass attribute is explicit, then the
// ordering could be wrong, fix the ordering before we write it out.
if (h instanceof Package.Class)
reorderBSMandICS(h);
writeShort(h.attributes.size());
for (Attribute a : h.attributes) {
a.finishRefs(cpIndex);
writeRef(a.getNameRef());
if (a.layout() == Package.attrCodeEmpty ||
a.layout() == Package.attrBootstrapMethodsEmpty ||
a.layout() == Package.attrInnerClassesEmpty) {
// These are hardwired.
DataOutputStream savedOut = out;
assert(out != bufOut);
buf.reset();
out = bufOut;
if ("Code".equals(a.name())) {
Class.Method m = (Class.Method) h;
writeCode(m.code);
} else if ("BootstrapMethods".equals(a.name())) {
assert(h == cls);
writeBootstrapMethods(cls);
} else if ("InnerClasses".equals(a.name())) {
assert(h == cls);
writeInnerClasses(cls);
} else {
throw new AssertionError();
}
out = savedOut;
if (verbose > 2)
Utils.log.fine("Attribute "+a.name()+" ["+buf.size()+"]");
writeInt(buf.size());
buf.writeTo(out);
} else {
if (verbose > 2)
Utils.log.fine("Attribute "+a.name()+" ["+a.size()+"]");
writeInt(a.size());
out.write(a.bytes());
}
}
}
void writeCode(Code code) throws IOException {
code.finishRefs(cpIndex);
writeShort(code.max_stack);
writeShort(code.max_locals);
writeInt(code.bytes.length);
out.write(code.bytes);
int nh = code.getHandlerCount();
writeShort(nh);
for (int i = 0; i < nh; i++) {
writeShort(code.handler_start[i]);
writeShort(code.handler_end[i]);
writeShort(code.handler_catch[i]);
writeRef(code.handler_class[i]);
}
writeAttributes(ATTR_CONTEXT_CODE, code);
}
void writeBootstrapMethods(Class cls) throws IOException {
List<BootstrapMethodEntry> bsms = cls.getBootstrapMethods();
writeShort(bsms.size());
for (BootstrapMethodEntry e : bsms) {
writeRef(e.bsmRef);
writeShort(e.argRefs.length);
for (Entry argRef : e.argRefs) {
writeRef(argRef);
}
}
}
void writeInnerClasses(Class cls) throws IOException {
List<InnerClass> ics = cls.getInnerClasses();
writeShort(ics.size());
for (InnerClass ic : ics) {
writeRef(ic.thisClass);
writeRef(ic.outerClass);
writeRef(ic.name);
writeShort(ic.flags);
}
}
}

View file

@ -0,0 +1,398 @@
/*
* Copyright (c) 2001, 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.java.util.jar.pack;
import com.sun.java.util.jar.pack.Package.Class;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Represents a chunk of bytecodes.
* @author John Rose
*/
class Code extends Attribute.Holder {
Class.Method m;
public Code(Class.Method m) {
this.m = m;
}
public Class.Method getMethod() {
return m;
}
public Class thisClass() {
return m.thisClass();
}
public Package getPackage() {
return m.thisClass().getPackage();
}
public ConstantPool.Entry[] getCPMap() {
return m.getCPMap();
}
private static final ConstantPool.Entry[] noRefs = ConstantPool.noRefs;
// The following fields are used directly by the ClassReader, etc.
int max_stack;
int max_locals;
ConstantPool.Entry handler_class[] = noRefs;
int handler_start[] = noInts;
int handler_end[] = noInts;
int handler_catch[] = noInts;
byte[] bytes;
Fixups fixups; // reference relocations, if any are required
Object insnMap; // array of instruction boundaries
int getLength() { return bytes.length; }
int getMaxStack() {
return max_stack;
}
void setMaxStack(int ms) {
max_stack = ms;
}
int getMaxNALocals() {
int argsize = m.getArgumentSize();
return max_locals - argsize;
}
void setMaxNALocals(int ml) {
int argsize = m.getArgumentSize();
max_locals = argsize + ml;
}
int getHandlerCount() {
assert(handler_class.length == handler_start.length);
assert(handler_class.length == handler_end.length);
assert(handler_class.length == handler_catch.length);
return handler_class.length;
}
void setHandlerCount(int h) {
if (h > 0) {
handler_class = new ConstantPool.Entry[h];
handler_start = new int[h];
handler_end = new int[h];
handler_catch = new int[h];
// caller must fill these in ASAP
}
}
void setBytes(byte[] bytes) {
this.bytes = bytes;
if (fixups != null)
fixups.setBytes(bytes);
}
void setInstructionMap(int[] insnMap, int mapLen) {
//int[] oldMap = null;
//assert((oldMap = getInstructionMap()) != null);
this.insnMap = allocateInstructionMap(insnMap, mapLen);
//assert(Arrays.equals(oldMap, getInstructionMap()));
}
void setInstructionMap(int[] insnMap) {
setInstructionMap(insnMap, insnMap.length);
}
int[] getInstructionMap() {
return expandInstructionMap(getInsnMap());
}
void addFixups(Collection<Fixups.Fixup> moreFixups) {
if (fixups == null) {
fixups = new Fixups(bytes);
}
assert(fixups.getBytes() == bytes);
fixups.addAll(moreFixups);
}
public void trimToSize() {
if (fixups != null) {
fixups.trimToSize();
if (fixups.size() == 0)
fixups = null;
}
super.trimToSize();
}
protected void visitRefs(int mode, Collection<ConstantPool.Entry> refs) {
int verbose = getPackage().verbose;
if (verbose > 2)
System.out.println("Reference scan "+this);
refs.addAll(Arrays.asList(handler_class));
if (fixups != null) {
fixups.visitRefs(refs);
} else {
// References (to a local cpMap) are embedded in the bytes.
ConstantPool.Entry[] cpMap = getCPMap();
for (Instruction i = instructionAt(0); i != null; i = i.next()) {
if (verbose > 4)
System.out.println(i);
int cpref = i.getCPIndex();
if (cpref >= 0) {
refs.add(cpMap[cpref]);
}
}
}
// Handle attribute list:
super.visitRefs(mode, refs);
}
// Since bytecodes are the single largest contributor to
// package size, it's worth a little bit of trouble
// to reduce the per-bytecode memory footprint.
// In the current scheme, half of the bulk of these arrays
// due to bytes, and half to shorts. (Ints are insignificant.)
// Given an average of 1.8 bytes per instruction, this means
// instruction boundary arrays are about a 75% overhead--tolerable.
// (By using bytes, we get 33% savings over just shorts and ints.
// Using both bytes and shorts gives 66% savings over just ints.)
static final boolean shrinkMaps = true;
private Object allocateInstructionMap(int[] insnMap, int mapLen) {
int PClimit = getLength();
if (shrinkMaps && PClimit <= Byte.MAX_VALUE - Byte.MIN_VALUE) {
byte[] map = new byte[mapLen+1];
for (int i = 0; i < mapLen; i++) {
map[i] = (byte)(insnMap[i] + Byte.MIN_VALUE);
}
map[mapLen] = (byte)(PClimit + Byte.MIN_VALUE);
return map;
} else if (shrinkMaps && PClimit < Short.MAX_VALUE - Short.MIN_VALUE) {
short[] map = new short[mapLen+1];
for (int i = 0; i < mapLen; i++) {
map[i] = (short)(insnMap[i] + Short.MIN_VALUE);
}
map[mapLen] = (short)(PClimit + Short.MIN_VALUE);
return map;
} else {
int[] map = Arrays.copyOf(insnMap, mapLen + 1);
map[mapLen] = PClimit;
return map;
}
}
private int[] expandInstructionMap(Object map0) {
int[] imap;
if (map0 instanceof byte[]) {
byte[] map = (byte[]) map0;
imap = new int[map.length-1];
for (int i = 0; i < imap.length; i++) {
imap[i] = map[i] - Byte.MIN_VALUE;
}
} else if (map0 instanceof short[]) {
short[] map = (short[]) map0;
imap = new int[map.length-1];
for (int i = 0; i < imap.length; i++) {
imap[i] = map[i] - Byte.MIN_VALUE;
}
} else {
int[] map = (int[]) map0;
imap = Arrays.copyOfRange(map, 0, map.length - 1);
}
return imap;
}
Object getInsnMap() {
// Build a map of instruction boundaries.
if (insnMap != null) {
return insnMap;
}
int[] map = new int[getLength()];
int fillp = 0;
for (Instruction i = instructionAt(0); i != null; i = i.next()) {
map[fillp++] = i.getPC();
}
// Make it byte[], short[], or int[] according to the max BCI.
insnMap = allocateInstructionMap(map, fillp);
//assert(assertBCICodingsOK());
return insnMap;
}
/** Encode the given BCI as an instruction boundary number.
* For completeness, irregular (non-boundary) BCIs are
* encoded compactly immediately after the boundary numbers.
* This encoding is the identity mapping outside 0..length,
* and it is 1-1 everywhere. All by itself this technique
* improved zipped rt.jar compression by 2.6%.
*/
public int encodeBCI(int bci) {
if (bci <= 0 || bci > getLength()) return bci;
Object map0 = getInsnMap();
int i, len;
if (shrinkMaps && map0 instanceof byte[]) {
byte[] map = (byte[]) map0;
len = map.length;
i = Arrays.binarySearch(map, (byte)(bci + Byte.MIN_VALUE));
} else if (shrinkMaps && map0 instanceof short[]) {
short[] map = (short[]) map0;
len = map.length;
i = Arrays.binarySearch(map, (short)(bci + Short.MIN_VALUE));
} else {
int[] map = (int[]) map0;
len = map.length;
i = Arrays.binarySearch(map, bci);
}
assert(i != -1);
assert(i != 0);
assert(i != len);
assert(i != -len-1);
return (i >= 0) ? i : len + bci - (-i-1);
}
public int decodeBCI(int bciCode) {
if (bciCode <= 0 || bciCode > getLength()) return bciCode;
Object map0 = getInsnMap();
int i, len;
// len == map.length
// If bciCode < len, result is map[bciCode], the common and fast case.
// Otherwise, let map[i] be the smallest map[*] larger than bci.
// Then, required by the return statement of encodeBCI:
// bciCode == len + bci - i
// Thus:
// bci-i == bciCode-len
// map[i]-adj-i == bciCode-len ; adj in (0..map[i]-map[i-1])
// We can solve this by searching for adjacent entries
// map[i-1], map[i] such that:
// map[i-1]-(i-1) <= bciCode-len < map[i]-i
// This can be approximated by searching map[i] for bciCode and then
// linear searching backward. Given the right i, we then have:
// bci == bciCode-len + i
// This linear search is at its worst case for indexes in the beginning
// of a large method, but it's not clear that this is a problem in
// practice, since BCIs are usually on instruction boundaries.
if (shrinkMaps && map0 instanceof byte[]) {
byte[] map = (byte[]) map0;
len = map.length;
if (bciCode < len)
return map[bciCode] - Byte.MIN_VALUE;
i = Arrays.binarySearch(map, (byte)(bciCode + Byte.MIN_VALUE));
if (i < 0) i = -i-1;
int key = bciCode-len + Byte.MIN_VALUE;
for (;; i--) {
if (map[i-1]-(i-1) <= key) break;
}
} else if (shrinkMaps && map0 instanceof short[]) {
short[] map = (short[]) map0;
len = map.length;
if (bciCode < len)
return map[bciCode] - Short.MIN_VALUE;
i = Arrays.binarySearch(map, (short)(bciCode + Short.MIN_VALUE));
if (i < 0) i = -i-1;
int key = bciCode-len + Short.MIN_VALUE;
for (;; i--) {
if (map[i-1]-(i-1) <= key) break;
}
} else {
int[] map = (int[]) map0;
len = map.length;
if (bciCode < len)
return map[bciCode];
i = Arrays.binarySearch(map, bciCode);
if (i < 0) i = -i-1;
int key = bciCode-len;
for (;; i--) {
if (map[i-1]-(i-1) <= key) break;
}
}
return bciCode-len + i;
}
public void finishRefs(ConstantPool.Index ix) {
if (fixups != null) {
fixups.finishRefs(ix);
fixups = null;
}
// Code attributes are finished in ClassWriter.writeAttributes.
}
Instruction instructionAt(int pc) {
return Instruction.at(bytes, pc);
}
static boolean flagsRequireCode(int flags) {
// A method's flags force it to have a Code attribute,
// if the flags are neither native nor abstract.
return (flags & (Modifier.NATIVE | Modifier.ABSTRACT)) == 0;
}
public String toString() {
return m+".Code";
}
/// Fetching values from my own array.
public int getInt(int pc) { return Instruction.getInt(bytes, pc); }
public int getShort(int pc) { return Instruction.getShort(bytes, pc); }
public int getByte(int pc) { return Instruction.getByte(bytes, pc); }
void setInt(int pc, int x) { Instruction.setInt(bytes, pc, x); }
void setShort(int pc, int x) { Instruction.setShort(bytes, pc, x); }
void setByte(int pc, int x) { Instruction.setByte(bytes, pc, x); }
/* TEST CODE ONLY
private boolean assertBCICodingsOK() {
boolean ok = true;
int len = java.lang.reflect.Array.getLength(insnMap);
int base = 0;
if (insnMap.getClass().getComponentType() == Byte.TYPE)
base = Byte.MIN_VALUE;
if (insnMap.getClass().getComponentType() == Short.TYPE)
base = Short.MIN_VALUE;
for (int i = -1, imax = getLength()+1; i <= imax; i++) {
int bci = i;
int enc = Math.min(-999, bci-1);
int dec = enc;
try {
enc = encodeBCI(bci);
dec = decodeBCI(enc);
} catch (RuntimeException ee) {
ee.printStackTrace();
}
if (dec == bci) {
//System.out.println("BCI="+bci+(enc<len?"":" ")+" enc="+enc);
continue;
}
if (ok) {
for (int q = 0; q <= 1; q++) {
StringBuffer sb = new StringBuffer();
sb.append("bci "+(q==0?"map":"del")+"["+len+"] = {");
for (int j = 0; j < len; j++) {
int mapi = ((Number)java.lang.reflect.Array.get(insnMap, j)).intValue() - base;
mapi -= j*q;
sb.append(" "+mapi);
}
sb.append(" }");
System.out.println("*** "+sb);
}
}
System.out.println("*** BCI="+bci+" enc="+enc+" dec="+dec);
ok = false;
}
return ok;
}
//*/
}

View file

@ -0,0 +1,906 @@
/*
* Copyright (c) 2001, 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.java.util.jar.pack;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Define the conversions between sequences of small integers and raw bytes.
* This is a schema of encodings which incorporates varying lengths,
* varying degrees of length variability, and varying amounts of signed-ness.
* @author John Rose
*/
class Coding implements Comparable<Coding>, CodingMethod, Histogram.BitMetric {
/*
Coding schema for single integers, parameterized by (B,H,S):
Let B in [1,5], H in [1,256], S in [0,3].
(S limit is arbitrary. B follows the 32-bit limit. H is byte size.)
A given (B,H,S) code varies in length from 1 to B bytes.
The 256 values a byte may take on are divided into L=(256-H) and H
values, with all the H values larger than the L values.
(That is, the L values are [0,L) and the H are [L,256).)
The last byte is always either the B-th byte, a byte with "L value"
(<L), or both. There is no other byte that satisfies these conditions.
All bytes before the last always have "H values" (>=L).
Therefore, if L==0, the code always has the full length of B bytes.
The coding then becomes a classic B-byte little-endian unsigned integer.
(Also, if L==128, the high bit of each byte acts signals the presence
of a following byte, up to the maximum length.)
In the unsigned case (S==0), the coding is compact and monotonic
in the ordering of byte sequences defined by appending zero bytes
to pad them to a common length B, reversing them, and ordering them
lexicographically. (This agrees with "little-endian" byte order.)
Therefore, the unsigned value of a byte sequence may be defined as:
<pre>
U(b0) == b0
in [0..L)
or [0..256) if B==1 (**)
U(b0,b1) == b0 + b1*H
in [L..L*(1+H))
or [L..L*(1+H) + H^2) if B==2
U(b0,b1,b2) == b0 + b1*H + b2*H^2
in [L*(1+H)..L*(1+H+H^2))
or [L*(1+H)..L*(1+H+H^2) + H^3) if B==3
U(b[i]: i<n) == Sum[i<n]( b[i] * H^i )
up to L*Sum[i<n]( H^i )
or to L*Sum[i<n]( H^i ) + H^n if n==B
</pre>
(**) If B==1, the values H,L play no role in the coding.
As a convention, we require that any (1,H,S) code must always
encode values less than H. Thus, a simple unsigned byte is coded
specifically by the code (1,256,0).
(Properly speaking, the unsigned case should be parameterized as
S==Infinity. If the schema were regular, the case S==0 would really
denote a numbering in which all coded values are negative.)
If S>0, the unsigned value of a byte sequence is regarded as a binary
integer. If any of the S low-order bits are zero, the corresponding
signed value will be non-negative. If all of the S low-order bits
(S>0) are one, the corresponding signed value will be negative.
The non-negative signed values are compact and monotonically increasing
(from 0) in the ordering of the corresponding unsigned values.
The negative signed values are compact and monotonically decreasing
(from -1) in the ordering of the corresponding unsigned values.
In essence, the low-order S bits function as a collective sign bit
for negative signed numbers, and as a low-order base-(2^S-1) digit
for non-negative signed numbers.
Therefore, the signed value corresponding to an unsigned value is:
<pre>
Sgn(x) == x if S==0
Sgn(x) == (x / 2^S)*(2^S-1) + (x % 2^S), if S>0, (x % 2^S) < 2^S-1
Sgn(x) == -(x / 2^S)-1, if S>0, (x % 2^S) == 2^S-1
</pre>
Finally, the value of a byte sequence, given the coding parameters
(B,H,S), is defined as:
<pre>
V(b[i]: i<n) == Sgn(U(b[i]: i<n))
</pre>
The extremal positive and negative signed value for a given range
of unsigned values may be found by sign-encoding the largest unsigned
value which is not 2^S-1 mod 2^S, and that which is, respectively.
Because B,H,S are variable, this is not a single coding but a schema
of codings. For optimal compression, it is necessary to adaptively
select specific codings to the data being compressed.
For example, if a sequence of values happens never to be negative,
S==0 is the best choice. If the values are equally balanced between
negative and positive, S==1. If negative values are rare, then S>1
is more appropriate.
A (B,H,S) encoding is called a "subrange" if it does not encode
the largest 32-bit value, and if the number R of values it does
encode can be expressed as a positive 32-bit value. (Note that
B=1 implies R<=256, B=2 implies R<=65536, etc.)
A delta version of a given (B,H,S) coding encodes an array of integers
by writing their successive differences in the (B,H,S) coding.
The original integers themselves may be recovered by making a
running accumulation of sum of the differences as they are read.
As a special case, if a (B,H,S) encoding is a subrange, its delta
version will only encode arrays of numbers in the coding's unsigned
range, [0..R-1]. The coding of deltas is still in the normal signed
range, if S!=0. During delta encoding, all subtraction results are
reduced to the signed range, by adding multiples of R. Likewise,
. during encoding, all addition results are reduced to the unsigned range.
This special case for subranges allows the benefits of wraparound
when encoding correlated sequences of very small positive numbers.
*/
// Code-specific limits:
private static int saturate32(long x) {
if (x > Integer.MAX_VALUE) return Integer.MAX_VALUE;
if (x < Integer.MIN_VALUE) return Integer.MIN_VALUE;
return (int)x;
}
private static long codeRangeLong(int B, int H) {
return codeRangeLong(B, H, B);
}
private static long codeRangeLong(int B, int H, int nMax) {
// Code range for a all (B,H) codes of length <=nMax (<=B).
// n < B: L*Sum[i<n]( H^i )
// n == B: L*Sum[i<B]( H^i ) + H^B
assert(nMax >= 0 && nMax <= B);
assert(B >= 1 && B <= 5);
assert(H >= 1 && H <= 256);
if (nMax == 0) return 0; // no codes of zero length
if (B == 1) return H; // special case; see (**) above
int L = 256-H;
long sum = 0;
long H_i = 1;
for (int n = 1; n <= nMax; n++) {
sum += H_i;
H_i *= H;
}
sum *= L;
if (nMax == B)
sum += H_i;
return sum;
}
/** Largest int representable by (B,H,S) in up to nMax bytes. */
public static int codeMax(int B, int H, int S, int nMax) {
//assert(S >= 0 && S <= S_MAX);
long range = codeRangeLong(B, H, nMax);
if (range == 0)
return -1; // degenerate max value for empty set of codes
if (S == 0 || range >= (long)1<<32)
return saturate32(range-1);
long maxPos = range-1;
while (isNegativeCode(maxPos, S)) {
--maxPos;
}
if (maxPos < 0) return -1; // No positive codings at all.
int smax = decodeSign32(maxPos, S);
// check for 32-bit wraparound:
if (smax < 0)
return Integer.MAX_VALUE;
return smax;
}
/** Smallest int representable by (B,H,S) in up to nMax bytes.
Returns Integer.MIN_VALUE if 32-bit wraparound covers
the entire negative range.
*/
public static int codeMin(int B, int H, int S, int nMax) {
//assert(S >= 0 && S <= S_MAX);
long range = codeRangeLong(B, H, nMax);
if (range >= (long)1<<32 && nMax == B) {
// Can code negative values via 32-bit wraparound.
return Integer.MIN_VALUE;
}
if (S == 0) {
return 0;
}
long maxNeg = range-1;
while (!isNegativeCode(maxNeg, S))
--maxNeg;
if (maxNeg < 0) return 0; // No negative codings at all.
return decodeSign32(maxNeg, S);
}
// Some of the arithmetic below is on unsigned 32-bit integers.
// These must be represented in Java as longs in the range [0..2^32-1].
// The conversion to a signed int is just the Java cast (int), but
// the conversion to an unsigned int is the following little method:
private static long toUnsigned32(int sx) {
return ((long)sx << 32) >>> 32;
}
// Sign encoding:
private static boolean isNegativeCode(long ux, int S) {
assert(S > 0);
assert(ux >= -1); // can be out of 32-bit range; who cares
int Smask = (1<<S)-1;
return (((int)ux+1) & Smask) == 0;
}
private static boolean hasNegativeCode(int sx, int S) {
assert(S > 0);
// If S>=2 very low negatives are coded by 32-bit-wrapped positives.
// The lowest negative representable by a negative coding is
// ~(umax32 >> S), and the next lower number is coded by wrapping
// the highest positive:
// CodePos(umax32-1) -> (umax32-1)-((umax32-1)>>S)
// which simplifies to ~(umax32 >> S)-1.
return (0 > sx) && (sx >= ~(-1>>>S));
}
private static int decodeSign32(long ux, int S) {
assert(ux == toUnsigned32((int)ux)) // must be unsigned 32-bit number
: (Long.toHexString(ux));
if (S == 0) {
return (int) ux; // cast to signed int
}
int sx;
if (isNegativeCode(ux, S)) {
// Sgn(x) == -(x / 2^S)-1
sx = ~((int)ux >>> S);
} else {
// Sgn(x) == (x / 2^S)*(2^S-1) + (x % 2^S)
sx = (int)ux - ((int)ux >>> S);
}
// Assert special case of S==1:
assert(!(S == 1) || sx == (((int)ux >>> 1) ^ -((int)ux & 1)));
return sx;
}
private static long encodeSign32(int sx, int S) {
if (S == 0) {
return toUnsigned32(sx); // unsigned 32-bit int
}
int Smask = (1<<S)-1;
long ux;
if (!hasNegativeCode(sx, S)) {
// InvSgn(sx) = (sx / (2^S-1))*2^S + (sx % (2^S-1))
ux = sx + (toUnsigned32(sx) / Smask);
} else {
// InvSgn(sx) = (-sx-1)*2^S + (2^S-1)
ux = (-sx << S) - 1;
}
ux = toUnsigned32((int)ux);
assert(sx == decodeSign32(ux, S))
: (Long.toHexString(ux)+" -> "+
Integer.toHexString(sx)+" != "+
Integer.toHexString(decodeSign32(ux, S)));
return ux;
}
// Top-level coding of single integers:
public static void writeInt(byte[] out, int[] outpos, int sx, int B, int H, int S) {
long ux = encodeSign32(sx, S);
assert(ux == toUnsigned32((int)ux));
assert(ux < codeRangeLong(B, H))
: Long.toHexString(ux);
int L = 256-H;
long sum = ux;
int pos = outpos[0];
for (int i = 0; i < B-1; i++) {
if (sum < L)
break;
sum -= L;
int b_i = (int)( L + (sum % H) );
sum /= H;
out[pos++] = (byte)b_i;
}
out[pos++] = (byte)sum;
// Report number of bytes written by updating outpos[0]:
outpos[0] = pos;
// Check right away for mis-coding.
//assert(sx == readInt(out, new int[1], B, H, S));
}
public static int readInt(byte[] in, int[] inpos, int B, int H, int S) {
// U(b[i]: i<n) == Sum[i<n]( b[i] * H^i )
int L = 256-H;
long sum = 0;
long H_i = 1;
int pos = inpos[0];
for (int i = 0; i < B; i++) {
int b_i = in[pos++] & 0xFF;
sum += b_i*H_i;
H_i *= H;
if (b_i < L) break;
}
//assert(sum >= 0 && sum < codeRangeLong(B, H));
// Report number of bytes read by updating inpos[0]:
inpos[0] = pos;
return decodeSign32(sum, S);
}
// The Stream version doesn't fetch a byte unless it is needed for coding.
public static int readIntFrom(InputStream in, int B, int H, int S) throws IOException {
// U(b[i]: i<n) == Sum[i<n]( b[i] * H^i )
int L = 256-H;
long sum = 0;
long H_i = 1;
for (int i = 0; i < B; i++) {
int b_i = in.read();
if (b_i < 0) throw new RuntimeException("unexpected EOF");
sum += b_i*H_i;
H_i *= H;
if (b_i < L) break;
}
assert(sum >= 0 && sum < codeRangeLong(B, H));
return decodeSign32(sum, S);
}
public static final int B_MAX = 5; /* B: [1,5] */
public static final int H_MAX = 256; /* H: [1,256] */
public static final int S_MAX = 2; /* S: [0,2] */
// END OF STATICS.
private final int B; /*1..5*/ // # bytes (1..5)
private final int H; /*1..256*/ // # codes requiring a higher byte
private final int L; /*0..255*/ // # codes requiring a higher byte
private final int S; /*0..3*/ // # low-order bits representing sign
private final int del; /*0..2*/ // type of delta encoding (0 == none)
private final int min; // smallest representable value
private final int max; // largest representable value
private final int umin; // smallest representable uns. value
private final int umax; // largest representable uns. value
private final int[] byteMin; // smallest repr. value, given # bytes
private final int[] byteMax; // largest repr. value, given # bytes
private Coding(int B, int H, int S) {
this(B, H, S, 0);
}
private Coding(int B, int H, int S, int del) {
this.B = B;
this.H = H;
this.L = 256-H;
this.S = S;
this.del = del;
this.min = codeMin(B, H, S, B);
this.max = codeMax(B, H, S, B);
this.umin = codeMin(B, H, 0, B);
this.umax = codeMax(B, H, 0, B);
this.byteMin = new int[B];
this.byteMax = new int[B];
for (int nMax = 1; nMax <= B; nMax++) {
byteMin[nMax-1] = codeMin(B, H, S, nMax);
byteMax[nMax-1] = codeMax(B, H, S, nMax);
}
}
public boolean equals(Object x) {
if (!(x instanceof Coding)) return false;
Coding that = (Coding) x;
if (this.B != that.B) return false;
if (this.H != that.H) return false;
if (this.S != that.S) return false;
if (this.del != that.del) return false;
return true;
}
public int hashCode() {
return (del<<14)+(S<<11)+(B<<8)+(H<<0);
}
private static Map<Coding, Coding> codeMap;
private static synchronized Coding of(int B, int H, int S, int del) {
if (codeMap == null) codeMap = new HashMap<>();
Coding x0 = new Coding(B, H, S, del);
Coding x1 = codeMap.get(x0);
if (x1 == null) codeMap.put(x0, x1 = x0);
return x1;
}
public static Coding of(int B, int H) {
return of(B, H, 0, 0);
}
public static Coding of(int B, int H, int S) {
return of(B, H, S, 0);
}
public boolean canRepresentValue(int x) {
if (isSubrange())
return canRepresentUnsigned(x);
else
return canRepresentSigned(x);
}
/** Can this coding represent a single value, possibly a delta?
* This ignores the D property. That is, for delta codings,
* this tests whether a delta value of 'x' can be coded.
* For signed delta codings which produce unsigned end values,
* use canRepresentUnsigned.
*/
public boolean canRepresentSigned(int x) {
return (x >= min && x <= max);
}
/** Can this coding, apart from its S property,
* represent a single value? (Negative values
* can only be represented via 32-bit overflow,
* so this returns true for negative values
* if isFullRange is true.)
*/
public boolean canRepresentUnsigned(int x) {
return (x >= umin && x <= umax);
}
// object-oriented code/decode
public int readFrom(byte[] in, int[] inpos) {
return readInt(in, inpos, B, H, S);
}
public void writeTo(byte[] out, int[] outpos, int x) {
writeInt(out, outpos, x, B, H, S);
}
// Stream versions
public int readFrom(InputStream in) throws IOException {
return readIntFrom(in, B, H, S);
}
public void writeTo(OutputStream out, int x) throws IOException {
byte[] buf = new byte[B];
int[] pos = new int[1];
writeInt(buf, pos, x, B, H, S);
out.write(buf, 0, pos[0]);
}
// Stream/array versions
public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException {
// %%% use byte[] buffer
for (int i = start; i < end; i++)
a[i] = readFrom(in);
for (int dstep = 0; dstep < del; dstep++) {
long state = 0;
for (int i = start; i < end; i++) {
state += a[i];
// Reduce array values to the required range.
if (isSubrange()) {
state = reduceToUnsignedRange(state);
}
a[i] = (int) state;
}
}
}
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException {
if (end <= start) return;
for (int dstep = 0; dstep < del; dstep++) {
int[] deltas;
if (!isSubrange())
deltas = makeDeltas(a, start, end, 0, 0);
else
deltas = makeDeltas(a, start, end, min, max);
a = deltas;
start = 0;
end = deltas.length;
}
// The following code is a buffered version of this loop:
// for (int i = start; i < end; i++)
// writeTo(out, a[i]);
byte[] buf = new byte[1<<8];
final int bufmax = buf.length-B;
int[] pos = { 0 };
for (int i = start; i < end; ) {
while (pos[0] <= bufmax) {
writeTo(buf, pos, a[i++]);
if (i >= end) break;
}
out.write(buf, 0, pos[0]);
pos[0] = 0;
}
}
/** Tell if the range of this coding (number of distinct
* representable values) can be expressed in 32 bits.
*/
boolean isSubrange() {
return max < Integer.MAX_VALUE
&& ((long)max - (long)min + 1) <= Integer.MAX_VALUE;
}
/** Tell if this coding can represent all 32-bit values.
* Note: Some codings, such as unsigned ones, can be neither
* subranges nor full-range codings.
*/
boolean isFullRange() {
return max == Integer.MAX_VALUE && min == Integer.MIN_VALUE;
}
/** Return the number of values this coding (a subrange) can represent. */
int getRange() {
assert(isSubrange());
return (max - min) + 1; // range includes both min & max
}
Coding setB(int B) { return Coding.of(B, H, S, del); }
Coding setH(int H) { return Coding.of(B, H, S, del); }
Coding setS(int S) { return Coding.of(B, H, S, del); }
Coding setL(int L) { return setH(256-L); }
Coding setD(int del) { return Coding.of(B, H, S, del); }
Coding getDeltaCoding() { return setD(del+1); }
/** Return a coding suitable for representing summed, modulo-reduced values. */
Coding getValueCoding() {
if (isDelta())
return Coding.of(B, H, 0, del-1);
else
return this;
}
/** Reduce the given value to be within this coding's unsigned range,
* by adding or subtracting a multiple of (max-min+1).
*/
int reduceToUnsignedRange(long value) {
if (value == (int)value && canRepresentUnsigned((int)value))
// already in unsigned range
return (int)value;
int range = getRange();
assert(range > 0);
value %= range;
if (value < 0) value += range;
assert(canRepresentUnsigned((int)value));
return (int)value;
}
int reduceToSignedRange(int value) {
if (canRepresentSigned(value))
// already in signed range
return value;
return reduceToSignedRange(value, min, max);
}
static int reduceToSignedRange(int value, int min, int max) {
int range = (max-min+1);
assert(range > 0);
int value0 = value;
value -= min;
if (value < 0 && value0 >= 0) {
// 32-bit overflow, but the next '%=' op needs to be unsigned
value -= range;
assert(value >= 0);
}
value %= range;
if (value < 0) value += range;
value += min;
assert(min <= value && value <= max);
return value;
}
/** Does this coding support at least one negative value?
Includes codings that can do so via 32-bit wraparound.
*/
boolean isSigned() {
return min < 0;
}
/** Does this coding code arrays by making successive differences? */
boolean isDelta() {
return del != 0;
}
public int B() { return B; }
public int H() { return H; }
public int L() { return L; }
public int S() { return S; }
public int del() { return del; }
public int min() { return min; }
public int max() { return max; }
public int umin() { return umin; }
public int umax() { return umax; }
public int byteMin(int b) { return byteMin[b-1]; }
public int byteMax(int b) { return byteMax[b-1]; }
public int compareTo(Coding that) {
int dkey = this.del - that.del;
if (dkey == 0)
dkey = this.B - that.B;
if (dkey == 0)
dkey = this.H - that.H;
if (dkey == 0)
dkey = this.S - that.S;
return dkey;
}
/** Heuristic measure of the difference between two codings. */
public int distanceFrom(Coding that) {
int diffdel = this.del - that.del;
if (diffdel < 0) diffdel = -diffdel;
int diffS = this.S - that.S;
if (diffS < 0) diffS = -diffS;
int diffB = this.B - that.B;
if (diffB < 0) diffB = -diffB;
int diffHL;
if (this.H == that.H) {
diffHL = 0;
} else {
// Distance in log space of H (<=128) and L (<128).
int thisHL = this.getHL();
int thatHL = that.getHL();
// Double the accuracy of the log:
thisHL *= thisHL;
thatHL *= thatHL;
if (thisHL > thatHL)
diffHL = ceil_lg2(1+(thisHL-1)/thatHL);
else
diffHL = ceil_lg2(1+(thatHL-1)/thisHL);
}
int norm = 5*(diffdel + diffS + diffB) + diffHL;
assert(norm != 0 || this.compareTo(that) == 0);
return norm;
}
private int getHL() {
// Follow H in log space by the multiplicative inverse of L.
if (H <= 128) return H;
if (L >= 1) return 128*128/L;
return 128*256;
}
/** ceiling(log[2](x)): {1->0, 2->1, 3->2, 4->2, ...} */
static int ceil_lg2(int x) {
assert(x-1 >= 0); // x in range (int.MIN_VALUE -> 32)
x -= 1;
int lg = 0;
while (x != 0) {
lg++;
x >>= 1;
}
return lg;
}
private static final byte[] byteBitWidths = new byte[0x100];
static {
for (int b = 0; b < byteBitWidths.length; b++) {
byteBitWidths[b] = (byte) ceil_lg2(b + 1);
}
for (int i = 10; i >= 0; i = (i << 1) - (i >> 3)) {
assert(bitWidth(i) == ceil_lg2(i + 1));
}
}
/** Number of significant bits in i, not counting sign bits.
* For positive i, it is ceil_lg2(i + 1).
*/
static int bitWidth(int i) {
if (i < 0) i = ~i; // change sign
int w = 0;
int lo = i;
if (lo < byteBitWidths.length)
return byteBitWidths[lo];
int hi;
hi = (lo >>> 16);
if (hi != 0) {
lo = hi;
w += 16;
}
hi = (lo >>> 8);
if (hi != 0) {
lo = hi;
w += 8;
}
w += byteBitWidths[lo];
//assert(w == ceil_lg2(i + 1));
return w;
}
/** Create an array of successive differences.
* If min==max, accept any and all 32-bit overflow.
* Otherwise, avoid 32-bit overflow, and reduce all differences
* to a value in the given range, by adding or subtracting
* multiples of the range cardinality (max-min+1).
* Also, the values are assumed to be in the range [0..(max-min)].
*/
static int[] makeDeltas(int[] values, int start, int end,
int min, int max) {
assert(max >= min);
int count = end-start;
int[] deltas = new int[count];
int state = 0;
if (min == max) {
for (int i = 0; i < count; i++) {
int value = values[start+i];
deltas[i] = value - state;
state = value;
}
} else {
for (int i = 0; i < count; i++) {
int value = values[start+i];
assert(value >= 0 && value+min <= max);
int delta = value - state;
assert(delta == (long)value - (long)state); // no overflow
state = value;
// Reduce delta values to the required range.
delta = reduceToSignedRange(delta, min, max);
deltas[i] = delta;
}
}
return deltas;
}
boolean canRepresent(int minValue, int maxValue) {
assert(minValue <= maxValue);
if (del > 0) {
if (isSubrange()) {
// We will force the values to reduce to the right subrange.
return canRepresentUnsigned(maxValue)
&& canRepresentUnsigned(minValue);
} else {
// Huge range; delta values must assume full 32-bit range.
return isFullRange();
}
}
else
// final values must be representable
return canRepresentSigned(maxValue)
&& canRepresentSigned(minValue);
}
boolean canRepresent(int[] values, int start, int end) {
int len = end-start;
if (len == 0) return true;
if (isFullRange()) return true;
// Calculate max, min:
int lmax = values[start];
int lmin = lmax;
for (int i = 1; i < len; i++) {
int value = values[start+i];
if (lmax < value) lmax = value;
if (lmin > value) lmin = value;
}
return canRepresent(lmin, lmax);
}
public double getBitLength(int value) { // implements BitMetric
return (double) getLength(value) * 8;
}
/** How many bytes are in the coding of this value?
* Returns Integer.MAX_VALUE if the value has no coding.
* The coding must not be a delta coding, since there is no
* definite size for a single value apart from its context.
*/
public int getLength(int value) {
if (isDelta() && isSubrange()) {
if (!canRepresentUnsigned(value))
return Integer.MAX_VALUE;
value = reduceToSignedRange(value);
}
if (value >= 0) {
for (int n = 0; n < B; n++) {
if (value <= byteMax[n]) return n+1;
}
} else {
for (int n = 0; n < B; n++) {
if (value >= byteMin[n]) return n+1;
}
}
return Integer.MAX_VALUE;
}
public int getLength(int[] values, int start, int end) {
int len = end-start;
if (B == 1) return len;
if (L == 0) return len * B;
if (isDelta()) {
int[] deltas;
if (!isSubrange())
deltas = makeDeltas(values, start, end, 0, 0);
else
deltas = makeDeltas(values, start, end, min, max);
//return Coding.of(B, H, S).getLength(deltas, 0, len);
values = deltas;
start = 0;
}
int sum = len; // at least 1 byte per
// add extra bytes for extra-long values
for (int n = 1; n <= B; n++) {
// what is the coding interval [min..max] for n bytes?
int lmax = byteMax[n-1];
int lmin = byteMin[n-1];
int longer = 0; // count of guys longer than n bytes
for (int i = 0; i < len; i++) {
int value = values[start+i];
if (value >= 0) {
if (value > lmax) longer++;
} else {
if (value < lmin) longer++;
}
}
if (longer == 0) break; // no more passes needed
if (n == B) return Integer.MAX_VALUE; // cannot represent!
sum += longer;
}
return sum;
}
public byte[] getMetaCoding(Coding dflt) {
if (dflt == this) return new byte[]{ (byte) _meta_default };
int canonicalIndex = BandStructure.indexOf(this);
if (canonicalIndex > 0)
return new byte[]{ (byte) canonicalIndex };
return new byte[]{
(byte)_meta_arb,
(byte)(del + 2*S + 8*(B-1)),
(byte)(H-1)
};
}
public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) {
int op = bytes[pos++] & 0xFF;
if (_meta_canon_min <= op && op <= _meta_canon_max) {
Coding c = BandStructure.codingForIndex(op);
assert(c != null);
res[0] = c;
return pos;
}
if (op == _meta_arb) {
int dsb = bytes[pos++] & 0xFF;
int H_1 = bytes[pos++] & 0xFF;
int del = dsb % 2;
int S = (dsb / 2) % 4;
int B = (dsb / 8)+1;
int H = H_1+1;
if (!((1 <= B && B <= B_MAX) &&
(0 <= S && S <= S_MAX) &&
(1 <= H && H <= H_MAX) &&
(0 <= del && del <= 1))
|| (B == 1 && H != 256)
|| (B == 5 && H == 256)) {
throw new RuntimeException("Bad arb. coding: ("+B+","+H+","+S+","+del);
}
res[0] = Coding.of(B, H, S, del);
return pos;
}
return pos-1; // backup
}
public String keyString() {
return "("+B+","+H+","+S+","+del+")";
}
public String toString() {
String str = "Coding"+keyString();
// If -ea, print out more informative strings!
//assert((str = stringForDebug()) != null);
return str;
}
static boolean verboseStringForDebug = false;
String stringForDebug() {
String minS = (min == Integer.MIN_VALUE ? "min" : ""+min);
String maxS = (max == Integer.MAX_VALUE ? "max" : ""+max);
String str = keyString()+" L="+L+" r=["+minS+","+maxS+"]";
if (isSubrange())
str += " subrange";
else if (!isFullRange())
str += " MIDRANGE";
if (verboseStringForDebug) {
str += " {";
int prev_range = 0;
for (int n = 1; n <= B; n++) {
int range_n = saturate32((long)byteMax[n-1] - byteMin[n-1] + 1);
assert(range_n == saturate32(codeRangeLong(B, H, n)));
range_n -= prev_range;
prev_range = range_n;
String rngS = (range_n == Integer.MAX_VALUE ? "max" : ""+range_n);
str += " #"+n+"="+rngS;
}
str += " }";
}
return str;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2003, 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.java.util.jar.pack;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Interface for encoding and decoding int arrays using bytewise codes.
* @author John Rose
*/
interface CodingMethod {
// Read and write an array of ints from/to a stream.
public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException;
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException;
// how to express me in a band header?
public byte[] getMetaCoding(Coding dflt);
}

Some files were not shown because too many files have changed in this diff Show more