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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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