-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHTTPS_Server.java
More file actions
232 lines (203 loc) · 10.2 KB
/
HTTPS_Server.java
File metadata and controls
232 lines (203 loc) · 10.2 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
package sbp;
import com.sun.net.httpserver.*;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.HttpCookie;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
public class HTTPS_Server {
private static final int port = 9000;
private static final String KEY_STORE = "certs/store.keys";
private static final String KEY_STORE_PASSWORD = "keystorepassword";
private static final String KPS;
private static List<String> sessionTokens = new ArrayList<>();
private static boolean debugMode = false;
static {
try {
SecretKey Kp = KeyGenerator.getInstance("AES").generateKey(); // 16
KPS = Base64.getEncoder().encodeToString(Kp.getEncoded()); // 24
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static class ChangeHandler implements HttpHandler {
@Override
public void handle(HttpExchange x) throws IOException {
System.out.println("A change request was received.");
try {
List<String> cookie = x.getRequestHeaders().get("Cookie");
if (cookie == null) {
System.out.println("Change request, no cookie found, ignoring request");
return;
}
String response;
String encryptedToken = HttpCookie.parse(cookie.get(0)).get(0).getValue();
SecretKey clientKey = getClientSpecificKey(x);
String token = Optional.ofNullable(decrypt(encryptedToken, clientKey)).orElse("decryption failed");
if (debugMode) {
response = "did you give us the right token on the right channel? " + sessionTokens.contains(token) + "\n";
response = response.concat("your encrypted token: " + encryptedToken + "\n");
response = response.concat("your decrypted token: " + token + "\n");
response = response.concat("your client key: " + Base64.getEncoder().encodeToString(clientKey.getEncoded()) + "\n");
} else if (sessionTokens.contains(token)) {
response = "Valid change request received, request will be honored!";
} else {
response = "Invalid change request received, request will be ignored";
}
System.out.println(response);
} catch (Exception e) {
System.out.println(e.getMessage());
throw new RuntimeException(e);
}
}
}
public static class AccountHandler implements HttpHandler {
@Override
public void handle(HttpExchange x) throws IOException {
System.out.println("A user is attempting to access the account page.");
try {
SecretKey clientKey = getClientSpecificKey(x);
String token = generateNewSessionToken();
sessionTokens.add(token);
String encryptedToken = encrypt(token, clientKey);
HttpCookie newCookie = new HttpCookie("session_token", encryptedToken);
x.getResponseHeaders().add("Set-Cookie", newCookie.toString());
String response;
if (debugMode) {
response = "This is the response from the server, we didn't find a cookie so we are providing you with one here\n";
response = response.concat("your encrypted cookie: " + newCookie + "\n");
response = response.concat("your plain session token: " + token + "\n");
response = response.concat("your client key: " + Base64.getEncoder().encodeToString(clientKey.getEncoded()) + "\n");
} else {
Path filePath = Path.of("html/account.html");
response = Files.readString(filePath);
}
x.sendResponseHeaders(200, response.getBytes().length);
OutputStream Output_Stream = x.getResponseBody();
Output_Stream.write(response.getBytes());
Output_Stream.close();
} catch (Exception e) {
System.out.println(e.getMessage());
throw new RuntimeException(e);
}
}
}
public static class LoginHandler implements HttpHandler {
@Override
public void handle(HttpExchange x) throws IOException {
System.out.println("A user is requesting the login page");
Path filePath = Path.of("html/login.html");
String response = Files.readString(filePath);
x.sendResponseHeaders(200, response.getBytes().length);
OutputStream Output_Stream = x.getResponseBody();
Output_Stream.write(response.getBytes());
Output_Stream.close();
}
}
public static void main(String[] args) throws Exception {
try {
// setup the socket address
InetSocketAddress Inet_Address = new InetSocketAddress(port);
//initialize the HTTPS server
HttpsServer HTTPS_Server = HttpsServer.create(Inet_Address, 0);
SSLContext SSL_Context = SSLContext.getInstance("TLS");
// initialise the keystore
KeyStore Key_Store = KeyStore.getInstance("JKS");
FileInputStream Input_Stream = new FileInputStream(KEY_STORE);
char[] Password = KEY_STORE_PASSWORD.toCharArray();
Key_Store.load(Input_Stream, Password);
// setup the key manager factory
KeyManagerFactory Key_Manager = KeyManagerFactory.getInstance("SunX509");
Key_Manager.init(Key_Store, Password);
// setup the trust manager factory
TrustManagerFactory Trust_Manager = TrustManagerFactory.getInstance("SunX509");
Trust_Manager.init(Key_Store);
// setup the HTTPS context and parameters
SSL_Context.init(Key_Manager.getKeyManagers(), Trust_Manager.getTrustManagers(), null);
HTTPS_Server.setHttpsConfigurator(new HttpsConfigurator(SSL_Context) {
public void configure(HttpsParameters params) {
try {
// initialise the SSL context
SSLContext SSL_Context = getSSLContext();
SSLEngine SSL_Engine = SSL_Context.createSSLEngine();
params.setNeedClientAuth(false);
params.setCipherSuites(SSL_Engine.getEnabledCipherSuites());
params.setProtocols(SSL_Engine.getEnabledProtocols());
// Set the SSL parameters
SSLParameters SSL_Parameters = SSL_Context.getSupportedSSLParameters();
params.setSSLParameters(SSL_Parameters);
System.out.println("The HTTPS server is connected");
} catch (Exception ex) {
System.out.println("Failed to create the HTTPS port");
}
}
});
HTTPS_Server.createContext("/login" , new LoginHandler());
HTTPS_Server.createContext("/account", new AccountHandler());
HTTPS_Server.createContext("/change", new ChangeHandler());
HTTPS_Server.setExecutor(null); // creates a default executor
HTTPS_Server.start();
} catch (Exception exception) {
System.out.println("Failed to create HTTPS server on port " + port + " of localhost");
exception.printStackTrace();
}
}
private static SecretKey getClientSpecificKey(HttpExchange x) throws Exception {
HttpsExchange xs = (HttpsExchange) x;
Class<?> c = Class.forName("sun.security.ssl.SSLSessionImpl");
Field masterSecretField = c.getDeclaredField("resumptionMasterSecret");
masterSecretField.setAccessible(true);
SecretKey k = (SecretKey)masterSecretField.get(xs.getSSLSession()); // 48
String ks = Base64.getEncoder().encodeToString(k.getEncoded()); // 64
String kcs = KPS.concat(ks); // 88
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(kcs.getBytes()); // 32 after digest
String kcsh = Base64.getEncoder().encodeToString(md.digest()); // 44
byte[] kcb = Base64.getDecoder().decode(kcsh);
return new SecretKeySpec(kcb, 0, kcb.length, "AES");
}
private static String generateNewSessionToken() {
byte[] token = new byte[16];
new SecureRandom().nextBytes(token);
return Base64.getEncoder().encodeToString(token);
}
private static String encrypt(String input, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = generateIV(); // 16
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] cipherText = cipher.doFinal(input.getBytes()); // 32
String cipherTextString = Base64.getEncoder().encodeToString(cipherText); // 44
String ivString = Base64.getEncoder().encodeToString(iv.getIV()); // 24
return ivString.concat(cipherTextString);
}
private static IvParameterSpec generateIV() {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
}
private static String decrypt(String input, SecretKey key) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = Base64.getDecoder().decode(input.substring(0,24));
byte[] cipherText = Base64.getDecoder().decode(input.substring(24));
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText));
} catch (Exception e) {
return null;
}
}
}