package ch.ge.ve.commons.crypto; /*- * #%L * Common crypto utilities * %% * Copyright (C) 2015 - 2016 République et Canton de Genève * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import ch.ge.ve.commons.crypto.exceptions.CryptoOperationRuntimeException; import ch.ge.ve.commons.crypto.utils.SecureRandomFactory; import ch.ge.ve.commons.streamutils.SafeObjectReader; import com.google.common.base.Preconditions; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.SealedObject; import java.io.*; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** * This utility class offers the mechanisms to seal and unseal objects using a pre-determined cipher and key */ public class ObjectSealer { /** * 4 instances are needed for an ObjectSealer. */ private static final int MAX_OBJECTS = 4; private final Cipher cipher; private final Key key; public ObjectSealer(Cipher cipher, Key key) { Preconditions.checkNotNull(cipher, "A valid cipher must be defined"); Preconditions.checkNotNull(key, "A valid key must be defined"); this.cipher = cipher; this.key = key; } /** * Wraps any serializable object into a SealedObject and returns the corresponding byte array * * @param object the object to seal * @return the byte array representing the SealedObject (locked with the cipher and key provided to the constructor) * @throws CryptoOperationRuntimeException * @see #unsealObject(byte[], long) the matching unwrapping method */ public byte[] sealObject(Serializable object) { ByteArrayOutputStream byteArrayOutputStream = null; try { cipher.init(Cipher.ENCRYPT_MODE, key, SecureRandomFactory.createPRNG()); SealedObject sealedObject = new SealedObject(object, cipher); byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(sealedObject); } catch (InvalidKeyException | IOException | IllegalBlockSizeException e) { throw new CryptoOperationRuntimeException("cannot seal object", e); } return byteArrayOutputStream.toByteArray(); } /** * Parses a SealedObject from the given byte array and retrieves the original wrapped object * * @param encryptedObject a byte array representing a SealedObject * @param maxBytes the maximum size allowed for the read object * @return the original Serializable object * @throws CryptoOperationRuntimeException * @see #sealObject(java.io.Serializable) the matching wrapping operation */ public Object unsealObject(byte[] encryptedObject, long maxBytes) { try { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(encryptedObject); SealedObject sealedObject = SafeObjectReader.safeReadObject(SealedObject.class, new ArrayList<>(), MAX_OBJECTS, maxBytes, byteArrayInputStream); return sealedObject.getObject(key); } catch (IOException | ClassNotFoundException | InvalidKeyException | NoSuchAlgorithmException e) { throw new CryptoOperationRuntimeException("cannot unseal object", e); } } }