· 5 years ago · May 16, 2020, 11:00 PM
1 //These will be used as the source of the configuration file's stored attributes.
2 private static final Map<String, String> COMMON_ATTRIBUTES = new HashMap<String, String>();
3 private static final Map<String, char[]> SECURE_ATTRIBUTES = new HashMap<String, char[]>();
4 //Ciphering (encryption and decryption) password/key.
5 private static final char[] PASSWORD = "Unauthorized_Personel_Is_Unauthorized".toCharArray();
6 //Cipher salt.
7 private static final byte[] SALT = {
8 (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
9 (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,};
10 //Desktop dir:
11 private static final File DESKTOP = new File(System.getProperty("user.home") + "/Desktop");
12 //File names:
13 private static final String NO_ENCRYPTION = "no_layers.txt";
14 private static final String SINGLE_LAYER = "single_layer.txt";
15 private static final String DOUBLE_LAYER = "double_layer.txt";
16
17 /**
18 * @param args the command line arguments
19 */
20 public static void main(String[] args) throws GeneralSecurityException, FileNotFoundException, IOException {
21 //Set common attributes.
22 COMMON_ATTRIBUTES.put("Gender", "Male");
23 COMMON_ATTRIBUTES.put("Age", "21");
24 COMMON_ATTRIBUTES.put("Name", "Hypot Hetical");
25 COMMON_ATTRIBUTES.put("Nickname", "HH");
26
27 /*
28 * Set secure attributes.
29 * NOTE: Ignore the use of Strings here, it's being used for convenience only.
30 * In real implementations, JPasswordField.getPassword() would send the arrays directly.
31 */
32 SECURE_ATTRIBUTES.put("Username", "Hypothetical".toCharArray());
33 SECURE_ATTRIBUTES.put("Password", "LetMePass_Word".toCharArray());
34
35 /*
36 * For demosntration purposes, I make the three encryption layer-levels I mention.
37 * To leave no doubt the code works, I use real file IO.
38 */
39 //File without encryption.
40 create_EncryptedFile(NO_ENCRYPTION, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 0);
41 //File with encryption to secure attributes only.
42 create_EncryptedFile(SINGLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 1);
43 //File completely encrypted, including re-encryption of secure attributes.
44 create_EncryptedFile(DOUBLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 2);
45
46 /*
47 * Show contents of all three encryption levels, from file.
48 */
49 System.out.println("NO ENCRYPTION: \n" + readFile_NoDecryption(NO_ENCRYPTION) + "\n\n\n");
50 System.out.println("SINGLE LAYER ENCRYPTION: \n" + readFile_NoDecryption(SINGLE_LAYER) + "\n\n\n");
51 System.out.println("DOUBLE LAYER ENCRYPTION: \n" + readFile_NoDecryption(DOUBLE_LAYER) + "\n\n\n");
52
53 /*
54 * Decryption is demonstrated with the Double-Layer encryption file.
55 */
56 //Descrypt first layer. (file content) (REMEMBER: Layers are in reverse order from writing).
57 String decryptedContent = readFile_ApplyDecryption(DOUBLE_LAYER);
58 System.out.println("READ: [first layer decrypted]\n" + decryptedContent + "\n\n\n");
59 //Decrypt second layer (secure data).
60 for (String line : decryptedContent.split("\n")) {
61 String[] pair = line.split(": ", 2);
62 if (pair[0].equalsIgnoreCase("Username") || pair[0].equalsIgnoreCase("Password")) {
63 System.out.println("Decrypted: " + pair[0] + ": " + decrypt(pair[1]));
64 }
65 }
66 }
67
68 private static String encrypt(byte[] property) throws GeneralSecurityException {
69 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
70 SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
71 Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
72 pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
73
74 //Encrypt and save to temporary storage.
75 String encrypted = Base64.encodeBytes(pbeCipher.doFinal(property));
76
77 //Cleanup data-sources - Leave no traces behind.
78 for (int i = 0; i < property.length; i++) {
79 property[i] = 0;
80 }
81 property = null;
82 System.gc();
83
84 //Return encryption result.
85 return encrypted;
86 }
87
88 private static String encrypt(char[] property) throws GeneralSecurityException {
89 //Prepare and encrypt.
90 byte[] bytes = new byte[property.length];
91 for (int i = 0; i < property.length; i++) {
92 bytes[i] = (byte) property[i];
93 }
94 String encrypted = encrypt(bytes);
95
96 /*
97 * Cleanup property here. (child data-source 'bytes' is cleaned inside 'encrypt(byte[])').
98 * It's not being done because the sources are being used multiple times for the different layer samples.
99 */
100// for (int i = 0; i < property.length; i++) { //cleanup allocated data.
101// property[i] = 0;
102// }
103// property = null; //de-allocate data (set for GC).
104// System.gc(); //Attempt triggering garbage-collection.
105
106 return encrypted;
107 }
108
109 private static String encrypt(String property) throws GeneralSecurityException {
110 String encrypted = encrypt(property.getBytes());
111 /*
112 * Strings can't really have their allocated data cleaned before CG,
113 * that's why secure data should be handled with char[] or byte[].
114 * Still, don't forget to set for GC, even for data of sesser importancy;
115 * You are making everything safer still, and freeing up memory as bonus.
116 */
117 property = null;
118 return encrypted;
119 }
120
121 private static String decrypt(String property) throws GeneralSecurityException, IOException {
122 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
123 SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
124 Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
125 pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
126 return new String(pbeCipher.doFinal(Base64.decode(property)));
127 }
128
129 private static void create_EncryptedFile(
130 String fileName,
131 Map<String, String> commonAttributes,
132 Map<String, char[]> secureAttributes,
133 int layers)
134 throws GeneralSecurityException, FileNotFoundException, IOException {
135 StringBuilder sb = new StringBuilder();
136 for (String k : commonAttributes.keySet()) {
137 sb.append(k).append(": ").append(commonAttributes.get(k)).append(System.lineSeparator());
138 }
139 //First encryption layer. Encrypts secure attribute values only.
140 for (String k : secureAttributes.keySet()) {
141 String encryptedValue;
142 if (layers >= 1) {
143 encryptedValue = encrypt(secureAttributes.get(k));
144 } else {
145 encryptedValue = new String(secureAttributes.get(k));
146 }
147 sb.append(k).append(": ").append(encryptedValue).append(System.lineSeparator());
148 }
149
150 //Prepare file and file-writing process.
151 File f = new File(DESKTOP, fileName);
152 if (!f.getParentFile().exists()) {
153 f.getParentFile().mkdirs();
154 } else if (f.exists()) {
155 f.delete();
156 }
157 BufferedWriter bw = new BufferedWriter(new FileWriter(f));
158 //Second encryption layer. Encrypts whole file content including previously encrypted stuff.
159 if (layers >= 2) {
160 bw.append(encrypt(sb.toString().trim()));
161 } else {
162 bw.append(sb.toString().trim());
163 }
164 bw.flush();
165 bw.close();
166 }
167
168 private static String readFile_NoDecryption(String fileName) throws FileNotFoundException, IOException, GeneralSecurityException {
169 File f = new File(DESKTOP, fileName);
170 BufferedReader br = new BufferedReader(new FileReader(f));
171 StringBuilder sb = new StringBuilder();
172 while (br.ready()) {
173 sb.append(br.readLine()).append(System.lineSeparator());
174 }
175 return sb.toString();
176 }
177
178 private static String readFile_ApplyDecryption(String fileName) throws FileNotFoundException, IOException, GeneralSecurityException {
179 File f = new File(DESKTOP, fileName);
180 BufferedReader br = new BufferedReader(new FileReader(f));
181 StringBuilder sb = new StringBuilder();
182 while (br.ready()) {
183 sb.append(br.readLine()).append(System.lineSeparator());
184 }
185 return decrypt(sb.toString());
186 }