This is an automated email from the ASF dual-hosted git repository.
pzampino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new de3c78cbc KNOX-3085: Metadata API should return the certificate chain
(if any) instead of only the configured Knox instance certificate (#988)
de3c78cbc is described below
commit de3c78cbc44098d273f520b0510cad9519843849
Author: Phil Zampino <[email protected]>
AuthorDate: Thu Jan 23 10:53:10 2025 -0500
KNOX-3085: Metadata API should return the certificate chain (if any)
instead of only the configured Knox instance certificate (#988)
Co-authored-by: Sandor Molnar <[email protected]>
---
.../service/metadata/KnoxMetadataResource.java | 42 +++++------
.../java/org/apache/knox/gateway/shell/KnoxSh.java | 63 +---------------
.../knox/gateway/util/X509CertificateUtil.java | 84 +++++++++++++++++++++-
3 files changed, 100 insertions(+), 89 deletions(-)
diff --git
a/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/KnoxMetadataResource.java
b/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/KnoxMetadataResource.java
index 7af07fc0b..03846150e 100644
---
a/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/KnoxMetadataResource.java
+++
b/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/KnoxMetadataResource.java
@@ -63,8 +63,6 @@ import org.apache.knox.gateway.services.ServiceType;
import org.apache.knox.gateway.services.registry.ServiceDefinitionRegistry;
import org.apache.knox.gateway.services.security.AliasService;
import org.apache.knox.gateway.services.security.AliasServiceException;
-import org.apache.knox.gateway.services.security.KeystoreService;
-import org.apache.knox.gateway.services.security.KeystoreServiceException;
import org.apache.knox.gateway.services.security.token.impl.TokenMAC;
import org.apache.knox.gateway.services.topology.TopologyService;
import org.apache.knox.gateway.topology.Service;
@@ -138,20 +136,17 @@ public class KnoxMetadataResource {
@Produces(APPLICATION_OCTET_STREAM)
@Path("publicCert")
public Response getPublicCertification(@QueryParam("type")
@DefaultValue("pem") String certType) {
- final GatewayServices gatewayServices = (GatewayServices)
request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
- if (gatewayServices != null) {
- final GatewayConfig config = (GatewayConfig)
request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
- final Certificate certificate = getPublicCertificate(gatewayServices,
config);
- if (certificate != null) {
- if ("pem".equals(certType)) {
- generateCertificatePem(certificate, config);
- return generateSuccessFileDownloadResponse(pemFilePath);
- } else if ("jks".equals(certType)) {
- generateCertificateJks(certificate, config);
- return generateSuccessFileDownloadResponse(jksFilePath);
- } else {
- return generateFailureFileDownloadResponse(Status.BAD_REQUEST,
"Invalid certification type provided!");
- }
+ final GatewayConfig config = (GatewayConfig)
request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
+ final Certificate[] certificateChain = getPublicCertificates();
+ if (certificateChain != null) {
+ if ("pem".equals(certType)) {
+ generateCertificatePem(certificateChain, config);
+ return generateSuccessFileDownloadResponse(pemFilePath);
+ } else if ("jks".equals(certType)) {
+ generateCertificateJks(certificateChain, config);
+ return generateSuccessFileDownloadResponse(jksFilePath);
+ } else {
+ return generateFailureFileDownloadResponse(Status.BAD_REQUEST,
"Invalid certification type provided!");
}
}
return generateFailureFileDownloadResponse(Status.SERVICE_UNAVAILABLE,
"Could not generate public certificate");
@@ -169,32 +164,31 @@ public class KnoxMetadataResource {
return responseBuilder.build();
}
- private Certificate getPublicCertificate(GatewayServices gatewayServices,
GatewayConfig config) {
+ private Certificate[] getPublicCertificates() {
try {
- final KeystoreService keystoreService =
gatewayServices.getService(ServiceType.KEYSTORE_SERVICE);
- return
keystoreService.getKeystoreForGateway().getCertificate(config.getIdentityKeyAlias());
- } catch (KeyStoreException | KeystoreServiceException e) {
+ return
X509CertificateUtil.fetchPublicCertsFromServer(request.getRequestURL().toString(),
true, null);
+ } catch (Exception e) {
LOG.failedToFetchPublicCert(e.getMessage(), e);
return null;
}
}
- private void generateCertificatePem(Certificate certificate, GatewayConfig
gatewayConfig) {
+ private void generateCertificatePem(Certificate[] certificateChain,
GatewayConfig gatewayConfig) {
try {
if (pemFilePath == null || !pemFilePath.toFile().exists()) {
pemFilePath = Paths.get(gatewayConfig.getGatewaySecurityDir(),
"gateway-client-trust.pem");
- X509CertificateUtil.writeCertificateToFile(certificate,
pemFilePath.toFile());
+ X509CertificateUtil.writeCertificatesToFile(certificateChain,
pemFilePath.toFile());
}
} catch (CertificateEncodingException | IOException e) {
LOG.failedToGeneratePublicCert("PEM", e.getMessage(), e);
}
}
- private void generateCertificateJks(Certificate certificate, GatewayConfig
gatewayConfig) {
+ private void generateCertificateJks(Certificate[] certificateChain,
GatewayConfig gatewayConfig) {
try {
if (jksFilePath == null || !jksFilePath.toFile().exists()) {
jksFilePath = Paths.get(gatewayConfig.getGatewaySecurityDir(),
"gateway-client-trust.jks");
- X509CertificateUtil.writeCertificateToJks(certificate,
jksFilePath.toFile());
+ X509CertificateUtil.writeCertificatesToJks(certificateChain,
jksFilePath.toFile(), null);
}
} catch (IOException | KeyStoreException | NoSuchAlgorithmException |
CertificateException e) {
LOG.failedToGeneratePublicCert("JKS", e.getMessage(), e);
diff --git
a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxSh.java
b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxSh.java
index 205b80058..ec169b51d 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxSh.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxSh.java
@@ -31,14 +31,10 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
-import java.net.Socket;
-import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
-import java.security.KeyStore;
-import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@@ -51,13 +47,6 @@ import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-
public class KnoxSh {
private static final String USAGE_PREFIX = "KnoxSh {cmd} [options]";
@@ -192,7 +181,7 @@ public class KnoxSh {
public void execute() throws Exception {
String result = GATEWAY_CERT_NOT_EXPORTED;
try {
- final X509Certificate[] gatewayServerPublicCerts =
fetchPublicCertsFromGatewayServer();
+ final X509Certificate[] gatewayServerPublicCerts =
X509CertificateUtil.fetchPublicCertsFromServer(gateway, false, out);
if (gatewayServerPublicCerts != null) {
final File trustStoreFile =
ClientTrustStoreHelper.getClientTrustStoreFile();
final String trustStorePassword =
ClientTrustStoreHelper.getClientTrustStoreFilePassword();
@@ -205,60 +194,10 @@ public class KnoxSh {
out.println(result);
}
- private X509Certificate[] fetchPublicCertsFromGatewayServer() throws
Exception {
- final TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- trustManagerFactory.init((KeyStore) null);
- final X509TrustManager defaultTrustManager = (X509TrustManager)
trustManagerFactory.getTrustManagers()[0];
- final CertificateChainAwareTrustManager trustManagerWithCertificateChain
= new CertificateChainAwareTrustManager(defaultTrustManager);
- final SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, new TrustManager[] {
trustManagerWithCertificateChain }, null);
-
- final URL url = new URL(gateway);
- final int port = url.getPort() == -1 ? url.getDefaultPort() :
url.getPort();
- out.println("Opening connection to " + url.getHost() + ":" + port +
"...");
- try (Socket socket =
sslContext.getSocketFactory().createSocket(url.getHost(), port)) {
- socket.setSoTimeout(10000);
- out.println("Starting SSL handshake...");
- ((SSLSocket) socket).startHandshake();
- out.println("No errors, certificate is already trusted");
- return null; //we already trust the given site's certs; it does not
make sense to build a new truststore
- } catch (SSLException e) {
- // NOP; this is expected in case the gateway server's certificate is
not in the
- // trust store the JVM uses
- }
-
- return trustManagerWithCertificateChain.serverCertificateChain;
- }
-
@Override
public String getUsage() {
return USAGE + ":\n\n" + DESC;
}
-
- private class CertificateChainAwareTrustManager implements
X509TrustManager {
- private final X509TrustManager defaultTrustManager;
- private X509Certificate[] serverCertificateChain;
-
- CertificateChainAwareTrustManager(X509TrustManager tm) {
- this.defaultTrustManager = tm;
- }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return defaultTrustManager.getAcceptedIssuers();
- }
-
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
- defaultTrustManager.checkClientTrusted(chain, authType);
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
- this.serverCertificateChain = chain;
- defaultTrustManager.checkServerTrusted(chain, authType);
- }
- }
}
private class KnoxInit extends Command {
diff --git
a/gateway-util-common/src/main/java/org/apache/knox/gateway/util/X509CertificateUtil.java
b/gateway-util-common/src/main/java/org/apache/knox/gateway/util/X509CertificateUtil.java
index 741511bd4..e0ddaa303 100644
---
a/gateway-util-common/src/main/java/org/apache/knox/gateway/util/X509CertificateUtil.java
+++
b/gateway-util-common/src/main/java/org/apache/knox/gateway/util/X509CertificateUtil.java
@@ -20,12 +20,15 @@ package org.apache.knox.gateway.util;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.KeyPair;
@@ -41,6 +44,13 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
import org.apache.commons.codec.binary.Base64;
import org.apache.knox.gateway.i18n.GatewayUtilCommonMessages;
import org.apache.knox.gateway.i18n.messages.MessagesFactory;
@@ -383,14 +393,24 @@ public class X509CertificateUtil {
}
public static void writeCertificateToFile(Certificate cert, final File file)
+ throws CertificateEncodingException, IOException {
+ writeCertificatesToFile(new Certificate[] {cert}, file);
+ }
+
+ public static void writeCertificatesToFile(Certificate[] certs, final File
file)
throws CertificateEncodingException, IOException {
- byte[] bytes = cert.getEncoded();
- Base64 encoder = new Base64( 76, "\n".getBytes( StandardCharsets.US_ASCII
) );
+ final Base64 encoder = new Base64( 76, "\n".getBytes(
StandardCharsets.US_ASCII ) );
try(OutputStream out = Files.newOutputStream(file.toPath()) ) {
+ for (Certificate cert : certs) {
+ saveCertificate(out, cert.getEncoded(), encoder);
+ }
+ }
+ }
+
+ private static void saveCertificate(OutputStream out, byte[] bytes, Base64
encoder) throws IOException {
out.write( "-----BEGIN CERTIFICATE-----\n".getBytes(
StandardCharsets.US_ASCII ) );
out.write( encoder.encodeToString( bytes ).getBytes(
StandardCharsets.US_ASCII ) );
out.write( "-----END CERTIFICATE-----\n".getBytes(
StandardCharsets.US_ASCII ) );
- }
}
/*
@@ -471,4 +491,62 @@ public class X509CertificateUtil {
return false;
}
}
+
+ public static X509Certificate[] fetchPublicCertsFromServer(String serverUrl,
boolean forceReturnCert, PrintStream out) throws Exception {
+ final TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init((KeyStore) null);
+ final X509TrustManager defaultTrustManager = (X509TrustManager)
trustManagerFactory.getTrustManagers()[0];
+ final CertificateChainAwareTrustManager trustManagerWithCertificateChain
= new CertificateChainAwareTrustManager(defaultTrustManager);
+ final SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, new TrustManager[] {
trustManagerWithCertificateChain }, null);
+
+ final URL url = new URL(serverUrl);
+ final int port = url.getPort() == -1 ? url.getDefaultPort() :
url.getPort();
+ logOutput(out, "Opening connection to " + url.getHost() + ":" + port +
"...");
+ try (Socket socket =
sslContext.getSocketFactory().createSocket(url.getHost(), port)) {
+ socket.setSoTimeout(10000);
+ logOutput(out, "Starting SSL handshake...");
+ ((SSLSocket) socket).startHandshake();
+ logOutput(out, "No errors, certificate is already trusted");
+ if (!forceReturnCert) {
+ return null; //we already trust the given site's certs; it does
not make sense to build a new truststore
+ }
+ } catch (SSLException e) {
+ // NOP; this is expected in case the gateway server's certificate is
not in the
+ // trust store the JVM uses
+ }
+
+ return trustManagerWithCertificateChain.serverCertificateChain;
+ }
+
+ private static void logOutput(PrintStream out, String message) {
+ if (out != null) {
+ out.println(message);
+ }
+ }
+
+ private static class CertificateChainAwareTrustManager implements
X509TrustManager {
+ private final X509TrustManager defaultTrustManager;
+ private X509Certificate[] serverCertificateChain;
+
+ CertificateChainAwareTrustManager(X509TrustManager tm) {
+ this.defaultTrustManager = tm;
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return defaultTrustManager.getAcceptedIssuers();
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
+ defaultTrustManager.checkClientTrusted(chain, authType);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
+ this.serverCertificateChain = chain;
+ defaultTrustManager.checkServerTrusted(chain, authType);
+ }
+ }
}