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:
+ * + *
Cipher
object.
+ *
+ * This method requires a fully initialized Cipher
object,
+ * initialized with the
+ * exact same algorithm, key, padding scheme, etc., that were used to seal the
+ * object.
+ *
+ *
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. + * + *
+ * + *
Key
object.
+ *
+ * In this approach, the getObject
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.
+ *
+ *
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. + * + *
+ * 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);
+ }
+ }
+}