source: trunk/src/org/expeditee/auth/Authenticator.java@ 1270

Last change on this file since 1270 was 1270, checked in by bln4, 5 years ago

On profile creation a new parameter has been introducted. By passing a map, the user is able to nominate settings frames for which to be notified of their construction.

The above functionalty has been used to programmatically establish the frame number for a users credentials frame.

On user account creation, the users credential frame is migrated to the <username>-credentials folder and a redirect is setup. Functionality for doing this has been generisised and placed in FrameIO.

File size: 17.1 KB
Line 
1package org.expeditee.auth;
2
3import java.io.File;
4import java.io.FileInputStream;
5import java.io.FileNotFoundException;
6import java.io.FileOutputStream;
7import java.io.IOException;
8import java.io.InputStream;
9import java.nio.file.Path;
10import java.nio.file.Paths;
11import java.security.KeyFactory;
12import java.security.KeyStore;
13import java.security.KeyStore.SecretKeyEntry;
14import java.security.KeyStoreException;
15import java.security.NoSuchAlgorithmException;
16import java.security.PublicKey;
17import java.security.SecureRandom;
18import java.security.UnrecoverableEntryException;
19import java.security.cert.CertificateException;
20import java.security.spec.InvalidKeySpecException;
21import java.security.spec.X509EncodedKeySpec;
22import java.sql.Connection;
23import java.sql.DriverManager;
24import java.sql.PreparedStatement;
25import java.sql.ResultSet;
26import java.sql.SQLException;
27import java.util.Arrays;
28import java.util.Base64;
29import java.util.Collection;
30import java.util.HashMap;
31import java.util.Map;
32import java.util.Scanner;
33import java.util.stream.Stream;
34
35import javax.crypto.SecretKey;
36import javax.crypto.spec.SecretKeySpec;
37
38import org.expeditee.actions.Actions;
39import org.expeditee.auth.tags.Constants;
40import org.expeditee.core.Dimension;
41import org.expeditee.core.Point;
42import org.expeditee.gio.EcosystemManager;
43import org.expeditee.gio.GraphicsManager;
44import org.expeditee.gio.InputManager;
45import org.expeditee.gio.InputManager.WindowEventListener;
46import org.expeditee.gio.InputManager.WindowEventType;
47import org.expeditee.gio.gesture.StandardGestureActions;
48import org.expeditee.gui.Browser;
49import org.expeditee.gui.DisplayController;
50import org.expeditee.gui.Frame;
51import org.expeditee.gui.FrameIO;
52import org.expeditee.gui.FrameUtils;
53import org.expeditee.gui.MessageBay;
54import org.expeditee.io.ExpReader;
55import org.expeditee.items.Item;
56import org.expeditee.items.ItemUtils;
57import org.expeditee.items.Text;
58import org.expeditee.settings.Settings;
59import org.expeditee.settings.UserSettings;
60import org.expeditee.settings.identity.secrets.KeyList;
61import org.ngikm.cryptography.CryptographyConstants;
62
63public final class Authenticator implements CryptographyConstants {
64
65 // The frame number of the frame containing the current authenticated users public key.
66 public static int CREDENTIALS_FRAME = -1;
67 public static final String ADMINACCOUNT = "authadmin";
68
69 public static boolean Authenticated = false;
70
71 private KeyStore keyStore = KeyStore.getInstance(KeystoreType);
72
73 private static final byte[] TRUE = "yes".getBytes();
74 private static final byte[] FALSE = "no".getBytes();
75 private static final String KEYSTOREFILENAME = "keystore.ks" + File.separator;
76
77 private static Authenticator instance;
78
79 public static Authenticator getInstance() throws KeyStoreException, FileNotFoundException, NoSuchAlgorithmException, CertificateException, IOException, ClassNotFoundException, SQLException {
80 if (instance == null) { instance = new Authenticator(); }
81 return instance;
82 }
83
84 private Authenticator() throws KeyStoreException, FileNotFoundException, IOException, NoSuchAlgorithmException, CertificateException, ClassNotFoundException, SQLException {
85 System.out.println("Running Expeditee in Authentication Mode.");
86 UserSettings.setupDefaultFolders();
87
88 // initialise keystore and actions
89 loadKeystore();
90 Actions.LoadMethods(org.expeditee.auth.Actions.class);
91 Actions.LoadMethods(org.expeditee.auth.sharing.Actions.class);
92
93 // Does the account Authentication.ADMINACCOUNT exist?
94 // If not then we have get the user to assign a password to it.
95 if (!keyStore.containsAlias(Authenticator.ADMINACCOUNT)) {
96 new File(FrameIO.PARENT_FOLDER).mkdirs();
97 protectAdmin();
98 }
99
100 // draw the window
101 GraphicsManager g = EcosystemManager.getGraphicsManager();
102 g.setWindowLocation(new Point(50, 50));
103 DisplayController.Init();
104 g.setWindowSize(new Dimension(UserSettings.InitialWidth.get(), UserSettings.InitialHeight.get()));
105 setInputManagerWindowRoutines();
106
107 // Load documentation and start pages
108 FrameUtils.extractResources(false);
109
110 // Load fonts before loading any frames so the items on the frames will be able to access their fonts
111 Text.InitFonts();
112
113 // initialing settings does not require a user profile established
114 Settings.Init();
115
116 // navigate to authentication frame
117 Frame authFrame = FrameIO.LoadFrame("authentication1");
118 DisplayController.setCurrentFrame(authFrame, true);
119
120 // set initial values
121 Stream<Text> usernameItemsStream = authFrame.getTextItems().stream().filter(t -> t.getData() != null && t.getData().contains("txtUsername"));
122 Stream<Text> passwordItemsStream = authFrame.getTextItems().stream().filter(t -> t.getData() != null && t.getData().contains("txtPassword"));
123 usernameItemsStream.forEach(txtUsername -> txtUsername.setText(System.getProperty("startinguser.name", "")));
124 passwordItemsStream.forEach(txtPassword -> { txtPassword.setText(""); txtPassword.invalidateAll(); });
125
126 MessageBay.warningMessages(org.expeditee.actions.Actions.Init());
127
128 // class load database classes
129 Class.forName("org.sqlite.JDBC");
130 }
131
132 private void protectAdmin() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
133 FileNotFoundException, IOException {
134 // Fetch desired password
135 Scanner in = new Scanner(System.in);
136 System.out.println("No administrative password set.");
137 boolean passwordIsSet = false;
138
139 for (int i = 0; i < 3; i++) {
140 System.out.print("Please enter it now: ");
141 System.out.flush();
142 String password = in.nextLine();
143 System.out.print("And again: ");
144 System.out.flush();
145 if (in.nextLine().equals(password)) {
146 // Register account.
147 putKey(ADMINACCOUNT, password, new SecretKeySpec("null".getBytes(), AsymmetricAlgorithm));
148 in.close();
149 passwordIsSet = true;
150 break;
151 } else {
152 System.out.println("Mismatched passwords, let's try that again.");
153 }
154 }
155
156 if (!passwordIsSet) {
157 System.out.println("Failed to set an admin password. Exiting Expeditee.");
158 System.exit(1);
159 }
160 }
161
162 private void loadKeystore()
163 throws IOException, NoSuchAlgorithmException, CertificateException, FileNotFoundException {
164 final File keyStoreFile = new File(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME);
165 if (!keyStoreFile.exists()) {
166 keyStore.load(null, Constants.CREDENTIALS_KEYSTORE_PASSWORD.toCharArray());
167 } else {
168 try (final InputStream in = new FileInputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME)) {
169 keyStore.load(in, Constants.CREDENTIALS_KEYSTORE_PASSWORD.toCharArray());
170 }
171 }
172 }
173
174 final void loadMailFromDirectory(Path contactDir) throws SQLException {
175 // Load in all mail.
176 Connection c = DriverManager.getConnection("jdbc:sqlite:" + contactDir.resolve("expmail.db"));
177 String sql = "SELECT * FROM EXPMAIL";
178 PreparedStatement query = c.prepareStatement(sql);
179 ResultSet allMail = query.executeQuery();
180
181 // Construct all mail objects using content from database.
182 while(allMail.next()) {
183 String timestamp = allMail.getString("time");
184 String from = allMail.getString("snd");
185 String to = allMail.getString("rec");
186 String msg = allMail.getString("msg");
187 String msg2 = allMail.getString("msg2");
188 String[] opts = allMail.getString("opts").split(",");
189 opts[0] = opts[0].replace("[", "");
190 opts[opts.length - 1] = opts[opts.length - 1].replace("]", "");
191 String[] optsVal = allMail.getString("optsval").split(",");
192 optsVal[0] = optsVal[0].replace("[", "");
193 optsVal[optsVal.length - 1] = optsVal[optsVal.length - 1].replace("]", "");
194
195 Map<String, String> options = new HashMap<String, String>();
196 for (int i = 0, o = 0; i < opts.length && o < optsVal.length; i++, o++) {
197 String key = opts[i].trim();
198 String val = optsVal[o].trim();
199 options.put(key, val);
200 }
201
202 Mail.addEntry(new Mail.MailEntry(timestamp, from, to, msg, msg2, options));
203 }
204
205 // Disconnect from database.
206 allMail.close();
207 query.close();
208 c.close();
209 }
210
211 final void loadMailDatabase() throws SQLException {
212 Path contactsPath = Paths.get(FrameIO.CONTACTS_PATH);
213 File[] contacts = contactsPath.toFile().listFiles();
214 for (int i = 0; i < contacts.length; i++) {
215 if (contacts[i].isDirectory()) {
216 Path contact = Paths.get(contacts[i].getAbsolutePath());
217 loadMailFromDirectory(contact);
218 }
219 }
220 }
221
222 final SecretKey getSecretKey(final String label, final String password) throws NoSuchAlgorithmException, KeyStoreException {
223
224 char[] password_ca = password.toCharArray();
225 //final KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(password_ca);
226
227 SecretKey secret_key;
228 try {
229 //SecretKeyEntry entry = (SecretKeyEntry) keyStore.getEntry(label, entryPassword);
230 //secret_key = entry.getSecretKey();
231 secret_key = (SecretKey) keyStore.getKey(label, password_ca);
232 } catch (final UnrecoverableEntryException e) {
233 e.printStackTrace();
234 secret_key = null;
235 }
236
237 return secret_key;
238 }
239
240 final void putKey(final String label, final String password, final SecretKey key) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
241 final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(key);
242 final KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(password.toCharArray());
243 keyStore.setEntry(label, entry, entryPassword);
244 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), Constants.CREDENTIALS_KEYSTORE_PASSWORD.toCharArray());
245 }
246
247 final boolean confirmIntergalaticNumber(final String username, final String email, final String intergalacticNumber) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException {
248 try {
249 final KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(intergalacticNumber.toCharArray());
250 final KeyStore.SecretKeyEntry entry = (SecretKeyEntry) keyStore.getEntry(email + username, entryPassword);
251 if (entry == null) {
252 return false;
253 } else if (entry.getSecretKey().getEncoded() == TRUE) {
254 keyStore.deleteEntry(email + username);
255 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), Constants.CREDENTIALS_KEYSTORE_PASSWORD.toCharArray());
256 return true;
257 } else { return false; }
258 } catch (final UnrecoverableEntryException e) {
259 return false;
260 }
261 }
262
263 final String newIntergalacticNumber(final String username, final String email) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
264 // generate intergalactic number
265 final SecureRandom rand = new SecureRandom();
266 final byte[] intergalacticNumberBytes = new byte[20];
267 rand.nextBytes(intergalacticNumberBytes);
268 final String intergalacticNumber = Base64.getEncoder().encodeToString(intergalacticNumberBytes);
269
270 // store intergalactic number
271 final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(new SecretKeySpec(TRUE, SymmetricAlgorithm));
272 final KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(intergalacticNumber.toCharArray());
273 keyStore.setEntry(email + username, entry, entryPassword);
274 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), Constants.CREDENTIALS_KEYSTORE_PASSWORD.toCharArray());
275
276 return intergalacticNumber;
277 }
278
279 final PublicKey getPublicKey(String username) throws InvalidKeySpecException, NoSuchAlgorithmException, FileNotFoundException {
280 // load in frame with public key on it.
281 String credentialsFramesetPath = FrameIO.CONTACTS_PATH + username + "-credentials" + File.separator;
282 if (!new File(credentialsFramesetPath).exists()) {
283 return null;
284 }
285 Scanner in = new Scanner(new File(credentialsFramesetPath + "credentials.inf"));
286 String credentialsFrameNumber = in.nextLine().replace(ExpReader.EXTENTION, "");
287 in.close();
288 Frame frame = FrameIO.LoadFrame(username + "-credentials" + credentialsFrameNumber, FrameIO.CONTACTS_PATH);
289 if (frame == null) {
290 return null;
291 }
292
293 // obtain public key from frame
294 Collection<Item> canditates = org.expeditee.auth.Actions.getByContent(frame, "PublicKey");
295 String keyEncoded = "";
296 for (Item i: canditates) {
297 if (i.getData() != null) {
298 keyEncoded = i.getData().get(0);
299 }
300 }
301 if (keyEncoded.isEmpty()) {
302 return null;
303 }
304 byte[] keyBytes = Base64.getDecoder().decode(keyEncoded);
305 return KeyFactory.getInstance(AsymmetricAlgorithm).generatePublic(new X509EncodedKeySpec(keyBytes));
306 }
307
308 final void markRequestedColleagues(String username) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
309 KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(new SecretKeySpec(TRUE, SymmetricAlgorithm));
310 KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
311 keyStore.setEntry(username + "colleaguesRequested", entry, entryPassword);
312 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), Constants.CREDENTIALS_KEYSTORE_PASSWORD.toCharArray());
313 }
314
315 final void clearRequestedColleagues(String username) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
316 KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(new SecretKeySpec(FALSE, SymmetricAlgorithm));
317 KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
318 keyStore.setEntry(username + "colleaguesRequested", entry, entryPassword);
319 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), Constants.CREDENTIALS_KEYSTORE_PASSWORD.toCharArray());
320 }
321
322 final boolean hasRequestedColleagues(String username) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
323 String alias = username + "colleaguesRequested";
324 if (!keyStore.containsAlias(alias)) {
325 return false;
326 } else {
327 KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
328 KeyStore.SecretKeyEntry entry = (SecretKeyEntry) keyStore.getEntry(alias, entryPassword);
329 return Arrays.equals(entry.getSecretKey().getEncoded(), TRUE);
330 }
331 }
332
333// final void putColleagues(String username, String[] colleagues) throws KeyStoreException {
334// String alias = username + "colleagues";
335// final SecretKeySpec secretKeySpec = new SecretKeySpec((colleagues[0] + System.getProperty("line.separator") + colleagues[1]).getBytes(), SymmetricAlgorithm);
336// KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(secretKeySpec);
337// KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
338// keyStore.setEntry(alias, entry, entryPassword);
339// }
340//
341// final String[] getColleagues(String username) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
342// String alias = username + "colleagues";
343// if (!keyStore.containsAlias(alias)) {
344// return null;
345// } else {
346// KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
347// KeyStore.SecretKeyEntry entry = (SecretKeyEntry) keyStore.getEntry(alias, entryPassword);
348// byte[] colleaguesEncoded = entry.getSecretKey().getEncoded();
349// String colleagues = new String(colleaguesEncoded);
350// return colleagues.split(System.getProperty("line.separator"));
351// }
352// }
353
354 private static void setInputManagerWindowRoutines() {
355 InputManager manager = EcosystemManager.getInputManager();
356
357 // Refresh the layout when the window resizes
358 manager.addWindowEventListener(new WindowEventListener() {
359 @Override
360 public void onWindowEvent(WindowEventType type)
361 {
362 if (type != WindowEventType.WINDOW_RESIZED) {
363 return;
364 }
365 DisplayController.refreshWindowSize();
366 FrameIO.RefreshCacheImages();
367 for (Frame frame : DisplayController.getFrames()) {
368 if (frame != null) {
369 ItemUtils.Justify(frame);
370 frame.refreshSize();
371 }
372 }
373 DisplayController.requestRefresh(false);
374 }
375 });
376
377 manager.addWindowEventListener(new WindowEventListener() {
378 @Override
379 public void onWindowEvent(WindowEventType type)
380 {
381 if (type != WindowEventType.MOUSE_EXITED_WINDOW) {
382 return;
383 }
384 StandardGestureActions.mouseExitedWindow();
385 }
386 });
387
388 manager.addWindowEventListener(new WindowEventListener() {
389 @Override
390 public void onWindowEvent(WindowEventType type)
391 {
392 if (type != WindowEventType.MOUSE_ENTERED_WINDOW) {
393 return;
394 }
395 StandardGestureActions.mouseEnteredWindow();
396 }
397 });
398
399 manager.addWindowEventListener(new WindowEventListener() {
400 @Override
401 public void onWindowEvent(WindowEventType type)
402 {
403 if (type != WindowEventType.WINDOW_CLOSED) {
404 return;
405 }
406 if (Browser._theBrowser != null) {
407 Browser._theBrowser.exit();
408 }
409 }
410 });
411 }
412}
Note: See TracBrowser for help on using the repository browser.