aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/spongycastle/jce/src/main/java/javax/crypto/SealedObject.java
blob: 576c03bd3f4f97722dbad6c5140ee306d6d8da61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
package javax.crypto;

import java.io.*;
import java.security.*;

/**
 * This class enables a programmer to create an object and protect its
 * confidentiality with a cryptographic algorithm.
 *
 * <p>
 * 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.
 *
 * <p>
 * Note that the Cipher object must be fully initialized with the
 * correct algorithm, key, padding scheme, etc., before being applied
 * to a SealedObject.
 *
 * <p>
 * The original object that was sealed can be recovered in two different
 * ways:
 * <p>
 *
 * <ul>
 *
 * <li>by using the <a href="#getObject(javax.crypto.Cipher)">getObject</a>
 * method that takes a <code>Cipher</code> object.
 * 
 * <p>
 * This method requires a fully initialized <code>Cipher</code> object,
 * initialized with the
 * exact same algorithm, key, padding scheme, etc., that were used to seal the
 * object.
 *
 * <p>
 * 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.
 *
 * <p>
 * 
 * <li>by using one of the
 * <a href="#getObject(java.security.Key)">getObject</a> methods
 * that take a <code>Key</code> object.
 *
 * <p> In this approach, the <code>getObject</code> 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.
 *
 * <p> 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.
 *
 * </ul>
 *
 * @see Cipher 
 */
public class SealedObject
    implements Serializable
{
    private static final long serialVersionUID = 4482838265551344752L;

    private byte[]  encodedParams;
    private byte[]  encryptedContent;
    private String  paramsAlg;
    private String  sealAlg;

    /**
     * Constructs a SealedObject from any Serializable object.
     * <p>
     * The given object is serialized, and its serialized contents are
     * encrypted using the given Cipher, which must be fully initialized.
     * <p>
     * Any algorithm parameters that may be used in the encryption
     * operation are stored inside of the new <code>SealedObject</code>.
     *
     * @param object the object to be sealed.
     * @param c the cipher used to seal the object.
     * @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
    {
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
        oOut.writeObject(object);
        oOut.close();
        byte[] encodedObject = bOut.toByteArray();

        if (c == null)
        {
            throw new IllegalArgumentException("cipher object is null!");
        }

        try
        {
            this.encryptedContent = c.doFinal(encodedObject);
        }
        catch (BadPaddingException e)
        {
            // should not happen
            throw new IOException(e.getMessage());
        }

        this.sealAlg = c.getAlgorithm();
        AlgorithmParameters params = c.getParameters();
        if (params != null)
        {
            this.encodedParams = params.getEncoded();
            this.paramsAlg = params.getAlgorithm();
        }
    }

    /**
     * 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 sealAlg;
    }

    /**
     * Retrieves the original (encapsulated) object.
     * <p>
     * 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
     * <code>key</code> and the parameters (if any) that had been used in the
     * sealing operation.
     * <p>
     * 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).
     */
    public final Object getObject(
        Key     key)
    throws IOException, ClassNotFoundException, NoSuchAlgorithmException, InvalidKeyException
    {
        if (key == null)
        {
            throw new IllegalArgumentException("key object is null!");
        }

        try
        {
            return getObject(key, null);
        }
        catch (NoSuchProviderException e)
        {
            throw new NoSuchAlgorithmException(e.getMessage());
        }
    }

    /**
     * Retrieves the original (encapsulated) object.
     * <p>
     * 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 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
    {
        if (c == null)
        {
            throw new IllegalArgumentException("cipher object is null!");
        }

        byte[] encodedObject = c.doFinal(encryptedContent);
        ObjectInputStream oIn = new ObjectInputStream(
            new ByteArrayInputStream(encodedObject));
        return oIn.readObject();
    }

    /**
     * Retrieves the original (encapsulated) object.
     * <p>
     * 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 <code>provider</code>.
     * The Cipher object is initialized for decryption, using the given
     * <code>key</code> and the parameters (if any) that had been used in the
     * sealing operation.
     * <p>
     * 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 IOException if an error occurs during de-serialiazation.
     * @exception ClassNotFoundException if an error occurs during 
     * de-serialization.
     * @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).
     */
    public final Object getObject(
        Key     key,
        String  provider)
        throws IOException, ClassNotFoundException,
            NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
    {
        if (key == null)
        {
            throw new IllegalArgumentException("key object is null!");
        }

        Cipher cipher = null;
        try
        {
            if (provider != null)
            {
                cipher = Cipher.getInstance(sealAlg, provider);
            }
            else
            {
                cipher = Cipher.getInstance(sealAlg);
            }
        }
        catch (NoSuchPaddingException e)
        {
            throw new NoSuchAlgorithmException(e.getMessage());
        }

        if (paramsAlg == null)
        {
            cipher.init(Cipher.DECRYPT_MODE, key);
        }
        else
        {
            AlgorithmParameters algParams =
                AlgorithmParameters.getInstance(paramsAlg);
            algParams.init(encodedParams);

            try
            {
                cipher.init(Cipher.DECRYPT_MODE, key, algParams);
            }
            catch (InvalidAlgorithmParameterException e)
            {
                throw new IOException(e.getMessage());
            }
        }

        try
        {
            return getObject(cipher);
        }
        catch (BadPaddingException e)
        {
            throw new IOException(e.getMessage());
        }
        catch (IllegalBlockSizeException e2)
        {
            throw new IOException(e2.getMessage());
        }
    }
}