· 6 years ago · Feb 25, 2020, 05:42 PM
1//
2// Code for Exercise 3 of the Intro to Comp. Sec. Module
3// Tom Chothia, The University of Birmingham, Dec. 2013
4//
5//This program runs the server side of the following protocol:
6
7//1. C -> S: g^x
8//2. S -> C: g^y,
9//3. C -> S: { Nc }_key(g^xy)
10//4. S -> C: { {Nc+1}_Kcs, Ns }_key(g^xy)
11//5. C -> S: { {Ns+1}_Kcs }_key(g^xy)
12//6. S -> C: {secret}_key(g^xy)
13
14//Encryption is 128-bit AES, CBC, PKCS5 padding, nonces are ints.
15//g^x, and g^y are sent as certificates, length sent as int first.
16//The first 128-bits of g^xy are used as the AES key.
17//Values of p & g for Diffie-Hellman were found using generateDHprams()
18
19import java.io.DataInputStream;
20import java.io.DataOutputStream;
21import java.io.IOException;
22import java.math.BigInteger;
23import java.net.ServerSocket;
24import java.net.Socket;
25import java.security.AlgorithmParameterGenerator;
26import java.security.AlgorithmParameters;
27import java.security.InvalidAlgorithmParameterException;
28import java.security.InvalidKeyException;
29import java.security.Key;
30import java.security.KeyFactory;
31import java.security.KeyPair;
32import java.security.KeyPairGenerator;
33import java.security.NoSuchAlgorithmException;
34import java.security.PrivateKey;
35import java.security.PublicKey;
36import java.security.SecureRandom;
37import java.security.spec.InvalidKeySpecException;
38import java.security.spec.InvalidParameterSpecException;
39import java.security.spec.X509EncodedKeySpec;
40
41import javax.crypto.BadPaddingException;
42import javax.crypto.Cipher;
43import javax.crypto.IllegalBlockSizeException;
44import javax.crypto.KeyAgreement;
45import javax.crypto.NoSuchPaddingException;
46import javax.crypto.spec.DHParameterSpec;
47import javax.crypto.spec.SecretKeySpec;
48
49public class Protocol2Server {
50
51 static int portNo = 11338;
52 static String hexKey= ?????;
53 // Values of p & g for Diffie-Hellman found using generateDHprams()
54 static BigInteger g = new BigInteger("129115595377796797872260754286990587373919932143310995152019820961988539107450691898237693336192317366206087177510922095217647062219921553183876476232430921888985287191036474977937325461650715797148343570627272553218190796724095304058885497484176448065844273193302032730583977829212948191249234100369155852168");
55 static BigInteger p = new BigInteger("165599299559711461271372014575825561168377583182463070194199862059444967049140626852928438236366187571526887969259319366449971919367665844413099962594758448603310339244779450534926105586093307455534702963575018551055314397497631095446414992955062052587163874172731570053362641344616087601787442281135614434639");
56
57 public static void main(String[] args) {
58
59 // Listen for connections, when client connects spin off a
60 // thread to run the protocol over that connection and go
61 // back to listening for new connections
62 try {
63 ServerSocket listening = new ServerSocket(portNo);
64 while (true) {
65 // For each connection spin off a new protocol instance.
66 Socket connection = listening.accept();
67 Thread instance = new Thread(new Protocol2Instance(connection));
68 instance.start();
69 }
70 } catch (Exception e) {
71 System.out.println("Doh "+e);
72 }
73 }
74
75
76 private static class Protocol2Instance implements Runnable {
77
78 Socket myConnection;
79 boolean debug = true;
80 static Cipher decAEScipher;
81 static Cipher encAEScipher;
82 static Cipher decAESsessionCipher;
83 static Cipher encAESsessionCipher;
84
85 public Protocol2Instance(Socket myConnection) {
86 this.myConnection = myConnection;
87 //Set up the cipher objects
88 Key aesKey = new SecretKeySpec(hexStringToByteArray(hexKey), "AES");
89 try {
90 decAEScipher = Cipher.getInstance("AES");
91 decAEScipher.init(Cipher.DECRYPT_MODE, aesKey);
92 encAEScipher = Cipher.getInstance("AES");
93 encAEScipher.init(Cipher.ENCRYPT_MODE, aesKey);
94 } catch (Exception e) {
95 System.out.println("Doh "+e);
96 }
97 }
98
99 public void run() {
100 // Data streams used because we want to send bytes and ints
101 DataOutputStream outStream;
102 DataInputStream inStream;
103 try {
104 outStream = new DataOutputStream(myConnection.getOutputStream());
105 inStream = new DataInputStream(myConnection.getInputStream());
106 try {
107 // Use crypto API to calculate y & g^y
108 DHParameterSpec dhSpec = new DHParameterSpec(p,g);
109 KeyPairGenerator diffieHellmanGen = KeyPairGenerator.getInstance("DiffieHellman");
110 diffieHellmanGen.initialize(dhSpec);
111 KeyPair serverPair = diffieHellmanGen.generateKeyPair();
112 PrivateKey y = serverPair.getPrivate();
113 PublicKey gToTheY = serverPair.getPublic();
114
115 //Protocol message 1
116 //PublicKey cert can vary in length, therefore the length is sent first
117 int publicKeyLen = inStream.readInt();
118 byte[] message1 = new byte[publicKeyLen];
119 inStream.read(message1);
120 KeyFactory keyfactoryDH = KeyFactory.getInstance("DH");
121 X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(message1);
122 PublicKey gToTheX = keyfactoryDH.generatePublic(x509Spec);
123 if (debug) System.out.println("g^x len: "+publicKeyLen);
124 if (debug) System.out.println("g^x cert: "+byteArrayToHexString(gToTheX.getEncoded()));
125
126 //Protocol message 2
127 outStream.writeInt(gToTheY.getEncoded().length);
128 outStream.write(gToTheY.getEncoded());
129 if (debug) System.out.println("g^y len: "+gToTheY.getEncoded().length);
130 if (debug) System.out.println("g^y cert: "+byteArrayToHexString(gToTheY.getEncoded()));
131
132 //Calculate session key
133 // This method sets decAESsessionCipher & encAESsessionCipher
134 calculateSessionKey(y, gToTheX);
135
136 //Protocol Step 3
137 byte[] message3ct = new byte[16];
138 inStream.read(message3ct);
139 byte[] clientNonceBytes = decAESsessionCipher.doFinal(message3ct);
140 int clientNonce = new BigInteger(clientNonceBytes).intValue();
141 if (debug) System.out.println("Client nonce: "+clientNonce);
142
143 //Protocol Step 4
144 SecureRandom gen = new SecureRandom();
145 int serverNonce = gen.nextInt();
146 byte[] encryptedClientNonceInc = encAEScipher.doFinal(BigInteger.valueOf(clientNonce+1).toByteArray());
147 byte[] serverNonceBytes = BigInteger.valueOf(serverNonce).toByteArray();
148 byte[] message4body = new byte[20];
149 System.arraycopy(encryptedClientNonceInc,0,message4body,0,16);
150 System.arraycopy(serverNonceBytes,0,message4body,16,4);
151 byte[] message4ct = encAESsessionCipher.doFinal(message4body);
152 outStream.write(message4ct);
153 if (debug) System.out.println("Server nonce: "+serverNonce);
154
155 //Protocol Step 5
156 byte[] message5ct = new byte[32];
157 inStream.read(message5ct);
158 byte[] nonceReplyBytes = decAEScipher.doFinal(decAESsessionCipher.doFinal(message5ct));
159 int serverNonceReply = new BigInteger(nonceReplyBytes).intValue();
160 if (debug) System.out.println("Server Nonce Reply: "+serverNonceReply);
161
162 //Check nonce value
163 if (serverNonce+1!=serverNonceReply) {
164 if (debug) System.out.println("Nonces dont match");
165 outStream.write("Nonces dont match".getBytes());
166 myConnection.close();
167 return;
168 } else {
169 if (debug) System.out.println("Nonces match");
170 }
171
172 //Protocol Step 6
173 byte[] message6pt = ("Well Done. Submit this value: "+secretValue()).getBytes();
174 byte[] message6ct = encAESsessionCipher.doFinal(message6pt);
175 outStream.write(message6ct);
176 if (debug) System.out.println("Secret sent: "+new String(message6pt));
177 myConnection.close();
178
179 } catch (IllegalBlockSizeException e) {
180 outStream.write("Bad block size".getBytes());
181 if (debug) System.out.println("Doh "+e);
182 myConnection.close();
183 return;
184 } catch (BadPaddingException e) {
185 outStream.write("Bad padding".getBytes());
186 myConnection.close();
187 if (debug) System.out.println("Doh "+e);
188 return;
189 } catch (InvalidKeySpecException e) {
190 outStream.write("Bad certificate for PublicKey (g^x)".getBytes());
191 myConnection.close();
192 if (debug) System.out.println("Doh "+e);
193 return;
194 } catch (NoSuchAlgorithmException e) {
195 System.out.println(e);// Not going to happen, AES hard wired
196 } catch (InvalidAlgorithmParameterException e) {
197 System.out.println(e);// Not going to happen, DH Spec hard wired
198 e.printStackTrace();
199 }
200
201 } catch (IOException e) {
202 //Nothing we can do about this one
203 if (debug) System.out.println("Your wi-fi sucks: "+e);
204 return;
205 }
206 }
207
208 // This method sets decAESsessioncipher & encAESsessioncipher
209 private void calculateSessionKey(PrivateKey y, PublicKey gToTheX) {
210 try {
211 // Find g^xy
212 KeyAgreement serverKeyAgree = KeyAgreement.getInstance("DiffieHellman");
213 serverKeyAgree.init(y);
214 serverKeyAgree.doPhase(gToTheX, true);
215 byte[] secretDH = serverKeyAgree.generateSecret();
216 if (debug) System.out.println("g^xy: "+byteArrayToHexString(secretDH));
217 //Use first 16 bytes of g^xy to make an AES key
218 byte[] aesSecret = new byte[16];
219 System.arraycopy(secretDH,0,aesSecret,0,16);
220 Key aesSessionKey = new SecretKeySpec(aesSecret, "AES");
221 if (debug) System.out.println("Session key: "+byteArrayToHexString(aesSessionKey.getEncoded()));
222 // Set up Cipher Objects
223 decAESsessionCipher = Cipher.getInstance("AES");
224 decAESsessionCipher.init(Cipher.DECRYPT_MODE, aesSessionKey);
225 encAESsessionCipher = Cipher.getInstance("AES");
226 encAESsessionCipher.init(Cipher.ENCRYPT_MODE, aesSessionKey);
227 } catch (NoSuchAlgorithmException e ) {
228 System.out.println(e);
229 } catch (InvalidKeyException e) {
230 System.out.println(e);
231 } catch (NoSuchPaddingException e) {
232 e.printStackTrace();
233 }
234 }
235
236 @SuppressWarnings("unused")
237 public static void generateDHprams() throws NoSuchAlgorithmException, InvalidParameterSpecException {
238 AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DH");
239 paramGen.init(1024);
240 //Generate the parameters
241 AlgorithmParameters params = paramGen.generateParameters();
242 DHParameterSpec dhSpec = (DHParameterSpec)params.getParameterSpec(DHParameterSpec.class);
243 System.out.println("These are some good values to use for p & g with Diffie Hellman");
244 System.out.println("p: "+dhSpec.getP());
245 System.out.println("g: "+dhSpec.getG());
246
247 }
248
249 private static String byteArrayToHexString(byte[] data) {
250 StringBuffer buf = new StringBuffer();
251 for (int i = 0; i < data.length; i++) {
252 int halfbyte = (data[i] >>> 4) & 0x0F;
253 int two_halfs = 0;
254 do {
255 if ((0 <= halfbyte) && (halfbyte <= 9))
256 buf.append((char) ('0' + halfbyte));
257 else
258 buf.append((char) ('a' + (halfbyte - 10)));
259 halfbyte = data[i] & 0x0F;
260 } while(two_halfs++ < 1);
261 }
262 return buf.toString();
263 }
264
265 private static byte[] hexStringToByteArray(String s) {
266 int len = s.length();
267 byte[] data = new byte[len / 2];
268 for (int i = 0; i < len; i += 2) {
269 data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
270 + Character.digit(s.charAt(i+1), 16));
271 }
272 return data;
273 }
274
275 private static String secretValue() {
276 ??????;
277 }
278
279 }
280}