Home | History | Annotate | Download | only in crypto
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package javax.crypto;
     19 
     20 import java.io.ByteArrayInputStream;
     21 import java.io.ByteArrayOutputStream;
     22 import java.io.IOException;
     23 import java.io.ObjectInputStream;
     24 import java.io.ObjectOutputStream;
     25 import java.io.Serializable;
     26 import java.security.AlgorithmParameters;
     27 import java.security.InvalidAlgorithmParameterException;
     28 import java.security.InvalidKeyException;
     29 import java.security.Key;
     30 import java.security.NoSuchAlgorithmException;
     31 import java.security.NoSuchProviderException;
     32 
     33 /**
     34  * A {@code SealedObject} is a wrapper around a {@code serializable} object
     35  * instance and encrypts it using a cryptographic cipher.
     36  * <p>
     37  * Since a {@code SealedObject} instance is a serializable object itself it can
     38  * either be stored or transmitted over an insecure channel.
     39  * <p>
     40  * The wrapped object can later be decrypted (unsealed) using the corresponding
     41  * key and then be deserialized to retrieve the original object.The sealed
     42  * object itself keeps track of the cipher and corresponding parameters.
     43  */
     44 public class SealedObject implements Serializable {
     45 
     46     private static final long serialVersionUID = 4482838265551344752L;
     47 
     48     /**
     49      * The {@link AlgorithmParameters} in encoded format.
     50      */
     51     protected byte[] encodedParams;
     52     private byte[] encryptedContent;
     53     private String sealAlg;
     54     private String paramsAlg;
     55 
     56     private void readObject(ObjectInputStream s)
     57                 throws IOException, ClassNotFoundException {
     58         encodedParams = (byte []) s.readUnshared();
     59         encryptedContent = (byte []) s.readUnshared();
     60         sealAlg = (String) s.readUnshared();
     61         paramsAlg = (String) s.readUnshared();
     62     }
     63 
     64     /**
     65      * Creates a new {@code SealedObject} instance wrapping the specified object
     66      * and sealing it using the specified cipher.
     67      * <p>
     68      * The cipher must be fully initialized.
     69      *
     70      * @param object
     71      *            the object to seal, can be {@code null}.
     72      * @param c
     73      *            the cipher to encrypt the object.
     74      * @throws IOException
     75      *             if the serialization fails.
     76      * @throws IllegalBlockSizeException
     77      *             if the specified cipher is a block cipher and the length of
     78      *             the serialized data is not a multiple of the ciphers block
     79      *             size.
     80      * @throws NullPointerException
     81      *             if the cipher is {@code null}.
     82      */
     83     public SealedObject(Serializable object, Cipher c)
     84                 throws IOException, IllegalBlockSizeException {
     85         if (c == null) {
     86             throw new NullPointerException("c == null");
     87         }
     88         try {
     89             ByteArrayOutputStream bos = new ByteArrayOutputStream();
     90             ObjectOutputStream oos = new ObjectOutputStream(bos);
     91             oos.writeObject(object);
     92             oos.flush();
     93             AlgorithmParameters ap = c.getParameters();
     94             this.encodedParams = (ap == null) ? null : ap.getEncoded();
     95             this.paramsAlg = (ap == null) ? null : ap.getAlgorithm();
     96             this.sealAlg = c.getAlgorithm();
     97             this.encryptedContent = c.doFinal(bos.toByteArray());
     98         } catch (BadPaddingException e) {
     99             // should be never thrown because the cipher
    100             // should be initialized for encryption
    101             throw new IOException(e.toString());
    102         }
    103     }
    104 
    105     /**
    106      * Creates a new {@code SealedObject} instance by copying the data from
    107      * the specified object.
    108      *
    109      * @param so
    110      *            the object to copy.
    111      */
    112     protected SealedObject(SealedObject so) {
    113         if (so == null) {
    114             throw new NullPointerException("so == null");
    115         }
    116         this.encryptedContent = so.encryptedContent;
    117         this.encodedParams = so.encodedParams;
    118         this.sealAlg = so.sealAlg;
    119         this.paramsAlg = so.paramsAlg;
    120     }
    121 
    122     /**
    123      * Returns the algorithm this object was sealed with.
    124      *
    125      * @return the algorithm this object was sealed with.
    126      */
    127     public final String getAlgorithm() {
    128         return sealAlg;
    129     }
    130 
    131     /**
    132      * Returns the wrapped object, decrypting it using the specified key.
    133      *
    134      * @param key
    135      *            the key to decrypt the data with.
    136      * @return the encapsulated object.
    137      * @throws IOException
    138      *             if deserialization fails.
    139      * @throws ClassNotFoundException
    140      *             if deserialization fails.
    141      * @throws NoSuchAlgorithmException
    142      *             if the algorithm to decrypt the data is not available.
    143      * @throws InvalidKeyException
    144      *             if the specified key cannot be used to decrypt the data.
    145      */
    146     public final Object getObject(Key key)
    147                 throws IOException, ClassNotFoundException,
    148                        NoSuchAlgorithmException, InvalidKeyException {
    149         // BEGIN android-added
    150         if (key == null) {
    151             throw new InvalidKeyException("key == null");
    152         }
    153         // END android-added
    154         try {
    155             Cipher cipher = Cipher.getInstance(sealAlg);
    156             if ((paramsAlg != null) && (paramsAlg.length() != 0)) {
    157                 AlgorithmParameters params =
    158                     AlgorithmParameters.getInstance(paramsAlg);
    159                 params.init(encodedParams);
    160                 cipher.init(Cipher.DECRYPT_MODE, key, params);
    161             } else {
    162                 cipher.init(Cipher.DECRYPT_MODE, key);
    163             }
    164             byte[] serialized = cipher.doFinal(encryptedContent);
    165             ObjectInputStream ois =
    166                     new ObjectInputStream(
    167                             new ByteArrayInputStream(serialized));
    168             return ois.readObject();
    169         } catch (NoSuchPaddingException e)  {
    170             // should not be thrown because cipher text was made
    171             // with existing padding
    172             throw new NoSuchAlgorithmException(e.toString());
    173         } catch (InvalidAlgorithmParameterException e) {
    174             // should not be thrown because cipher text was made
    175             // with correct algorithm parameters
    176             throw new NoSuchAlgorithmException(e.toString());
    177         } catch (IllegalBlockSizeException e) {
    178             // should not be thrown because the cipher text
    179             // was correctly made
    180             throw new NoSuchAlgorithmException(e.toString());
    181         } catch (BadPaddingException e) {
    182             // should not be thrown because the cipher text
    183             // was correctly made
    184             throw new NoSuchAlgorithmException(e.toString());
    185         } catch (IllegalStateException  e) {
    186             // should never be thrown because cipher is initialized
    187             throw new NoSuchAlgorithmException(e.toString());
    188         }
    189     }
    190 
    191     /**
    192      * Returns the wrapped object, decrypting it using the specified
    193      * cipher.
    194      *
    195      * @param c
    196      *            the cipher to decrypt the data.
    197      * @return the encapsulated object.
    198      * @throws IOException
    199      *             if deserialization fails.
    200      * @throws ClassNotFoundException
    201      *             if deserialization fails.
    202      * @throws IllegalBlockSizeException
    203      *             if the specified cipher is a block cipher and the length of
    204      *             the serialized data is not a multiple of the ciphers block
    205      *             size.
    206      * @throws BadPaddingException
    207      *             if the padding of the data does not match the padding scheme.
    208      */
    209     public final Object getObject(Cipher c)
    210                 throws IOException, ClassNotFoundException,
    211                        IllegalBlockSizeException, BadPaddingException {
    212         if (c == null) {
    213             throw new NullPointerException("c == null");
    214         }
    215         byte[] serialized = c.doFinal(encryptedContent);
    216         ObjectInputStream ois =
    217                 new ObjectInputStream(
    218                         new ByteArrayInputStream(serialized));
    219         return ois.readObject();
    220     }
    221 
    222     /**
    223      * Returns the wrapped object, decrypting it using the specified key. The
    224      * specified provider is used to retrieve the cipher algorithm.
    225      *
    226      * @param key
    227      *            the key to decrypt the data.
    228      * @param provider
    229      *            the name of the provider that provides the cipher algorithm.
    230      * @return the encapsulated object.
    231      * @throws IOException
    232      *             if deserialization fails.
    233      * @throws ClassNotFoundException
    234      *             if deserialization fails.
    235      * @throws NoSuchAlgorithmException
    236      *             if the algorithm used to decrypt the data is not available.
    237      * @throws NoSuchProviderException
    238      *             if the specified provider is not available.
    239      * @throws InvalidKeyException
    240      *             if the specified key cannot be used to decrypt the data.
    241      */
    242     public final Object getObject(Key key, String provider)
    243                 throws IOException, ClassNotFoundException,
    244                        NoSuchAlgorithmException, NoSuchProviderException,
    245                        InvalidKeyException {
    246         if (provider == null || provider.isEmpty()) {
    247             throw new IllegalArgumentException("provider name empty or null");
    248         }
    249         try {
    250             Cipher cipher = Cipher.getInstance(sealAlg, provider);
    251             if ((paramsAlg != null) && (paramsAlg.length() != 0)) {
    252                 AlgorithmParameters params =
    253                     AlgorithmParameters.getInstance(paramsAlg);
    254                 params.init(encodedParams);
    255                 cipher.init(Cipher.DECRYPT_MODE, key, params);
    256             } else {
    257                 cipher.init(Cipher.DECRYPT_MODE, key);
    258             }
    259             byte[] serialized = cipher.doFinal(encryptedContent);
    260             ObjectInputStream ois =
    261                     new ObjectInputStream(
    262                             new ByteArrayInputStream(serialized));
    263             return ois.readObject();
    264         } catch (NoSuchPaddingException e)  {
    265             // should not be thrown because cipher text was made
    266             // with existing padding
    267             throw new NoSuchAlgorithmException(e.toString());
    268         } catch (InvalidAlgorithmParameterException e) {
    269             // should not be thrown because cipher text was made
    270             // with correct algorithm parameters
    271             throw new NoSuchAlgorithmException(e.toString());
    272         } catch (IllegalBlockSizeException e) {
    273             // should not be thrown because the cipher text
    274             // was correctly made
    275             throw new NoSuchAlgorithmException(e.toString());
    276         } catch (BadPaddingException e) {
    277             // should not be thrown because the cipher text
    278             // was correctly made
    279             throw new NoSuchAlgorithmException(e.toString());
    280         } catch (IllegalStateException  e) {
    281             // should never be thrown because cipher is initialized
    282             throw new NoSuchAlgorithmException(e.toString());
    283         }
    284     }
    285 }
    286