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

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

Deaddrops folder for each relationship is now named a combination of each member of the relationship so that renaming by user is not needed.

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