-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathServerThread.java
More file actions
332 lines (274 loc) · 7.31 KB
/
ServerThread.java
File metadata and controls
332 lines (274 loc) · 7.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
import java.net.*;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.math.*;
import java.util.*;
/**
* This class represents a thread to deal with clients who connect to Server.
* Put what you want the thread to do in it's run() method.
*
* @author Mike Jacobson
* @version 1.0, October 23, 2013
*/
public class ServerThread extends Thread
{
private Socket sock; //The socket it communicates with the client on.
private Server parent; //Reference to Server object for message passing.
private int idnum; //The client's id number.
private DataOutputStream out;
private DataInputStream in;
private SecretKeySpec key; // AES encryption key
private RSATool RSA; // class for RSA decryption
/**
* Utility for printing protocol messages
* @param s protocol message to be printed
*/
private void debug(String s) {
if(parent.getDebug())
System.out.println("Debug Server: " + s);
}
/**
* Constructor, does the usual stuff.
* @param s Communication Socket.
* @param p Reference to parent thread.
* @param id ID Number.
*/
public ServerThread (Socket s, Server p, int id, RSATool RSA_in)
{
parent = p;
sock = s;
idnum = id;
in = null;
out = null;
RSA = RSA_in;
}
/**
* Getter for id number.
* @return ID Number
*/
public int getID ()
{
return idnum;
}
/**
* Getter for the socket, this way the parent thread can
* access the socket and close it, causing the thread to
* stop blocking on IO operations and see that the server's
* shutdown flag is true and terminate.
* @return The Socket.
*/
public Socket getSocket ()
{
return sock;
}
/**
* Decrypts an AES-128 key send by a client using the server's RSA private key.
*/
public void getKey() {
debug("Starting key transport");
// Send public key to client
debug("Sending n to client");
BigInteger n = RSA.get_n();
byte[] encodedn = n.toByteArray();
try {
CryptoUtilities.send(encodedn,out);
}
catch (IOException e) {
System.out.println("Error sending n to client.");
close();
return;
}
debug("Sent n = " + n);
debug("Sending e to client");
BigInteger e = RSA.get_e();
byte[] encodede = e.toByteArray();
try {
CryptoUtilities.send(encodede,out);
}
catch (IOException ex) {
System.out.println("Error sending e to client.");
close();
return;
}
debug("Sent e = " + e);
// get encrypted AES key
debug("Receiving client's encrypted AES key");
byte[] encryptedKey;
try {
encryptedKey = CryptoUtilities.receive(in);
}
catch (IOException ex) {
System.out.println("Error receiving encrypted key from client.");
close();
return;
}
debug("Received C = " + CryptoUtilities.toHexString(encryptedKey));
// decrypt key
debug("Decrypting the key");
byte[] encodedKey = null;
try {
encodedKey = RSA.decrypt(encryptedKey);
}
catch (IllegalArgumentException ex) {
System.out.println(ex);
}
catch (IllegalStateException ex) {
System.out.println(ex);
}
debug("M = " + CryptoUtilities.toHexString(encodedKey));
// compute shared AES key
debug("Computing the AES key");
byte[] raw = new byte[CryptoUtilities.AES_KEY_LEN];
System.arraycopy(encodedKey, 0, raw, 0, CryptoUtilities.AES_KEY_LEN);
debug("AES key = " + CryptoUtilities.toHexString(raw));
// initialize the AES key
key = new SecretKeySpec(raw, "AES");
}
/**
* Encrypted file transfer
* @return true if file transfer was successful
*/
public boolean receiveFile() {
debug("Starting File Transfer");
// get the output file name
String outfilename;
try {
debug("Receiving output file name");
outfilename = new String(CryptoUtilities.receiveAndDecrypt(key,in));
debug("Got file name = " + outfilename);
}
catch (IOException e) {
System.out.println("Error receiving the output file name");
close();
return false;
}
System.out.println("Output file: " + outfilename);
// get the file size
int size;
try {
debug("Receiving file size");
size = Integer.parseInt(new String(CryptoUtilities.receiveAndDecrypt(key,in)));
debug("Got file size = " + size);
}
catch (IOException e) {
System.out.println("Error sending the file length");
close();
return false;
}
System.out.println("File size = " + size);
// get the encrypted, integrity-protected file
byte[] hashed_plaintext;
try {
debug("Receiving and decrypting file with MAC appended");
hashed_plaintext = CryptoUtilities.receiveAndDecrypt(key,in);
}
catch (IOException e) {
System.out.println("Error receiving encrypted file");
close();
return false;
}
// check validity of MAC. Write to the file if valid.
debug("Checking MAC");
boolean fileOK = false;
if (CryptoUtilities.verify_hash(hashed_plaintext,key)) {
debug("Message digest OK. Writing file.");
System.out.println("Message digest OK. Writing file");
// extract plaintext and output to file
byte[] plaintext = CryptoUtilities.extract_message(hashed_plaintext);
// writing file
FileOutputStream outfile = null;
try {
outfile = new FileOutputStream(outfilename);
outfile.write(plaintext,0,plaintext.length);
outfile.close();
}
catch (IOException e) {
System.out.println("Error writing decrypted file.");
close();
return false;
}
finally {
try {
outfile.close();
}
catch (IOException e) {
System.out.println("Error closing output file.");
return false;
}
}
fileOK = true;
// send acknowledgement to client
try {
debug("Sending \"passed\" acknowledgement.");
CryptoUtilities.encryptAndSend("Passed".getBytes(),key,out);
}
catch (IOException e) {
System.out.println("Error sending passed acknowledgement.");
close();
return true;
}
System.out.println("File written successfully.");
}
else {
System.out.println("Integrity check failed. File not written.");
try {
debug("Sending \"Failed\" acknowledgement.");
CryptoUtilities.encryptAndSend("Failed".getBytes(),key,out);
}
catch (IOException e) {
System.out.println("Error sending failed acknowledgement.");
close();
return false;
}
}
close();
return fileOK;
}
/**
* Shuts down the socket connection
*/
public void close() {
// shutdown socket and input reader
try {
sock.close();
if (in != null)
in.close();
if (out != null)
out.close();
}
catch (IOException e) {
return;
}
}
/**
* This is what the thread does as it executes. Gets the encryption key,
* receives a file from the client, and shuts down.
*/
public void run ()
{
// open input and output streams for file transfer
try {
in = new DataInputStream(sock.getInputStream());
out = new DataOutputStream(sock.getOutputStream());
}
catch (UnknownHostException e) {
System.out.println ("Unknown host error.");
close();
return;
}
catch (IOException e) {
System.out.println ("Could not create input and output streams.");
close();
return;
}
// get the encryption key
getKey();
// do file transfer
receiveFile();
// shut down the client and kill the server
close();
parent.killall();
}
}