source: trunk/src/org/expeditee/auth/Mail.java@ 1320

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

You can now check for new mail while logged in (when you open the mail bay)

File size: 10.7 KB
Line 
1package org.expeditee.auth;
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.InvalidKeyException;
9import java.security.KeyStoreException;
10import java.security.NoSuchAlgorithmException;
11import java.security.PrivateKey;
12import java.security.PublicKey;
13import java.security.cert.CertificateException;
14import java.security.spec.InvalidKeySpecException;
15import java.sql.Connection;
16import java.sql.DriverManager;
17import java.sql.PreparedStatement;
18import java.sql.SQLException;
19import java.sql.Statement;
20import java.text.ParseException;
21import java.text.SimpleDateFormat;
22import java.util.ArrayList;
23import java.util.Arrays;
24import java.util.Base64;
25import java.util.Date;
26import java.util.HashMap;
27import java.util.List;
28import java.util.Map;
29import java.util.Scanner;
30
31import javax.crypto.BadPaddingException;
32import javax.crypto.Cipher;
33import javax.crypto.IllegalBlockSizeException;
34import javax.crypto.NoSuchPaddingException;
35
36import org.expeditee.auth.gui.MailBay;
37import org.expeditee.gui.FrameIO;
38import org.expeditee.settings.UserSettings;
39import org.ngikm.cryptography.CryptographyConstants;
40
41public class Mail implements CryptographyConstants {
42
43 private static List<MailEntry> messages = new ArrayList<MailEntry>();
44
45 /**
46 * Add a piece of mail, used during initialisation.
47 */
48 public static void addEntry(MailEntry mail) {
49 messages.add(mail);
50 }
51
52 public static void clear() {
53 messages.clear();
54 }
55
56 public static void sendMail(MailEntry mail, String colleagueName) {
57 // Ensure dead drop area is set up.
58 String me = UserSettings.UserName.get().toLowerCase();
59 String them = colleagueName.toLowerCase();
60 Path databaseFileDirPath = Paths.get(FrameIO.DEAD_DROPS_PATH).resolve(me + "+" + them);
61 if (!databaseFileDirPath.toFile().exists()) {
62 databaseFileDirPath = Paths.get(FrameIO.DEAD_DROPS_PATH).resolve(them + "+" + me);
63 }
64 Path databaseFilePath = databaseFileDirPath.resolve(colleagueName + ".db");
65 File databaseFile = databaseFilePath.toFile();
66 if (!databaseFile.exists()) {
67 databaseFileDirPath.toFile().mkdirs();
68 String sql =
69 "CREATE TABLE EXPMAIL (" +
70 "TIME TEXT NOT NULL, " +
71 "SND TEXT NOT NULL, " +
72 "REC TEXT NOT NULL, " +
73 "MSG TEXT NOT NULL, " +
74 "MSG2 TEXT NOT NULL, " +
75 "OPTS ARRAY NOT NULL, " +
76 "OPTSVAL ARRAY NOT NULL)";
77 try {
78 Connection c = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getAbsolutePath());
79 Statement createTable = c.createStatement();
80 createTable.executeUpdate(sql);
81 createTable.close();
82 c.close();
83 } catch (SQLException e) {
84 System.err.println("Error while creating database file.");
85 e.printStackTrace();
86 }
87 }
88
89 // Obtain public key
90 PublicKey publicKey = null;
91 try {
92 publicKey = AuthenticatorBrowser.getInstance().getPublicKey(colleagueName);
93 } catch (InvalidKeySpecException | NoSuchAlgorithmException | KeyStoreException | CertificateException
94 | ClassNotFoundException | IOException | SQLException e) {
95 System.err.println("Error while sending message. Unable to obtain public key for colleague " +
96 colleagueName + ". Exception message: " + e.getMessage());
97 return;
98 }
99
100 // Check we got public key
101 if (publicKey == null) {
102 System.err.println("Error while sending message. Unable to obtain public key for colleague. Have you exchanged contact details?");
103 return;
104 }
105
106 // Send message
107 sendMail(mail, publicKey, databaseFilePath);
108 }
109
110 private static void sendMail(MailEntry mail, PublicKey key, Path databaseFile) {
111 try {
112 Cipher cipher = Cipher.getInstance(AsymmetricAlgorithm + AsymmetricAlgorithmParameters);
113
114 // encrypt the necessary parts of the message
115 //cipher.init(Cipher.ENCRYPT_MODE, key);
116 //String time = Base64.getEncoder().encodeToString(cipher.doFinal(mail.timestamp.getBytes()));
117 cipher.init(Cipher.ENCRYPT_MODE, key);
118 String sender = Base64.getEncoder().encodeToString(cipher.doFinal(mail.sender.getBytes()));
119 cipher.init(Cipher.ENCRYPT_MODE, key);
120 String rec = Base64.getEncoder().encodeToString(cipher.doFinal(mail.receiver.getBytes()));
121 cipher.init(Cipher.ENCRYPT_MODE, key);
122 String message = Base64.getEncoder().encodeToString(cipher.doFinal(mail.message.getBytes()));
123 cipher.init(Cipher.ENCRYPT_MODE, key);
124 String message2 = Base64.getEncoder().encodeToString(cipher.doFinal(mail.message2.getBytes()));
125
126 Map<String, String> options = new HashMap<String, String>();
127 for (String label: mail.options.keySet()) {
128 cipher.init(Cipher.ENCRYPT_MODE, key);
129 String labelEncrypted = Base64.getEncoder().encodeToString(cipher.doFinal(label.getBytes()));
130 cipher.init(Cipher.ENCRYPT_MODE, key);
131 String actionNameEncrypted = Base64.getEncoder().encodeToString(cipher.doFinal(mail.options.get(label).getBytes()));
132 options.put(labelEncrypted, actionNameEncrypted);
133 }
134
135 // write to mail database
136 Connection c = DriverManager.getConnection("jdbc:sqlite:" + databaseFile);
137 String sql = "INSERT INTO EXPMAIL (TIME,SND,REC,MSG,MSG2,OPTS,OPTSVAL) VALUES (?, ?, ?, ?, ?, ?, ?);";
138 PreparedStatement statement = c.prepareStatement(sql);
139 statement.setString(1, mail.timestamp);
140 statement.setString(2, sender);
141 statement.setString(3, rec);
142 statement.setString(4, message);
143 statement.setString(5, message2);
144 String opts = Arrays.toString(options.keySet().toArray());
145 statement.setString(6, opts);
146 String optsval = Arrays.toString(options.values().toArray());
147 statement.setString(7, optsval);
148 statement.execute();
149 statement.close();
150 c.close();
151 } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | SQLException e) {
152 e.printStackTrace();
153 }
154 }
155
156 /**
157 * Gets the mail messages that the specified user is able to read.
158 * The caller supplies their username and private key.
159 * If the private key can decrypt a message, then it was encrypted using the users public key, and is therefore for them.
160 */
161 public static List<MailEntry> getEntries(String name, PrivateKey key) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
162 List<MailEntry> filtered = new ArrayList<MailEntry>();
163
164 for (MailEntry mail: messages) {
165 // confirm this is a message for the requester of entries
166 String receiver = mail.receiver;
167 byte[] receiverBytes = Base64.getDecoder().decode(receiver);
168 String receiverDecrypted = null;
169 Cipher c = Cipher.getInstance(AsymmetricAlgorithm + AsymmetricAlgorithmParameters);
170 try {
171 c.init(Cipher.DECRYPT_MODE, key);
172 receiverDecrypted = new String(c.doFinal(receiverBytes));
173 } catch (InvalidKeyException | BadPaddingException e) {
174 // this is not a message for 'us'
175 continue;
176 }
177
178 // add an unencrypted version of the message to the return list
179 if (receiverDecrypted.compareToIgnoreCase(name) == 0) {
180 c.init(Cipher.DECRYPT_MODE, key);
181 String sender = new String(c.doFinal(Base64.getDecoder().decode(mail.sender)));
182 c.init(Cipher.DECRYPT_MODE, key);
183 String message = new String(c.doFinal(Base64.getDecoder().decode(mail.message)));
184 c.init(Cipher.DECRYPT_MODE, key);
185 String message2 = new String(c.doFinal(Base64.getDecoder().decode(mail.message2)));
186
187 Map<String, String> options = new HashMap<String, String>(); //mail.options;
188 for (String label: mail.options.keySet()) {
189 c.init(Cipher.DECRYPT_MODE, key);
190 String labelDecrypted = new String(c.doFinal(Base64.getDecoder().decode(label)));
191 c.init(Cipher.DECRYPT_MODE, key);
192 String actionNameDecrypted = new String(c.doFinal(Base64.getDecoder().decode(mail.options.get(label))));
193 options.put(labelDecrypted, actionNameDecrypted);
194 }
195
196 Path lastAccessedFile = Paths.get(FrameIO.DEAD_DROPS_PATH).resolve(UserSettings.UserName.get() + "+" + sender).resolve(name + ".last-accessed");
197 if (!lastAccessedFile.toFile().exists()) {
198 lastAccessedFile = Paths.get(FrameIO.DEAD_DROPS_PATH).resolve(sender + "+" + UserSettings.UserName.get()).resolve(name + ".last-accessed");
199 }
200 SimpleDateFormat format = new SimpleDateFormat("ddMMMyyyy[HH:mm]");
201 MailEntry mailEntry = new MailEntry(mail.timestamp, sender, receiverDecrypted, message, message2, options);
202 try (Scanner in = new Scanner(lastAccessedFile.toFile())) {
203 Date lastAccessedTimestamp = format.parse(in.nextLine());
204 Date mailTimestamp = format.parse(mail.timestamp);
205 if (mailTimestamp.after(lastAccessedTimestamp)) {
206 filtered.add(mailEntry);
207 }
208 } catch (FileNotFoundException e) {
209 // It may not have been created yet, then err on the safe side and add it in.
210 filtered.add(mailEntry);
211 } catch (ParseException e) {
212 // If we fail to parse, then err on the safe side and add it in.
213 filtered.add(mailEntry);
214 }
215
216 }
217 }
218
219 return filtered;
220 }
221
222 /**
223 * Describes a piece of mail, either encrypted or decrypted.
224 */
225 public static class MailEntry {
226 public String timestamp;
227 public String sender;
228 public String receiver;
229 public String message;
230 public String message2;
231 public Map<String, String> options;
232
233 public MailEntry(String timestamp, String sender, String rec, String message, String message2, Map<String, String> options) {
234 this.timestamp = timestamp;
235 this.sender = sender;
236 this.receiver = rec;
237 this.message = message;
238 this.message2 = message2;
239 this.options = options;
240 }
241 }
242
243 public static void checkMail(PrivateKey key) throws NoSuchAlgorithmException, NoSuchPaddingException,
244 IllegalBlockSizeException, BadPaddingException, InvalidKeyException, KeyStoreException,
245 FileNotFoundException, CertificateException, IOException, ClassNotFoundException, SQLException, ParseException {
246 MailBay.clear();
247 AuthenticatorBrowser.getInstance().loadMailDatabase();
248 List<MailEntry> mailForLoggingInUser = Mail.getEntries(UserSettings.UserName.get(), key);
249 for (MailEntry mail: mailForLoggingInUser) {
250 MailBay.addMessage(mail.timestamp, mail.message, mail.message2, mail.options);
251 }
252
253 // Update last read files.
254 Path deadDropPath = Paths.get(FrameIO.DEAD_DROPS_PATH);
255 for (File connectionDir: deadDropPath.toFile().listFiles()) {
256 if (connectionDir.isDirectory()) {
257 Path deaddropforcontactPath = Paths.get(connectionDir.getAbsolutePath());
258 AuthenticatorBrowser.getInstance().updateLastReadMailTime(deaddropforcontactPath);
259 }
260 }
261 }
262}
Note: See TracBrowser for help on using the repository browser.