- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSecureChatServer.java
More file actions
Latest commit
320 lines (293 loc) · 9.9 KB
/
Copy pathSecureChatServer.java
File metadata and controls
320 lines (293 loc) · 9.9 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
importjava.util.*;
importjava.io.*;
importjava.net.*;
importjava.math.*;
/** Main Program for Chat Server to be used with Assignment 4.
* You must use the program as written without any changes. Note that this
* program requires the following:
* keys.txt where the RSA keys are stored
* SymCipher.java, the interface for the symmetric ciphers
* Substitute.java, the substitution cipher
* Add128.java, the addition cipher
* The first two files are provided for you, but you must write the other two, as
* well as the client program, yourself.
* See more details on these requirements in the assignment description.
* Adapted from Dr. John Ramirez's CS 1501 Assignment 4
*/
publicclassSecureChatServer {
publicstaticfinalintPORT = 8765;
privateintMaxUsers;
privateSocket [] users; // Need array of Sockets and Threads,
privateUserThread [] threads; // one of each per client -- using an
privateintnumUsers; // ArrayList for these would make less work
// for the programmer
privateStringStringE, StringD, StringN;
privateBigIntegerE, D, N;
RandomR;
publicSecureChatServer(intMaxU) throwsIOException
{
MaxUsers = MaxU;
users = newSocket[MaxUsers];
threads = newUserThread[MaxUsers]; // Set things up and start
numUsers = 0; // Server running
ScannerinScan = newScanner(newFile("keys.txt"));
StringE = inScan.nextLine();
StringD = inScan.nextLine();
StringN = inScan.nextLine();
E = newBigInteger(StringE);
D = newBigInteger(StringD);
N = newBigInteger(StringN);
R = newRandom();
System.out.println("My E: " + E);
System.out.println("My D: " + D);
System.out.println("My N: " + N);
try
{
System.out.println("Server starting up");
runServer();
}
catch (Exceptione)
{
System.out.println("Problem starting server");
}
}
/** Send current message to all clients (even sender). This
* must be synchronized so that chatters do not "interrupt"
* each other. For each chatter, get the cipher, then use
* it to encode the message, and send the result.
* @param msg the message to be sent
*/
publicsynchronizedvoidSendMsg(Stringmsg)
{
for (inti = 0; i < numUsers; i++)
{
System.out.println("Sending to user " + i);
ObjectOutputStreamcurrWriter = threads[i].getWriter();
SymCiphercurrCipher = threads[i].getCipher();
byte [] sendmsg = currCipher.encode(msg);
try
{
currWriter.writeObject(sendmsg);
}
catch (IOExceptione)
{
System.out.println("Output error " + e);
}
}
}
publicsynchronizedvoidremoveClient(intid, Stringname)
{
try// Remove a client from the server. This
{ // also must be synchronized, since we
users[id].close(); // could have an inconsistent state if this
} // is interrupted in the middle
catch (IOExceptione)
{
System.out.println("Already closed");
}
users[id] = null;
threads[id] = null;
for (inti = id; i < numUsers-1; i++) // Shift remaining clients in
{ // array up one position
users[i] = users[i+1];
threads[i] = threads[i+1];
threads[i].setId(i);
}
numUsers--;
SendMsg(name + " has logged off"); // Announce departure
}
privatevoidrunServer() throwsIOException
{
ServerSockets = newServerSocket(PORT);
System.out.println("Started: " + s);
SocketnewSocket = null;
try
{
while (true)
{
if (numUsers < MaxUsers)
{
try
{
newSocket = s.accept(); // get next client
newSocket.setSoTimeout(20000); // timeout if client does not respond
// within 20 seconds. This keeps
// server from getting hung up by an unresponsive client
// during the handshaking phase
ObjectOutputStreamtempWriter = newObjectOutputStream(newSocket.getOutputStream());
tempWriter.flush();
ObjectInputStreamtempReader = newObjectInputStream(newSocket.getInputStream());
System.out.println("Sending E");
tempWriter.writeObject(E); tempWriter.flush();
System.out.println("Sending N");
tempWriter.writeObject(N); tempWriter.flush();
doubletest = R.nextDouble();
StringencType = null;
// Randomly determine which cipher will be used and send the
// appropriate string to the client.
if (test > 0.5)
encType = newString("Sub");
else
encType = newString("Add");
tempWriter.writeObject(encType);
tempWriter.flush();
//String addKey = tempReader.readLine();
BigIntegerbigKey = (BigInteger) tempReader.readObject();
System.out.println("Encrypted key: " + bigKey);
bigKey = bigKey.modPow(D, N);
System.out.println("Decrypted key: " + bigKey);
byte [] byteKey = bigKey.toByteArray();
System.out.println("Byte array length: " + byteKey.length);
SymCiphercipher = null;
// Since the key is sent from the client as a positive BigInteger,
// when converted back the result could have an extra byte. This
// code tests for that byte and removes it if necessary.
if (encType.equals("Add") && byteKey.length > 128)
{
byte [] temp = newbyte[128];
System.arraycopy(byteKey, 1, temp, 0, 128);
byteKey = temp;
}
elseif (encType.equals("Sub") && byteKey.length > 256)
{
byte [] temp = newbyte[256];
System.arraycopy(byteKey, 1, temp, 0, 256);
byteKey = temp;
}
if (encType.equals("Add"))
{
System.out.println("Chosen Cipher is Add128");
cipher = newAdd128(byteKey);
}
else
{
System.out.println("Chosen Cipher is Substitute");
cipher = newSubstitute(byteKey);
}
System.out.println("Key: ");
for (inti = 0; i < byteKey.length; i++)
System.out.print(byteKey[i] + " ");
System.out.println();
// Get the Client's name (note that this is encoded). Then
// pass it on the all of the current chatters before adding
// the new chatter to the list.
byte [] newBytes = (byte []) tempReader.readObject();
StringnewName = cipher.decode(newBytes);
newSocket.setSoTimeout(0); // A 0 value for the timeout is actually
// no timeout -- once the client is correctly connected there
// can be an arbitrary delay between messages.
synchronized (this)
{
users[numUsers] = newSocket;
SendMsg(newName + " has just joined the chat group");
// Above the server gets the new chatter's and announces
// to the rest of the group
threads[numUsers] = newUserThread(newSocket, numUsers, newName, tempReader, tempWriter, cipher);
threads[numUsers].start();
// Above a new thread is created and started for the
// new user
System.out.println("Connection " + numUsers + users[numUsers]);
numUsers++;
}
}
catch (java.net.SocketTimeoutExceptione1)
{
System.out.println("Client timed out during login " + e1);
newSocket.close();
}
catch (Exceptione2)
{
System.out.println("Problem with connection " + e2);
e2.printStackTrace();
newSocket.close();
}
} // if
else
{
Thread.sleep(1000);
}
} // while
} // try
catch (Exceptione)
{
System.out.println("Something went wrong " + e);
}
finally
{
System.out.println("Server shutting down");
// s.close(); //TODO if seeing errors remove this, i added it
}
} // end of runServer method
// Below is the class used by the server to keep track of the clients. Each
// client is a new UserThread object, with the data shown. Note that the cipher
// being used is encapsulated within the UserThread, so that each client can have
// a different encryption scheme, as long as it satisfies the SymCipher interface.
privateclassUserThreadextendsThread
{
privateSocketmySocket;
privateObjectInputStreammyReader;
privateObjectOutputStreammyWriter;
privateSymCiphermyCipher;
privateintmyId;
privateStringmyName;
privateUserThread(SocketnewSocket, intid, StringnewName,
ObjectInputStreamnewReader,
ObjectOutputStreamnewWriter, SymCipherc) throwsIOException
{
mySocket = newSocket;
myId = id;
myName = newName;
myReader = newReader;
myWriter = newWriter;
myCipher = c;
}
publicObjectInputStreamgetReader()
{
returnmyReader;
}
publicObjectOutputStreamgetWriter()
{
returnmyWriter;
}
publicSymCiphergetCipher()
{
returnmyCipher;
}
publicsynchronizedvoidsetId(intnewId)
{
myId = newId; // id may change when a previous chatter quits
}
// While running, each UserThread will get the next message from its
// corresponding client, and then send it to the other clients (through
// the Server). A departing client is detected by an IOException in
// trying to read, which causes the removeClient method to be executed.
publicvoidrun()
{
booleanok = true;
while (ok)
{
StringnewMsg = null;
byte[] newBytes = null;
try {
newBytes = (byte []) myReader.readObject();
newMsg = myCipher.decode(newBytes);
if (newBytes == null || newMsg.equals("CLIENT CLOSING"))
ok = false;
else
SecureChatServer.this.SendMsg(newMsg);
}
catch (Exceptione)
{
System.out.println("Client closing!!" + e);
ok = false;
}
}
removeClient(myId, myName);
}
}
@SuppressWarnings("unused")
publicstaticvoidmain(String [] args) throwsIOException
{
SecureChatServerSecure = newSecureChatServer(30);
}
}