• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
readObject(ObjectInputStream s)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      */
SealedObject(Serializable object, Cipher c)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      */
SealedObject(SealedObject so)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      */
getAlgorithm()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      */
getObject(Key key)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      */
getObject(Cipher c)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      */
getObject(Key key, String provider)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