1
2
3
4
5
6
7
8
9
10
11 package psiprobe.controllers.certificates;
12
13 import java.io.File;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.lang.reflect.InvocationTargetException;
17 import java.net.URI;
18 import java.net.URL;
19 import java.nio.file.Files;
20 import java.security.KeyStore;
21 import java.security.cert.Certificate;
22 import java.security.cert.X509Certificate;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.List;
27
28 import javax.management.ObjectName;
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
31
32 import org.apache.catalina.connector.Connector;
33 import org.apache.coyote.ProtocolHandler;
34 import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.springframework.beans.factory.annotation.Value;
38 import org.springframework.stereotype.Controller;
39 import org.springframework.web.bind.annotation.RequestMapping;
40 import org.springframework.web.servlet.ModelAndView;
41
42 import psiprobe.controllers.AbstractTomcatContainerController;
43 import psiprobe.model.certificates.Cert;
44 import psiprobe.model.certificates.CertificateInfo;
45 import psiprobe.model.certificates.ConnectorInfo;
46 import psiprobe.model.certificates.SslHostConfigInfo;
47
48
49
50
51 @Controller
52 public class ListCertificatesController extends AbstractTomcatContainerController {
53
54
55 private static final Logger logger = LoggerFactory.getLogger(ListCertificatesController.class);
56
57 @RequestMapping(path = "/certificates.htm")
58 @Override
59 public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
60 throws Exception {
61 return super.handleRequest(request, response);
62 }
63
64 @Override
65 protected ModelAndView handleRequestInternal(HttpServletRequest request,
66 HttpServletResponse response) throws Exception {
67
68 ModelAndView modelAndView = new ModelAndView(getViewName());
69
70 try {
71 List<Connector> connectors = getContainerWrapper().getTomcatContainer().findConnectors();
72 List<ConnectorInfo> infos = getConnectorInfos(connectors);
73
74 for (ConnectorInfo info : infos) {
75 List<Cert> certs;
76
77 List<SslHostConfigInfo> sslHostConfigInfos = info.getSslHostConfigInfos();
78 for (SslHostConfigInfo sslHostConfigInfo : sslHostConfigInfos) {
79 if (sslHostConfigInfo.getTruststoreFile() != null) {
80 certs = getCertificates(sslHostConfigInfo.getTruststoreType(),
81 sslHostConfigInfo.getTruststoreFile(), sslHostConfigInfo.getTruststorePassword());
82 sslHostConfigInfo.setTrustStoreCerts(certs);
83 }
84
85 List<CertificateInfo> certificateInfos = sslHostConfigInfo.getCertificateInfos();
86 for (CertificateInfo certificateInfo : certificateInfos) {
87 if (certificateInfo.getCertificateKeystoreFile() != null) {
88 certs = getCertificates(certificateInfo.getCertificateKeystoreType(),
89 certificateInfo.getCertificateKeystoreFile(),
90 certificateInfo.getCertificateKeystorePassword());
91 certificateInfo.setKeyStoreCerts(certs);
92 }
93 }
94 sslHostConfigInfo.setCertificateInfos(certificateInfos);
95 }
96 info.setSslHostConfigInfos(sslHostConfigInfos);
97 }
98
99 modelAndView.addObject("connectors", infos);
100 } catch (Exception e) {
101 logger.error("There was an exception listing certificates", e);
102 }
103
104 return modelAndView;
105
106 }
107
108
109
110
111
112
113
114
115
116
117
118
119 public List<Cert> getCertificates(String storeType, String storeFile, String storePassword)
120 throws Exception {
121 KeyStore keyStore;
122
123
124 if (storeType != null) {
125 keyStore = KeyStore.getInstance(storeType);
126 } else {
127 keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
128 }
129
130
131 char[] password = null;
132 if (storePassword != null) {
133 password = storePassword.toCharArray();
134 }
135
136
137 try (InputStream storeInput = getStoreInputStream(storeFile)) {
138 keyStore.load(storeInput, password);
139 } catch (IOException e) {
140 logger.error("Error loading store file {}", storeFile, e);
141 return Collections.emptyList();
142 }
143
144 List<Cert> certs = new ArrayList<>();
145
146 for (String alias : Collections.list(keyStore.aliases())) {
147
148 Certificate[] certificateChains = keyStore.getCertificateChain(alias);
149
150 if (certificateChains != null) {
151 for (Certificate certificateChain : certificateChains) {
152 X509Certificate x509Cert = (X509Certificate) certificateChain;
153 addToStore(certs, alias, x509Cert);
154 }
155 } else {
156 X509Certificate x509Cert = (X509Certificate) keyStore.getCertificate(alias);
157 addToStore(certs, alias, x509Cert);
158 }
159 }
160 return certs;
161 }
162
163
164
165
166
167
168
169
170
171
172
173 private List<ConnectorInfo> getConnectorInfos(Collection<Connector> connectors)
174 throws IllegalAccessException, InvocationTargetException {
175 List<ConnectorInfo> infos = new ArrayList<>();
176 for (Connector connector : connectors) {
177 if (!connector.getSecure()) {
178 continue;
179 }
180
181 ProtocolHandler protocolHandler = connector.getProtocolHandler();
182
183 if (protocolHandler instanceof AbstractHttp11JsseProtocol) {
184 AbstractHttp11JsseProtocol<?> protocol = (AbstractHttp11JsseProtocol<?>) protocolHandler;
185 if (!protocol.getSecure()) {
186 continue;
187 }
188 infos.add(toConnectorInfo(protocol));
189 }
190 }
191 return infos;
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205 private InputStream getStoreInputStream(String path) throws IOException {
206 File file = new File(path);
207 if (file.exists()) {
208 return Files.newInputStream(file.toPath());
209 }
210
211 File catalinaBaseFolder = new File(System.getProperty("catalina.base"));
212 file = new File(catalinaBaseFolder, path);
213 if (file.exists()) {
214 return Files.newInputStream(file.toPath());
215 }
216
217 URI uri = catalinaBaseFolder.toURI().resolve(path);
218
219 URL url = uri.toURL();
220
221 return url.openConnection().getInputStream();
222 }
223
224
225
226
227
228
229
230
231
232
233
234 private ConnectorInfo toConnectorInfo(AbstractHttp11JsseProtocol<?> protocol)
235 throws IllegalAccessException, InvocationTargetException {
236 ConnectorInfo info = new ConnectorInfo();
237 info.setName(ObjectName.unquote(protocol.getName()));
238
239 String defaultSslHostConfigName = protocol.getDefaultSSLHostConfigName();
240 if (defaultSslHostConfigName == null) {
241 logger.error("Cannot determine defaultSslHostConfigName");
242 return info;
243 }
244 info.setDefaultSslHostConfigName(defaultSslHostConfigName);
245 new SslHostConfigHelper(protocol, info);
246
247 return info;
248 }
249
250
251
252
253
254
255
256
257 private void addToStore(Collection<Cert> certs, String alias, X509Certificate x509Cert) {
258 Cert cert = new Cert();
259
260 cert.setAlias(alias);
261 cert.setSubjectDistinguishedName(x509Cert.getSubjectDN().toString());
262 cert.setNotBefore(x509Cert.getNotBefore().toInstant());
263 cert.setNotAfter(x509Cert.getNotAfter().toInstant());
264 cert.setIssuerDistinguishedName(x509Cert.getIssuerDN().toString());
265
266 certs.add(cert);
267 }
268
269 @Value("certificates")
270 @Override
271 public void setViewName(String viewName) {
272 super.setViewName(viewName);
273 }
274
275 }