0N/A/*
2362N/A * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage javax.crypto;
0N/A
0N/Aimport java.io.*;
0N/Aimport java.security.AlgorithmParameters;
0N/Aimport java.security.Key;
0N/Aimport java.security.InvalidKeyException;
0N/Aimport java.security.InvalidAlgorithmParameterException;
0N/Aimport java.security.NoSuchAlgorithmException;
0N/Aimport java.security.NoSuchProviderException;
0N/A
0N/A/**
0N/A * This class enables a programmer to create an object and protect its
0N/A * confidentiality with a cryptographic algorithm.
0N/A *
0N/A * <p> Given any Serializable object, one can create a SealedObject
0N/A * that encapsulates the original object, in serialized
0N/A * format (i.e., a "deep copy"), and seals (encrypts) its serialized contents,
0N/A * using a cryptographic algorithm such as DES, to protect its
0N/A * confidentiality. The encrypted content can later be decrypted (with
0N/A * the corresponding algorithm using the correct decryption key) and
0N/A * de-serialized, yielding the original object.
0N/A *
0N/A * <p> Note that the Cipher object must be fully initialized with the
0N/A * correct algorithm, key, padding scheme, etc., before being applied
0N/A * to a SealedObject.
0N/A *
0N/A * <p> The original object that was sealed can be recovered in two different
0N/A * ways: <p>
0N/A *
0N/A * <ul>
0N/A *
0N/A * <li>by using the {@link #getObject(javax.crypto.Cipher) getObject}
0N/A * method that takes a <code>Cipher</code> object.
0N/A *
0N/A * <p> This method requires a fully initialized <code>Cipher</code> object,
0N/A * initialized with the
0N/A * exact same algorithm, key, padding scheme, etc., that were used to seal the
0N/A * object.
0N/A *
0N/A * <p> This approach has the advantage that the party who unseals the
0N/A * sealed object does not require knowledge of the decryption key. For example,
0N/A * after one party has initialized the cipher object with the required
0N/A * decryption key, it could hand over the cipher object to
0N/A * another party who then unseals the sealed object.
0N/A *
0N/A * <p>
0N/A *
0N/A * <li>by using one of the
0N/A * {@link #getObject(java.security.Key) getObject} methods
0N/A * that take a <code>Key</code> object.
0N/A *
0N/A * <p> In this approach, the <code>getObject</code> method creates a cipher
0N/A * object for the appropriate decryption algorithm and initializes it with the
0N/A * given decryption key and the algorithm parameters (if any) that were stored
0N/A * in the sealed object.
0N/A *
0N/A * <p> This approach has the advantage that the party who
0N/A * unseals the object does not need to keep track of the parameters (e.g., an
0N/A * IV) that were used to seal the object.
0N/A *
0N/A * </ul>
0N/A *
0N/A * @author Li Gong
0N/A * @author Jan Luehe
0N/A * @see Cipher
0N/A * @since 1.4
0N/A */
0N/A
0N/Apublic class SealedObject implements Serializable {
0N/A
0N/A static final long serialVersionUID = 4482838265551344752L;
0N/A
0N/A /**
0N/A * The serialized object contents in encrypted format.
0N/A *
0N/A * @serial
0N/A */
0N/A private byte[] encryptedContent = null;
0N/A
0N/A /**
0N/A * The algorithm that was used to seal this object.
0N/A *
0N/A * @serial
0N/A */
0N/A private String sealAlg = null;
0N/A
0N/A /**
0N/A * The algorithm of the parameters used.
0N/A *
0N/A * @serial
0N/A */
0N/A private String paramsAlg = null;
0N/A
0N/A /**
0N/A * The cryptographic parameters used by the sealing Cipher,
0N/A * encoded in the default format.
0N/A * <p>
0N/A * That is, <code>cipher.getParameters().getEncoded()</code>.
0N/A *
0N/A * @serial
0N/A */
0N/A protected byte[] encodedParams = null;
0N/A
0N/A /**
0N/A * Constructs a SealedObject from any Serializable object.
0N/A *
0N/A * <p>The given object is serialized, and its serialized contents are
0N/A * encrypted using the given Cipher, which must be fully initialized.
0N/A *
0N/A * <p>Any algorithm parameters that may be used in the encryption
0N/A * operation are stored inside of the new <code>SealedObject</code>.
0N/A *
0N/A * @param object the object to be sealed; can be null.
0N/A * @param c the cipher used to seal the object.
0N/A *
0N/A * @exception NullPointerException if the given cipher is null.
0N/A * @exception IOException if an error occurs during serialization
0N/A * @exception IllegalBlockSizeException if the given cipher is a block
0N/A * cipher, no padding has been requested, and the total input length
0N/A * (i.e., the length of the serialized object contents) is not a multiple
0N/A * of the cipher's block size
0N/A */
0N/A public SealedObject(Serializable object, Cipher c) throws IOException,
0N/A IllegalBlockSizeException
0N/A {
0N/A /*
0N/A * Serialize the object
0N/A */
0N/A
0N/A // creating a stream pipe-line, from a to b
0N/A ByteArrayOutputStream b = new ByteArrayOutputStream();
0N/A ObjectOutput a = new ObjectOutputStream(b);
0N/A byte[] content;
0N/A try {
0N/A // write and flush the object content to byte array
0N/A a.writeObject(object);
0N/A a.flush();
0N/A content = b.toByteArray();
0N/A } finally {
0N/A a.close();
0N/A }
0N/A
0N/A /*
0N/A * Seal the object
0N/A */
0N/A try {
0N/A this.encryptedContent = c.doFinal(content);
0N/A }
0N/A catch (BadPaddingException ex) {
0N/A // if sealing is encryption only
0N/A // Should never happen??
0N/A }
0N/A
0N/A // Save the parameters
0N/A if (c.getParameters() != null) {
0N/A this.encodedParams = c.getParameters().getEncoded();
0N/A this.paramsAlg = c.getParameters().getAlgorithm();
0N/A }
0N/A
0N/A // Save the encryption algorithm
0N/A this.sealAlg = c.getAlgorithm();
0N/A }
0N/A
0N/A /**
0N/A * Constructs a SealedObject object from the passed-in SealedObject.
0N/A *
0N/A * @param so a SealedObject object
0N/A * @exception NullPointerException if the given sealed object is null.
0N/A */
0N/A protected SealedObject(SealedObject so) {
0N/A this.encryptedContent = (byte[]) so.encryptedContent.clone();
0N/A this.sealAlg = so.sealAlg;
0N/A this.paramsAlg = so.paramsAlg;
0N/A if (so.encodedParams != null) {
0N/A this.encodedParams = (byte[]) so.encodedParams.clone();
0N/A } else {
0N/A this.encodedParams = null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the algorithm that was used to seal this object.
0N/A *
0N/A * @return the algorithm that was used to seal this object.
0N/A */
0N/A public final String getAlgorithm() {
0N/A return this.sealAlg;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the original (encapsulated) object.
0N/A *
0N/A * <p>This method creates a cipher for the algorithm that had been used in
0N/A * the sealing operation.
0N/A * If the default provider package provides an implementation of that
0N/A * algorithm, an instance of Cipher containing that implementation is used.
0N/A * If the algorithm is not available in the default package, other
0N/A * packages are searched.
0N/A * The Cipher object is initialized for decryption, using the given
0N/A * <code>key</code> and the parameters (if any) that had been used in the
0N/A * sealing operation.
0N/A *
0N/A * <p>The encapsulated object is unsealed and de-serialized, before it is
0N/A * returned.
0N/A *
0N/A * @param key the key used to unseal the object.
0N/A *
0N/A * @return the original object.
0N/A *
0N/A * @exception IOException if an error occurs during de-serialiazation.
0N/A * @exception ClassNotFoundException if an error occurs during
0N/A * de-serialiazation.
0N/A * @exception NoSuchAlgorithmException if the algorithm to unseal the
0N/A * object is not available.
0N/A * @exception InvalidKeyException if the given key cannot be used to unseal
0N/A * the object (e.g., it has the wrong algorithm).
0N/A * @exception NullPointerException if <code>key</code> is null.
0N/A */
0N/A public final Object getObject(Key key)
0N/A throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
0N/A InvalidKeyException
0N/A {
0N/A if (key == null) {
0N/A throw new NullPointerException("key is null");
0N/A }
0N/A
0N/A try {
0N/A return unseal(key, null);
0N/A } catch (NoSuchProviderException nspe) {
0N/A // we've already caught NoSuchProviderException's and converted
0N/A // them into NoSuchAlgorithmException's with details about
0N/A // the failing algorithm
0N/A throw new NoSuchAlgorithmException("algorithm not found");
0N/A } catch (IllegalBlockSizeException ibse) {
0N/A throw new InvalidKeyException(ibse.getMessage());
0N/A } catch (BadPaddingException bpe) {
0N/A throw new InvalidKeyException(bpe.getMessage());
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the original (encapsulated) object.
0N/A *
0N/A * <p>The encapsulated object is unsealed (using the given Cipher,
0N/A * assuming that the Cipher is already properly initialized) and
0N/A * de-serialized, before it is returned.
0N/A *
0N/A * @param c the cipher used to unseal the object
0N/A *
0N/A * @return the original object.
0N/A *
0N/A * @exception NullPointerException if the given cipher is null.
0N/A * @exception IOException if an error occurs during de-serialiazation
0N/A * @exception ClassNotFoundException if an error occurs during
0N/A * de-serialiazation
0N/A * @exception IllegalBlockSizeException if the given cipher is a block
0N/A * cipher, no padding has been requested, and the total input length is
0N/A * not a multiple of the cipher's block size
0N/A * @exception BadPaddingException if the given cipher has been
0N/A * initialized for decryption, and padding has been specified, but
0N/A * the input data does not have proper expected padding bytes
0N/A */
0N/A public final Object getObject(Cipher c)
0N/A throws IOException, ClassNotFoundException, IllegalBlockSizeException,
0N/A BadPaddingException
0N/A {
0N/A /*
0N/A * Unseal the object
0N/A */
0N/A byte[] content = c.doFinal(this.encryptedContent);
0N/A
0N/A /*
0N/A * De-serialize it
0N/A */
0N/A // creating a stream pipe-line, from b to a
0N/A ByteArrayInputStream b = new ByteArrayInputStream(content);
0N/A ObjectInput a = new extObjectInputStream(b);
0N/A try {
0N/A Object obj = a.readObject();
0N/A return obj;
0N/A } finally {
0N/A a.close();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the original (encapsulated) object.
0N/A *
0N/A * <p>This method creates a cipher for the algorithm that had been used in
0N/A * the sealing operation, using an implementation of that algorithm from
0N/A * the given <code>provider</code>.
0N/A * The Cipher object is initialized for decryption, using the given
0N/A * <code>key</code> and the parameters (if any) that had been used in the
0N/A * sealing operation.
0N/A *
0N/A * <p>The encapsulated object is unsealed and de-serialized, before it is
0N/A * returned.
0N/A *
0N/A * @param key the key used to unseal the object.
0N/A * @param provider the name of the provider of the algorithm to unseal
0N/A * the object.
0N/A *
0N/A * @return the original object.
0N/A *
0N/A * @exception IllegalArgumentException if the given provider is null
0N/A * or empty.
0N/A * @exception IOException if an error occurs during de-serialiazation.
0N/A * @exception ClassNotFoundException if an error occurs during
0N/A * de-serialiazation.
0N/A * @exception NoSuchAlgorithmException if the algorithm to unseal the
0N/A * object is not available.
0N/A * @exception NoSuchProviderException if the given provider is not
0N/A * configured.
0N/A * @exception InvalidKeyException if the given key cannot be used to unseal
0N/A * the object (e.g., it has the wrong algorithm).
0N/A * @exception NullPointerException if <code>key</code> is null.
0N/A */
0N/A public final Object getObject(Key key, String provider)
0N/A throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
0N/A NoSuchProviderException, InvalidKeyException
0N/A {
0N/A if (key == null) {
0N/A throw new NullPointerException("key is null");
0N/A }
0N/A if (provider == null || provider.length() == 0) {
0N/A throw new IllegalArgumentException("missing provider");
0N/A }
0N/A
0N/A try {
0N/A return unseal(key, provider);
0N/A } catch (IllegalBlockSizeException ibse) {
0N/A throw new InvalidKeyException(ibse.getMessage());
0N/A } catch (BadPaddingException bpe) {
0N/A throw new InvalidKeyException(bpe.getMessage());
0N/A }
0N/A }
0N/A
0N/A
0N/A private Object unseal(Key key, String provider)
0N/A throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
0N/A NoSuchProviderException, InvalidKeyException,
0N/A IllegalBlockSizeException, BadPaddingException
0N/A {
0N/A /*
0N/A * Create the parameter object.
0N/A */
0N/A AlgorithmParameters params = null;
0N/A if (this.encodedParams != null) {
0N/A try {
0N/A if (provider != null)
0N/A params = AlgorithmParameters.getInstance(this.paramsAlg,
0N/A provider);
0N/A else
0N/A params = AlgorithmParameters.getInstance(this.paramsAlg);
0N/A
0N/A } catch (NoSuchProviderException nspe) {
0N/A if (provider == null) {
0N/A throw new NoSuchAlgorithmException(this.paramsAlg
0N/A + " not found");
0N/A } else {
0N/A throw new NoSuchProviderException(nspe.getMessage());
0N/A }
0N/A }
0N/A params.init(this.encodedParams);
0N/A }
0N/A
0N/A /*
0N/A * Create and initialize the cipher.
0N/A */
0N/A Cipher c;
0N/A try {
0N/A if (provider != null)
0N/A c = Cipher.getInstance(this.sealAlg, provider);
0N/A else
0N/A c = Cipher.getInstance(this.sealAlg);
0N/A } catch (NoSuchPaddingException nspe) {
0N/A throw new NoSuchAlgorithmException("Padding that was used in "
0N/A + "sealing operation not "
0N/A + "available");
0N/A } catch (NoSuchProviderException nspe) {
0N/A if (provider == null) {
0N/A throw new NoSuchAlgorithmException(this.sealAlg+" not found");
0N/A } else {
0N/A throw new NoSuchProviderException(nspe.getMessage());
0N/A }
0N/A }
0N/A
0N/A try {
0N/A if (params != null)
0N/A c.init(Cipher.DECRYPT_MODE, key, params);
0N/A else
0N/A c.init(Cipher.DECRYPT_MODE, key);
0N/A } catch (InvalidAlgorithmParameterException iape) {
0N/A // this should never happen, because we use the exact same
0N/A // parameters that were used in the sealing operation
0N/A throw new RuntimeException(iape.getMessage());
0N/A }
0N/A
0N/A /*
0N/A * Unseal the object
0N/A */
0N/A byte[] content = c.doFinal(this.encryptedContent);
0N/A
0N/A /*
0N/A * De-serialize it
0N/A */
0N/A // creating a stream pipe-line, from b to a
0N/A ByteArrayInputStream b = new ByteArrayInputStream(content);
0N/A ObjectInput a = new extObjectInputStream(b);
0N/A try {
0N/A Object obj = a.readObject();
0N/A return obj;
0N/A } finally {
0N/A a.close();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Restores the state of the SealedObject from a stream.
0N/A * @param s the object input stream.
0N/A * @exception NullPointerException if s is null.
0N/A */
0N/A private void readObject(java.io.ObjectInputStream s)
0N/A throws java.io.IOException, ClassNotFoundException
0N/A {
0N/A s.defaultReadObject();
0N/A if (encryptedContent != null)
0N/A encryptedContent = (byte[])encryptedContent.clone();
0N/A if (encodedParams != null)
0N/A encodedParams = (byte[])encodedParams.clone();
0N/A }
0N/A}
0N/A
0N/Afinal class extObjectInputStream extends ObjectInputStream {
0N/A
0N/A private static ClassLoader systemClassLoader = null;
0N/A
0N/A extObjectInputStream(InputStream in)
0N/A throws IOException, StreamCorruptedException {
0N/A super(in);
0N/A }
0N/A
0N/A protected Class resolveClass(ObjectStreamClass v)
0N/A throws IOException, ClassNotFoundException
0N/A {
0N/A
0N/A try {
0N/A /*
0N/A * Calling the super.resolveClass() first
0N/A * will let us pick up bug fixes in the super
0N/A * class (e.g., 4171142).
0N/A */
0N/A return super.resolveClass(v);
0N/A } catch (ClassNotFoundException cnfe) {
0N/A /*
0N/A * This is a workaround for bug 4224921.
0N/A */
0N/A ClassLoader loader = Thread.currentThread().getContextClassLoader();
0N/A if (loader == null) {
0N/A if (systemClassLoader == null) {
0N/A systemClassLoader = ClassLoader.getSystemClassLoader();
0N/A }
0N/A loader = systemClassLoader;
0N/A if (loader == null) {
0N/A throw new ClassNotFoundException(v.getName());
0N/A }
0N/A }
0N/A
0N/A return Class.forName(v.getName(), false, loader);
0N/A }
0N/A }
0N/A}