/** * This method returns a EncryptedData structure with all the necessary information to allow * storage in database. Encryted data is stored in file system without defined file name. Returned * data must be treated in this way: * * <ul> * <li>key must be stored NOWHERE, only must exist in the shared URL * <li>id, encryptedPath and encryptedName must be stored in database * </ul> * * With this data this methods builds a NEW KEY based on seed and applying the provided key. KEY * is the provided key and must be regenerated in each method invocation (one-time use). ID is the * encrypted seed, using the generated NEW KEY and an initialization vector based on seed itself, * built by #getIv(String). PATH and NAME is encrypted with the same IV and NEW KEY as the ID. * With this protocol we are trying to avoid the possibility of recovering encrypted data because * the original key is never stored, only the owner of the shared URL has such key. So if the * server is compromised by an attacker, this one could not discover the path of a file associated * to a database record or decrypt a file content (because the lack of keys). * * @param content conted to be encrypted * @param name file name * @param seed value used to genererate the AES Initialization Vector * @param key AES key used for encryption * @return struct with reference info of encryptation result * @throws IOException * @throws FileNotFoundException */ protected EncryptedData doEncryptContent( InputStream content, String name, String seed, byte[] key) throws IOException, FileNotFoundException { byte[] iv = getIv(seed); byte[] encryptedIv = cryptoHelper.encryptIv(iv, key); byte[] newKey = Arrays.copyOf( encryptedIv, CryptoHelper.KEY_LENGTH / 8); // only first key size bytes (from a 16 bytes iv, the encrypted data is // 128+128, being last 128 padding data InputStream encryptedStream = cryptoHelper.encrypt(content, iv, newKey); String targetDirPath = getTargetDirPath(); File targetFile = getTargetFile(targetDirPath); FileOutputStream fos = new FileOutputStream(targetFile); IOUtils.copyLarge(encryptedStream, fos, new byte[512]); byte[] id = encryptString(seed, iv, newKey); byte[] encryptedPath = encryptString(targetFile.getPath(), iv, newKey); byte[] encryptedName = encryptString(name, iv, newKey); EncryptedData ed = new EncryptedData(); ed.setKey(key); ed.setId(id); ed.setEncryptedPath(encryptedPath); ed.setEncryptedName(encryptedName); return ed; }
protected String decryptString(byte[] encryptedString, byte[] iv, byte[] key) throws IOException, UnsupportedEncodingException { InputStream encryptedStream = cryptoHelper.decrypt(new ByteArrayInputStream(encryptedString), iv, key); byte[] b = IOUtils.toByteArray(encryptedStream); return new String(b, "utf-8"); }
/** * Generate the Initialization Vector from seed bytes * * @param seed the seed * @return correct padded IV for AES use */ protected byte[] getIv(String seed) { try { return cryptoHelper.fixIv(seed.getBytes("utf-8")); } catch (UnsupportedEncodingException e) { // weird, hardcoded UTF-8 throw new RuntimeException(e); } }
/** * Encrypt content, using AES with a random key and a Initialization Vector generated from 'seed' * parameter. This method returns the key used for encryption, and stores in database the * ID-PATH-NAME tupla that will allow to recover the orignal content only with the returned key * (see #doEncryptContent()) * * @param content conted to be encrypted * @param name name of the file * @param seed value used to genererate the AES Initialization Vector * @return struct with reference info of encryptation result * @see #doEncryptContent(java.io.InputStream, java.lang.String, byte[]) */ public byte[] encryptContent(InputStream content, String name, String seed) { try { byte[] key = cryptoHelper.getNewKey(); EncryptedData ed = doEncryptContent(content, name, seed, key); createDatabaseRecord(ed); return key; } catch (IOException e) { throw new RuntimeException(e); } }
/** * Decrypts content from provided seed and key. This method search the path of the file with the * encrypted data by executing a SQL similar to "select path from datatable where id=?" where ID * is the seed encrypted with the provided key and an initialization vector based on seed (built * with #getIv(String) method). Path recovered is encrypted using the same key an IV used for ID. * * @param seed value used to genererate the AES Initialization Vector * @param key AES key used for encryption * @return an DecryptData with decrypted content and file name, or null if the file is not present * (expired?) */ public DecryptedData decryptContent(String seed, byte[] key) { try { byte[] iv = getIv(seed); byte[] encryptedIv = cryptoHelper.encryptIv(iv, key); byte[] newKey = Arrays.copyOf( encryptedIv, CryptoHelper.KEY_LENGTH / 8); // only first key size bytes (from a 16 bytes iv, the encrypted data is // 128+128, being last 128 padding data byte[] id = encryptString(seed, iv, newKey); return getContentFromFileSystem(id, iv, newKey); } catch (IOException e) { throw new RuntimeException(e); } }
protected DecryptedData getContentFromFileSystem(byte[] id, byte[] iv, byte[] key) { String[] s = getPathAndNameFromDatabase(id, iv, key); if (s != null) { try { FileInputStream fis = new FileInputStream(s[0]); DecryptedData dd = new DecryptedData(); InputStream is = cryptoHelper.decrypt(fis, iv, key); String fileName = s[1]; dd.setContent(is); dd.setFilaName(fileName); return dd; } catch (IOException e) { throw new RuntimeException(e); } } else { return null; } }
protected byte[] encryptString(String s, byte[] iv, byte[] key) throws IOException, UnsupportedEncodingException { InputStream encryptedStream = cryptoHelper.encrypt(new ByteArrayInputStream(s.getBytes("utf-8")), iv, key); return IOUtils.toByteArray(encryptedStream); }