| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package javax.crypto; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.security.AlgorithmParameters; |
| import java.security.InvalidAlgorithmParameterException; |
| import java.security.InvalidKeyException; |
| import java.security.Key; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.NoSuchProviderException; |
| |
| /** |
| * A {@code SealedObject} is a wrapper around a {@code serializable} object |
| * instance and encrypts it using a cryptographic cipher. |
| * |
| * <p>Since a {@code SealedObject} instance is serializable it can |
| * either be stored or transmitted over an insecure channel. |
| * |
| * <p>The wrapped object can later be decrypted (unsealed) using the corresponding |
| * key and then be deserialized to retrieve the original object. The sealed |
| * object itself keeps track of the cipher and corresponding parameters. |
| */ |
| public class SealedObject implements Serializable { |
| |
| private static final long serialVersionUID = 4482838265551344752L; |
| |
| /** |
| * The cipher's {@link AlgorithmParameters} in encoded format. |
| * Equivalent to {@code cipher.getParameters().getEncoded()}, |
| * or null if the cipher did not use any parameters. |
| */ |
| protected byte[] encodedParams; |
| |
| private byte[] encryptedContent; |
| private String sealAlg; |
| private String paramsAlg; |
| |
| private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { |
| // We do unshared reads here to ensure we have our own clones of the byte[]s. |
| encodedParams = (byte[]) s.readUnshared(); |
| encryptedContent = (byte[]) s.readUnshared(); |
| // These are regular shared reads because the algorithms used by a given stream are |
| // almost certain to the be same for each object, and String is immutable anyway, |
| // so there's no security concern about sharing. |
| sealAlg = (String) s.readObject(); |
| paramsAlg = (String) s.readObject(); |
| } |
| |
| /** |
| * Creates a new {@code SealedObject} instance wrapping the specified object |
| * and sealing it using the specified cipher. |
| * <p> |
| * The cipher must be fully initialized. |
| * |
| * @param object |
| * the object to seal, can be {@code null}. |
| * @param c |
| * the cipher to encrypt the object. |
| * @throws IOException |
| * if the serialization fails. |
| * @throws IllegalBlockSizeException |
| * if the specified cipher is a block cipher and the length of |
| * the serialized data is not a multiple of the ciphers block |
| * size. |
| * @throws NullPointerException |
| * if the cipher is {@code null}. |
| */ |
| public SealedObject(Serializable object, Cipher c) |
| throws IOException, IllegalBlockSizeException { |
| if (c == null) { |
| throw new NullPointerException("c == null"); |
| } |
| try { |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| ObjectOutputStream oos = new ObjectOutputStream(bos); |
| oos.writeObject(object); |
| oos.flush(); |
| AlgorithmParameters ap = c.getParameters(); |
| this.encodedParams = (ap == null) ? null : ap.getEncoded(); |
| this.paramsAlg = (ap == null) ? null : ap.getAlgorithm(); |
| this.sealAlg = c.getAlgorithm(); |
| this.encryptedContent = c.doFinal(bos.toByteArray()); |
| } catch (BadPaddingException e) { |
| // should be never thrown because the cipher |
| // should be initialized for encryption |
| throw new IOException(e.toString()); |
| } |
| } |
| |
| /** |
| * Creates a new {@code SealedObject} instance by copying the data from |
| * the specified object. |
| * |
| * @param so |
| * the object to copy. |
| */ |
| protected SealedObject(SealedObject so) { |
| if (so == null) { |
| throw new NullPointerException("so == null"); |
| } |
| this.encryptedContent = so.encryptedContent; |
| this.encodedParams = so.encodedParams; |
| this.sealAlg = so.sealAlg; |
| this.paramsAlg = so.paramsAlg; |
| } |
| |
| /** |
| * Returns the algorithm this object was sealed with. |
| * |
| * @return the algorithm this object was sealed with. |
| */ |
| public final String getAlgorithm() { |
| return sealAlg; |
| } |
| |
| /** |
| * Returns the wrapped object, decrypting it using the specified key. |
| * |
| * @param key |
| * the key to decrypt the data with. |
| * @return the encapsulated object. |
| * @throws IOException |
| * if deserialization fails. |
| * @throws ClassNotFoundException |
| * if deserialization fails. |
| * @throws NoSuchAlgorithmException |
| * if the algorithm to decrypt the data is not available. |
| * @throws InvalidKeyException |
| * if the specified key cannot be used to decrypt the data. |
| */ |
| public final Object getObject(Key key) |
| throws IOException, ClassNotFoundException, |
| NoSuchAlgorithmException, InvalidKeyException { |
| if (key == null) { |
| throw new InvalidKeyException("key == null"); |
| } |
| try { |
| Cipher cipher = Cipher.getInstance(sealAlg); |
| if ((paramsAlg != null) && (paramsAlg.length() != 0)) { |
| AlgorithmParameters params = |
| AlgorithmParameters.getInstance(paramsAlg); |
| params.init(encodedParams); |
| cipher.init(Cipher.DECRYPT_MODE, key, params); |
| } else { |
| cipher.init(Cipher.DECRYPT_MODE, key); |
| } |
| byte[] serialized = cipher.doFinal(encryptedContent); |
| ObjectInputStream ois = |
| new ObjectInputStream( |
| new ByteArrayInputStream(serialized)); |
| return ois.readObject(); |
| } catch (NoSuchPaddingException e) { |
| // should not be thrown because cipher text was made |
| // with existing padding |
| throw new NoSuchAlgorithmException(e.toString()); |
| } catch (InvalidAlgorithmParameterException e) { |
| // should not be thrown because cipher text was made |
| // with correct algorithm parameters |
| throw new NoSuchAlgorithmException(e.toString()); |
| } catch (IllegalBlockSizeException e) { |
| // should not be thrown because the cipher text |
| // was correctly made |
| throw new NoSuchAlgorithmException(e.toString()); |
| } catch (BadPaddingException e) { |
| // should not be thrown because the cipher text |
| // was correctly made |
| throw new NoSuchAlgorithmException(e.toString()); |
| } catch (IllegalStateException e) { |
| // should never be thrown because cipher is initialized |
| throw new NoSuchAlgorithmException(e.toString()); |
| } |
| } |
| |
| /** |
| * Returns the wrapped object, decrypting it using the specified |
| * cipher. |
| * |
| * @param c |
| * the cipher to decrypt the data. |
| * @return the encapsulated object. |
| * @throws IOException |
| * if deserialization fails. |
| * @throws ClassNotFoundException |
| * if deserialization fails. |
| * @throws IllegalBlockSizeException |
| * if the specified cipher is a block cipher and the length of |
| * the serialized data is not a multiple of the ciphers block |
| * size. |
| * @throws BadPaddingException |
| * if the padding of the data does not match the padding scheme. |
| */ |
| public final Object getObject(Cipher c) |
| throws IOException, ClassNotFoundException, |
| IllegalBlockSizeException, BadPaddingException { |
| if (c == null) { |
| throw new NullPointerException("c == null"); |
| } |
| byte[] serialized = c.doFinal(encryptedContent); |
| ObjectInputStream ois = |
| new ObjectInputStream( |
| new ByteArrayInputStream(serialized)); |
| return ois.readObject(); |
| } |
| |
| /** |
| * Returns the wrapped object, decrypting it using the specified key. The |
| * specified provider is used to retrieve the cipher algorithm. |
| * |
| * @param key |
| * the key to decrypt the data. |
| * @param provider |
| * the name of the provider that provides the cipher algorithm. |
| * @return the encapsulated object. |
| * @throws IOException |
| * if deserialization fails. |
| * @throws ClassNotFoundException |
| * if deserialization fails. |
| * @throws NoSuchAlgorithmException |
| * if the algorithm used to decrypt the data is not available. |
| * @throws NoSuchProviderException |
| * if the specified provider is not available. |
| * @throws InvalidKeyException |
| * if the specified key cannot be used to decrypt the data. |
| */ |
| public final Object getObject(Key key, String provider) |
| throws IOException, ClassNotFoundException, |
| NoSuchAlgorithmException, NoSuchProviderException, |
| InvalidKeyException { |
| if (provider == null || provider.isEmpty()) { |
| throw new IllegalArgumentException("provider name empty or null"); |
| } |
| try { |
| Cipher cipher = Cipher.getInstance(sealAlg, provider); |
| if ((paramsAlg != null) && (paramsAlg.length() != 0)) { |
| AlgorithmParameters params = |
| AlgorithmParameters.getInstance(paramsAlg); |
| params.init(encodedParams); |
| cipher.init(Cipher.DECRYPT_MODE, key, params); |
| } else { |
| cipher.init(Cipher.DECRYPT_MODE, key); |
| } |
| byte[] serialized = cipher.doFinal(encryptedContent); |
| ObjectInputStream ois = |
| new ObjectInputStream( |
| new ByteArrayInputStream(serialized)); |
| return ois.readObject(); |
| } catch (NoSuchPaddingException e) { |
| // should not be thrown because cipher text was made |
| // with existing padding |
| throw new NoSuchAlgorithmException(e.toString()); |
| } catch (InvalidAlgorithmParameterException e) { |
| // should not be thrown because cipher text was made |
| // with correct algorithm parameters |
| throw new NoSuchAlgorithmException(e.toString()); |
| } catch (IllegalBlockSizeException e) { |
| // should not be thrown because the cipher text |
| // was correctly made |
| throw new NoSuchAlgorithmException(e.toString()); |
| } catch (BadPaddingException e) { |
| // should not be thrown because the cipher text |
| // was correctly made |
| throw new NoSuchAlgorithmException(e.toString()); |
| } catch (IllegalStateException e) { |
| // should never be thrown because cipher is initialized |
| throw new NoSuchAlgorithmException(e.toString()); |
| } |
| } |
| } |