/
MyTrustManager.java
344 lines (291 loc) · 12 KB
/
MyTrustManager.java
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
package edu.uiuc.ncsa.security.util.ssl;
/**
* Trust manager formerly part of MyProxy, but universally useful.
*/
import edu.uiuc.ncsa.security.core.util.DebugUtil;
import edu.uiuc.ncsa.security.core.util.MyLoggingFacade;
import edu.uiuc.ncsa.security.util.pkcs.CertUtil;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.*;
import java.util.*;
public class MyTrustManager implements X509TrustManager {
public SSLConfiguration getSslConfiguration() {
return sslConfiguration;
}
SSLConfiguration sslConfiguration = null;
boolean debugOn = DebugUtil.isEnabled(); // set to false for release
boolean stackTracesOn = false; // set to false for use as a library, true for standalone release
public MyTrustManager(MyLoggingFacade logger, String trustRootPath) {
this(logger, null, trustRootPath);
}
public MyTrustManager(MyLoggingFacade logger, SSLConfiguration sslConfiguration) {
this(logger, null, sslConfiguration);
}
public MyTrustManager(MyLoggingFacade logger, String serverDN, SSLConfiguration sslConfiguration) {
this.logger = logger;
this.serverDN = serverDN;
this.sslConfiguration = sslConfiguration;
this.trustRootPath = sslConfiguration.getTrustrootPath();
}
public MyTrustManager(MyLoggingFacade logger, String trustRootPath, String serverDN) {
this.logger = logger;
this.serverDN = serverDN;
this.trustRootPath = trustRootPath;
}
public boolean hasServerDN() {
return serverDN != null;
}
public String getServerDN() {
return serverDN;
}
public void setServerDN(String serverDN) {
this.serverDN = serverDN;
}
String serverDN = null;
MyLoggingFacade logger;
public MyLoggingFacade getLogger() {
if (logger == null) {
logger = new MyLoggingFacade(MyTrustManager.class.getName());
logger.setDebugOn(debugOn);
}
return logger;
}
protected boolean hasSSLConfiguration(){
return sslConfiguration != null;
}
public final String DEFAULT_TRUST_ROOT_PATH = "/etc/grid-security/certificates";
String trustRootPath = DEFAULT_TRUST_ROOT_PATH;
public String getTrustRootPath() {
return trustRootPath;
}
public void setTrustRootPath(String trustRootPath) {
this.trustRootPath = trustRootPath;
}
public boolean isRequestTrustRoots() {
return requestTrustRoots;
}
public void setRequestTrustRoots(boolean requestTrustRoots) {
this.requestTrustRoots = requestTrustRoots;
}
boolean requestTrustRoots;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
void dbg(String x) {
DebugUtil.dbg(this, x);
}
String host;
/**
* Try to read the key store certs from a directory of certificates.
*
* @return
*/
protected X509Certificate[] getIssuersFromDirectory(File dir) {
X509Certificate[] issuers = null;
String certDirPath = getTrustRootPath();
// Comment: This might fail for any number of reasons, such as having the
// trust root path not exist. Throwing exceptions will not work here since
// they will be intercepted by the SSL layer and not propagated to the caller...
String[] certFilenames = dir.list();
String[] certData = new String[certFilenames.length];
for (int i = 0; i < certFilenames.length; i++) {
try {
FileInputStream fileStream = new FileInputStream(
certDirPath + File.separator + certFilenames[i]);
byte[] buffer = new byte[fileStream.available()];
fileStream.read(buffer);
certData[i] = new String(buffer);
fileStream.close();
} catch (Exception e) {
dbg("Exception Reading issues " + e.getMessage());
// ignore
}
}
try {
issuers = CertUtil.getX509CertsFromStringList(certData, certFilenames);
dbg("Got " + issuers.length + " issuers.");
} catch (Exception e) {
if (stackTracesOn) e.printStackTrace();
dbg("Exception getting issuers. Returning null. " + e.getMessage());
}
return issuers;
}
/**
* Read the certs for the key store from a JKS file
*
* @return
*/
protected X509Certificate[] getIssuersFromFile(File certFile) {
ArrayList<X509Certificate> issuerList = new ArrayList<>();
try {
FileInputStream fis = new FileInputStream(certFile);
KeyStore keystore = KeyStore.getInstance(getSslConfiguration().getTrustRootType());
keystore.load(fis, getSslConfiguration().getTrustRootPassword().toCharArray());
Enumeration e = keystore.aliases();
while (e.hasMoreElements()) {
Certificate cert = keystore.getCertificate((String) e.nextElement());
if (cert instanceof X509Certificate) {
issuerList.add((X509Certificate) cert);
}
}
fis.close();
return issuerList.toArray(new X509Certificate[]{});
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public X509Certificate[] getAcceptedIssuers() {
String certDirPath = getTrustRootPath();
if (certDirPath == null) {
dbg("cert dir path null. Aborting");
return null;
}
File dir = new File(certDirPath);
if (dir.isDirectory()) {
dbg(" cert dir path is not a directory. Getting from file.");
return getIssuersFromDirectory(dir);
}
return getIssuersFromFile(dir);
}
public void checkClientTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
throw new CertificateException(
"checkClientTrusted not implemented by " + getClass().getName());
}
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
checkServerCertPath(certs);
checkServerDN(certs[0]);
}
protected void checkServerCertPath(X509Certificate[] certs)
throws CertificateException {
try {
CertPathValidator validator = CertPathValidator
.getInstance(CertPathValidator.getDefaultType());
CertificateFactory certFactory = CertificateFactory
.getInstance("X.509");
CertPath certPath = certFactory.generateCertPath(Arrays
.asList(certs));
X509Certificate[] acceptedIssuers = getAcceptedIssuers();
if (acceptedIssuers == null) {
String certDir = getTrustRootPath();
if (certDir != null) {
throw new CertificateException(
"no CA certificates found in " + certDir);
} else if (!isRequestTrustRoots()) {
throw new CertificateException(
"no CA certificates directory found");
}
getLogger()
.info("no trusted CAs configured -- bootstrapping trust from MyProxy server");
acceptedIssuers = new X509Certificate[1];
acceptedIssuers[0] = certs[certs.length - 1];
}
Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>(
acceptedIssuers.length);
for (int i = 0; i < acceptedIssuers.length; i++) {
TrustAnchor ta = new TrustAnchor(acceptedIssuers[i], null);
trustAnchors.add(ta);
}
PKIXParameters pkixParameters = new PKIXParameters(trustAnchors);
pkixParameters.setRevocationEnabled(false);
validator.validate(certPath, pkixParameters);
} catch (CertificateException e) {
if (stackTracesOn) e.printStackTrace();
throw e;
} catch (GeneralSecurityException e) {
if (stackTracesOn) e.printStackTrace();
throw new CertificateException(e);
} catch (Throwable t) {
if (stackTracesOn) t.printStackTrace();
}
}
/**
* Parses the server DN for the CN and does a few sanity checks on the result before returning.
*
* @param subject
* @return
* @throws CertificateException
*/
private String getCommonName(String subject) throws CertificateException {
int index = subject.indexOf("CN=");
if (index == -1) {
throw new CertificateException("Server certificate subject ("
+ subject + "does not contain a CN component.");
}
String CN = subject.substring(index + 3);
index = CN.indexOf(',');
if (index >= 0) {
CN = CN.substring(0, index);
}
if ((index = CN.indexOf('/')) >= 0) {
String service = CN.substring(0, index);
CN = CN.substring(index + 1);
if (!service.equals("host") && !service.equals("myproxy")) {
dbg("common name =\"" + CN + "\" has unknown server element \"" + subject + "\"");
throw new CertificateException(
"Server certificate subject CN contains unknown server element: "
+ subject);
}
}
return CN;
}
// Note that System.err is used for debugging because that will go to catalina.out whereas the current logging will send
// these to a central log that is very hard to sift through.
private void checkServerDN(X509Certificate cert)
throws CertificateException {
// Essentially, we have to check that the CN and the host match,
MyLoggingFacade ll = getLogger();
String CN = getCommonName(cert.getSubjectX500Principal().getName());
//System.err.println(getClass().getSimpleName() + ".checkServerDN: CN on cert = " + CN);
if (hasServerDN()) {
// Fixes OAUTH-176: server DN can be overridden
String configuredCN = getCommonName(getServerDN());
dbg(".checkServerDN: Configured serverDN has CN = " + configuredCN);
if (CN.equals(configuredCN)) {
return;
}
dbg(".checkServerDN: Configured serverDN check failed.");
}
dbg(".checkServerDN: Checking cert CN against hostname");
// So if the serDN and the returned hostname do NOT match, check the cert name against the hostname
if (getHost().equals("localhost")) {
try {
setHost(InetAddress.getLocalHost().getHostName());
} catch (Exception e) {
// ignore
}
}
dbg(".checkServerDN: Configured host = " + getHost());
dbg(".checkServerDN: host=CN? " + CN.equals(getHost()));
if (!CN.equals(getHost())) {
dbg("common name =\"" + CN + "\" does not match host from reverse lookup = \"" + host + "\"");
throw new CertificateException(
"Server certificate subject CN (" + CN
+ ") does not match server hostname (" + host
+ ").");
}
dbg("Success! common name =\"" + CN + "\" matches host = \"" + host + "\"");
}
}