· 5 years ago · Mar 27, 2020, 08:10 PM
1import java.net.*;
2import java.io.*;
3import javax.crypto.*;
4import javax.crypto.spec.IvParameterSpec;
5import javax.crypto.spec.SecretKeySpec;
6import java.security.*;
7
8public class Client {
9 public static void main(String[] args) {
10
11 Socket s = null;
12 int serversocket = 4567;
13 try {
14 s = new Socket("127.0.0.1", serversocket);
15
16 DataInputStream in = new DataInputStream(s.getInputStream());
17 DataOutputStream out = new DataOutputStream(s.getOutputStream());
18
19 while (true){
20 SecureRandom sr = new SecureRandom();
21
22 //shared private key
23 byte [] key = createPrivateKey(sr, out, in);
24
25 //hash function and new keys
26 byte [] byte1 = {1};
27 SecretKey k1 = createHashKeys(byte1, key);
28 byte [] byte2 = {2};
29 SecretKey k2 = createHashKeys(byte2, key);
30
31 //message authentication code
32 Mac mac = Mac.getInstance("HmacSHA256");
33 mac.init(k2);
34 int nSeq = 0;
35
36 //nonce - sends the bytes in plaintext so that the server can create the same IV
37 byte[] bytesForIV=new byte[16];
38 sr.nextBytes(bytesForIV);
39 IvParameterSpec iv = new IvParameterSpec(bytesForIV);
40 out.write(bytesForIV);
41
42 //E(m, k1) = c
43 Cipher c = Cipher.getInstance("AES/CTR/PKCS5Padding");
44 c.init(Cipher.ENCRYPT_MODE, k1, iv);
45
46 String text;
47 InputStreamReader input = new InputStreamReader(System.in);
48 BufferedReader reader = new BufferedReader(input);
49
50 while (true) {
51 try {
52 //encrypt text sent by client
53 text = reader.readLine();
54 byte [] ciphertext = c.doFinal(text.getBytes());
55
56 //calculates the authentication tag
57 byte [] nSeqByte = intToByteArray(nSeq);
58 byte [] authTagAux = concat(ciphertext, nSeqByte);
59 byte [] authTag = mac.doFinal(authTagAux);
60
61 // info = [size of ciphertext(4 bytes)][mac(32 bytes)][ciphertext]
62 byte[] info = concat(intToByteArray(ciphertext.length),concat(authTag,ciphertext));
63 out.write(info);
64 nSeq++;
65 }
66 catch (Exception ignored) { }
67 }
68 }
69 } catch (UnknownHostException e) {
70 System.out.println("Sock:" + e.getMessage());
71 } catch (EOFException e) {
72 System.out.println("EOF:" + e.getMessage());
73 } catch (IOException e) {
74 System.out.println("IO:" + e.getMessage());
75 } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidKeyException e) {
76 e.printStackTrace();
77 } finally {
78 if (s != null)
79 try {
80 s.close();
81 } catch (IOException e) {
82 System.out.println("close:" + e.getMessage());
83 }
84 }
85 }
86
87 private static byte [] createPrivateKey(SecureRandom sr, DataOutputStream out, DataInputStream in) throws IOException {
88 //diffie-hellman
89 //create the variables p, q and g and send them to the server
90 byte [] p = new byte[16];
91 sr.nextBytes(p);
92 out.write(p);
93 byte [] q = new byte[16];
94 sr.nextBytes(q);
95 out.write(q);
96 byte [] g = new byte[16];
97 sr.nextBytes(g);
98 out.write(g);
99
100 //the private key of the client should range from 0 to q-1
101 int privateKeyClient = getRandomNumber(byteArrayToInt(q)-1);
102
103 //calculate and send to the server the public key of the client - g^(privateKeyClient) mod p
104 int powClient = (int)Math.pow(byteArrayToInt(g), privateKeyClient);
105 int publicKeyClientAux = powClient%byteArrayToInt(p);
106 byte [] publicKeyClient = intToByteArray(publicKeyClientAux);
107 out.write(publicKeyClient);
108
109 //receive the public key of the server
110 byte [] publicKeyServer = new byte[16];
111 in.read(publicKeyServer);
112
113 //calculate the shared private key - publickKeyServer^(privateKeyClient) mod p
114 int pow = (int)Math.pow(byteArrayToInt(publicKeyServer), privateKeyClient);
115 int privateKeyAux = pow%byteArrayToInt(p);
116
117 return intToByteArray(privateKeyAux);
118 }
119
120 private static SecretKey createHashKeys(byte [] b, byte [] key){
121 SecretKey k = null;
122 try {
123 MessageDigest md = MessageDigest.getInstance("SHA-256");
124
125 byte[] aux = concat(key, b);
126 byte[] hash = md.digest(aux);
127 k = new SecretKeySpec(hash, 0, hash.length, "AES");
128 } catch (NoSuchAlgorithmException | IOException e) {
129 e.printStackTrace();
130 }
131 return k;
132 }
133
134 private static byte [] concat(byte[] first, byte[] second) throws IOException {
135 ByteArrayOutputStream baos = new ByteArrayOutputStream();
136 baos.write(first);
137 baos.write(second);
138 byte [] result = baos.toByteArray();
139 baos.reset();
140 return result;
141 }
142
143 //https://mkyong.com/java/java-generate-random-integers-in-a-range/
144 private static int getRandomNumber(int max) {
145 return (int)(Math.random() * ((max) + 1));
146 }
147
148 //https://stackoverflow.com/questions/2183240/java-integer-to-byte-array
149 //the function needs to be final because if it's not the MAC is invalid
150 private static final byte[] intToByteArray(int value) {
151 return new byte[]{
152 (byte)(value >>> 24),
153 (byte)(value >>> 16),
154 (byte)(value >>> 8),
155 (byte)value};
156 }
157
158 //https://stackoverflow.com/questions/4950598/convert-byte-to-int
159 private static int byteArrayToInt(byte[] b) {
160 int value= 0;
161 for (byte item : b) value = (value << 8) | item;
162 return value;
163 }
164}