Skip to content

Commit

Permalink
Certiticates > Add more info for static and manual certificates in UI (
Browse files Browse the repository at this point in the history
…#257)

* Certiticates > Add Expiring Date in static and manual certificates UI #196

* unit tests

* UI changes

Co-authored-by: Paolo Venturi <[email protected]>
  • Loading branch information
pv3ntur1 and Paolo Venturi authored Feb 23, 2021
1 parent 24df05f commit 8bf762a
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 190 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import static org.carapaceproxy.server.certificates.DynamicCertificatesManager.DEFAULT_DAYS_BEFORE_RENEWAL;
import static org.carapaceproxy.utils.CertificatesUtils.loadKeyStoreFromFile;
import java.io.IOException;
import java.security.KeyStore;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -152,7 +155,7 @@ public void setSerialNumber(String serialNumber) {
public Map<String, CertificateBean> getAllCertificates() {
HttpProxyServer server = (HttpProxyServer) context.getAttribute("server");
RuntimeServerConfiguration conf = server.getCurrentConfiguration();
DynamicCertificatesManager dynamicCertificateManager = server.getDynamicCertificatesManager();
DynamicCertificatesManager dCManager = server.getDynamicCertificatesManager();
Map<String, CertificateBean> res = new HashMap<>();
for (Map.Entry<String, SSLCertificateConfiguration> certificateEntry : conf.getCertificates().entrySet()) {
SSLCertificateConfiguration certificate = certificateEntry.getValue();
Expand All @@ -163,33 +166,45 @@ public Map<String, CertificateBean> getAllCertificates() {
certificate.isDynamic(),
certificate.getFile()
);
if (certificate.isDynamic()) {
certBean.setStatus(certificateStateToString(dynamicCertificateManager.getStateOfCertificate(certificate.getId())));
try {
CertificateData cert = dynamicCertificateManager.getCertificateDataForDomain(certificate.getId());
fillDynamicCertificateBean(certBean, cert);
} catch (GeneralSecurityException e) {
LOG.log(Level.SEVERE, "Unable to read Keystore for certificate {0}. Reason: {1}", new Object[]{certificate.getId(), e});
}
}
fillCertificateBean(certBean, certificate, dCManager, server);
res.put(certificateEntry.getKey(), certBean);
}

return res;
}

private void fillDynamicCertificateBean(CertificateBean bean, CertificateData cert) throws GeneralSecurityException {
if (cert == null) {
return;
}
Certificate[] chain = base64DecodeCertificateChain(cert.getChain());
if (chain != null && chain.length > 0) {
X509Certificate _cert = ((X509Certificate) chain[0]);
bean.setExpiringDate(_cert.getNotAfter().toString());
bean.setSerialNumber(_cert.getSerialNumber().toString(16).toUpperCase()); // HEX
}
if (!cert.isManual()) { // ACME
bean.setDaysBeforeRenewal(cert.getDaysBeforeRenewal() + "");
private static void fillCertificateBean(CertificateBean bean, SSLCertificateConfiguration certificate, DynamicCertificatesManager dCManager, HttpProxyServer server) {
try {
Certificate[] chain;
DynamicCertificateState state = null;
if (certificate.isDynamic()) {
CertificateData cert = dCManager.getCertificateDataForDomain(certificate.getId());
if (cert == null) {
return;
}
chain = base64DecodeCertificateChain(cert.getChain());
state = cert.getState();
} else {
KeyStore keystore = loadKeyStoreFromFile(certificate.getFile(), certificate.getPassword(), server.getBasePath());
if (keystore == null) {
return;
}
chain = CertificatesUtils.readChainFromKeystore(keystore);
}
if (chain != null && chain.length > 0) {
X509Certificate _cert = ((X509Certificate) chain[0]);
bean.setExpiringDate(_cert.getNotAfter().toString());
bean.setSerialNumber(_cert.getSerialNumber().toString(16).toUpperCase()); // HEX
if (!certificate.isAcme()) {
state = CertificatesUtils.isCertificateExpired(chain, 0) ? DynamicCertificateState.EXPIRED : DynamicCertificateState.AVAILABLE;
}
}
if (certificate.isAcme()) {
bean.setDaysBeforeRenewal(certificate.getDaysBeforeRenewal() + "");
}
bean.setStatus(certificateStateToString(state));
} catch (GeneralSecurityException | IOException ex) {
LOG.log(Level.SEVERE, "Unable to read Keystore for certificate {0}. Reason: {1}", new Object[]{certificate.getId(), ex});
}
}

Expand Down Expand Up @@ -220,6 +235,7 @@ public Response downloadCertificateById(@PathParam("certId") String certId) thro
private CertificateBean findCertificateById(String certId) {
HttpProxyServer server = (HttpProxyServer) context.getAttribute("server");
SSLCertificateConfiguration certificate = server.getCurrentConfiguration().getCertificates().get(certId);
DynamicCertificatesManager dCManager = server.getDynamicCertificatesManager();
if (certificate != null) {
CertificateBean certBean = new CertificateBean(
certificate.getId(),
Expand All @@ -228,15 +244,7 @@ private CertificateBean findCertificateById(String certId) {
certificate.isDynamic(),
certificate.getFile()
);
if (certificate.isDynamic()) {
certBean.setStatus(certificateStateToString(server.getDynamicCertificatesManager().getStateOfCertificate(certificate.getId())));
try {
CertificateData cert = server.getDynamicCertificatesManager().getCertificateDataForDomain(certificate.getId());
fillDynamicCertificateBean(certBean, cert);
} catch (GeneralSecurityException e) {
LOG.log(Level.SEVERE, "Unable to read Keystore for certificate {0}. Reason: {1}", new Object[]{certificate.getId(), e});
}
}
fillCertificateBean(certBean, certificate, dCManager, server);
return certBean;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package org.carapaceproxy.server;

import static org.carapaceproxy.utils.CertificatesUtils.loadKeyStoreData;
import com.google.common.annotations.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.bootstrap.ServerBootstrap;
Expand Down Expand Up @@ -47,18 +48,12 @@
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Promise;
import io.prometheus.client.Gauge;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand All @@ -75,6 +70,7 @@
import org.carapaceproxy.utils.PrometheusUtils;
import static org.carapaceproxy.utils.CertificatesUtils.readChainFromKeystore;
import io.netty.handler.timeout.IdleStateHandler;
import static org.carapaceproxy.utils.CertificatesUtils.loadKeyStoreFromFile;

/**
*
Expand Down Expand Up @@ -122,43 +118,35 @@ private SslContext bootSslContext(NetworkListenerConfiguration listener, SSLCert
int port = listener.getPort() + parent.getListenersOffsetPort();
String sslCiphers = listener.getSslCiphers();

String trustStrorePassword = listener.getSslTrustorePassword();
String trustStoreFile = listener.getSslTrustoreFile();
File trustStoreCertFile = null;
boolean caFileConfigured = trustStoreFile != null && !trustStoreFile.isEmpty();
if (caFileConfigured) {
trustStoreCertFile = trustStoreFile.startsWith("/") ? new File(trustStoreFile) : new File(basePath, trustStoreFile);
trustStoreCertFile = trustStoreCertFile.getAbsoluteFile();
}

try {
KeyManagerFactory keyFactory;

// Try to find certificate data on db
byte[] keystoreContent = parent.getDynamicCertificatesManager().getCertificateForDomain(certificate.getId());
Certificate[] chain;
KeyStore keystore;
if (keystoreContent != null) {
LOG.log(Level.INFO, "start SSL with dynamic certificate id " + certificate.getId() + ", on listener " + listener.getHost() + ":" + port + " OCSP " + listener.isOcsp());
chain = readChainFromKeystore(loadKeyStore("PKCS12", keystoreContent, certificate.getPassword()));
keyFactory = initKeyManagerFactory("PKCS12", keystoreContent, certificate.getPassword());
keystore = loadKeyStoreData(keystoreContent, certificate.getPassword());
} else {
if (certificate.isDynamic()) { // fallback to default certificate
certificate = currentConfiguration.getCertificates().get(listener.getDefaultCertificate());
if (certificate == null) {
throw new ConfigurationNotValidException("Unable to boot SSL context for listener " + listener.getHost() + ": no default certificate setup.");
}
}
String certificateFile = certificate.getFile();
File sslCertFile = certificateFile.startsWith("/") ? new File(certificateFile) : new File(basePath, certificateFile);
sslCertFile = sslCertFile.getAbsoluteFile();
LOG.log(Level.INFO, "start SSL with certificate id " + certificate.getId() + ", on listener " + listener.getHost() + ":" + port + " file=" + sslCertFile + " OCSP " + listener.isOcsp());
chain = readChainFromKeystore(loadKeyStore("PKCS12", sslCertFile, certificate.getPassword()));
keyFactory = initKeyManagerFactory("PKCS12", sslCertFile, certificate.getPassword());
LOG.log(Level.INFO, "start SSL with certificate id {0}, on listener {1}:{2} file={3} OCSP {4}",
new Object[]{certificate.getId(), listener.getHost(), port, certificate.getFile(), listener.isOcsp()}
);
keystore = loadKeyStoreFromFile(certificate.getFile(), certificate.getPassword(), basePath);
}
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keystore, certificate.getPassword().toCharArray());

LOG.log(Level.INFO, "loading trustore from {0}", listener.getSslTrustoreFile());
TrustManagerFactory trustManagerFactory = null;
if (caFileConfigured) {
LOG.log(Level.INFO, "loading trustore from " + trustStoreCertFile);
trustManagerFactory = initTrustManagerFactory("PKCS12", trustStoreCertFile, trustStrorePassword);
KeyStore truststore = loadKeyStoreFromFile(listener.getSslTrustoreFile(), listener.getSslTrustorePassword(), basePath);
if (truststore != null) {
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(truststore);
}

List<String> ciphers = null;
Expand All @@ -174,6 +162,7 @@ private SslContext bootSslContext(NetworkListenerConfiguration listener, SSLCert
.protocols(listener.getSslProtocols())
.ciphers(ciphers).build();

Certificate[] chain = readChainFromKeystore(keystore);
if (listener.isOcsp() && OpenSsl.isOcspSupported() && chain != null && chain.length > 0) {
parent.getOcspStaplingManager().addCertificateForStapling(chain);
Attribute<Object> attr = sslContext.attributes().attr(AttributeKey.valueOf(OCSP_CERTIFICATE_CHAIN));
Expand Down Expand Up @@ -260,57 +249,6 @@ protected SslHandler newSslHandler(SslContext context, ByteBufAllocator allocato

}

private KeyManagerFactory initKeyManagerFactory(String keyStoreType, File keyStoreLocation,
String keyStorePassword) throws SecurityException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableKeyException {
KeyStore ks = loadKeyStore(keyStoreType, keyStoreLocation, keyStorePassword);
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyStorePassword.toCharArray());
return kmf;
}

private KeyManagerFactory initKeyManagerFactory(String keyStoreType, byte[] keyStoreData,
String keyStorePassword) throws SecurityException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableKeyException {
KeyStore ks = loadKeyStore(keyStoreType, keyStoreData, keyStorePassword);
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyStorePassword.toCharArray());
return kmf;
}

private TrustManagerFactory initTrustManagerFactory(String trustStoreType, File trustStoreLocation,
String trustStorePassword)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, SecurityException {
TrustManagerFactory tmf;

// Initialize trust store
KeyStore ts = loadKeyStore(trustStoreType, trustStoreLocation, trustStorePassword);
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);

return tmf;
}

private static KeyStore loadKeyStore(String keyStoreType, File keyStoreLocation, String keyStorePassword)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore ks = KeyStore.getInstance(keyStoreType);
try (FileInputStream in = new FileInputStream(keyStoreLocation)) {
ks.load(in, keyStorePassword.trim().toCharArray());
}
return ks;
}

private static KeyStore loadKeyStore(String keyStoreType, byte[] keyStoreData, String keyStorePassword)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore ks = KeyStore.getInstance(keyStoreType);
try (ByteArrayInputStream is = new ByteArrayInputStream(keyStoreData)) {
ks.load(is, keyStorePassword.trim().toCharArray());
}
return ks;
}

public int getLocalPort() {
for (Channel c : listeningChannels.values()) {
InetSocketAddress addr = (InetSocketAddress) c.localAddress();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package org.carapaceproxy.server.certificates;

import static org.carapaceproxy.configstore.ConfigurationStoreUtils.base64DecodeCertificateChain;
import static org.carapaceproxy.configstore.ConfigurationStoreUtils.base64EncodeCertificateChain;
import static org.carapaceproxy.server.certificates.DynamicCertificateState.AVAILABLE;
import static org.carapaceproxy.server.certificates.DynamicCertificateState.DNS_CHALLENGE_WAIT;
Expand Down Expand Up @@ -322,7 +323,7 @@ private void certificatesLifecycle() {
break;
}
case AVAILABLE: { // certificate saved/available/not expired
if (isCertificateExpired(cert)) {
if (isCertificateExpired(base64DecodeCertificateChain(cert.getChain()), cert.getDaysBeforeRenewal())) {
cert.setState(EXPIRED);
notifyCertAvailChanged = true; // all other peers need to know that this cert is expired.
} else {
Expand Down
Loading

0 comments on commit 8bf762a

Please sign in to comment.