· 7 years ago · Feb 27, 2018, 04:32 PM
1import java.nio.ByteBuffer;
2import java.nio.CharBuffer;
3import java.nio.charset.Charset;
4import java.security.InvalidKeyException;
5import java.security.NoSuchAlgorithmException;
6import java.security.SecureRandom;
7import java.util.Arrays;
8
9import javax.crypto.BadPaddingException;
10import javax.crypto.Cipher;
11import javax.crypto.IllegalBlockSizeException;
12import javax.crypto.KeyGenerator;
13import javax.crypto.NoSuchPaddingException;
14import javax.crypto.SecretKey;
15import javax.security.auth.Destroyable;
16
17public final class SensitiveChars implements CharSequence, Destroyable {
18
19 private static final int KEYSIZE = 56;
20 private static final String DES = "DES";
21 private static final String DES_ECB_PKCS5_PADDING = DES + "/ECB/PKCS5Padding";
22
23 private byte[] data;
24 private Charset charset;
25 private int lenght;
26
27 private SecretKey secretKey;
28
29 public SensitiveChars(char[] data) {
30 super();
31 this.charset = Charset.defaultCharset();
32 ByteBuffer byteBuffer = charset.encode(CharBuffer.wrap(data));
33 lenght = data.length;
34
35 try {
36 KeyGenerator keyGenerator = KeyGenerator.getInstance(DES);
37
38 SecureRandom secureRandom = new SecureRandom();
39 int keyBitSize = KEYSIZE;
40 keyGenerator.init(keyBitSize, secureRandom);
41 secretKey = keyGenerator.generateKey();
42
43 setRawData(byteBuffer.array());
44 } catch (NoSuchAlgorithmException e) {
45 throw new IllegalStateException(e);
46 }
47 }
48
49 public static SensitiveChars of(byte[] bytes) {
50 return of(bytes, Charset.defaultCharset());
51 }
52
53 public static SensitiveChars of(byte[] bytes, Charset charset) {
54 return of(bytes, charset, false);
55 }
56
57 public static SensitiveChars of(byte[] bytes, Charset charset, boolean cleanBytes) {
58 char[] chars = toChar(bytes, charset, cleanBytes);
59 return new SensitiveChars(chars);
60 }
61
62 @Override
63 public int length() {
64 return lenght;
65 }
66
67 @Override
68 public char charAt(int index) {
69 char[] tempArray = getChars();
70 char c = tempArray[index];
71 clearData(tempArray);
72 return c;
73 }
74
75 @Override
76 public CharSequence subSequence(int start, int end) {
77 char[] dataTmp = getChars();
78 char[] range = Arrays.copyOfRange(dataTmp, start, end);
79 clearData(dataTmp);
80 return new SensitiveChars(range);
81 }
82
83 public char[] getChars() {
84 if (lenght == 0) {
85 return new char[0];
86 } else {
87 byte[] rawData = getRawData();
88 ByteBuffer byteBuffer = ByteBuffer.wrap(rawData);
89 CharBuffer charBuffer = charset.decode(byteBuffer);
90 char[] result = Arrays.copyOf(charBuffer.array(), lenght);
91 clearData(charBuffer.array());
92 clearData(rawData);
93 return result;
94 }
95 }
96
97 public void append(char[] chars) {
98 char[] dataTmp = getChars();
99 int lengthData = dataTmp.length;
100 char[] newData = Arrays.copyOf(dataTmp, lengthData + chars.length);
101 clearData(data);
102
103 int posIni = lengthData;
104 for (int i = 0; i < chars.length; i++) {
105 newData[i + posIni] = chars[i];
106 }
107
108 CharBuffer charBuffer = CharBuffer.wrap(newData);
109 ByteBuffer byteBuffer = charset.encode(charBuffer);
110 byteBuffer.compact();
111 setRawData(byteBuffer.array());
112 lenght = newData.length;
113
114 clearData(dataTmp);
115 clearData(newData);
116 }
117
118 private void setRawData(byte[] data) {
119 this.data = encrypt(data);
120 }
121
122 private byte[] getRawData() {
123 return decrypt(data);
124 }
125
126 @Override
127 public void destroy() {
128 clearData(data);
129 data = new byte[0];
130 lenght = 0;
131 }
132
133 private byte[] encrypt(byte[] bytes) {
134 try {
135 Cipher cipher = Cipher.getInstance(DES_ECB_PKCS5_PADDING);
136 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
137 return cipher.doFinal(bytes);
138 } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
139 | BadPaddingException e) {
140 throw new IllegalStateException(e);
141 }
142 }
143
144 private byte[] decrypt(byte[] bytes) {
145 try {
146 Cipher cipher = Cipher.getInstance(DES_ECB_PKCS5_PADDING);
147 cipher.init(Cipher.DECRYPT_MODE, secretKey);
148 return cipher.doFinal(bytes);
149 } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
150 | BadPaddingException e) {
151 throw new IllegalStateException(e);
152 }
153 }
154
155 private static void clearData(byte[] cs) {
156 Arrays.fill(cs, (byte) 0);
157 }
158
159 private static void clearData(char[] cs) {
160 CharsUtils.clearData(cs);
161 }
162
163}