001package co.codewizards.cloudstore.core.otp; 002 003import static co.codewizards.cloudstore.core.io.StreamUtil.*; 004import static co.codewizards.cloudstore.core.oio.OioFileFactory.*; 005 006import java.io.IOException; 007import java.io.OutputStream; 008import java.nio.ByteBuffer; 009import java.nio.CharBuffer; 010import java.nio.charset.StandardCharsets; 011import java.util.Arrays; 012 013import co.codewizards.cloudstore.core.config.ConfigDir; 014import co.codewizards.cloudstore.core.oio.File; 015import co.codewizards.cloudstore.core.otp.OneTimePadEncryptor.Result; 016import co.codewizards.cloudstore.core.util.IOUtil; 017 018/** 019 * Registry for passwords that need to be encrypted with OTP technique. 020 * You can, using methods of this class, encrypt with OTP and store a password 021 * in a file in ConfigDir, and later retrieve from the file and decrypt it. 022 * Encrypted password is stored in a file named fileNamePrefix + PASSWORD_FILE_SUFFIX 023 * Random key in a file named fileNamePrefix + RANDOM_KEY_FILE_SUFFIX 024 * 025 * @author Wojtek Wilk - wilk.wojtek at gmail.com 026 */ 027public class OneTimePadRegistry { 028 029 public static final String PASSWORD_FILE_SUFFIX = "Password"; 030 public static final String RANDOM_KEY_FILE_SUFFIX = "RandomKey"; 031 032 private final String fileNamePrefix; 033 private final OneTimePadEncryptor encryptor = new OneTimePadEncryptor(); 034 035 public OneTimePadRegistry(final String fileNamePrefix){ 036 this.fileNamePrefix = fileNamePrefix; 037 } 038 039 public void encryptAndStorePassword(final char[] password){ 040 final Result result = encryptor.encrypt(toBytes(password)); 041 try { 042 writeToFile(result.getEncryptedMessage(), PASSWORD_FILE_SUFFIX); 043 writeToFile(result.getRandomKey(), RANDOM_KEY_FILE_SUFFIX); 044 } catch (IOException e) { 045 throw new RuntimeException(e); 046 } 047 } 048 049 public char[] readFromFileAndDecrypt(){ 050 try { 051 final byte[] encryptedPassword = readFromFile(PASSWORD_FILE_SUFFIX); 052 final byte[] randomKey = readFromFile(RANDOM_KEY_FILE_SUFFIX); 053 final byte[] decryptedPassword = encryptor.decrypt(encryptedPassword, randomKey); 054 return toChars(decryptedPassword); 055 } catch (IOException e) { 056 throw new RuntimeException(e); 057 } 058 } 059 060 private byte[] readFromFile(String fileNameSuffix) throws IOException { 061 final String fileName = fileNamePrefix + fileNameSuffix; 062 final File file = createFile(ConfigDir.getInstance().getFile(), fileName); 063 return IOUtil.getBytesFromFile(file); 064 } 065 066 private void writeToFile(byte[] bytes, String fileNameSuffix) throws IOException{ 067 final File file = createFile(ConfigDir.getInstance().getFile(), fileNamePrefix + fileNameSuffix); 068 try(final OutputStream os = castStream(file.createOutputStream())) { 069 os.write(bytes); 070 } 071 } 072 073 /** 074 * based on @link <a href="http://stackoverflow.com/questions/5513144/converting-char-to-byte#answer-9670279">this answer</a> 075 */ 076 private byte[] toBytes(final char[] chars) { 077 final CharBuffer charBuffer = CharBuffer.wrap(chars); 078 final ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer); 079 final byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), 080 byteBuffer.position(), byteBuffer.limit()); 081 clearSensitiveData(charBuffer.array(), byteBuffer.array()); 082 return bytes; 083 } 084 085 private char[] toChars(final byte[] bytes){ 086 final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 087 final CharBuffer charBuffer = StandardCharsets.UTF_8.decode(byteBuffer); 088 final char[] chars = Arrays.copyOfRange(charBuffer.array(), 089 charBuffer.position(), charBuffer.limit()); 090 clearSensitiveData(charBuffer.array(), byteBuffer.array()); 091 return chars; 092 } 093 094 private void clearSensitiveData(final char[] chars, final byte[] bytes){ 095 Arrays.fill(chars, '\u0000'); 096 Arrays.fill(bytes, (byte) 0); 097 } 098}