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