001/* SealedObject.java -- An encrypted Serializable object. 002 Copyright (C) 2004 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.crypto; 040 041import java.io.ByteArrayInputStream; 042import java.io.ByteArrayOutputStream; 043import java.io.IOException; 044import java.io.ObjectInputStream; 045import java.io.ObjectOutputStream; 046import java.io.Serializable; 047 048import java.security.AlgorithmParameters; 049import java.security.InvalidAlgorithmParameterException; 050import java.security.InvalidKeyException; 051import java.security.Key; 052import java.security.NoSuchAlgorithmException; 053import java.security.NoSuchProviderException; 054 055/** 056 * This class allows any {@link java.io.Serializable} object to be 057 * stored in an encrypted form. 058 * 059 * <p>When the sealed object is ready to be unsealed (and deserialized) 060 * the caller may use either 061 * 062 * <ol> 063 * <li>{@link #getObject(javax.crypto.Cipher)}, which uses an 064 * already-initialized {@link javax.crypto.Cipher}.<br> 065 * <br> 066 * or,</li> 067 * 068 * <li>{@link #getObject(java.security.Key)} or {@link 069 * #getObject(java.security.Key,java.lang.String)}, which will 070 * initialize a new cipher instance with the {@link #encodedParams} that 071 * were stored with this sealed object (this is so parameters, such as 072 * the IV, don't need to be known by the one unsealing the object).</li> 073 * </ol> 074 * 075 * @author Casey Marshall (csm@gnu.org) 076 * @since 1.4 077 */ 078public class SealedObject implements Serializable 079{ 080 081 // Constants and fields. 082 // ------------------------------------------------------------------------ 083 084 /** The encoded algorithm parameters. */ 085 protected byte[] encodedParams; 086 087 /** The serialized, encrypted object. */ 088 private byte[] encryptedContent; 089 090 /** The algorithm used to seal the object. */ 091 private String sealAlg; 092 093 /** The parameter type. */ 094 private String paramsAlg; 095 096 /** The cipher that decrypts when this object is unsealed. */ 097 private transient Cipher sealCipher; 098 099 /** Compatible with JDK1.4. */ 100 private static final long serialVersionUID = 4482838265551344752L; 101 102 // Constructors. 103 // ------------------------------------------------------------------------ 104 105 /** 106 * Create a new sealed object from a {@link java.io.Serializable} 107 * object and a cipher. 108 * 109 * @param object The object to seal. 110 * @param cipher The cipher to encrypt with. 111 * @throws java.io.IOException If serializing the object fails. 112 * @throws javax.crypto.IllegalBlockSizeException If the cipher has no 113 * padding and the size of the serialized representation of the 114 * object is not a multiple of the cipher's block size. 115 */ 116 public SealedObject(Serializable object, Cipher cipher) 117 throws IOException, IllegalBlockSizeException 118 { 119 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 120 ObjectOutputStream oos = new ObjectOutputStream(baos); 121 oos.writeObject(object); 122 oos.flush(); 123 try 124 { 125 encryptedContent = cipher.doFinal(baos.toByteArray()); 126 } 127 catch (IllegalStateException ise) 128 { 129 throw new IOException("cipher not in proper state"); 130 } 131 catch (BadPaddingException bpe) 132 { 133 throw new IOException( 134 "encrypting but got javax.crypto.BadPaddingException"); 135 } 136 sealAlg = cipher.getAlgorithm(); 137 encodedParams = cipher.getParameters().getEncoded(); 138 paramsAlg = cipher.getParameters().getAlgorithm(); 139 } 140 141 /** 142 * Create a new sealed object from another sealed object. 143 * 144 * @param so The other sealed object. 145 */ 146 protected SealedObject(SealedObject so) 147 { 148 this.encodedParams = (byte[]) so.encodedParams.clone(); 149 this.encryptedContent = (byte[]) so.encryptedContent.clone(); 150 this.sealAlg = so.sealAlg; 151 this.paramsAlg = so.paramsAlg; 152 } 153 154 // Instance methods. 155 // ------------------------------------------------------------------------ 156 157 /** 158 * Get the name of the algorithm used to seal this object. 159 * 160 * @return The algorithm's name. 161 */ 162 public final String getAlgorithm() 163 { 164 return sealAlg; 165 } 166 167 /** 168 * Unseal and deserialize this sealed object with a specified (already 169 * initialized) cipher. 170 * 171 * @param cipher The cipher to decrypt with. 172 * @return The original object. 173 * @throws java.io.IOException If reading fails. 174 * @throws java.lang.ClassNotFoundException If deserialization fails. 175 * @throws javax.crypto.IllegalBlockSizeException If the cipher has no 176 * padding and the encrypted data is not a multiple of the 177 * cipher's block size. 178 * @throws javax.crypto.BadPaddingException If the padding bytes are 179 * incorrect. 180 */ 181 public final Object getObject(Cipher cipher) 182 throws IOException, ClassNotFoundException, IllegalBlockSizeException, 183 BadPaddingException 184 { 185 sealCipher = cipher; 186 return unseal(); 187 } 188 189 /** 190 * Unseal and deserialize this sealed object with the specified key. 191 * 192 * @param key The key to decrypt with. 193 * @return The original object. 194 * @throws java.io.IOException If reading fails. 195 * @throws java.lang.ClassNotFoundException If deserialization fails. 196 * @throws java.security.InvalidKeyException If the supplied key 197 * cannot be used to unseal this object. 198 * @throws java.security.NoSuchAlgorithmException If the algorithm 199 * used to originally seal this object is not available. 200 */ 201 public final Object getObject(Key key) 202 throws IOException, ClassNotFoundException, InvalidKeyException, 203 NoSuchAlgorithmException 204 { 205 try 206 { 207 if (sealCipher == null) 208 sealCipher = Cipher.getInstance(sealAlg); 209 } 210 catch (NoSuchPaddingException nspe) 211 { 212 throw new NoSuchAlgorithmException(nspe.getMessage()); 213 } 214 AlgorithmParameters params = null; 215 if (encodedParams != null) 216 { 217 params = AlgorithmParameters.getInstance(paramsAlg); 218 params.init(encodedParams); 219 } 220 try 221 { 222 sealCipher.init(Cipher.DECRYPT_MODE, key, params); 223 return unseal(); 224 } 225 catch (InvalidAlgorithmParameterException iape) 226 { 227 throw new IOException("bad parameters"); 228 } 229 catch (IllegalBlockSizeException ibse) 230 { 231 throw new IOException("illegal block size"); 232 } 233 catch (BadPaddingException bpe) 234 { 235 throw new IOException("bad padding"); 236 } 237 } 238 239 /** 240 * Unseal and deserialize this sealed object with the specified key, 241 * using a cipher from the named provider. 242 * 243 * @param key The key to decrypt with. 244 * @param provider The name of the provider to use. 245 * @return The original object. 246 * @throws java.io.IOException If reading fails. 247 * @throws java.lang.ClassNotFoundException If deserialization fails. 248 * @throws java.security.InvalidKeyException If the supplied key 249 * cannot be used to unseal this object. 250 * @throws java.security.NoSuchAlgorithmException If the algorithm 251 * used to originally seal this object is not available from 252 * the named provider. 253 * @throws java.security.NoSuchProviderException If the named provider 254 * does not exist. 255 */ 256 public final Object getObject(Key key, String provider) 257 throws IOException, ClassNotFoundException, InvalidKeyException, 258 NoSuchAlgorithmException, NoSuchProviderException 259 { 260 try 261 { 262 sealCipher = Cipher.getInstance(sealAlg, provider); 263 } 264 catch (NoSuchPaddingException nspe) 265 { 266 throw new NoSuchAlgorithmException(nspe.getMessage()); 267 } 268 AlgorithmParameters params = null; 269 if (encodedParams != null) 270 { 271 params = AlgorithmParameters.getInstance(paramsAlg, provider); 272 params.init(encodedParams); 273 } 274 try 275 { 276 sealCipher.init(Cipher.DECRYPT_MODE, key, params); 277 return unseal(); 278 } 279 catch (InvalidAlgorithmParameterException iape) 280 { 281 throw new IOException("bad parameters"); 282 } 283 catch (IllegalBlockSizeException ibse) 284 { 285 throw new IOException("illegal block size"); 286 } 287 catch (BadPaddingException bpe) 288 { 289 throw new IOException("bad padding"); 290 } 291 } 292 293 // Own methods. 294 // ------------------------------------------------------------------------ 295 296 /** 297 * Deserialize this object. 298 * 299 * @param ois The input stream. 300 * @throws java.io.IOException If reading fails. 301 * @throws java.lang.ClassNotFoundException If reading fails. 302 */ 303 private void readObject(ObjectInputStream ois) 304 throws IOException, ClassNotFoundException 305 { 306 encodedParams = (byte[]) ois.readObject(); 307 encryptedContent = (byte[]) ois.readObject(); 308 sealAlg = (String) ois.readObject(); 309 paramsAlg = (String) ois.readObject(); 310 } 311 312 /** 313 * Serialize this object. 314 * 315 * @param oos The output stream. 316 * @throws java.io.IOException If writing fails. 317 */ 318 private void writeObject(ObjectOutputStream oos) 319 throws IOException 320 { 321 oos.writeObject(encodedParams); 322 oos.writeObject(encryptedContent); 323 oos.writeObject(sealAlg); 324 oos.writeObject(paramsAlg); 325 } 326 327 /** 328 * Unseal this object, returning it. 329 * 330 * @return The unsealed, deserialized Object. 331 * @throws java.io.IOException If reading fails. 332 * @throws java.io.ClassNotFoundException If reading fails. 333 * @throws javax.crypto.IllegalBlockSizeException If the cipher has no 334 * padding and the encrypted data is not a multiple of the 335 * cipher's block size. 336 * @throws javax.crypto.BadPaddingException If the padding bytes are 337 * incorrect. 338 */ 339 private Object unseal() 340 throws IOException, ClassNotFoundException, IllegalBlockSizeException, 341 BadPaddingException 342 { 343 ByteArrayInputStream bais = null; 344 try 345 { 346 bais = new ByteArrayInputStream(sealCipher.doFinal(encryptedContent)); 347 } 348 catch (IllegalStateException ise) 349 { 350 throw new IOException("cipher not initialized"); 351 } 352 ObjectInputStream ois = new ObjectInputStream(bais); 353 return ois.readObject(); 354 } 355}