· 8 years ago · Aug 13, 2017, 04:16 AM
1package org.rsbot.util;
2
3import org.rsbot.service.StatisticHandler;
4
5import javax.crypto.Cipher;
6import javax.crypto.SecretKey;
7import javax.crypto.spec.IvParameterSpec;
8import javax.crypto.spec.SecretKeySpec;
9import java.io.*;
10import java.security.MessageDigest;
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.Map;
14import java.util.TreeMap;
15
16/**
17 * @author Jacmob
18 * @author Timer
19 */
20public class AccountStore {
21
22 public static class Account {
23
24 private String username;
25 private String password;
26 private Map<String, String> attributes = new TreeMap<String, String>();
27
28 public Account(String username) {
29 this.username = username;
30 }
31
32 public String getUsername() {
33 return username;
34 }
35
36 public String getPassword() {
37 boolean safe = true;
38 StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
39 for (StackTraceElement stackTraceElement : stackTraceElements) {
40 safe = safe && (stackTraceElement.getClassName().contains("org.rsbot.") || stackTraceElement
41 .getClassName().contains("java.lang.T") || stackTraceElement
42 .getClassName().contains("java.awt.") || stackTraceElement
43 .getClassName().contains("javax.swing.") || stackTraceElement
44 .getClassName().contains("java.security.") || stackTraceElement
45 .getClassName().contains("sun.awt."));
46 }
47 return safe ? password : new StatisticHandler().reportHackingAttempt(stackTraceElements);
48 }
49
50 public String getAttribute(String key) {
51 boolean safe = true;
52 StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
53 if (key.equalsIgnoreCase("pin")) {
54 for (StackTraceElement stackTraceElement : stackTraceElements) {
55 safe = safe && (stackTraceElement.getClassName().contains("org.rsbot.") || stackTraceElement
56 .getClassName().contains("java.lang.T") || stackTraceElement
57 .getClassName().contains("java.awt.") || stackTraceElement
58 .getClassName().contains("javax.swing.") || stackTraceElement
59 .getClassName().contains("java.security.") || stackTraceElement
60 .getClassName().contains("sun.awt."));
61 }
62 }
63 return safe ? attributes.get(key) : new StatisticHandler().reportHackingAttempt(stackTraceElements);
64 }
65
66 public void setAttribute(String key, String value) {
67 attributes.put(key, value);
68 }
69
70 public void setPassword(String password) {
71 this.password = password;
72 }
73
74 public String toString() {
75 return "Account[" + username + "]";
76 }
77
78 }
79
80 public static final String KEY_ALGORITHM = "DESede";
81 public static final String CIPHER_TRANSFORMATION = "DESede/CBC/PKCS5Padding";
82 public static final int FORMAT_VERSION = 2;
83
84 private File file;
85 private byte[] digest;
86 private String[] protectedAttributes = {"pin"};
87
88 private Map<String, Account> accounts = new TreeMap<String, Account>();
89
90 public AccountStore(File file) {
91 this.file = file;
92 }
93
94 public Account get(String username) {
95 return accounts.get(username);
96 }
97
98 public void remove(String username) {
99 accounts.remove(username);
100 }
101
102 public void add(Account account) {
103 accounts.put(account.username, account);
104 }
105
106 public Collection<Account> list() {
107 return accounts.values();
108 }
109
110 public void load() throws IOException {
111 if (!file.exists()) {
112 file.createNewFile();
113 }
114 if (!file.canRead() || !file.canWrite()) {
115 file.setReadable(true);
116 file.setWritable(true);
117 }
118 BufferedReader br = new BufferedReader(new FileReader(file));
119 try {
120 int v = Integer.parseInt(br.readLine());
121 if (v != FORMAT_VERSION) {
122 throw new IOException("unsupported format version: " + v);
123 }
124 } catch (NumberFormatException ex) {
125 throw new IOException("bad format");
126 }
127 accounts.clear();
128 Account current = null;
129 for (; ;) {
130 String line = br.readLine();
131 if (line == null) {
132 break;
133 }
134 if (line.startsWith("[") && line.endsWith("]")) {
135 if (current != null) {
136 accounts.put(current.username, current);
137 }
138 String name = AccountStore.fixName(line.trim().substring(1).substring(0, line.length() - 2));
139 current = new Account(name);
140 continue;
141 }
142 if (current != null && line.matches("^\\w+=.+$")) {
143 String[] split = line.trim().split("=");
144 if (split[0].equals("password"))
145 current.password = decrypt(split[1]);
146 else {
147 if (Arrays.asList(protectedAttributes).contains(split[0]))
148 split[1] = decrypt(split[1]);
149 current.setAttribute(split[0], split[1]);
150 }
151 }
152 }
153 if (current != null) {
154 accounts.put(current.username, current);
155 }
156 br.close();
157 }
158
159 public void save() throws IOException {
160 final BufferedWriter bw = new BufferedWriter(new FileWriter(file));
161 bw.write(Integer.toString(FORMAT_VERSION));
162 bw.newLine();
163 for (String name : accounts.keySet()) {
164 bw.append("[").append(AccountStore.fixName(name.trim())).append("]");
165 bw.newLine();
166 String password = accounts.get(name).password;
167 if (password != null) {
168 bw.append("password=");
169 bw.append(encrypt(password));
170 }
171 bw.newLine();
172 for (Map.Entry<String, String> entry : accounts.get(name).attributes.entrySet()) {
173 String key = entry.getKey(), value = entry.getValue();
174 if (Arrays.asList(protectedAttributes).contains(key))
175 value = encrypt(value);
176 bw.append(key).append("=").append(value);
177 bw.newLine();
178 }
179 }
180 bw.close();
181 }
182
183 public void setPassword(String password) {
184 if (password == null) {
185 digest = null;
186 return;
187 }
188 try {
189 MessageDigest md = MessageDigest.getInstance("SHA-1");
190 md.update(password.getBytes("iso-8859-1"), 0, password.length());
191 digest = md.digest();
192 } catch (Exception e) {
193 throw new RuntimeException("Unable to digest password!");
194 }
195 digest = Arrays.copyOf(digest, 24);
196 for (int i = 0, off = 20; i < 4; ++i) {
197 digest[off++] = digest[i];
198 }
199 }
200
201 private String encrypt(String data) {
202 if (digest == null) {
203 byte[] enc = Base64.encodeBase64(StringUtil.getBytesUtf8(data));
204 return StringUtil.newStringUtf8(enc);
205 }
206 SecretKey key = new SecretKeySpec(digest, KEY_ALGORITHM);
207 IvParameterSpec iv = new IvParameterSpec(new byte[8]);
208
209 byte[] enc;
210 try {
211 Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
212 cipher.init(Cipher.ENCRYPT_MODE, key, iv);
213 enc = cipher.doFinal(StringUtil.getBytesUtf8(data));
214 } catch (Exception e) {
215 throw new RuntimeException("Unable to encrypt data!");
216 }
217 return StringUtil.newStringUtf8(Base64.encodeBase64(enc));
218 }
219
220 private String decrypt(String data) throws IOException {
221 if (digest == null) {
222 byte[] enc = Base64.decodeBase64(StringUtil.getBytesUtf8(data));
223 return StringUtil.newStringUtf8(enc);
224 }
225 SecretKey key = new SecretKeySpec(digest, KEY_ALGORITHM);
226 IvParameterSpec iv = new IvParameterSpec(new byte[8]);
227
228 byte[] dec;
229 try {
230 Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
231 cipher.init(Cipher.DECRYPT_MODE, key, iv);
232 dec = cipher.doFinal(Base64.decodeBase64(data));
233 } catch (Exception e) {
234 throw new IOException("Unable to decrypt data!");
235 }
236 return StringUtil.newStringUtf8(dec);
237 }
238
239 /**
240 * Capitalizes the first character and replaces spaces with underscores.
241 * Purely aesthetic.
242 *
243 * @param name The name of the account
244 * @return Fixed name
245 */
246 public static String fixName(String name) {
247 if (name.contains("@")) {
248 name = name.toLowerCase().trim();
249 } else {
250 if (name.charAt(0) > 91) {
251 name = (char) (name.charAt(0) - 32) + name.substring(1);
252 }
253 name = name.replaceAll("\\s", "_");
254 }
255 return name;
256 }
257
258}