1 /* 2 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.crypto; 27 28 import jdk.internal.access.SharedSecrets; 29 30 import java.io.*; 31 import java.security.AlgorithmParameters; 32 import java.security.Key; 33 import java.security.InvalidKeyException; 34 import java.security.InvalidAlgorithmParameterException; 35 import java.security.NoSuchAlgorithmException; 36 import java.security.NoSuchProviderException; 37 38 /** 39 * This class enables a programmer to create an object and protect its 40 * confidentiality with a cryptographic algorithm. 41 * 42 * <p> Given any Serializable object, one can create a SealedObject 43 * that encapsulates the original object, in serialized 44 * format (i.e., a "deep copy"), and seals (encrypts) its serialized contents, 45 * using a cryptographic algorithm such as AES, to protect its 46 * confidentiality. The encrypted content can later be decrypted (with 47 * the corresponding algorithm using the correct decryption key) and 48 * de-serialized, yielding the original object. 49 * 50 * <p> Note that the Cipher object must be fully initialized with the 51 * correct algorithm, key, padding scheme, etc., before being applied 52 * to a SealedObject. 53 * 54 * <p> The original object that was sealed can be recovered in two different 55 * ways: 56 * 57 * <ul> 58 * 59 * <li>by using the {@link #getObject(javax.crypto.Cipher) getObject} 60 * method that takes a <code>Cipher</code> object. 61 * 62 * <p> This method requires a fully initialized <code>Cipher</code> object, 63 * initialized with the 64 * exact same algorithm, key, padding scheme, etc., that were used to seal the 65 * object. 66 * 67 * <p> This approach has the advantage that the party who unseals the 68 * sealed object does not require knowledge of the decryption key. For example, 69 * after one party has initialized the cipher object with the required 70 * decryption key, it could hand over the cipher object to 71 * another party who then unseals the sealed object. 72 * 73 * <li>by using one of the 74 * {@link #getObject(java.security.Key) getObject} methods 75 * that take a <code>Key</code> object. 76 * 77 * <p> In this approach, the <code>getObject</code> method creates a cipher 78 * object for the appropriate decryption algorithm and initializes it with the 79 * given decryption key and the algorithm parameters (if any) that were stored 80 * in the sealed object. 81 * 82 * <p> This approach has the advantage that the party who 83 * unseals the object does not need to keep track of the parameters (e.g., an 84 * IV) that were used to seal the object. 85 * 86 * </ul> 87 * 88 * @author Li Gong 89 * @author Jan Luehe 90 * @see Cipher 91 * @since 1.4 92 */ 93 94 public class SealedObject implements Serializable { 95 96 static final long serialVersionUID = 4482838265551344752L; 97 98 /** 99 * The serialized object contents in encrypted format. 100 * 101 * @serial 102 */ 103 private byte[] encryptedContent = null; 104 105 /** 106 * The algorithm that was used to seal this object. 107 * 108 * @serial 109 */ 110 private String sealAlg = null; 111 112 /** 113 * The algorithm of the parameters used. 114 * 115 * @serial 116 */ 117 private String paramsAlg = null; 118 119 /** 120 * The cryptographic parameters used by the sealing Cipher, 121 * encoded in the default format. 122 * <p> 123 * That is, <code>cipher.getParameters().getEncoded()</code>. 124 * 125 * @serial 126 */ 127 protected byte[] encodedParams = null; 128 129 /** 130 * Constructs a SealedObject from any Serializable object. 131 * 132 * <p>The given object is serialized, and its serialized contents are 133 * encrypted using the given Cipher, which must be fully initialized. 134 * 135 * <p>Any algorithm parameters that may be used in the encryption 136 * operation are stored inside of the new <code>SealedObject</code>. 137 * 138 * @param object the object to be sealed; can be null. 139 * @param c the cipher used to seal the object. 140 * 141 * @exception NullPointerException if the given cipher is null. 142 * @exception IOException if an error occurs during serialization 143 * @exception IllegalBlockSizeException if the given cipher is a block 144 * cipher, no padding has been requested, and the total input length 145 * (i.e., the length of the serialized object contents) is not a multiple 146 * of the cipher's block size 147 */ 148 public SealedObject(Serializable object, Cipher c) throws IOException, 149 IllegalBlockSizeException 150 { 151 /* 152 * Serialize the object 153 */ 154 155 // creating a stream pipe-line, from a to b 156 ByteArrayOutputStream b = new ByteArrayOutputStream(); 157 ObjectOutput a = new ObjectOutputStream(b); 158 byte[] content; 159 try { 160 // write and flush the object content to byte array 161 a.writeObject(object); 162 a.flush(); 163 content = b.toByteArray(); 164 } finally { 165 a.close(); 166 } 167 168 /* 169 * Seal the object 170 */ 171 try { 172 this.encryptedContent = c.doFinal(content); 173 } 174 catch (BadPaddingException ex) { 175 // if sealing is encryption only 176 // Should never happen?? 177 } 178 179 // Save the parameters 180 if (c.getParameters() != null) { 181 this.encodedParams = c.getParameters().getEncoded(); 182 this.paramsAlg = c.getParameters().getAlgorithm(); 183 } 184 185 // Save the encryption algorithm 186 this.sealAlg = c.getAlgorithm(); 187 } 188 189 /** 190 * Constructs a SealedObject object from the passed-in SealedObject. 191 * 192 * @param so a SealedObject object 193 * @exception NullPointerException if the given sealed object is null. 194 */ 195 protected SealedObject(SealedObject so) { 196 this.encryptedContent = so.encryptedContent.clone(); 197 this.sealAlg = so.sealAlg; 198 this.paramsAlg = so.paramsAlg; 199 if (so.encodedParams != null) { 200 this.encodedParams = so.encodedParams.clone(); 201 } else { 202 this.encodedParams = null; 203 } 204 } 205 206 /** 207 * Returns the algorithm that was used to seal this object. 208 * 209 * @return the algorithm that was used to seal this object. 210 */ 211 public final String getAlgorithm() { 212 return this.sealAlg; 213 } 214 215 /** 216 * Retrieves the original (encapsulated) object. 217 * 218 * <p>This method creates a cipher for the algorithm that had been used in 219 * the sealing operation. 220 * If the default provider package provides an implementation of that 221 * algorithm, an instance of Cipher containing that implementation is used. 222 * If the algorithm is not available in the default package, other 223 * packages are searched. 224 * The Cipher object is initialized for decryption, using the given 225 * <code>key</code> and the parameters (if any) that had been used in the 226 * sealing operation. 227 * 228 * <p>The encapsulated object is unsealed and de-serialized, before it is 229 * returned. 230 * 231 * @param key the key used to unseal the object. 232 * 233 * @return the original object. 234 * 235 * @exception IOException if an error occurs during de-serialiazation. 236 * @exception ClassNotFoundException if an error occurs during 237 * de-serialiazation. 238 * @exception NoSuchAlgorithmException if the algorithm to unseal the 239 * object is not available. 240 * @exception InvalidKeyException if the given key cannot be used to unseal 241 * the object (e.g., it has the wrong algorithm). 242 * @exception NullPointerException if <code>key</code> is null. 243 */ 244 public final Object getObject(Key key) 245 throws IOException, ClassNotFoundException, NoSuchAlgorithmException, 246 InvalidKeyException 247 { 248 if (key == null) { 249 throw new NullPointerException("key is null"); 250 } 251 252 try { 253 return unseal(key, null); 254 } catch (NoSuchProviderException nspe) { 255 // we've already caught NoSuchProviderException's and converted 256 // them into NoSuchAlgorithmException's with details about 257 // the failing algorithm 258 throw new NoSuchAlgorithmException("algorithm not found"); 259 } catch (IllegalBlockSizeException ibse) { 260 throw new InvalidKeyException(ibse.getMessage()); 261 } catch (BadPaddingException bpe) { 262 throw new InvalidKeyException(bpe.getMessage()); 263 } 264 } 265 266 /** 267 * Retrieves the original (encapsulated) object. 268 * 269 * <p>The encapsulated object is unsealed (using the given Cipher, 270 * assuming that the Cipher is already properly initialized) and 271 * de-serialized, before it is returned. 272 * 273 * @param c the cipher used to unseal the object 274 * 275 * @return the original object. 276 * 277 * @exception NullPointerException if the given cipher is null. 278 * @exception IOException if an error occurs during de-serialiazation 279 * @exception ClassNotFoundException if an error occurs during 280 * de-serialiazation 281 * @exception IllegalBlockSizeException if the given cipher is a block 282 * cipher, no padding has been requested, and the total input length is 283 * not a multiple of the cipher's block size 284 * @exception BadPaddingException if the given cipher has been 285 * initialized for decryption, and padding has been specified, but 286 * the input data does not have proper expected padding bytes 287 */ 288 public final Object getObject(Cipher c) 289 throws IOException, ClassNotFoundException, IllegalBlockSizeException, 290 BadPaddingException 291 { 292 ObjectInput a = getExtObjectInputStream(c); 293 try { 294 Object obj = a.readObject(); 295 return obj; 296 } finally { 297 a.close(); 298 } 299 } 300 301 /** 302 * Retrieves the original (encapsulated) object. 303 * 304 * <p>This method creates a cipher for the algorithm that had been used in 305 * the sealing operation, using an implementation of that algorithm from 306 * the given <code>provider</code>. 307 * The Cipher object is initialized for decryption, using the given 308 * <code>key</code> and the parameters (if any) that had been used in the 309 * sealing operation. 310 * 311 * <p>The encapsulated object is unsealed and de-serialized, before it is 312 * returned. 313 * 314 * @param key the key used to unseal the object. 315 * @param provider the name of the provider of the algorithm to unseal 316 * the object. 317 * 318 * @return the original object. 319 * 320 * @exception IllegalArgumentException if the given provider is null 321 * or empty. 322 * @exception IOException if an error occurs during de-serialiazation. 323 * @exception ClassNotFoundException if an error occurs during 324 * de-serialiazation. 325 * @exception NoSuchAlgorithmException if the algorithm to unseal the 326 * object is not available. 327 * @exception NoSuchProviderException if the given provider is not 328 * configured. 329 * @exception InvalidKeyException if the given key cannot be used to unseal 330 * the object (e.g., it has the wrong algorithm). 331 * @exception NullPointerException if <code>key</code> is null. 332 */ 333 public final Object getObject(Key key, String provider) 334 throws IOException, ClassNotFoundException, NoSuchAlgorithmException, 335 NoSuchProviderException, InvalidKeyException 336 { 337 if (key == null) { 338 throw new NullPointerException("key is null"); 339 } 340 if (provider == null || provider.length() == 0) { 341 throw new IllegalArgumentException("missing provider"); 342 } 343 344 try { 345 return unseal(key, provider); 346 } catch (IllegalBlockSizeException | BadPaddingException ex) { 347 throw new InvalidKeyException(ex.getMessage()); 348 } 349 } 350 351 352 private Object unseal(Key key, String provider) 353 throws IOException, ClassNotFoundException, NoSuchAlgorithmException, 354 NoSuchProviderException, InvalidKeyException, 355 IllegalBlockSizeException, BadPaddingException 356 { 357 /* 358 * Create the parameter object. 359 */ 360 AlgorithmParameters params = null; 361 if (this.encodedParams != null) { 362 try { 363 if (provider != null) 364 params = AlgorithmParameters.getInstance(this.paramsAlg, 365 provider); 366 else 367 params = AlgorithmParameters.getInstance(this.paramsAlg); 368 369 } catch (NoSuchProviderException nspe) { 370 if (provider == null) { 371 throw new NoSuchAlgorithmException(this.paramsAlg 372 + " not found"); 373 } else { 374 throw new NoSuchProviderException(nspe.getMessage()); 375 } 376 } 377 params.init(this.encodedParams); 378 } 379 380 /* 381 * Create and initialize the cipher. 382 */ 383 Cipher c; 384 try { 385 if (provider != null) 386 c = Cipher.getInstance(this.sealAlg, provider); 387 else 388 c = Cipher.getInstance(this.sealAlg); 389 } catch (NoSuchPaddingException nspe) { 390 throw new NoSuchAlgorithmException("Padding that was used in " 391 + "sealing operation not " 392 + "available"); 393 } catch (NoSuchProviderException nspe) { 394 if (provider == null) { 395 throw new NoSuchAlgorithmException(this.sealAlg+" not found"); 396 } else { 397 throw new NoSuchProviderException(nspe.getMessage()); 398 } 399 } 400 401 try { 402 if (params != null) 403 c.init(Cipher.DECRYPT_MODE, key, params); 404 else 405 c.init(Cipher.DECRYPT_MODE, key); 406 } catch (InvalidAlgorithmParameterException iape) { 407 // this should never happen, because we use the exact same 408 // parameters that were used in the sealing operation 409 throw new RuntimeException(iape.getMessage()); 410 } 411 412 ObjectInput a = getExtObjectInputStream(c); 413 try { 414 Object obj = a.readObject(); 415 return obj; 416 } finally { 417 a.close(); 418 } 419 } 420 421 /** 422 * Restores the state of the SealedObject from a stream. 423 * @param s the object input stream. 424 * @exception NullPointerException if s is null. 425 */ 426 private void readObject(java.io.ObjectInputStream s) 427 throws java.io.IOException, ClassNotFoundException 428 { 429 s.defaultReadObject(); 430 if (encryptedContent != null) 431 encryptedContent = encryptedContent.clone(); 432 if (encodedParams != null) 433 encodedParams = encodedParams.clone(); 434 } 435 436 // This method is also called inside SealedObjectForKeyProtector.java. 437 private ObjectInputStream getExtObjectInputStream(Cipher c) 438 throws BadPaddingException, IllegalBlockSizeException, IOException { 439 440 byte[] content = c.doFinal(this.encryptedContent); 441 ByteArrayInputStream b = new ByteArrayInputStream(content); 442 return new extObjectInputStream(b); 443 } 444 445 static { 446 SharedSecrets.setJavaxCryptoSealedObjectAccess((obj,c) -> obj.getExtObjectInputStream(c)); 447 } 448 } 449 450 final class extObjectInputStream extends ObjectInputStream { 451 extObjectInputStream(InputStream in) 452 throws IOException, StreamCorruptedException { 453 super(in); 454 } 455 456 protected Class<?> resolveClass(ObjectStreamClass v) 457 throws IOException, ClassNotFoundException 458 { 459 460 try { 461 /* 462 * Calling the super.resolveClass() first 463 * will let us pick up bug fixes in the super 464 * class (e.g., 4171142). 465 */ 466 return super.resolveClass(v); 467 } catch (ClassNotFoundException cnfe) { 468 /* 469 * This is a workaround for bug 4224921. 470 */ 471 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 472 if (loader == null) { 473 loader = ClassLoader.getSystemClassLoader(); 474 if (loader == null) { 475 throw new ClassNotFoundException(v.getName()); 476 } 477 } 478 479 return Class.forName(v.getName(), false, loader); 480 } 481 } 482 }