-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSecureChatServer.java
More file actions
319 lines (287 loc) · 8.97 KB
/
SecureChatServer.java
File metadata and controls
319 lines (287 loc) · 8.97 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
/* CS/COE 1501
Main Program for Chat Server to be used with Assignment 3
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 sheet.
*/
import java.util.*;
import java.io.*;
import java.net.*;
import java.math.*;
public class SecureChatServer {
public static final int PORT = 8765;
private int MaxUsers;
private Socket [] users; // Need array of Sockets and Threads,
private UserThread [] threads; // one of each per client -- using an
private int numUsers; // ArrayList for these would make less work
// for the programmer
private String StringE, StringD, StringN;
private BigInteger E, D, N;
Random R;
public SecureChatServer(int MaxU) throws IOException
{
MaxUsers = MaxU;
users = new Socket[MaxUsers];
threads = new UserThread[MaxUsers]; // Set things up and start
numUsers = 0; // Server running
Scanner inScan = new Scanner(new File("keys.txt"));
StringE = inScan.nextLine();
StringD = inScan.nextLine();
StringN = inScan.nextLine();
E = new BigInteger(StringE);
D = new BigInteger(StringD);
N = new BigInteger(StringN);
R = new Random();
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 (Exception e)
{
System.out.println("Problem with server");
}
}
public synchronized void SendMsg(String msg)
// 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.
{
for (int i = 0; i < numUsers; i++)
{
System.out.println("Sending to user " + i);
ObjectOutputStream currWriter = threads[i].getWriter();
SymCipher currCipher = threads[i].getCipher();
byte [] sendmsg = currCipher.encode(msg);
try
{
currWriter.writeObject(sendmsg);
}
catch (IOException e)
{
System.out.println("Output error " + e);
}
}
}
public synchronized void removeClient(int id, String name)
{
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 (IOException e)
{
System.out.println("Already closed");
}
users[id] = null;
threads[id] = null;
for (int i = 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
}
private void runServer() throws IOException
{
ServerSocket s = new ServerSocket(PORT);
System.out.println("Started: " + s);
Socket newSocket = null;
try
{
while (true)
{
if (numUsers < MaxUsers)
{
try
{
newSocket = s.accept(); // get next client
newSocket.setSoTimeout(20000);
ObjectOutputStream tempWriter =
new ObjectOutputStream(newSocket.getOutputStream());
tempWriter.flush();
ObjectInputStream tempReader =
new ObjectInputStream(newSocket.getInputStream());
System.out.println("Sending E");
tempWriter.writeObject(E); tempWriter.flush();
System.out.println("Sending N");
tempWriter.writeObject(N); tempWriter.flush();
double test = R.nextDouble();
String encType = null;
// Randomly determine which cipher will be used and send the
// appropriate string to the client.
if (test > 0.5)
encType = new String("Sub");
else
encType = new String("Add");
tempWriter.writeObject(encType);
tempWriter.flush();
//String addKey = tempReader.readLine();
BigInteger bigKey = (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);
SymCipher cipher = 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 = new byte[128];
System.arraycopy(byteKey, 1, temp, 0, 128);
byteKey = temp;
}
else if (encType.equals("Sub") && byteKey.length > 256)
{
byte [] temp = new byte[256];
System.arraycopy(byteKey, 1, temp, 0, 256);
byteKey = temp;
}
if (encType.equals("Add"))
{
System.out.println("Chosen Cipher is Add128");
cipher = new Add128(byteKey);
}
else
{
System.out.println("Chosen Cipher is Substitute");
cipher = new Substitute(byteKey);
}
System.out.println("Key: ");
for (int i = 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();
String newName = cipher.decode(newBytes);
newSocket.setSoTimeout(0);
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] = new UserThread(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.SocketTimeoutException e1)
{
System.out.println("Client timed out during login " + e1);
newSocket.close();
}
catch (Exception e2)
{
System.out.println("Problem with connection " + e2);
e2.printStackTrace();
newSocket.close();
}
} // if
else
{
Thread.sleep(1000);
}
} // while
} // try
catch (Exception e)
{
System.out.println("Something went wrong " + e);
}
finally
{
System.out.println("Server shutting down");
}
} // 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.
private class UserThread extends Thread
{
private Socket mySocket;
private ObjectInputStream myReader;
private ObjectOutputStream myWriter;
private SymCipher myCipher;
private int myId;
private String myName;
private UserThread(Socket newSocket, int id, String newName,
ObjectInputStream newReader,
ObjectOutputStream newWriter, SymCipher c) throws IOException
{
mySocket = newSocket;
myId = id;
myName = newName;
myReader = newReader;
myWriter = newWriter;
myCipher = c;
}
public ObjectInputStream getReader()
{
return myReader;
}
public ObjectOutputStream getWriter()
{
return myWriter;
}
public SymCipher getCipher()
{
return myCipher;
}
public synchronized void setId(int newId)
{
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.
public void run()
{
boolean ok = true;
while (ok)
{
String newMsg = 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 (Exception e)
{
System.out.println("Client closing!!" + e);
ok = false;
}
}
removeClient(myId, myName);
}
}
public static void main(String [] args) throws IOException
{
SecureChatServer Secure = new SecureChatServer(30);
}
}