package co.codewizards.cloudstore.core.otp; import static co.codewizards.cloudstore.core.io.StreamUtil.*; import static co.codewizards.cloudstore.core.oio.OioFileFactory.*; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import co.codewizards.cloudstore.core.config.ConfigDir; import co.codewizards.cloudstore.core.oio.File; import co.codewizards.cloudstore.core.otp.OneTimePadEncryptor.Result; import co.codewizards.cloudstore.core.util.IOUtil; /** * Registry for passwords that need to be encrypted with OTP technique. * You can, using methods of this class, encrypt with OTP and store a password * in a file in ConfigDir, and later retrieve from the file and decrypt it. * Encrypted password is stored in a file named fileNamePrefix + PASSWORD_FILE_SUFFIX * Random key in a file named fileNamePrefix + RANDOM_KEY_FILE_SUFFIX * * @author Wojtek Wilk - wilk.wojtek at gmail.com */ public class OneTimePadRegistry { public static final String PASSWORD_FILE_SUFFIX = "Password"; public static final String RANDOM_KEY_FILE_SUFFIX = "RandomKey"; private final String fileNamePrefix; private final OneTimePadEncryptor encryptor = new OneTimePadEncryptor(); public OneTimePadRegistry(final String fileNamePrefix){ this.fileNamePrefix = fileNamePrefix; } public void encryptAndStorePassword(final char[] password){ final Result result = encryptor.encrypt(toBytes(password)); try { writeToFile(result.getEncryptedMessage(), PASSWORD_FILE_SUFFIX); writeToFile(result.getRandomKey(), RANDOM_KEY_FILE_SUFFIX); } catch (IOException e) { throw new RuntimeException(e); } } public char[] readFromFileAndDecrypt(){ try { final byte[] encryptedPassword = readFromFile(PASSWORD_FILE_SUFFIX); final byte[] randomKey = readFromFile(RANDOM_KEY_FILE_SUFFIX); final byte[] decryptedPassword = encryptor.decrypt(encryptedPassword, randomKey); return toChars(decryptedPassword); } catch (IOException e) { throw new RuntimeException(e); } } private byte[] readFromFile(String fileNameSuffix) throws IOException { final String fileName = fileNamePrefix + fileNameSuffix; final File file = createFile(ConfigDir.getInstance().getFile(), fileName); return IOUtil.getBytesFromFile(file); } private void writeToFile(byte[] bytes, String fileNameSuffix) throws IOException{ final File file = createFile(ConfigDir.getInstance().getFile(), fileNamePrefix + fileNameSuffix); try(final OutputStream os = castStream(file.createOutputStream())) { os.write(bytes); } } /** * based on @link <a href="http://stackoverflow.com/questions/5513144/converting-char-to-byte#answer-9670279">this answer</a> */ private byte[] toBytes(final char[] chars) { final CharBuffer charBuffer = CharBuffer.wrap(chars); final ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer); final byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()); clearSensitiveData(charBuffer.array(), byteBuffer.array()); return bytes; } private char[] toChars(final byte[] bytes){ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); final CharBuffer charBuffer = StandardCharsets.UTF_8.decode(byteBuffer); final char[] chars = Arrays.copyOfRange(charBuffer.array(), charBuffer.position(), charBuffer.limit()); clearSensitiveData(charBuffer.array(), byteBuffer.array()); return chars; } private void clearSensitiveData(final char[] chars, final byte[] bytes){ Arrays.fill(chars, '\u0000'); Arrays.fill(bytes, (byte) 0); } }