• 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  *
37  * <p>Since a {@code SealedObject} instance is serializable it can
38  * either be stored or transmitted over an insecure channel.
39  *
40  * <p>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 cipher's {@link AlgorithmParameters} in encoded format.
50      * Equivalent to {@code cipher.getParameters().getEncoded()},
51      * or null if the cipher did not use any parameters.
52      */
53     protected byte[] encodedParams;
54 
55     private byte[] encryptedContent;
56     private String sealAlg;
57     private String paramsAlg;
58 
readObject(ObjectInputStream s)59     private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
60         // We do unshared reads here to ensure we have our own clones of the byte[]s.
61         encodedParams = (byte[]) s.readUnshared();
62         encryptedContent = (byte[]) s.readUnshared();
63         // These are regular shared reads because the algorithms used by a given stream are
64         // almost certain to the be same for each object, and String is immutable anyway,
65         // so there's no security concern about sharing.
66         sealAlg = (String) s.readObject();
67         paramsAlg = (String) s.readObject();
68     }
69 
70     /**
71      * Creates a new {@code SealedObject} instance wrapping the specified object
72      * and sealing it using the specified cipher.
73      * <p>
74      * The cipher must be fully initialized.
75      *
76      * @param object
77      *            the object to seal, can be {@code null}.
78      * @param c
79      *            the cipher to encrypt the object.
80      * @throws IOException
81      *             if the serialization fails.
82      * @throws IllegalBlockSizeException
83      *             if the specified cipher is a block cipher and the length of
84      *             the serialized data is not a multiple of the ciphers block
85      *             size.
86      * @throws NullPointerException
87      *             if the cipher is {@code null}.
88      */
SealedObject(Serializable object, Cipher c)89     public SealedObject(Serializable object, Cipher c)
90                 throws IOException, IllegalBlockSizeException {
91         if (c == null) {
92             throw new NullPointerException("c == null");
93         }
94         try {
95             ByteArrayOutputStream bos = new ByteArrayOutputStream();
96             ObjectOutputStream oos = new ObjectOutputStream(bos);
97             oos.writeObject(object);
98             oos.flush();
99             AlgorithmParameters ap = c.getParameters();
100             this.encodedParams = (ap == null) ? null : ap.getEncoded();
101             this.paramsAlg = (ap == null) ? null : ap.getAlgorithm();
102             this.sealAlg = c.getAlgorithm();
103             this.encryptedContent = c.doFinal(bos.toByteArray());
104         } catch (BadPaddingException e) {
105             // should be never thrown because the cipher
106             // should be initialized for encryption
107             throw new IOException(e.toString());
108         }
109     }
110 
111     /**
112      * Creates a new {@code SealedObject} instance by copying the data from
113      * the specified object.
114      *
115      * @param so
116      *            the object to copy.
117      */
SealedObject(SealedObject so)118     protected SealedObject(SealedObject so) {
119         if (so == null) {
120             throw new NullPointerException("so == null");
121         }
122         this.encryptedContent = so.encryptedContent;
123         this.encodedParams = so.encodedParams;
124         this.sealAlg = so.sealAlg;
125         this.paramsAlg = so.paramsAlg;
126     }
127 
128     /**
129      * Returns the algorithm this object was sealed with.
130      *
131      * @return the algorithm this object was sealed with.
132      */
getAlgorithm()133     public final String getAlgorithm() {
134         return sealAlg;
135     }
136 
137     /**
138      * Returns the wrapped object, decrypting it using the specified key.
139      *
140      * @param key
141      *            the key to decrypt the data with.
142      * @return the encapsulated object.
143      * @throws IOException
144      *             if deserialization fails.
145      * @throws ClassNotFoundException
146      *             if deserialization fails.
147      * @throws NoSuchAlgorithmException
148      *             if the algorithm to decrypt the data is not available.
149      * @throws InvalidKeyException
150      *             if the specified key cannot be used to decrypt the data.
151      */
getObject(Key key)152     public final Object getObject(Key key)
153                 throws IOException, ClassNotFoundException,
154                        NoSuchAlgorithmException, InvalidKeyException {
155         if (key == null) {
156             throw new InvalidKeyException("key == null");
157         }
158         try {
159             Cipher cipher = Cipher.getInstance(sealAlg);
160             if ((paramsAlg != null) && (paramsAlg.length() != 0)) {
161                 AlgorithmParameters params =
162                     AlgorithmParameters.getInstance(paramsAlg);
163                 params.init(encodedParams);
164                 cipher.init(Cipher.DECRYPT_MODE, key, params);
165             } else {
166                 cipher.init(Cipher.DECRYPT_MODE, key);
167             }
168             byte[] serialized = cipher.doFinal(encryptedContent);
169             ObjectInputStream ois =
170                     new ObjectInputStream(
171                             new ByteArrayInputStream(serialized));
172             return ois.readObject();
173         } catch (NoSuchPaddingException e)  {
174             // should not be thrown because cipher text was made
175             // with existing padding
176             throw new NoSuchAlgorithmException(e.toString());
177         } catch (InvalidAlgorithmParameterException e) {
178             // should not be thrown because cipher text was made
179             // with correct algorithm parameters
180             throw new NoSuchAlgorithmException(e.toString());
181         } catch (IllegalBlockSizeException e) {
182             // should not be thrown because the cipher text
183             // was correctly made
184             throw new NoSuchAlgorithmException(e.toString());
185         } catch (BadPaddingException e) {
186             // should not be thrown because the cipher text
187             // was correctly made
188             throw new NoSuchAlgorithmException(e.toString());
189         } catch (IllegalStateException  e) {
190             // should never be thrown because cipher is initialized
191             throw new NoSuchAlgorithmException(e.toString());
192         }
193     }
194 
195     /**
196      * Returns the wrapped object, decrypting it using the specified
197      * cipher.
198      *
199      * @param c
200      *            the cipher to decrypt the data.
201      * @return the encapsulated object.
202      * @throws IOException
203      *             if deserialization fails.
204      * @throws ClassNotFoundException
205      *             if deserialization fails.
206      * @throws IllegalBlockSizeException
207      *             if the specified cipher is a block cipher and the length of
208      *             the serialized data is not a multiple of the ciphers block
209      *             size.
210      * @throws BadPaddingException
211      *             if the padding of the data does not match the padding scheme.
212      */
getObject(Cipher c)213     public final Object getObject(Cipher c)
214                 throws IOException, ClassNotFoundException,
215                        IllegalBlockSizeException, BadPaddingException {
216         if (c == null) {
217             throw new NullPointerException("c == null");
218         }
219         byte[] serialized = c.doFinal(encryptedContent);
220         ObjectInputStream ois =
221                 new ObjectInputStream(
222                         new ByteArrayInputStream(serialized));
223         return ois.readObject();
224     }
225 
226     /**
227      * Returns the wrapped object, decrypting it using the specified key. The
228      * specified provider is used to retrieve the cipher algorithm.
229      *
230      * @param key
231      *            the key to decrypt the data.
232      * @param provider
233      *            the name of the provider that provides the cipher algorithm.
234      * @return the encapsulated object.
235      * @throws IOException
236      *             if deserialization fails.
237      * @throws ClassNotFoundException
238      *             if deserialization fails.
239      * @throws NoSuchAlgorithmException
240      *             if the algorithm used to decrypt the data is not available.
241      * @throws NoSuchProviderException
242      *             if the specified provider is not available.
243      * @throws InvalidKeyException
244      *             if the specified key cannot be used to decrypt the data.
245      */
getObject(Key key, String provider)246     public final Object getObject(Key key, String provider)
247                 throws IOException, ClassNotFoundException,
248                        NoSuchAlgorithmException, NoSuchProviderException,
249                        InvalidKeyException {
250         if (provider == null || provider.isEmpty()) {
251             throw new IllegalArgumentException("provider name empty or null");
252         }
253         try {
254             Cipher cipher = Cipher.getInstance(sealAlg, provider);
255             if ((paramsAlg != null) && (paramsAlg.length() != 0)) {
256                 AlgorithmParameters params =
257                     AlgorithmParameters.getInstance(paramsAlg);
258                 params.init(encodedParams);
259                 cipher.init(Cipher.DECRYPT_MODE, key, params);
260             } else {
261                 cipher.init(Cipher.DECRYPT_MODE, key);
262             }
263             byte[] serialized = cipher.doFinal(encryptedContent);
264             ObjectInputStream ois =
265                     new ObjectInputStream(
266                             new ByteArrayInputStream(serialized));
267             return ois.readObject();
268         } catch (NoSuchPaddingException e)  {
269             // should not be thrown because cipher text was made
270             // with existing padding
271             throw new NoSuchAlgorithmException(e.toString());
272         } catch (InvalidAlgorithmParameterException e) {
273             // should not be thrown because cipher text was made
274             // with correct algorithm parameters
275             throw new NoSuchAlgorithmException(e.toString());
276         } catch (IllegalBlockSizeException e) {
277             // should not be thrown because the cipher text
278             // was correctly made
279             throw new NoSuchAlgorithmException(e.toString());
280         } catch (BadPaddingException e) {
281             // should not be thrown because the cipher text
282             // was correctly made
283             throw new NoSuchAlgorithmException(e.toString());
284         } catch (IllegalStateException  e) {
285             // should never be thrown because cipher is initialized
286             throw new NoSuchAlgorithmException(e.toString());
287         }
288     }
289 }
290