Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add certificate download to HTTP status page #401

Merged
merged 8 commits into from Jan 25, 2019
Copy path View file
@@ -1,5 +1,6 @@
package qz.common;

import org.apache.commons.ssl.Base64;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
@@ -8,8 +9,11 @@
import qz.utils.SystemUtilities;
import qz.ws.PrintSocketServer;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@@ -32,7 +36,7 @@ public static JSONObject gatherAbout(String domain) {
about.put("ssl", ssl(keyStore));
about.put("libraries", libraries());
}
catch(JSONException | KeyStoreException e) {
catch(JSONException | GeneralSecurityException e) {
log.error("Failed to write JSON data", e);
}

@@ -74,24 +78,36 @@ private static JSONObject environment() throws JSONException {
return environment;
}

private static JSONArray ssl(KeyStore keystore) throws JSONException, KeyStoreException {
JSONArray ssl = new JSONArray();

Enumeration<String> aliases = keystore.aliases();
while(aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if ("X.509".equals(keystore.getCertificate(alias).getType())) {
JSONObject cert = new JSONObject();
X509Certificate x509 = (X509Certificate)keystore.getCertificate(alias);
cert.put("subject", x509.getSubjectX500Principal().getName());
cert.put("expires", toISO(x509.getNotAfter()));
ssl.put(cert);
private static JSONObject ssl(KeyStore keystore) throws JSONException, KeyStoreException, CertificateEncodingException {
JSONObject ssl = new JSONObject();

JSONArray certs = new JSONArray();
if (keystore != null) {
Enumeration<String> aliases = keystore.aliases();
while(aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if ("X.509".equals(keystore.getCertificate(alias).getType())) {
JSONObject cert = new JSONObject();
X509Certificate x509 = (X509Certificate)keystore.getCertificate(alias);
cert.put("alias", alias);
cert.put("subject", x509.getSubjectX500Principal().getName());
cert.put("expires", toISO(x509.getNotAfter()));
cert.put("encoding", formatCert(x509.getEncoded()));
certs.put(cert);
}
}
}
ssl.put("certificates", certs);

return ssl;
}

private static String formatCert(byte[] encoding) {
return "-----BEGIN CERTIFICATE-----\r\n" +
new String(Base64.encodeBase64(encoding, true), StandardCharsets.UTF_8) +
"-----END CERTIFICATE-----\r\n";
}

private static JSONObject libraries() throws JSONException {
JSONObject libraries = new JSONObject();

Copy path View file
@@ -1,17 +1,26 @@
package qz.ws;

import org.apache.commons.ssl.Base64;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qz.common.AboutInfo;
import qz.deploy.DeployUtilities;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Iterator;
import java.util.Properties;

/**
* HTTP JSON endpoint for serving QZ Tray information
@@ -27,6 +36,8 @@
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
if ("application/json".equals(request.getHeader("Accept")) || "/json".equals(request.getServletPath())) {
generateJsonResponse(request, response);
} else if ("application/x-x509-ca-cert".equals(request.getHeader("Accept")) || request.getServletPath().startsWith("/cert/")) {
generateCertResponse(request, response);
} else {
generateHtmlResponse(request, response);
}
@@ -39,8 +50,9 @@ private void generateHtmlResponse(HttpServletRequest request, HttpServletRespons
.append("<h1>About</h1>");

display.append(newTable());

JSONObject aboutData = AboutInfo.gatherAbout(request.getServerName());
try {
JSONObject aboutData = AboutInfo.gatherAbout(request.getServerName());
display.append(generateFromKeys(aboutData, true));
}
catch(JSONException e) {
@@ -76,6 +88,48 @@ private void generateJsonResponse(HttpServletRequest request, HttpServletRespons
}
}

private void generateCertResponse(HttpServletRequest request, HttpServletResponse response) {
try {
String alias = request.getServletPath().split("/")[2];
String certData = loadCertificate(alias);

if (certData != null) {
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/x-x509-ca-cert");

response.getOutputStream().print(certData);
} else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.getOutputStream().print("Could not find certificate with alias \"" + alias + "\" to download.");
}
}
catch(Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
log.warn("Exception occurred loading certificate: {}", e.getMessage());
}
}

private String loadCertificate(String alias) throws GeneralSecurityException, IOException {
Properties sslProps = DeployUtilities.loadTrayProperties();

if (sslProps != null) {
KeyStore jks = KeyStore.getInstance("jks");
jks.load(new FileInputStream(new File(sslProps.getProperty("wss.keystore"))), sslProps.getProperty("wss.storepass").toCharArray());

if (jks.isCertificateEntry(alias)) {
Certificate cert = jks.getCertificate(alias);

return "-----BEGIN CERTIFICATE-----" + System.lineSeparator() +
This conversation was marked as resolved by tresf

This comment has been minimized.

Copy link
@tresf

tresf Jan 25, 2019

Author Contributor

@bberenz did you mean to leave in this System.lineSeparator()?

This comment has been minimized.

Copy link
@bberenz

bberenz Jan 25, 2019

Member

Nope, must of missed resetting it when I reverted a few changes.

new String(Base64.encodeBase64(cert.getEncoded(), true), StandardCharsets.UTF_8) +
"-----END CERTIFICATE-----";
} else {
return null;
}
} else {
return null;
}
}

private StringBuilder generateFromKeys(JSONObject obj, boolean printTitle) throws JSONException {
StringBuilder rows = new StringBuilder();

@@ -90,6 +144,9 @@ private StringBuilder generateFromKeys(JSONObject obj, boolean printTitle) throw
if (obj.optJSONObject(key) != null) {
rows.append(generateFromKeys(obj.getJSONObject(key), false));
} else {
if ("encoding".equals(key)) { //special case - replace with a "Download" button
obj.put(key, "<a href='/cert/" + obj.optString("alias") + "'>Download certificate</a>");
}
rows.append(contentRow(key, obj.get(key)));
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.