diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/javax/crypto/SealedObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/javax/crypto/SealedObject.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,497 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.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. + * + *

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. + * + *

Note that the Cipher object must be fully initialized with the + * correct algorithm, key, padding scheme, etc., before being applied + * to a SealedObject. + * + *

The original object that was sealed can be recovered in two different + * ways:

+ * + *

+ * + * @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. + *

+ * That is, cipher.getParameters().getEncoded(). + * + * @serial + */ + protected byte[] encodedParams = null; + + /** + * Constructs a SealedObject from any Serializable object. + * + *

The given object is serialized, and its serialized contents are + * encrypted using the given Cipher, which must be fully initialized. + * + *

Any algorithm parameters that may be used in the encryption + * operation are stored inside of the new SealedObject. + * + * @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. + * + *

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 + * key and the parameters (if any) that had been used in the + * sealing operation. + * + *

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 key 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. + * + *

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. + * + *

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 provider. + * The Cipher object is initialized for decryption, using the given + * key and the parameters (if any) that had been used in the + * sealing operation. + * + *

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 key 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); + } + } +}