source: trunk/src/org/expeditee/auth/account/Password.java@ 1500

Last change on this file since 1500 was 1500, checked in by bnemhaus, 4 years ago

org.expeditee.auth.account.Password =>
org.expeditee.encryption.EncryptedExpReader =>

When performing account recovery, the username of the account you are trying to recover now actually needs to match the recovered key.
Also, the order that the password pieces are entered no longer matters.

File size: 18.2 KB
Line 
1package org.expeditee.auth.account;
2
3import java.io.File;
4import java.io.FileNotFoundException;
5import java.io.IOException;
6import java.nio.file.Path;
7import java.nio.file.Paths;
8import java.security.KeyStoreException;
9import java.security.NoSuchAlgorithmException;
10import java.security.SecureRandom;
11import java.security.cert.CertificateException;
12import java.sql.SQLException;
13import java.util.Base64;
14import java.util.Collection;
15import java.util.HashMap;
16import java.util.Iterator;
17import java.util.Map;
18import java.util.Properties;
19import java.util.Scanner;
20
21import javax.crypto.SecretKey;
22import javax.crypto.spec.SecretKeySpec;
23import javax.mail.Message;
24import javax.mail.MessagingException;
25import javax.mail.PasswordAuthentication;
26import javax.mail.Session;
27import javax.mail.Transport;
28import javax.mail.internet.AddressException;
29import javax.mail.internet.InternetAddress;
30import javax.mail.internet.MimeMessage;
31
32import org.expeditee.auth.AuthenticatorBrowser;
33import org.expeditee.auth.mail.Mail;
34import org.expeditee.auth.mail.Mail.MailEntry;
35import org.expeditee.auth.tags.AuthenticationTag;
36import org.expeditee.encryption.CryptographyConstants;
37import org.expeditee.encryption.io.EncryptedExpReader;
38import org.expeditee.gui.DisplayController;
39import org.expeditee.gui.Frame;
40import org.expeditee.gui.FrameIO;
41import org.expeditee.gui.MessageBay;
42import org.expeditee.items.Text;
43import org.expeditee.settings.UserSettings;
44import org.expeditee.settings.identity.passwordrecovery.Colleagues;
45import org.expeditee.settings.identity.secrets.KeyList;
46import org.expeditee.stats.Formatter;
47
48import com.codahale.shamir.Scheme;
49
50public class Password implements CryptographyConstants {
51 /**
52 * Changes the recorded password for a user in the key store; given the username signaling whose password to change, along with the existing and new password.
53 */
54 public static void changePassword(Map<AuthenticationTag, String> userdata) throws NoSuchAlgorithmException, KeyStoreException, FileNotFoundException, CertificateException, IOException, ClassNotFoundException, SQLException {
55 String username = userdata.get(AuthenticationTag.Username);
56 String password = userdata.get(AuthenticationTag.Password);
57 String newpassword = userdata.get(AuthenticationTag.NewPassword);
58
59 final SecretKey key = AuthenticatorBrowser.getInstance().getSecretKey(username, password);
60 if (key == null) {
61 MessageBay.errorMessage("The username + existing password combination was incorrect.");
62 } else {
63 AuthenticatorBrowser.getInstance().putKey(username, newpassword, key);
64 MessageBay.displayMessage("Password changed successfully.");
65 DisplayController.setCurrentFrame(FrameIO.LoadFrame("multiuser1"), true);
66 }
67 }
68
69 /**
70 * Generates a intergalaictic number for a specified user and emails that number using the specified email.
71 * @param userData
72 */
73 public static void generateAndDeliverIntergalacticNumber(Map<AuthenticationTag, String> userData) {
74 String username = userData.get(AuthenticationTag.Username);
75 String email = userData.get(AuthenticationTag.Email);
76 try {
77 // Generate message text.
78 String intergalacticNumber = AuthenticatorBrowser.getInstance().newIntergalacticNumber(username, email);
79 String nl = System.getProperty("line.separator");
80 StringBuilder sb = new StringBuilder();
81 sb.append("You are receiving this email because someone is attempting to reset your Expeditee password." + nl);
82 sb.append("If you did not make this request then no action is required." + nl);
83 sb.append("If it was you who made this request, the following string of characters is your identity number: " + intergalacticNumber + nl);
84
85 sendEmail(email, sb);
86 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | ClassNotFoundException
87 | IOException | SQLException | MessagingException e) {
88 e.printStackTrace();
89 }
90 }
91
92 /**
93 * Confirms that the specified intergalaictic number matches the one of file for the specified username.
94 * Passing this test it then alerts the users pw colleagues through a one-off secure Expeditee message.
95 * @param tags
96 */
97 public static void confirmIntergalacticNumberAndAlertTrustedUsers(Map<AuthenticationTag, String> tags) {
98 // Confirm intergalactic numbers match
99 String username = tags.get(AuthenticationTag.Username);
100 String intergalacticNumber = tags.get(AuthenticationTag.IntergalacticNumber);
101 boolean match = false;
102 try {
103 match = AuthenticatorBrowser.getInstance().confirmIntergalaticNumber(username, intergalacticNumber);
104 } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | ClassNotFoundException
105 | IOException | SQLException e) {
106 e.printStackTrace();
107 return;
108 }
109 if (!match) {
110 MessageBay.errorMessage("The provided identity number does not match the one stored on file.");
111 return;
112 }
113
114 // Get colleagues to distribute messages too.
115 String[] colleagues = getPasswordColleaguesFromUsername(username);
116 // Send secure message to colleague one
117 String colleagueOne = colleagues[0];
118 String time = Formatter.getDateTime();
119 String topic = "Password Recovery for " + username;
120 String message = "Your colleague " + username + " would like you to help them recover access to their account.";
121 Map<String, String> options = new HashMap<String, String>();
122 options.put("Provide assistance", "AuthEmailPasswordShare " + username);
123 MailEntry mail = new MailEntry(time, username, colleagueOne, topic, message, options);
124 Mail.sendOneOffMail(mail, colleagueOne, Base64.getDecoder().decode(intergalacticNumber));
125
126 // Send secure message to colleague two
127 String colleagueTwo = colleagues[1];
128 Mail.sendOneOffMail(mail, colleagueTwo, Base64.getDecoder().decode(intergalacticNumber));
129
130 String nl = System.getProperty("line.separator");
131 StringBuilder sb = new StringBuilder();
132 sb.append("You are receiving this email because one of your Expeditee contacts has sent you a one-off secure message." + nl);
133 sb.append("When you log into Expeditee and check your mail it will be there waiting for you." + nl);
134 sb.append("You will need the following key to read this message: " + nl);
135 sb.append(intergalacticNumber);
136 try {
137 // Send email with key to colleague one
138 String colleagueOneEmail = colleagues[2];
139 sendEmail(colleagueOneEmail, sb);
140 // Send email with key to colleague two
141 String colleagueTwoEmail = colleagues[3];
142 sendEmail(colleagueTwoEmail, sb);
143
144 MessageBay.displayMessage("Identity confirmed. Your trusted contacts have been notified via one-off secure Expeditee message. "
145 + "You will recieve an email message with a password share from each once they have completed their part of the process. Enter them below.");
146 } catch (MessagingException e) {
147 e.printStackTrace();
148 }
149 }
150
151 private static String[] getPasswordColleaguesFromUsername(String username) {
152 Path credentialsFilePath = Paths.get(FrameIO.PROFILE_PATH).resolve(username).resolve("pwcolleagues.inf");
153 String fileName = null;
154 if (credentialsFilePath.toFile().exists()) {
155 try (Scanner in = new Scanner(credentialsFilePath)) {
156 fileName = in.nextLine();
157 } catch (IOException e) {
158 MessageBay.errorMessage("Unable to find trusted users contact frame for specified user, are they registered on this computer?");
159 return null;
160 }
161 } else {
162 MessageBay.errorMessage("Unable to find trusted users contact frame for specified user, are they registered on this computer?");
163 return null;
164 }
165
166 int number = Integer.parseInt(fileName.replace(".exp", ""));
167 Frame pwColleagueFrame = FrameIO.LoadFrame(username + number, FrameIO.PROFILE_PATH);
168 Collection<Text> textItems = pwColleagueFrame.getTextItems();
169 textItems.removeIf(text -> !text.getText().startsWith("User_"));
170
171 String[] ret = new String[4];
172 Iterator<Text> it = textItems.iterator();
173 while(it.hasNext()) {
174 String content = it.next().getText().toLowerCase().trim();
175 if (content.contains("user_one:")) {
176 ret[0] = content.replace("user_one:", "").trim();
177 } else if (content.contains("user_two:")) {
178 ret[1] = content.replace("user_two:", "").trim();
179 }
180 }
181
182 // find colleague one email
183 Path credentialsDirectoryPath = UserSettings.PublicAndPrivateResources
184 ? Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username)
185 : Paths.get(FrameIO.PARENT_FOLDER);
186 credentialsDirectoryPath = credentialsDirectoryPath.resolve("contacts").resolve(ret[0] + "-credentials");
187 try (Scanner in = new Scanner(credentialsDirectoryPath.resolve("credentials.inf").toFile())) {
188 int parseInt = Integer.parseInt(in.nextLine().replace(".exp", ""));
189 Frame frame = FrameIO.LoadFrame(ret[0] + "-credentials" + parseInt, credentialsDirectoryPath.toAbsolutePath().getParent().toString() + File.separator);
190 textItems = frame.getTextItems();
191 textItems.removeIf(text -> !text.getText().startsWith("Email:"));
192 ret[2] = textItems.iterator().next().getText().replace("Email:", "").trim();
193 } catch (FileNotFoundException e) {
194 MessageBay.errorMessage("You do not appear to have contact with your nominated password colleague: " + ret[0]);
195 return null;
196 }
197
198
199 // find colleague two email
200 credentialsDirectoryPath = UserSettings.PublicAndPrivateResources
201 ? Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username)
202 : Paths.get(FrameIO.PARENT_FOLDER);
203 credentialsDirectoryPath = credentialsDirectoryPath.resolve("contacts").resolve(ret[1] + "-credentials");
204 try (Scanner in = new Scanner(credentialsDirectoryPath.resolve("credentials.inf").toFile())) {
205 int parseInt = Integer.parseInt(in.nextLine().replace(".exp", ""));
206 Frame frame = FrameIO.LoadFrame(ret[1] + "-credentials" + parseInt, credentialsDirectoryPath.toAbsolutePath().getParent().toString() + File.separator);
207 textItems = frame.getTextItems();
208 textItems.removeIf(text -> !text.getText().startsWith("Email:"));
209 ret[3] = textItems.iterator().next().getText().replace("Email:", "").trim();
210 } catch (FileNotFoundException e) {
211 MessageBay.errorMessage("You do not appear to have contact with your nominated password colleague: " + ret[1]);
212 return null;
213 }
214
215 return ret;
216 }
217
218
219 public static void sendEmail(String email, StringBuilder sb) throws MessagingException, AddressException {
220 // Establish properties for email.
221 Properties properties = System.getProperties();
222 properties.setProperty("mail.transport.protocol", "smtp");
223 properties.setProperty("mail.smtp.host", "smtp.gmail.com");
224 properties.setProperty("mail.smtp.port", "465");
225 properties.setProperty("mail.smtp.starttls.enable", "true");
226 properties.setProperty("mail.smtp.auth", "true");
227 properties.setProperty("mail.smtp.debug", "true");
228 properties.setProperty("mail.smtp.auth", "true");
229 properties.setProperty("mail.smtp.socketFactory.port", "465");
230 properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
231 properties.setProperty("mail.smtp.socketFactory.fallback", "false");
232
233 Session session = Session.getDefaultInstance(properties, new javax.mail.Authenticator() {
234 @Override
235 protected PasswordAuthentication getPasswordAuthentication() {
236 //return new PasswordAuthentication("noreply.expeditee", "intergalacticnumber");
237 return new PasswordAuthentication("noreply.expeditee", "exped!tee");
238 };
239 });
240
241 // construct email message
242 final MimeMessage message = new MimeMessage(session);
243 message.setFrom(new InternetAddress("[email protected]"));
244 message.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
245 message.setSubject("Expeditee Password Recovery");
246 message.setText(sb.toString());
247
248 // send email message
249 Transport.send(message);
250 }
251
252 public static void setPWColleagues(String colleagueOne, String colleagueTwo) {
253 // Get needed text items.
254 Frame pwRecoveryFrame = FrameIO.LoadFrame(UserSettings.UserName.get() + AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME);
255 Collection<Text> textItems = pwRecoveryFrame.getTextItems();
256 textItems.removeIf(t -> !t.getText().toLowerCase().startsWith("user_"));
257
258 // Find colleague one and two text items. Set appropriate values.
259 Iterator<Text> it = textItems.iterator();
260 while (it.hasNext()) {
261 Text text = it.next();
262 if (text.getText().toLowerCase().startsWith("user_one:")) {
263 text.setText("User_one: " + colleagueOne);
264 Colleagues.User_One.set(colleagueOne);
265 } else if (text.getText().toLowerCase().startsWith("user_two:")) {
266 text.setText("User_two: " + colleagueTwo);
267 Colleagues.User_Two.set(colleagueTwo);
268 }
269 }
270 FrameIO.ForceSaveFrame(pwRecoveryFrame);
271
272 // Key to split and distribute
273 String fullKey = KeyList.PersonalKey.get().getData().get(0);
274 byte[] keyBytes = Base64.getDecoder().decode(fullKey);
275
276 // Initialise Shamir
277 int totalShares = 2;
278 int requiredShares = 2;
279 Scheme scheme = new Scheme(new SecureRandom(), totalShares, requiredShares);
280
281 // Create shares
282 Map<Integer, byte[]> shares = scheme.split(keyBytes);
283 String colleagueOneShare = Base64.getEncoder().encodeToString(shares.get(1));
284 String colleagueTwoShare = Base64.getEncoder().encodeToString(shares.get(2));
285
286 // Distribute share zero to colleague one
287 String time = org.expeditee.stats.Formatter.getDateTime();
288 String sender = UserSettings.UserName.get();
289 String topic = "Please help me secure my Expeditee account.";
290 String message = "Run the below action to store a secret key that will help me recover access to my account should I ever loose it.";
291 Map<String, String> options = new HashMap<String, String>();
292 options.put("Store Secret Key for " + sender, "AuthAddSecretKey " + sender + "PersonalKeyShare " + colleagueOneShare);
293 MailEntry mail = new MailEntry(time, sender, colleagueOne, topic, message, options);
294 Mail.sendMail(mail, colleagueOne);
295
296 // Distribute share one to colleague two
297 options = new HashMap<String, String>();
298 options.put("Store Secret Key for " + sender, "AuthAddSecretKey " + sender + "PersonalKeyShare " + colleagueTwoShare);
299 mail = new MailEntry(time, sender, colleagueTwo, topic, message, options);
300 Mail.sendMail(mail, colleagueTwo);
301
302 MessageBay.displayMessage("You PW Colleagues have been set to " + colleagueOne + " and " + colleagueTwo + ". "
303 + "They have been sent a Expeditee mail that they can use to store a share of your secret key.");
304 }
305
306 public static void emailPasswordShare(String colleagueName) {
307 Path credentialsDirectoryPath = Paths.get(FrameIO.CONTACTS_PATH).resolve(colleagueName + "-credentials");
308 String colleagueEmail = null;
309 try (Scanner in = new Scanner(credentialsDirectoryPath.resolve("credentials.inf").toFile())) {
310 int parseInt = Integer.parseInt(in.nextLine().replace(".exp", ""));
311 Frame frame = FrameIO.LoadFrame(colleagueName + "-credentials" + parseInt, credentialsDirectoryPath.toAbsolutePath().getParent().toString() + File.separator);
312 Collection<Text> textItems = frame.getTextItems();
313 textItems.removeIf(text -> !text.getText().startsWith("Email:"));
314 colleagueEmail = textItems.iterator().next().getText().replace("Email:", "").trim();
315 } catch (FileNotFoundException e) {
316 MessageBay.errorMessage("You do not appear to have contact with: " + colleagueName);
317 return;
318 }
319
320 Frame secretsFrame = FrameIO.LoadFrame(UserSettings.UserName.get() + AuthenticatorBrowser.SECRETS_FRAME);
321 Collection<Text> textItems = secretsFrame.getTextItems();
322 textItems.removeIf(text -> !text.getText().toLowerCase().equals(colleagueName + "personalkeyshare"));
323 String key = textItems.iterator().next().getData().get(0);
324
325 String nl = System.getProperty("line.separator");
326 StringBuilder sb = new StringBuilder();
327 sb.append("In responce to your request for assistance regaining access to your Expeditee account, your colleague " + UserSettings.UserName.get() + " has provided you with the following key share:" + nl);
328 sb.append(key + nl);
329
330 try {
331 sendEmail(colleagueEmail, sb);
332 MessageBay.displayMessage("Your share of " + colleagueName + "'s password has been sent to their public email address.");
333 } catch (MessagingException e) {
334 MessageBay.errorMessage("An error occured sending a email to your colleage " + colleagueName + " with the email " + colleagueEmail);
335 }
336 }
337
338 public static void regainAccountAccess(Map<AuthenticationTag, String> userData) {
339 regainAccountAccess(userData, false);
340 }
341
342 private static void regainAccountAccess(Map<AuthenticationTag, String> userData, boolean isAttemptTwo) {
343 // Store shares in map
344 Map<Integer, byte[]> contributingParts = new HashMap<Integer, byte[]>();
345 if (isAttemptTwo) {
346 contributingParts.put(1, Base64.getDecoder().decode(userData.get(AuthenticationTag.PasswordSliceTwo)));
347 contributingParts.put(2, Base64.getDecoder().decode(userData.get(AuthenticationTag.PasswordSliceOne)));
348 } else {
349 contributingParts.put(1, Base64.getDecoder().decode(userData.get(AuthenticationTag.PasswordSliceOne)));
350 contributingParts.put(2, Base64.getDecoder().decode(userData.get(AuthenticationTag.PasswordSliceTwo)));
351 }
352
353 // initialise shamir
354 int totalShares = 2;
355 int requiredShares = 2;
356 Scheme scheme = new Scheme(new SecureRandom(), totalShares, requiredShares);
357
358 // perform joining
359 byte[] join = scheme.join(contributingParts);
360
361 try {
362 String username = userData.get(AuthenticationTag.Username);
363 SecretKey key = new SecretKeySpec(join, SymmetricAlgorithm);
364 String filePathCheck = Paths.get(FrameIO.PROFILE_PATH).resolve(username).resolve("1.exp").toAbsolutePath()
365 .toString();
366 if (EncryptedExpReader.isAccessibleExpediteeFile(filePathCheck, key)) {
367 AuthenticatorBrowser.getInstance().putKey(username, userData.get(AuthenticationTag.NewPassword), key);
368 MessageBay.displayMessage("Your new password has been set.");
369 } else {
370 if (isAttemptTwo) {
371 MessageBay.displayMessage("Invalid information given for changing password for " + username);
372 } else {
373 regainAccountAccess(userData, true);
374 }
375 }
376 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | ClassNotFoundException
377 | IOException | SQLException e) {
378 e.printStackTrace();
379 }
380 }
381}
Note: See TracBrowser for help on using the repository browser.