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 18package javax.crypto; 19 20import java.io.ByteArrayInputStream; 21import java.io.ByteArrayOutputStream; 22import java.io.IOException; 23import java.io.ObjectInputStream; 24import java.io.ObjectOutputStream; 25import java.io.Serializable; 26import java.security.AlgorithmParameters; 27import java.security.InvalidAlgorithmParameterException; 28import java.security.InvalidKeyException; 29import java.security.Key; 30import java.security.NoSuchAlgorithmException; 31import java.security.NoSuchProviderException; 32import javax.crypto.IllegalBlockSizeException; 33import javax.crypto.NoSuchPaddingException; 34 35import org.apache.harmony.crypto.internal.nls.Messages; 36 37/** 38 * A {@code SealedObject} is a wrapper around a {@code serializable} object 39 * instance and encrypts it using a cryptographic cipher. 40 * <p> 41 * Since a {@code SealedObject} instance is a serializable object itself it can 42 * either be stored or transmitted over an insecure channel. 43 * <p> 44 * The wrapped object can later be decrypted (unsealed) using the corresponding 45 * key and then be deserialized to retrieve the original object.The sealed 46 * object itself keeps track of the cipher and corresponding parameters. 47 */ 48public class SealedObject implements Serializable { 49 50 private static final long serialVersionUID = 4482838265551344752L; 51 52 /** 53 * The {@link AlgorithmParameters} in encoded format. 54 */ 55 protected byte[] encodedParams; 56 private byte[] encryptedContent; 57 private String sealAlg; 58 private String paramsAlg; 59 60 private void readObject(ObjectInputStream s) 61 throws IOException, ClassNotFoundException { 62 encodedParams = (byte []) s.readUnshared(); 63 encryptedContent = (byte []) s.readUnshared(); 64 sealAlg = (String) s.readUnshared(); 65 paramsAlg = (String) s.readUnshared(); 66 } 67 68 /** 69 * Creates a new {@code SealedObject} instance wrapping the specified object 70 * and sealing it using the specified cipher. 71 * <p> 72 * The cipher must be fully initialized. 73 * 74 * @param object 75 * the object to seal, can be {@code null}. 76 * @param c 77 * the cipher to encrypt the object. 78 * @throws IOException 79 * if the serialization fails. 80 * @throws IllegalBlockSizeException 81 * if the specified cipher is a block cipher and the length of 82 * the serialized data is not a multiple of the ciphers block 83 * size. 84 * @throws NullPointerException 85 * if the cipher is {@code null}. 86 */ 87 public SealedObject(Serializable object, Cipher c) 88 throws IOException, IllegalBlockSizeException { 89 if (c == null) { 90 throw new NullPointerException(Messages.getString("crypto.13")); //$NON-NLS-1$ 91 } 92 try { 93 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 94 ObjectOutputStream oos = new ObjectOutputStream(bos); 95 oos.writeObject(object); 96 oos.flush(); 97 AlgorithmParameters ap = c.getParameters(); 98 this.encodedParams = (ap == null) ? null : ap.getEncoded(); 99 this.paramsAlg = (ap == null) ? null : ap.getAlgorithm(); 100 this.sealAlg = c.getAlgorithm(); 101 this.encryptedContent = c.doFinal(bos.toByteArray()); 102 } catch (BadPaddingException e) { 103 // should be never thrown because the cipher 104 // should be initialized for encryption 105 throw new IOException(e.toString()); 106 } 107 } 108 109 /** 110 * Creates a new {@code SealedObject} instance by copying the data from 111 * the specified object. 112 * 113 * @param so 114 * the object to copy. 115 */ 116 protected SealedObject(SealedObject so) { 117 if (so == null) { 118 throw new NullPointerException(Messages.getString("crypto.14")); //$NON-NLS-1$ 119 } 120 this.encryptedContent = so.encryptedContent; 121 this.encodedParams = so.encodedParams; 122 this.sealAlg = so.sealAlg; 123 this.paramsAlg = so.paramsAlg; 124 } 125 126 /** 127 * Returns the algorithm this object was sealed with. 128 * 129 * @return the algorithm this object was sealed with. 130 */ 131 public final String getAlgorithm() { 132 return sealAlg; 133 } 134 135 /** 136 * Returns the wrapped object, decrypting it using the specified key. 137 * 138 * @param key 139 * the key to decrypt the data with. 140 * @return the encapsulated object. 141 * @throws IOException 142 * if deserialization fails. 143 * @throws ClassNotFoundException 144 * if deserialization fails. 145 * @throws NoSuchAlgorithmException 146 * if the algorithm to decrypt the data is not available. 147 * @throws InvalidKeyException 148 * if the specified key cannot be used to decrypt the data. 149 */ 150 public final Object getObject(Key key) 151 throws IOException, ClassNotFoundException, 152 NoSuchAlgorithmException, InvalidKeyException { 153 // BEGIN android-added 154 if (key == null) { 155 throw new InvalidKeyException( 156 Messages.getString("crypto.05")); 157 } 158 // END android-added 159 try { 160 Cipher cipher = Cipher.getInstance(sealAlg); 161 if ((paramsAlg != null) && (paramsAlg.length() != 0)) { 162 AlgorithmParameters params = 163 AlgorithmParameters.getInstance(paramsAlg); 164 params.init(encodedParams); 165 cipher.init(Cipher.DECRYPT_MODE, key, params); 166 } else { 167 cipher.init(Cipher.DECRYPT_MODE, key); 168 } 169 byte[] serialized = cipher.doFinal(encryptedContent); 170 ObjectInputStream ois = 171 new ObjectInputStream( 172 new ByteArrayInputStream(serialized)); 173 return ois.readObject(); 174 } catch (NoSuchPaddingException e) { 175 // should not be thrown because cipher text was made 176 // with existing padding 177 throw new NoSuchAlgorithmException(e.toString()); 178 } catch (InvalidAlgorithmParameterException e) { 179 // should not be thrown because cipher text was made 180 // with correct algorithm parameters 181 throw new NoSuchAlgorithmException(e.toString()); 182 } catch (IllegalBlockSizeException e) { 183 // should not be thrown because the cipher text 184 // was correctly made 185 throw new NoSuchAlgorithmException(e.toString()); 186 } catch (BadPaddingException e) { 187 // should not be thrown because the cipher text 188 // was correctly made 189 throw new NoSuchAlgorithmException(e.toString()); 190 } catch (IllegalStateException e) { 191 // should never be thrown because cipher is initialized 192 throw new NoSuchAlgorithmException(e.toString()); 193 } 194 } 195 196 /** 197 * Returns the wrapped object, decrypting it using the specified 198 * cipher. 199 * 200 * @param c 201 * the cipher to decrypt the data. 202 * @return the encapsulated object. 203 * @throws IOException 204 * if deserialization fails. 205 * @throws ClassNotFoundException 206 * if deserialization fails. 207 * @throws IllegalBlockSizeException 208 * if the specified cipher is a block cipher and the length of 209 * the serialized data is not a multiple of the ciphers block 210 * size. 211 * @throws BadPaddingException 212 * if the padding of the data does not match the padding scheme. 213 */ 214 public final Object getObject(Cipher c) 215 throws IOException, ClassNotFoundException, 216 IllegalBlockSizeException, BadPaddingException { 217 if (c == null) { 218 throw new NullPointerException(Messages.getString("crypto.13")); //$NON-NLS-1$ 219 } 220 byte[] serialized = c.doFinal(encryptedContent); 221 ObjectInputStream ois = 222 new ObjectInputStream( 223 new ByteArrayInputStream(serialized)); 224 return ois.readObject(); 225 } 226 227 /** 228 * Returns the wrapped object, decrypting it using the specified key. The 229 * specified provider is used to retrieve the cipher algorithm. 230 * 231 * @param key 232 * the key to decrypt the data. 233 * @param provider 234 * the name of the provider that provides the cipher algorithm. 235 * @return the encapsulated object. 236 * @throws IOException 237 * if deserialization fails. 238 * @throws ClassNotFoundException 239 * if deserialization fails. 240 * @throws NoSuchAlgorithmException 241 * if the algorithm used to decrypt the data is not available. 242 * @throws NoSuchProviderException 243 * if the specified provider is not available. 244 * @throws InvalidKeyException 245 * if the specified key cannot be used to decrypt the data. 246 */ 247 public final Object getObject(Key key, String provider) 248 throws IOException, ClassNotFoundException, 249 NoSuchAlgorithmException, NoSuchProviderException, 250 InvalidKeyException { 251 if ((provider == null) || (provider.length() == 0)) { 252 throw new IllegalArgumentException( 253 Messages.getString("crypto.15")); //$NON-NLS-1$ 254 } 255 try { 256 Cipher cipher = Cipher.getInstance(sealAlg, provider); 257 if ((paramsAlg != null) && (paramsAlg.length() != 0)) { 258 AlgorithmParameters params = 259 AlgorithmParameters.getInstance(paramsAlg); 260 params.init(encodedParams); 261 cipher.init(Cipher.DECRYPT_MODE, key, params); 262 } else { 263 cipher.init(Cipher.DECRYPT_MODE, key); 264 } 265 byte[] serialized = cipher.doFinal(encryptedContent); 266 ObjectInputStream ois = 267 new ObjectInputStream( 268 new ByteArrayInputStream(serialized)); 269 return ois.readObject(); 270 } catch (NoSuchPaddingException e) { 271 // should not be thrown because cipher text was made 272 // with existing padding 273 throw new NoSuchAlgorithmException(e.toString()); 274 } catch (InvalidAlgorithmParameterException e) { 275 // should not be thrown because cipher text was made 276 // with correct algorithm parameters 277 throw new NoSuchAlgorithmException(e.toString()); 278 } catch (IllegalBlockSizeException e) { 279 // should not be thrown because the cipher text 280 // was correctly made 281 throw new NoSuchAlgorithmException(e.toString()); 282 } catch (BadPaddingException e) { 283 // should not be thrown because the cipher text 284 // was correctly made 285 throw new NoSuchAlgorithmException(e.toString()); 286 } catch (IllegalStateException e) { 287 // should never be thrown because cipher is initialized 288 throw new NoSuchAlgorithmException(e.toString()); 289 } 290 } 291} 292 293