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 readObject(ObjectInputStream s)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 */ SealedObject(Serializable object, Cipher c)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 */ SealedObject(SealedObject so)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 */ getAlgorithm()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 */ getObject(Key key)146 public final Object getObject(Key key) 147 throws IOException, ClassNotFoundException, 148 NoSuchAlgorithmException, InvalidKeyException { 149 if (key == null) { 150 throw new InvalidKeyException("key == null"); 151 } 152 try { 153 Cipher cipher = Cipher.getInstance(sealAlg); 154 if ((paramsAlg != null) && (paramsAlg.length() != 0)) { 155 AlgorithmParameters params = 156 AlgorithmParameters.getInstance(paramsAlg); 157 params.init(encodedParams); 158 cipher.init(Cipher.DECRYPT_MODE, key, params); 159 } else { 160 cipher.init(Cipher.DECRYPT_MODE, key); 161 } 162 byte[] serialized = cipher.doFinal(encryptedContent); 163 ObjectInputStream ois = 164 new ObjectInputStream( 165 new ByteArrayInputStream(serialized)); 166 return ois.readObject(); 167 } catch (NoSuchPaddingException e) { 168 // should not be thrown because cipher text was made 169 // with existing padding 170 throw new NoSuchAlgorithmException(e.toString()); 171 } catch (InvalidAlgorithmParameterException e) { 172 // should not be thrown because cipher text was made 173 // with correct algorithm parameters 174 throw new NoSuchAlgorithmException(e.toString()); 175 } catch (IllegalBlockSizeException e) { 176 // should not be thrown because the cipher text 177 // was correctly made 178 throw new NoSuchAlgorithmException(e.toString()); 179 } catch (BadPaddingException e) { 180 // should not be thrown because the cipher text 181 // was correctly made 182 throw new NoSuchAlgorithmException(e.toString()); 183 } catch (IllegalStateException e) { 184 // should never be thrown because cipher is initialized 185 throw new NoSuchAlgorithmException(e.toString()); 186 } 187 } 188 189 /** 190 * Returns the wrapped object, decrypting it using the specified 191 * cipher. 192 * 193 * @param c 194 * the cipher to decrypt the data. 195 * @return the encapsulated object. 196 * @throws IOException 197 * if deserialization fails. 198 * @throws ClassNotFoundException 199 * if deserialization fails. 200 * @throws IllegalBlockSizeException 201 * if the specified cipher is a block cipher and the length of 202 * the serialized data is not a multiple of the ciphers block 203 * size. 204 * @throws BadPaddingException 205 * if the padding of the data does not match the padding scheme. 206 */ getObject(Cipher c)207 public final Object getObject(Cipher c) 208 throws IOException, ClassNotFoundException, 209 IllegalBlockSizeException, BadPaddingException { 210 if (c == null) { 211 throw new NullPointerException("c == null"); 212 } 213 byte[] serialized = c.doFinal(encryptedContent); 214 ObjectInputStream ois = 215 new ObjectInputStream( 216 new ByteArrayInputStream(serialized)); 217 return ois.readObject(); 218 } 219 220 /** 221 * Returns the wrapped object, decrypting it using the specified key. The 222 * specified provider is used to retrieve the cipher algorithm. 223 * 224 * @param key 225 * the key to decrypt the data. 226 * @param provider 227 * the name of the provider that provides the cipher algorithm. 228 * @return the encapsulated object. 229 * @throws IOException 230 * if deserialization fails. 231 * @throws ClassNotFoundException 232 * if deserialization fails. 233 * @throws NoSuchAlgorithmException 234 * if the algorithm used to decrypt the data is not available. 235 * @throws NoSuchProviderException 236 * if the specified provider is not available. 237 * @throws InvalidKeyException 238 * if the specified key cannot be used to decrypt the data. 239 */ getObject(Key key, String provider)240 public final Object getObject(Key key, String provider) 241 throws IOException, ClassNotFoundException, 242 NoSuchAlgorithmException, NoSuchProviderException, 243 InvalidKeyException { 244 if (provider == null || provider.isEmpty()) { 245 throw new IllegalArgumentException("provider name empty or null"); 246 } 247 try { 248 Cipher cipher = Cipher.getInstance(sealAlg, provider); 249 if ((paramsAlg != null) && (paramsAlg.length() != 0)) { 250 AlgorithmParameters params = 251 AlgorithmParameters.getInstance(paramsAlg); 252 params.init(encodedParams); 253 cipher.init(Cipher.DECRYPT_MODE, key, params); 254 } else { 255 cipher.init(Cipher.DECRYPT_MODE, key); 256 } 257 byte[] serialized = cipher.doFinal(encryptedContent); 258 ObjectInputStream ois = 259 new ObjectInputStream( 260 new ByteArrayInputStream(serialized)); 261 return ois.readObject(); 262 } catch (NoSuchPaddingException e) { 263 // should not be thrown because cipher text was made 264 // with existing padding 265 throw new NoSuchAlgorithmException(e.toString()); 266 } catch (InvalidAlgorithmParameterException e) { 267 // should not be thrown because cipher text was made 268 // with correct algorithm parameters 269 throw new NoSuchAlgorithmException(e.toString()); 270 } catch (IllegalBlockSizeException e) { 271 // should not be thrown because the cipher text 272 // was correctly made 273 throw new NoSuchAlgorithmException(e.toString()); 274 } catch (BadPaddingException e) { 275 // should not be thrown because the cipher text 276 // was correctly made 277 throw new NoSuchAlgorithmException(e.toString()); 278 } catch (IllegalStateException e) { 279 // should never be thrown because cipher is initialized 280 throw new NoSuchAlgorithmException(e.toString()); 281 } 282 } 283 } 284