497 lines · 17.5 KB · Java
RawDownload
/* * 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 javax.crypto; import java.io.*;import java.security.AlgorithmParameters;import java.security.Key;import java.security.InvalidKeyException;import java.security.InvalidAlgorithmParameterException;import java.security.NoSuchAlgorithmException;import java.security.NoSuchProviderException; /** * This class enables a programmer to create an object and protect its * confidentiality with a cryptographic algorithm. * * <p> Given any Serializable object, one can create a SealedObject * that encapsulates the original object, in serialized * format (i.e., a "deep copy"), and seals (encrypts) its serialized contents, * using a cryptographic algorithm such as DES, to protect its * confidentiality.  The encrypted content can later be decrypted (with * the corresponding algorithm using the correct decryption key) and * de-serialized, yielding the original object. * * <p> Note that the Cipher object must be fully initialized with the * correct algorithm, key, padding scheme, etc., before being applied * to a SealedObject. * * <p> The original object that was sealed can be recovered in two different * ways: <p> * * <ul> * * <li>by using the {@link #getObject(javax.crypto.Cipher) getObject} * method that takes a <code>Cipher</code> object. * * <p> This method requires a fully initialized <code>Cipher</code> object, * initialized with the * exact same algorithm, key, padding scheme, etc., that were used to seal the * object. * * <p> This approach has the advantage that the party who unseals the * sealed object does not require knowledge of the decryption key. For example, * after one party has initialized the cipher object with the required * decryption key, it could hand over the cipher object to * another party who then unseals the sealed object. * * <p> * * <li>by using one of the * {@link #getObject(java.security.Key) getObject} methods * that take a <code>Key</code> object. * * <p> In this approach, the <code>getObject</code> method creates a cipher * object for the appropriate decryption algorithm and initializes it with the * given decryption key and the algorithm parameters (if any) that were stored * in the sealed object. * * <p> This approach has the advantage that the party who * unseals the object does not need to keep track of the parameters (e.g., an * IV) that were used to seal the object. * * </ul> * * @author Li Gong * @author Jan Luehe * @see Cipher * @since 1.4 */ public class SealedObject implements Serializable {     static final long serialVersionUID = 4482838265551344752L;     /**     * The serialized object contents in encrypted format.     *     * @serial     */    private byte[] encryptedContent = null;     /**     * The algorithm that was used to seal this object.     *     * @serial     */    private String sealAlg = null;     /**     * The algorithm of the parameters used.     *     * @serial     */    private String paramsAlg = null;     /**     * The cryptographic parameters used by the sealing Cipher,     * encoded in the default format.     * <p>     * That is, <code>cipher.getParameters().getEncoded()</code>.     *     * @serial     */    protected byte[] encodedParams = null;     /**     * Constructs a SealedObject from any Serializable object.     *     * <p>The given object is serialized, and its serialized contents are     * encrypted using the given Cipher, which must be fully initialized.     *     * <p>Any algorithm parameters that may be used in the encryption     * operation are stored inside of the new <code>SealedObject</code>.     *     * @param object the object to be sealed; can be null.     * @param c the cipher used to seal the object.     *     * @exception NullPointerException if the given cipher is null.     * @exception IOException if an error occurs during serialization     * @exception IllegalBlockSizeException if the given cipher is a block     * cipher, no padding has been requested, and the total input length     * (i.e., the length of the serialized object contents) is not a multiple     * of the cipher's block size     */    public SealedObject(Serializable object, Cipher c) throws IOException,        IllegalBlockSizeException    {        /*         * Serialize the object         */         // creating a stream pipe-line, from a to b        ByteArrayOutputStream b = new ByteArrayOutputStream();        ObjectOutput a = new ObjectOutputStream(b);        byte[] content;        try {            // write and flush the object content to byte array            a.writeObject(object);            a.flush();            content = b.toByteArray();        } finally {            a.close();        }         /*         * Seal the object         */        try {            this.encryptedContent = c.doFinal(content);        }        catch (BadPaddingException ex) {            // if sealing is encryption only            // Should never happen??        }         // Save the parameters        if (c.getParameters() != null) {            this.encodedParams = c.getParameters().getEncoded();            this.paramsAlg = c.getParameters().getAlgorithm();        }         // Save the encryption algorithm        this.sealAlg = c.getAlgorithm();    }     /**     * Constructs a SealedObject object from the passed-in SealedObject.     *     * @param so a SealedObject object     * @exception NullPointerException if the given sealed object is null.     */    protected SealedObject(SealedObject so) {        this.encryptedContent = (byte[]) so.encryptedContent.clone();        this.sealAlg = so.sealAlg;        this.paramsAlg = so.paramsAlg;        if (so.encodedParams != null) {            this.encodedParams = (byte[]) so.encodedParams.clone();        } else {            this.encodedParams = null;        }    }     /**     * Returns the algorithm that was used to seal this object.     *     * @return the algorithm that was used to seal this object.     */    public final String getAlgorithm() {        return this.sealAlg;    }     /**     * Retrieves the original (encapsulated) object.     *     * <p>This method creates a cipher for the algorithm that had been used in     * the sealing operation.     * If the default provider package provides an implementation of that     * algorithm, an instance of Cipher containing that implementation is used.     * If the algorithm is not available in the default package, other     * packages are searched.     * The Cipher object is initialized for decryption, using the given     * <code>key</code> and the parameters (if any) that had been used in the     * sealing operation.     *     * <p>The encapsulated object is unsealed and de-serialized, before it is     * returned.     *     * @param key the key used to unseal the object.     *     * @return the original object.     *     * @exception IOException if an error occurs during de-serialiazation.     * @exception ClassNotFoundException if an error occurs during     * de-serialiazation.     * @exception NoSuchAlgorithmException if the algorithm to unseal the     * object is not available.     * @exception InvalidKeyException if the given key cannot be used to unseal     * the object (e.g., it has the wrong algorithm).     * @exception NullPointerException if <code>key</code> is null.     */    public final Object getObject(Key key)        throws IOException, ClassNotFoundException, NoSuchAlgorithmException,            InvalidKeyException    {        if (key == null) {            throw new NullPointerException("key is null");        }         try {            return unseal(key, null);        } catch (NoSuchProviderException nspe) {            // we've already caught NoSuchProviderException's and converted            // them into NoSuchAlgorithmException's with details about            // the failing algorithm            throw new NoSuchAlgorithmException("algorithm not found");        } catch (IllegalBlockSizeException ibse) {            throw new InvalidKeyException(ibse.getMessage());        } catch (BadPaddingException bpe) {            throw new InvalidKeyException(bpe.getMessage());        }    }     /**     * Retrieves the original (encapsulated) object.     *     * <p>The encapsulated object is unsealed (using the given Cipher,     * assuming that the Cipher is already properly initialized) and     * de-serialized, before it is returned.     *     * @param c the cipher used to unseal the object     *     * @return the original object.     *     * @exception NullPointerException if the given cipher is null.     * @exception IOException if an error occurs during de-serialiazation     * @exception ClassNotFoundException if an error occurs during     * de-serialiazation     * @exception IllegalBlockSizeException if the given cipher is a block     * cipher, no padding has been requested, and the total input length is     * not a multiple of the cipher's block size     * @exception BadPaddingException if the given cipher has been     * initialized for decryption, and padding has been specified, but     * the input data does not have proper expected padding bytes     */    public final Object getObject(Cipher c)        throws IOException, ClassNotFoundException, IllegalBlockSizeException,            BadPaddingException    {        /*         * Unseal the object         */        byte[] content = c.doFinal(this.encryptedContent);         /*         * De-serialize it         */        // creating a stream pipe-line, from b to a        ByteArrayInputStream b = new ByteArrayInputStream(content);        ObjectInput a = new extObjectInputStream(b);        try {            Object obj = a.readObject();            return obj;        } finally {            a.close();        }    }     /**     * Retrieves the original (encapsulated) object.     *     * <p>This method creates a cipher for the algorithm that had been used in     * the sealing operation, using an implementation of that algorithm from     * the given <code>provider</code>.     * The Cipher object is initialized for decryption, using the given     * <code>key</code> and the parameters (if any) that had been used in the     * sealing operation.     *     * <p>The encapsulated object is unsealed and de-serialized, before it is     * returned.     *     * @param key the key used to unseal the object.     * @param provider the name of the provider of the algorithm to unseal     * the object.     *     * @return the original object.     *     * @exception IllegalArgumentException if the given provider is null     * or empty.     * @exception IOException if an error occurs during de-serialiazation.     * @exception ClassNotFoundException if an error occurs during     * de-serialiazation.     * @exception NoSuchAlgorithmException if the algorithm to unseal the     * object is not available.     * @exception NoSuchProviderException if the given provider is not     * configured.     * @exception InvalidKeyException if the given key cannot be used to unseal     * the object (e.g., it has the wrong algorithm).     * @exception NullPointerException if <code>key</code> is null.     */    public final Object getObject(Key key, String provider)        throws IOException, ClassNotFoundException, NoSuchAlgorithmException,            NoSuchProviderException, InvalidKeyException    {        if (key == null) {            throw new NullPointerException("key is null");        }        if (provider == null || provider.length() == 0) {            throw new IllegalArgumentException("missing provider");        }         try {            return unseal(key, provider);        } catch (IllegalBlockSizeException ibse) {            throw new InvalidKeyException(ibse.getMessage());        } catch (BadPaddingException bpe) {            throw new InvalidKeyException(bpe.getMessage());        }    }      private Object unseal(Key key, String provider)        throws IOException, ClassNotFoundException, NoSuchAlgorithmException,            NoSuchProviderException, InvalidKeyException,            IllegalBlockSizeException, BadPaddingException    {        /*         * Create the parameter object.         */        AlgorithmParameters params = null;        if (this.encodedParams != null) {            try {                if (provider != null)                    params = AlgorithmParameters.getInstance(this.paramsAlg,                                                             provider);                else                    params = AlgorithmParameters.getInstance(this.paramsAlg);             } catch (NoSuchProviderException nspe) {                if (provider == null) {                    throw new NoSuchAlgorithmException(this.paramsAlg                                                       + " not found");                } else {                    throw new NoSuchProviderException(nspe.getMessage());                }            }            params.init(this.encodedParams);        }         /*         * Create and initialize the cipher.         */        Cipher c;        try {            if (provider != null)                c = Cipher.getInstance(this.sealAlg, provider);            else                c = Cipher.getInstance(this.sealAlg);        } catch (NoSuchPaddingException nspe) {            throw new NoSuchAlgorithmException("Padding that was used in "                                               + "sealing operation not "                                               + "available");        } catch (NoSuchProviderException nspe) {            if (provider == null) {                throw new NoSuchAlgorithmException(this.sealAlg+" not found");            } else {                throw new NoSuchProviderException(nspe.getMessage());            }        }         try {            if (params != null)                c.init(Cipher.DECRYPT_MODE, key, params);            else                c.init(Cipher.DECRYPT_MODE, key);        } catch (InvalidAlgorithmParameterException iape) {            // this should never happen, because we use the exact same            // parameters that were used in the sealing operation            throw new RuntimeException(iape.getMessage());        }         /*         * Unseal the object         */        byte[] content = c.doFinal(this.encryptedContent);         /*         * De-serialize it         */        // creating a stream pipe-line, from b to a        ByteArrayInputStream b = new ByteArrayInputStream(content);        ObjectInput a = new extObjectInputStream(b);        try {            Object obj = a.readObject();            return obj;        } finally {            a.close();        }    }     /**     * Restores the state of the SealedObject from a stream.     * @param s the object input stream.     * @exception NullPointerException if s is null.     */    private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException    {        s.defaultReadObject();        if (encryptedContent != null)            encryptedContent = (byte[])encryptedContent.clone();        if (encodedParams != null)            encodedParams = (byte[])encodedParams.clone();    }} final class extObjectInputStream extends ObjectInputStream {     private static ClassLoader systemClassLoader = null;     extObjectInputStream(InputStream in)        throws IOException, StreamCorruptedException {        super(in);    }     protected Class resolveClass(ObjectStreamClass v)        throws IOException, ClassNotFoundException    {         try {            /*             * Calling the super.resolveClass() first             * will let us pick up bug fixes in the super             * class (e.g., 4171142).             */            return super.resolveClass(v);        } catch (ClassNotFoundException cnfe) {            /*             * This is a workaround for bug 4224921.             */            ClassLoader loader = Thread.currentThread().getContextClassLoader();            if (loader == null) {                if (systemClassLoader == null) {                    systemClassLoader = ClassLoader.getSystemClassLoader();                }                loader = systemClassLoader;                if (loader == null) {                    throw new ClassNotFoundException(v.getName());                }            }             return Class.forName(v.getName(), false, loader);        }    }}