-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSecurity.java
More file actions
453 lines (382 loc) · 13.9 KB
/
Security.java
File metadata and controls
453 lines (382 loc) · 13.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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* Handles security related tasks on behalf of Messenger, interacts with the javax.crypto and java.security libraries
*/
class Security{
/**
* Shared key for encryption
*/
private Cipher enCipher;
/**
* Shared key for decryption
*/
private Cipher deCipher;
/**
* Object for access in multiple methods
*/
private KeyAgreement keyAgree;
/**
* Instance key for creating ciphers
*/
private SecretKeySpec aesKey;
/**
* Messenger that we are communicating with
*/
private CommunicationInterface other;
/*
* Our Messenger parent
*/
private CommunicationInterface self;
/*
* Id of our Messenger parent
*/
private String id;
/*
* Private key (not diffie-hellman)
*/
private PrivateKey priv;
/*
* Public key (not diffie-hellman)
*/
PublicKey myPub;
/*
* Stores the 3 security flags in the following order: confidentiality, integrity, authentication
*/
private static Boolean[] flags;
/*
* Stores the boolean of whether the user has authenticated successfully as the id of our messenger parent.
*/
private Boolean authenticated = false;
/**
* Initialize Security
* @param parent object of caller
* @param parentID id of caller
*/
public Security(CommunicationInterface parent, String parentID) throws Exception{
self = parent; //set instance variables
id = parentID;
flags = new Boolean[]{false,false, false};
try{
priv = getPrivate(); //get from file
myPub = getPublic(id); //get from file
}catch(Exception e){
System.out.println("public/private key read error, maybe user does not exist");
}
}
/**
* First step in diffie-hellman protocol, generate public key and send to other party
* @param obj party to send to
*/
public void createSharedSecret(CommunicationInterface obj) throws Exception{
other = obj; //set instance var for use by other functions
KeyPairGenerator kPairGen = KeyPairGenerator.getInstance("DH");
kPairGen.initialize(2048);
KeyPair kPair = kPairGen.generateKeyPair(); //generate diffie-hellman public/private pair
keyAgree = KeyAgreement.getInstance("DH"); //init KeyAgreement instance for use in generating secret
keyAgree.init(kPair.getPrivate());
other.createPub(kPair.getPublic().getEncoded(), self); //send encoded public key to other party
}
/**
* Second step in diffie-hellman protocol, take a public key and generate our own, send to the other party to create a shared secret
* @param otherPubEnc other party's public key
* @param other object of other party
*/
public void createPub(byte[] otherPubEnc, CommunicationInterface other) throws Exception{
KeyFactory keyFac = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(otherPubEnc); //create a spec to determine other public key
PublicKey otherPub = keyFac.generatePublic(x509KeySpec); //get other public key
DHParameterSpec dhParam = ((DHPublicKey)otherPub).getParams(); //create spec to create a similar key
KeyPairGenerator kPairGen = KeyPairGenerator.getInstance("DH");
kPairGen.initialize(dhParam);
KeyPair kPair = kPairGen.generateKeyPair(); //generate keypair based on spec
KeyAgreement keyAgree = KeyAgreement.getInstance("DH"); //does not need to be externally defined as this step is self-contained
keyAgree.init(kPair.getPrivate());
keyAgree.doPhase(otherPub, true);
byte[] sharedSecret = keyAgree.generateSecret(); //create diffie-hellman secret
aesKey = new SecretKeySpec(sharedSecret, 0, 16, "AES");
enCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
enCipher.init(Cipher.ENCRYPT_MODE, aesKey); //create cipher for encoding
byte[] params = enCipher.getParameters().getEncoded();
other.share(kPair.getPublic().getEncoded(), params); //send required information to other party
}
/**
* Third step in diffie-hellman protocol, take a public key and create a shared secret, take cipher params and use secret to create a cipher
* @param otherPubEnc other party's public key
* @param otherParams other party's cypher paremeters
*/
public void share(byte[] otherPubEnc, byte[] otherParams) throws Exception{
KeyFactory keyFac = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(otherPubEnc); //create a spec to determine other public key
PublicKey otherPub = keyFac.generatePublic(x509KeySpec); //get other public key
keyAgree.doPhase(otherPub, true);
byte[] sharedSecret = keyAgree.generateSecret(); //create diffie-hellman secret
aesKey = new SecretKeySpec(sharedSecret, 0, 16, "AES");
enCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
enCipher.init(Cipher.ENCRYPT_MODE, aesKey); //create cipher for encoding
byte[] selfParams = enCipher.getParameters().getEncoded();
other.createDecoder(selfParams); //create decoder for this key
self.createDecoder(otherParams); //create decoder for other party's key (we do it here to avoid race condition)
}
/**
* Last step in diffie-hellman protocol, take cipher params to create a decoder
* @param params cipher parameters
*/
public void createDecoder(byte[] params) throws Exception{
AlgorithmParameters aesParams = AlgorithmParameters.getInstance("AES");
aesParams.init(params);
deCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
deCipher.init(Cipher.DECRYPT_MODE, aesKey, aesParams); //create cipher for decoding
}
/**
* Encrypt text using the cipher
* @param plaintext readable version of message
* @return unreadable version of message
*/
private byte[] encrypt(String plaintext) throws Exception{
return enCipher.doFinal(plaintext.getBytes());
}
/**
* Utilize flags to create a sendable message
* @param msg message to make sendable
* @return sendable message
*/
public byte[][] send(String msg, String receiver) throws Exception{
byte[] msg_ret = msg.getBytes();
byte[] checksum_ret = new byte[0];
if(flags[0]){
msg_ret = encrypt(msg);
}
if(flags[1]){
checksum_ret = encryptCheckSum(receiver, generateCheckSum(msg));
}
if(flags[2]){
if(!authenticated){
System.out.println(id+" attempted to send a message while unauthenticated.");
return null;
}
}
byte[][] ret = new byte[2][];
ret[0] = msg_ret;
ret[1] = checksum_ret;
return ret;
}
/**
* Decrypt text using the cipher
* @param ciphertext unreadable version of message
* @return readable version of message
*/
private String decrypt(byte[] ciphertext) throws Exception{
return new String(deCipher.doFinal(ciphertext));
}
/**
* Utilize flags to create a sendable message
* @param msg message to make readable
* @return readable message
*/
public String receive(byte[] msg, byte[] checksum) throws Exception{
String ret;
if(flags[0]){
ret = decrypt(msg);
}else{
ret = new String(msg);
}
if(flags[1]){
byte[] decrypted_cs = decryptCheckSum(checksum);
if(compareCheckSum(decrypted_cs, ret)){
return ret;
}else{
return "Checksum does not match";
}
}
if(flags[2]){
if(!authenticated){
System.out.println(id+" did not receive message due to not being authenticated.");
return null;
}
}
return ret;
}
/**
* Set security flags from Messenger
* @param newFlags security flags to set
*/
public void setFlags(Boolean[] newFlags){
flags = newFlags;
}
/**
* Get security flags and return them
* @return current security flags
*/
public Boolean[] getFlags(){
return flags;
}
/**
* Get private key for the current messenger from a file
* @return our private key
*/
private PrivateKey getPrivate() throws Exception{
byte[] key = Files.readAllBytes(Paths.get("keys/private-" + id + "/private.der"));
PKCS8EncodedKeySpec PKCS8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFac = KeyFactory.getInstance("RSA");
return keyFac.generatePrivate(PKCS8KeySpec);
}
/**
* Get public key for the specified messenger from a file
* @param messenger id of messenger to get public key for
* @return public key of the messenger
*/
private PublicKey getPublic(String messenger) throws Exception{
byte[] key = Files.readAllBytes(Paths.get("keys/public/" + messenger + ".der"));
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFac = KeyFactory.getInstance("RSA");
return keyFac.generatePublic(x509KeySpec);
}
/**
* Create a checksum for a specific message
* @param message message to generate a checksum for
* @return created checksum
*/
private byte[] generateCheckSum(String message){
int m = message.hashCode();
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putInt(m);
System.out.println("Generated checksum: "+toHexString(bb.array()));
return bb.array();
}
/**
* Encrypt a given checksum
* @param receiver id of Messenger to receive
* @param inputData data to encrypt
* @return encrypted checksum
*/
private byte[] encryptCheckSum(String receiver, byte[] inputData) throws Exception {
PublicKey key = getPublic(receiver);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.PUBLIC_KEY, key);
byte[] encryptedBytes = cipher.doFinal(inputData);
return encryptedBytes;
}
/**
* Decrypt a given checksum
* @param checksum checksum to decrypt
* @return decrypted checksum
*/
private byte[] decryptCheckSum(byte[] checksum) throws Exception {
PrivateKey key = getPrivate();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.PRIVATE_KEY, key);
byte[] decryptedBytes = cipher.doFinal(checksum);
return decryptedBytes;
}
/**
* Compare two checksums
* @param checksum checksum to check
* @param message message to confirm same as checksum
* @return true if same, false otherwise
*/
private boolean compareCheckSum(byte[] checksum, String message){
byte[] temp_checksum = generateCheckSum(message);
if(Arrays.equals(temp_checksum, checksum)){
System.out.println("Checksum matches: "+toHexString(temp_checksum));
return true;
}
return false;
}
/**
* Attempts to authenticate a user
* @param id id of user to authenticate
* @param pass attempted password to authenticate
* @return true if successful, false if not
*/
public boolean authenticate(String id, String pass)throws Exception{
byte[] bom = pass.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hash = md.digest(bom);
String attempted_hash = toHexString(hash);
String stored_hash = getStoredHash(id);
if(attempted_hash.equals(stored_hash)){
authenticated = true;
System.out.println(id+" authenticated successfully.");
return true;
}else{
System.out.println("Attempted password hash: "+attempted_hash);
System.out.println("Stored password hash: "+stored_hash);
System.out.println(id+" failed to authenticate.");
return false;
}
}
/**
* Gets the stored password hash for id
* @param id id of user to get password hash of
* @return password hash if found, "" if not
*/
private String getStoredHash(String id){
String line;
try {
BufferedReader bufferreader = new BufferedReader(new FileReader("users"));
line = bufferreader.readLine();
while (line != null) {
String[] el = line.split(":");
if(el[0].equals(id)){
return el[1];
}
line = bufferreader.readLine();
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
return "";
}
/**
* Converts MD5 byte array to a string
* @param bytes byte array to convert
* @return Base 16 string
*/
private String toHexString(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* Deauthorizes id
*/
public void deAuth(){
authenticated = false;
}
/**
* Checks to see if id is authenticated
* @return true or false
*/
public boolean getAuth(){
return authenticated;
}
}