Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/crypto/SealedObject.java
38829 views
/*1* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.crypto;2627import sun.misc.SharedSecrets;2829import java.io.*;30import java.security.AlgorithmParameters;31import java.security.Key;32import java.security.InvalidKeyException;33import java.security.InvalidAlgorithmParameterException;34import java.security.NoSuchAlgorithmException;35import java.security.NoSuchProviderException;3637/**38* This class enables a programmer to create an object and protect its39* confidentiality with a cryptographic algorithm.40*41* <p> Given any Serializable object, one can create a SealedObject42* that encapsulates the original object, in serialized43* format (i.e., a "deep copy"), and seals (encrypts) its serialized contents,44* using a cryptographic algorithm such as AES, to protect its45* confidentiality. The encrypted content can later be decrypted (with46* the corresponding algorithm using the correct decryption key) and47* de-serialized, yielding the original object.48*49* <p> Note that the Cipher object must be fully initialized with the50* correct algorithm, key, padding scheme, etc., before being applied51* to a SealedObject.52*53* <p> The original object that was sealed can be recovered in two different54* ways:55*56* <ul>57*58* <li>by using the {@link #getObject(javax.crypto.Cipher) getObject}59* method that takes a <code>Cipher</code> object.60*61* <p> This method requires a fully initialized <code>Cipher</code> object,62* initialized with the63* exact same algorithm, key, padding scheme, etc., that were used to seal the64* object.65*66* <p> This approach has the advantage that the party who unseals the67* sealed object does not require knowledge of the decryption key. For example,68* after one party has initialized the cipher object with the required69* decryption key, it could hand over the cipher object to70* another party who then unseals the sealed object.71*72* <li>by using one of the73* {@link #getObject(java.security.Key) getObject} methods74* that take a <code>Key</code> object.75*76* <p> In this approach, the <code>getObject</code> method creates a cipher77* object for the appropriate decryption algorithm and initializes it with the78* given decryption key and the algorithm parameters (if any) that were stored79* in the sealed object.80*81* <p> This approach has the advantage that the party who82* unseals the object does not need to keep track of the parameters (e.g., an83* IV) that were used to seal the object.84*85* </ul>86*87* @author Li Gong88* @author Jan Luehe89* @see Cipher90* @since 1.491*/9293public class SealedObject implements Serializable {9495static final long serialVersionUID = 4482838265551344752L;9697/**98* The serialized object contents in encrypted format.99*100* @serial101*/102private byte[] encryptedContent = null;103104/**105* The algorithm that was used to seal this object.106*107* @serial108*/109private String sealAlg = null;110111/**112* The algorithm of the parameters used.113*114* @serial115*/116private String paramsAlg = null;117118/**119* The cryptographic parameters used by the sealing Cipher,120* encoded in the default format.121* <p>122* That is, <code>cipher.getParameters().getEncoded()</code>.123*124* @serial125*/126protected byte[] encodedParams = null;127128/**129* Constructs a SealedObject from any Serializable object.130*131* <p>The given object is serialized, and its serialized contents are132* encrypted using the given Cipher, which must be fully initialized.133*134* <p>Any algorithm parameters that may be used in the encryption135* operation are stored inside of the new <code>SealedObject</code>.136*137* @param object the object to be sealed; can be null.138* @param c the cipher used to seal the object.139*140* @exception NullPointerException if the given cipher is null.141* @exception IOException if an error occurs during serialization142* @exception IllegalBlockSizeException if the given cipher is a block143* cipher, no padding has been requested, and the total input length144* (i.e., the length of the serialized object contents) is not a multiple145* of the cipher's block size146*/147public SealedObject(Serializable object, Cipher c) throws IOException,148IllegalBlockSizeException149{150/*151* Serialize the object152*/153154// creating a stream pipe-line, from a to b155ByteArrayOutputStream b = new ByteArrayOutputStream();156ObjectOutput a = new ObjectOutputStream(b);157byte[] content;158try {159// write and flush the object content to byte array160a.writeObject(object);161a.flush();162content = b.toByteArray();163} finally {164a.close();165}166167/*168* Seal the object169*/170try {171this.encryptedContent = c.doFinal(content);172}173catch (BadPaddingException ex) {174// if sealing is encryption only175// Should never happen??176}177178// Save the parameters179if (c.getParameters() != null) {180this.encodedParams = c.getParameters().getEncoded();181this.paramsAlg = c.getParameters().getAlgorithm();182}183184// Save the encryption algorithm185this.sealAlg = c.getAlgorithm();186}187188/**189* Constructs a SealedObject object from the passed-in SealedObject.190*191* @param so a SealedObject object192* @exception NullPointerException if the given sealed object is null.193*/194protected SealedObject(SealedObject so) {195this.encryptedContent = so.encryptedContent.clone();196this.sealAlg = so.sealAlg;197this.paramsAlg = so.paramsAlg;198if (so.encodedParams != null) {199this.encodedParams = so.encodedParams.clone();200} else {201this.encodedParams = null;202}203}204205/**206* Returns the algorithm that was used to seal this object.207*208* @return the algorithm that was used to seal this object.209*/210public final String getAlgorithm() {211return this.sealAlg;212}213214/**215* Retrieves the original (encapsulated) object.216*217* <p>This method creates a cipher for the algorithm that had been used in218* the sealing operation.219* If the default provider package provides an implementation of that220* algorithm, an instance of Cipher containing that implementation is used.221* If the algorithm is not available in the default package, other222* packages are searched.223* The Cipher object is initialized for decryption, using the given224* <code>key</code> and the parameters (if any) that had been used in the225* sealing operation.226*227* <p>The encapsulated object is unsealed and de-serialized, before it is228* returned.229*230* @param key the key used to unseal the object.231*232* @return the original object.233*234* @exception IOException if an error occurs during de-serialiazation.235* @exception ClassNotFoundException if an error occurs during236* de-serialiazation.237* @exception NoSuchAlgorithmException if the algorithm to unseal the238* object is not available.239* @exception InvalidKeyException if the given key cannot be used to unseal240* the object (e.g., it has the wrong algorithm).241* @exception NullPointerException if <code>key</code> is null.242*/243public final Object getObject(Key key)244throws IOException, ClassNotFoundException, NoSuchAlgorithmException,245InvalidKeyException246{247if (key == null) {248throw new NullPointerException("key is null");249}250251try {252return unseal(key, null);253} catch (NoSuchProviderException nspe) {254// we've already caught NoSuchProviderException's and converted255// them into NoSuchAlgorithmException's with details about256// the failing algorithm257throw new NoSuchAlgorithmException("algorithm not found");258} catch (IllegalBlockSizeException ibse) {259throw new InvalidKeyException(ibse.getMessage());260} catch (BadPaddingException bpe) {261throw new InvalidKeyException(bpe.getMessage());262}263}264265/**266* Retrieves the original (encapsulated) object.267*268* <p>The encapsulated object is unsealed (using the given Cipher,269* assuming that the Cipher is already properly initialized) and270* de-serialized, before it is returned.271*272* @param c the cipher used to unseal the object273*274* @return the original object.275*276* @exception NullPointerException if the given cipher is null.277* @exception IOException if an error occurs during de-serialiazation278* @exception ClassNotFoundException if an error occurs during279* de-serialiazation280* @exception IllegalBlockSizeException if the given cipher is a block281* cipher, no padding has been requested, and the total input length is282* not a multiple of the cipher's block size283* @exception BadPaddingException if the given cipher has been284* initialized for decryption, and padding has been specified, but285* the input data does not have proper expected padding bytes286*/287public final Object getObject(Cipher c)288throws IOException, ClassNotFoundException, IllegalBlockSizeException,289BadPaddingException290{291ObjectInput a = getExtObjectInputStream(c);292try {293Object obj = a.readObject();294return obj;295} finally {296a.close();297}298}299300/**301* Retrieves the original (encapsulated) object.302*303* <p>This method creates a cipher for the algorithm that had been used in304* the sealing operation, using an implementation of that algorithm from305* the given <code>provider</code>.306* The Cipher object is initialized for decryption, using the given307* <code>key</code> and the parameters (if any) that had been used in the308* sealing operation.309*310* <p>The encapsulated object is unsealed and de-serialized, before it is311* returned.312*313* @param key the key used to unseal the object.314* @param provider the name of the provider of the algorithm to unseal315* the object.316*317* @return the original object.318*319* @exception IllegalArgumentException if the given provider is null320* or empty.321* @exception IOException if an error occurs during de-serialiazation.322* @exception ClassNotFoundException if an error occurs during323* de-serialiazation.324* @exception NoSuchAlgorithmException if the algorithm to unseal the325* object is not available.326* @exception NoSuchProviderException if the given provider is not327* configured.328* @exception InvalidKeyException if the given key cannot be used to unseal329* the object (e.g., it has the wrong algorithm).330* @exception NullPointerException if <code>key</code> is null.331*/332public final Object getObject(Key key, String provider)333throws IOException, ClassNotFoundException, NoSuchAlgorithmException,334NoSuchProviderException, InvalidKeyException335{336if (key == null) {337throw new NullPointerException("key is null");338}339if (provider == null || provider.length() == 0) {340throw new IllegalArgumentException("missing provider");341}342343try {344return unseal(key, provider);345} catch (IllegalBlockSizeException | BadPaddingException ex) {346throw new InvalidKeyException(ex.getMessage());347}348}349350351private Object unseal(Key key, String provider)352throws IOException, ClassNotFoundException, NoSuchAlgorithmException,353NoSuchProviderException, InvalidKeyException,354IllegalBlockSizeException, BadPaddingException355{356/*357* Create the parameter object.358*/359AlgorithmParameters params = null;360if (this.encodedParams != null) {361try {362if (provider != null)363params = AlgorithmParameters.getInstance(this.paramsAlg,364provider);365else366params = AlgorithmParameters.getInstance(this.paramsAlg);367368} catch (NoSuchProviderException nspe) {369if (provider == null) {370throw new NoSuchAlgorithmException(this.paramsAlg371+ " not found");372} else {373throw new NoSuchProviderException(nspe.getMessage());374}375}376params.init(this.encodedParams);377}378379/*380* Create and initialize the cipher.381*/382Cipher c;383try {384if (provider != null)385c = Cipher.getInstance(this.sealAlg, provider);386else387c = Cipher.getInstance(this.sealAlg);388} catch (NoSuchPaddingException nspe) {389throw new NoSuchAlgorithmException("Padding that was used in "390+ "sealing operation not "391+ "available");392} catch (NoSuchProviderException nspe) {393if (provider == null) {394throw new NoSuchAlgorithmException(this.sealAlg+" not found");395} else {396throw new NoSuchProviderException(nspe.getMessage());397}398}399400try {401if (params != null)402c.init(Cipher.DECRYPT_MODE, key, params);403else404c.init(Cipher.DECRYPT_MODE, key);405} catch (InvalidAlgorithmParameterException iape) {406// this should never happen, because we use the exact same407// parameters that were used in the sealing operation408throw new RuntimeException(iape.getMessage());409}410411ObjectInput a = getExtObjectInputStream(c);412try {413Object obj = a.readObject();414return obj;415} finally {416a.close();417}418}419420/**421* Restores the state of the SealedObject from a stream.422* @param s the object input stream.423* @exception NullPointerException if s is null.424*/425private void readObject(java.io.ObjectInputStream s)426throws java.io.IOException, ClassNotFoundException427{428s.defaultReadObject();429if (encryptedContent != null)430encryptedContent = encryptedContent.clone();431if (encodedParams != null)432encodedParams = encodedParams.clone();433}434435// This method is also called inside SealedObjectForKeyProtector.java.436private ObjectInputStream getExtObjectInputStream(Cipher c)437throws BadPaddingException, IllegalBlockSizeException, IOException {438439byte[] content = c.doFinal(this.encryptedContent);440ByteArrayInputStream b = new ByteArrayInputStream(content);441return new extObjectInputStream(b);442}443444static {445SharedSecrets.setJavaxCryptoSealedObjectAccess((obj,c) -> obj.getExtObjectInputStream(c));446}447}448449final class extObjectInputStream extends ObjectInputStream {450451private static ClassLoader systemClassLoader = null;452453extObjectInputStream(InputStream in)454throws IOException, StreamCorruptedException {455super(in);456}457458protected Class<?> resolveClass(ObjectStreamClass v)459throws IOException, ClassNotFoundException460{461462try {463/*464* Calling the super.resolveClass() first465* will let us pick up bug fixes in the super466* class (e.g., 4171142).467*/468return super.resolveClass(v);469} catch (ClassNotFoundException cnfe) {470/*471* This is a workaround for bug 4224921.472*/473ClassLoader loader = Thread.currentThread().getContextClassLoader();474if (loader == null) {475if (systemClassLoader == null) {476systemClassLoader = ClassLoader.getSystemClassLoader();477}478loader = systemClassLoader;479if (loader == null) {480throw new ClassNotFoundException(v.getName());481}482}483484return Class.forName(v.getName(), false, loader);485}486}487}488489490