· 5 years ago · Mar 27, 2020, 08:12 PM
1import java.net.*;
2import java.io.*;
3import java.security.*;
4import javax.crypto.*;
5import javax.crypto.spec.IvParameterSpec;
6import javax.crypto.spec.SecretKeySpec;
7import java.util.Arrays;
8
9public class Server{
10 public static void main(String[] args){
11 int number=0;
12 try{
13 int serverPort = 4567;
14 ServerSocket listenSocket = new ServerSocket(serverPort);
15 while(true) {
16 Socket clientSocket = listenSocket.accept();
17 new Connection(clientSocket,number);
18 number++;
19 }
20 } catch(IOException e) {
21 System.out.println("Listen:" + e.getMessage());
22 }
23 }
24}
25
26class Connection extends Thread {
27 DataInputStream in;
28 DataOutputStream out;
29 Socket clientSocket;
30 int number;
31 int stopped;
32
33 public Connection (Socket aClientSocket,int clientNumber) {
34 try{
35 number=clientNumber;
36 clientSocket = aClientSocket;
37 in = new DataInputStream(clientSocket.getInputStream());
38 out = new DataOutputStream(clientSocket.getOutputStream());
39 stopped=0;
40 this.start();
41 }catch(IOException e){System.out.println("Connection:" + e.getMessage());}
42 }
43
44 public void run(){
45 try{
46 while(true){
47 //shared private key
48 byte [] key = createPrivateKey();
49
50 //hash function and new keys
51 byte [] byte1 = {1};
52 SecretKey k1 = createHashKeys(byte1, key);
53 byte [] byte2 = {2};
54 SecretKey k2 = createHashKeys(byte2, key);
55
56 //message authentication code
57 Mac mac = Mac.getInstance("HmacSHA256");
58 mac.init(k2);
59 int nSeq = 0;
60
61 //nonce - receives IV in plaintext
62 byte[] bytesForIV = new byte[16];
63 in.read(bytesForIV);
64 IvParameterSpec iv = new IvParameterSpec(bytesForIV);
65
66 //D(m, k1) = c - c is only decrypted if the macs match
67 Cipher c = Cipher.getInstance("AES/CTR/PKCS5Padding");
68 c.init(Cipher.DECRYPT_MODE, k1, iv);
69
70 while (stopped!=1){
71 try{
72 //size of the ciphertext
73 byte[] sizeInBytes = new byte[4];
74
75 if(in.read(sizeInBytes)!=-1){
76 int size=byteArrayToInt(sizeInBytes);
77
78 //256 bits represent the mac
79 byte[] authTagClient = new byte[32];
80 in.read(authTagClient);
81
82 //ciphertext
83 byte[] ciphertext = new byte[size];
84 in.read(ciphertext);
85
86 //calculates the server authentication tag
87 byte [] nSeqByte = intToByteArray(nSeq);
88 byte [] authTagAux = concat(ciphertext, nSeqByte);
89 byte [] authTag = mac.doFinal(authTagAux);
90
91 if(Arrays.equals(authTagClient, authTag)){
92 byte [] dec = c.doFinal(ciphertext);
93 System.out.println("Text from client : "+ new String (dec));
94 }
95 else{
96 System.out.println("Invalid MAC");
97 }
98 nSeq++;
99 }else{
100 stopped=1;
101 }
102 }
103 catch (IOException ignored) {
104 }
105 }
106 }
107 }
108 catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | IOException | BadPaddingException e) {
109 e.printStackTrace();
110 }
111 }
112
113 private byte[] createPrivateKey() throws IOException {
114 //diffie-hellman
115 //receive the variables p, q and g
116 byte [] p = new byte[16];
117 in.read(p);
118 byte [] q = new byte[16];
119 in.read(q);
120 byte [] g = new byte[16];
121 in.read(g);
122
123 //the private key of the server should range from 0 to q-1
124 int privateKeyServer = getRandomNumber(byteArrayToInt(q)-1);
125
126 //calculate and send to the client the public key of the server - g^(privateKeyServer) mod p
127 int powServer = (int)Math.pow(byteArrayToInt(g), privateKeyServer);
128 int publicKeyServerAux = powServer%byteArrayToInt(p);
129 byte [] publicKeyServer = intToByteArray(publicKeyServerAux);
130 out.write(publicKeyServer);
131
132 //receive the public key of the client
133 byte [] publicKeyClient = new byte[16];
134 in.read(publicKeyClient);
135
136 //calculate the shared private key - publickKeyClient^(privateKeyServer) mod p
137 int pow = (int)Math.pow(byteArrayToInt(publicKeyClient), privateKeyServer);
138 int privateKeyAux = pow%byteArrayToInt(p);
139
140 return intToByteArray(privateKeyAux);
141 }
142
143 private SecretKey createHashKeys(byte [] b, byte [] key){
144 SecretKey k = null;
145 try {
146 MessageDigest md = MessageDigest.getInstance("SHA-256");
147
148 byte[] aux = concat(key, b);
149 byte[] hash = md.digest(aux);
150 k = new SecretKeySpec(hash, 0, hash.length, "AES");
151 } catch (NoSuchAlgorithmException | IOException e) {
152 e.printStackTrace();
153 }
154 return k;
155 }
156
157 private byte [] concat(byte[] first, byte[] second) throws IOException {
158 ByteArrayOutputStream baos = new ByteArrayOutputStream();
159 baos.write(first);
160 baos.write(second);
161 byte [] result = baos.toByteArray();
162 baos.reset();
163 return result;
164 }
165
166 //https://mkyong.com/java/java-generate-random-integers-in-a-range/
167 private static int getRandomNumber(int max) {
168 return (int) (Math.random() * ((max) + 1));
169 }
170
171 //https://stackoverflow.com/questions/2183240/java-integer-to-byte-array
172 //the function needs to be final because if it's not the MAC is invalid
173 public static final byte[] intToByteArray(int value) {
174 return new byte[]{
175 (byte)(value >>> 24),
176 (byte)(value >>> 16),
177 (byte)(value >>> 8),
178 (byte)value};
179 }
180
181 //https://stackoverflow.com/questions/4950598/convert-byte-to-int
182 public int byteArrayToInt(byte[] b) {
183 int value= 0;
184 for (byte item : b) value = (value << 8) | item;
185 return value;
186 }
187}