diff --git a/appserver/admin/gf_template/src/main/resources/config/domain.xml b/appserver/admin/gf_template/src/main/resources/config/domain.xml
index 467828bd330..a8aa51aedfb 100644
--- a/appserver/admin/gf_template/src/main/resources/config/domain.xml
+++ b/appserver/admin/gf_template/src/main/resources/config/domain.xml
@@ -144,7 +144,9 @@
-
+
+
+
@@ -370,7 +372,9 @@
-
+
+
+
diff --git a/appserver/admin/gf_template_web/src/main/resources/config/domain.xml b/appserver/admin/gf_template_web/src/main/resources/config/domain.xml
index 4e659f97cc4..10b9180bee0 100644
--- a/appserver/admin/gf_template_web/src/main/resources/config/domain.xml
+++ b/appserver/admin/gf_template_web/src/main/resources/config/domain.xml
@@ -139,7 +139,9 @@
-
+
+
+
@@ -361,7 +363,9 @@
-
+
+
+
diff --git a/appserver/tests/payara-samples/samples/client-certificate-validator/pom.xml b/appserver/tests/payara-samples/samples/client-certificate-validator/pom.xml
new file mode 100644
index 00000000000..36f7d8e0841
--- /dev/null
+++ b/appserver/tests/payara-samples/samples/client-certificate-validator/pom.xml
@@ -0,0 +1,61 @@
+
+
+
+
+ fish.payara.samples
+ payara-samples-profiled-tests
+ 5.2021.8-SNAPSHOT
+
+ 4.0.0
+
+ war
+ fish.payara.samples.client-certificate-validator
+ client-certificate-validator
+ Payara-Samples - Payara - Client Certificate Validator
+
+
+ 8
+ 8
+
+
+
\ No newline at end of file
diff --git a/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/java/fish/payara/samples/security/validation/HelloServlet.java b/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/java/fish/payara/samples/security/validation/HelloServlet.java
new file mode 100644
index 00000000000..de2dc9f5184
--- /dev/null
+++ b/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/java/fish/payara/samples/security/validation/HelloServlet.java
@@ -0,0 +1,61 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://github.com/payara/Payara/blob/master/LICENSE.txt
+ * See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at glassfish/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * The Payara Foundation designates this particular file as subject to the "Classpath"
+ * exception as provided by the Payara Foundation in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package fish.payara.samples.security.validation;
+
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ *
+ * @author James Hillyard
+ */
+
+@WebServlet(value = "/secure/hello")
+public class HelloServlet extends HttpServlet {
+
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ resp.getOutputStream().print("Hello " + req.getRemoteUser());
+ }
+}
diff --git a/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/webapp/WEB-INF/beans.xml b/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 00000000000..ec9d5950981
--- /dev/null
+++ b/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
diff --git a/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/webapp/WEB-INF/web.xml b/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000000..236dfc752aa
--- /dev/null
+++ b/appserver/tests/payara-samples/samples/client-certificate-validator/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+ CLIENT-CERT
+ certificate
+
+
+
+
+
+ secureAccess
+ /secure/*
+
+
+ myRole
+
+
+
+
+ myRole
+
+
\ No newline at end of file
diff --git a/appserver/tests/payara-samples/samples/client-certificate-validator/src/test/java/fish/payara/samples/security/validation/ClientValidationTest.java b/appserver/tests/payara-samples/samples/client-certificate-validator/src/test/java/fish/payara/samples/security/validation/ClientValidationTest.java
new file mode 100644
index 00000000000..0b25607ac95
--- /dev/null
+++ b/appserver/tests/payara-samples/samples/client-certificate-validator/src/test/java/fish/payara/samples/security/validation/ClientValidationTest.java
@@ -0,0 +1,159 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://github.com/payara/Payara/blob/master/LICENSE.txt
+ * See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at glassfish/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * The Payara Foundation designates this particular file as subject to the "Classpath"
+ * exception as provided by the Payara Foundation in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package fish.payara.samples.security.validation;
+
+import fish.payara.samples.PayaraArquillianTestRunner;
+import fish.payara.samples.SecurityUtils;
+import fish.payara.samples.ServerOperations;
+import fish.payara.samples.SincePayara;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.InSequence;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.omnifaces.utils.security.Certificates;
+
+import javax.net.ssl.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ * @author James Hillyard
+ */
+
+@RunWith(PayaraArquillianTestRunner.class)
+@SincePayara("5.2021.8")
+public class ClientValidationTest {
+
+ private static String certPath;
+
+ private static final String CERTIFICATE_ALIAS = "omnikey";
+ private static final String LOCALHOST_URL = "https://localhost:8181/security/secure/hello";
+
+ @Deployment
+ public static WebArchive deploy() {
+ return ShrinkWrap.create(WebArchive.class, "security.war")
+ .addPackage(ClientValidationTest.class.getPackage())
+ .addPackages(true, "org.bouncycastle")
+ .addPackages(true, "com.gargoylesoftware")
+ .addPackages(true, "net.sourceforge.htmlunit")
+ .addPackages(true, "org.eclipse")
+ .addPackages(true, PayaraArquillianTestRunner.class.getPackage())
+ .addClasses(ServerOperations.class, SecurityUtils.class, Certificates.class)
+ .addAsWebInfResource(new File("src/main/webapp", "WEB-INF/web.xml"))
+ .addAsWebInfResource(new File("src/main/webapp", "WEB-INF/beans.xml"));
+ }
+
+ @Test
+ @InSequence(1)
+ public void generateCertsInTrustStore() throws IOException {
+ if (ServerOperations.isServer()) {
+ certPath = ServerOperations.generateClientKeyStore(true, true, CERTIFICATE_ALIAS);
+ }
+ }
+
+ @Test
+ @InSequence(2)
+ public void validationFailTest() throws Exception {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ SSLSocketFactory sslSocketFactory = getSslSocketFactory(certPath, kmf, CERTIFICATE_ALIAS);
+ assertEquals(401, callEndpoint(sslSocketFactory));
+ assertTrue(checkForAPIValidationFailure());
+
+ }
+
+ private static int callEndpoint(SSLSocketFactory sslSocketFactory) throws IOException {
+ URL url = new URL(LOCALHOST_URL);
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setSSLSocketFactory(sslSocketFactory);
+ return connection.getResponseCode();
+ }
+
+ private static SSLSocketFactory getSslSocketFactory(String certPath, KeyManagerFactory kmf, String alias) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException {
+ String keystorePassword = "changeit";
+
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(new FileInputStream(certPath), keystorePassword.toCharArray());
+ kmf.init(keyStore, keystorePassword.toCharArray());
+
+ KeyManager[] keyManagers = kmf.getKeyManagers();
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ ctx.init(new KeyManager[]{new MyKeyManager((X509ExtendedKeyManager) keyManagers[0], alias)}, null, null);
+ return ctx.getSocketFactory();
+ }
+
+ /**
+ * @return true if the correct warning is found in the logs
+ * @throws IOException
+ */
+ public boolean checkForAPIValidationFailure() throws IOException {
+ List log = viewLog();
+ for (String line : log) {
+ if (line.contains("Certificate Validation Failed via API")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return the contents of the server log
+ */
+ private List viewLog() throws IOException {
+ Path serverLog = ServerOperations.getDomainPath("logs/server.log");
+ return Files.readAllLines(serverLog);
+ }
+
+
+}
diff --git a/appserver/tests/payara-samples/samples/client-certificate-validator/src/test/java/fish/payara/samples/security/validation/MyKeyManager.java b/appserver/tests/payara-samples/samples/client-certificate-validator/src/test/java/fish/payara/samples/security/validation/MyKeyManager.java
new file mode 100644
index 00000000000..3ab79346abe
--- /dev/null
+++ b/appserver/tests/payara-samples/samples/client-certificate-validator/src/test/java/fish/payara/samples/security/validation/MyKeyManager.java
@@ -0,0 +1,90 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://github.com/payara/Payara/blob/master/LICENSE.txt
+ * See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at glassfish/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * The Payara Foundation designates this particular file as subject to the "Classpath"
+ * exception as provided by the Payara Foundation in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package fish.payara.samples.security.validation;
+
+
+import javax.net.ssl.X509ExtendedKeyManager;
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+public class MyKeyManager extends X509ExtendedKeyManager {
+
+ private final X509ExtendedKeyManager wrapped;
+
+ private final String certAlias;
+
+ public MyKeyManager(X509ExtendedKeyManager wrapped, String certAlias) {
+ this.wrapped = wrapped;
+ this.certAlias = certAlias;
+ }
+
+ @Override
+ public String[] getClientAliases(String s, Principal[] principals) {
+ return wrapped.getClientAliases(s, principals);
+ }
+
+ @Override
+ public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
+ return certAlias;
+ }
+
+ @Override
+ public String[] getServerAliases(String s, Principal[] principals) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String s) {
+ return wrapped.getCertificateChain(s);
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String s) {
+ return wrapped.getPrivateKey(s);
+ }
+}
diff --git a/appserver/tests/payara-samples/samples/pom.xml b/appserver/tests/payara-samples/samples/pom.xml
index 2b4cfb3da90..1183473325e 100644
--- a/appserver/tests/payara-samples/samples/pom.xml
+++ b/appserver/tests/payara-samples/samples/pom.xml
@@ -41,6 +41,7 @@
rolesallowed-unprotected-methods
microprofile-rc-ft
multiple-keystores
+ client-certificate-validator
diff --git a/appserver/tests/payara-samples/test-utils/src/main/java/fish/payara/samples/SecurityUtils.java b/appserver/tests/payara-samples/test-utils/src/main/java/fish/payara/samples/SecurityUtils.java
index bf327dc8f2f..ce91db17f90 100644
--- a/appserver/tests/payara-samples/test-utils/src/main/java/fish/payara/samples/SecurityUtils.java
+++ b/appserver/tests/payara-samples/test-utils/src/main/java/fish/payara/samples/SecurityUtils.java
@@ -41,8 +41,7 @@
package fish.payara.samples;
import java.net.MalformedURLException;
import java.net.URL;
-import java.security.KeyPair;
-import java.security.PrivateKey;
+import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
@@ -57,8 +56,6 @@
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.omnifaces.utils.security.Certificates;
import static java.math.BigInteger.ONE;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
import static java.time.Instant.now;
import static java.time.temporal.ChronoUnit.DAYS;
@@ -92,6 +89,27 @@ public static X509Certificate createSelfSignedCertificate(KeyPair keys) {
}
}
+ public static X509Certificate createExpiredSelfSignedCertificate(KeyPair keys) {
+ try {
+ return new JcaX509CertificateConverter()
+ .setProvider(PROVIDER)
+ .getCertificate(
+ new X509v3CertificateBuilder(
+ new X500Name("CN=lfoo, OU=bar, O=kaz, L=zak, ST=lak, C=UK"),
+ ONE,
+ Date.from(now().minus(2, DAYS)),
+ Date.from(now().minus(1, DAYS)),
+ new X500Name("CN=lfoo, OU=bar, O=kaz, L=zak, ST=lak, C=UK"),
+ SubjectPublicKeyInfo.getInstance(keys.getPublic().getEncoded()))
+ .build(
+ new JcaContentSignerBuilder("SHA256WithRSA")
+ .setProvider(PROVIDER)
+ .build(keys.getPrivate())));
+ } catch (CertificateException | OperatorCreationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
static URL getHostFromCertificate(X509Certificate[] serverCertificateChain, URL existingURL) {
try {
URL httpsUrl = new URL(
diff --git a/appserver/tests/payara-samples/test-utils/src/main/java/fish/payara/samples/ServerOperations.java b/appserver/tests/payara-samples/test-utils/src/main/java/fish/payara/samples/ServerOperations.java
index b1e0403a430..ecfa5366bb8 100644
--- a/appserver/tests/payara-samples/test-utils/src/main/java/fish/payara/samples/ServerOperations.java
+++ b/appserver/tests/payara-samples/test-utils/src/main/java/fish/payara/samples/ServerOperations.java
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020-2021 Payara Foundation and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
@@ -95,6 +95,7 @@ public class ServerOperations {
= "Check 'payara.containerType' system property configuration";
private static final String CLIENT_CERTIFICATE_FILE = "payara-test-client.crt";
private static final String CLIENT_CERTIFICATE_PATH = "certificates";
+ private static final String DEFAULT_ALIAS = "arquillianClientTestCert";
public enum ServerType {
SERVER, MICRO
@@ -212,6 +213,10 @@ public static Path getDomainPath(String relativePathInDomain) {
}
static void addCertificateToContainerTrustStore(Certificate clientCertificate, KeyPair clientKeyPair) throws IOException {
+ addCertificateToContainerTrustStore(clientCertificate, clientKeyPair, DEFAULT_ALIAS);
+ }
+
+ static void addCertificateToContainerTrustStore(Certificate clientCertificate, KeyPair clientKeyPair, String alias) throws IOException {
if (runtimeType != RuntimeType.SERVER) {
throw new IllegalStateException(ERR_MESSAGE_UNSUPPORTED);
}
@@ -224,12 +229,12 @@ static void addCertificateToContainerTrustStore(Certificate clientCertificate, K
logger.info("*** Adding certificate to container trust store: " + cacertsPath.toAbsolutePath());
- KeyStore keyStore = null;
+ KeyStore keyStore;
try (InputStream in = new FileInputStream(cacertsPath.toAbsolutePath().toFile())) {
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(in, "changeit".toCharArray());
- keyStore.setCertificateEntry("arquillianClientTestCert", clientCertificate);
+ keyStore.setCertificateEntry(alias, clientCertificate);
keyStore.store(new FileOutputStream(cacertsPath.toAbsolutePath().toFile()), "changeit".toCharArray());
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
@@ -431,12 +436,31 @@ public static String createClientKeyStore() throws IOException {
}
public static String generateClientKeyStore(boolean addToContainer) throws IOException {
+ return generateClientKeyStore(addToContainer, false, DEFAULT_ALIAS);
+ }
+
+ /**
+ *
+ * @param addToContainer Adds the client certificate to the truststore
+ * @param expired if true will create an expired SSL certificate
+ * @param alias the alias of the keystore, useful to not cause conflict with the cert of another payara-sample
+ * @return path to the new keystore created
+ * @throws IOException
+ */
+ public static String generateClientKeyStore(boolean addToContainer, boolean expired, String alias) throws IOException {
// ### Generate keys for the client, create a certificate, and add those to a new local key store
// Generate a Private/Public key pair for the client
KeyPair clientKeyPair = SecurityUtils.generateRandomRSAKeys();
// Create a certificate containing the client public key and signed with the private key
- X509Certificate clientCertificate = SecurityUtils.createSelfSignedCertificate(clientKeyPair);
+ X509Certificate clientCertificate;
+ if(expired) {
+ clientCertificate = SecurityUtils.createExpiredSelfSignedCertificate(clientKeyPair);
+ }
+ else {
+ clientCertificate = SecurityUtils.createSelfSignedCertificate(clientKeyPair);
+ }
+
if (addToContainer) {
if (runtimeType != RuntimeType.SERVER) {
@@ -446,7 +470,7 @@ public static String generateClientKeyStore(boolean addToContainer) throws IOExc
// Add the client certificate that we just generated to the trust store of the server.
// That way the server will trust our certificate.
// Set the actual domain used with -Dpayara.domain.name=[domain name]
- addCertificateToContainerTrustStore(clientCertificate, clientKeyPair);
+ addCertificateToContainerTrustStore(clientCertificate, clientKeyPair, alias);
}
// Create a new local key store containing the client private key and the certificate
diff --git a/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/certificate/CertificateRealm.java b/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/certificate/CertificateRealm.java
index adea359cad7..3acef296db7 100644
--- a/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/certificate/CertificateRealm.java
+++ b/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/certificate/CertificateRealm.java
@@ -100,6 +100,7 @@ public final class CertificateRealm extends BaseRealm {
private static final String COMMON_NAME_AS_PRINCIPAL_NAME = "common-name-as-principal-name";
private static final String DN_PARTS_USED_FOR_GROUPS = "dn-parts-used-for-groups";
+ private static final String VALIDATION_CHECK_PROP = "certificate-validation";
/** Descriptive string of the authentication type of this realm. */
public static final String AUTH_TYPE = "certificate";
@@ -114,6 +115,9 @@ public final class CertificateRealm extends BaseRealm {
protected void init(Properties props) throws BadRealmException, NoSuchRealmException {
super.init(props);
+ final String validationCheck = props.getProperty(VALIDATION_CHECK_PROP);
+ setProperty(VALIDATION_CHECK_PROP, validationCheck);
+
final String jaasCtx = props.getProperty(JAAS_CONTEXT_PARAM);
setProperty(JAAS_CONTEXT_PARAM, jaasCtx);
@@ -199,16 +203,14 @@ private void validateSubjectViaAPI(Subject subject, X500Principal principal) {
_logger.log(Level.WARNING, "Exception while loading certificate validation class", exc);
clientCertificateValidatorMap.remove(Utility.getClassLoader());
}
-
+ validators.add(new ClientCertificateExpiryValidator(getProperty(VALIDATION_CHECK_PROP)));
boolean failed = false;
- if (!validators.isEmpty()) {
- for (ClientCertificateValidator validator : validators) {
- if (!validator.isValid(subject, principal, certificate)) {
- _logger.info(() -> String.format("Client Certificate validation failed for (subject=%s, principal=%s) by %s"
- , subject, principal, validator.getClass().getName()));
- failed = true;
- break;
- }
+ for (ClientCertificateValidator validator : validators) {
+ if (!validator.isValid(subject, principal, certificate)) {
+ _logger.info(() -> String.format("Client Certificate validation failed for (subject=%s, principal=%s) by %s"
+ , subject, principal, validator.getClass().getName()));
+ failed = true;
+ break;
}
}
if (failed) {
diff --git a/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/certificate/ClientCertificateExpiryValidator.java b/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/certificate/ClientCertificateExpiryValidator.java
new file mode 100644
index 00000000000..e8ca3f78c82
--- /dev/null
+++ b/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/certificate/ClientCertificateExpiryValidator.java
@@ -0,0 +1,71 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://github.com/payara/Payara/blob/master/LICENSE.txt
+ * See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at glassfish/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * The Payara Foundation designates this particular file as subject to the "Classpath"
+ * exception as provided by the Payara Foundation in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package com.sun.enterprise.security.auth.realm.certificate;
+
+import fish.payara.security.client.ClientCertificateValidator;
+
+import javax.security.auth.Subject;
+import javax.security.auth.x500.X500Principal;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+
+public class ClientCertificateExpiryValidator implements ClientCertificateValidator {
+ private boolean validationCheck;
+
+ public ClientCertificateExpiryValidator(String validationCheck) {
+ this.validationCheck = "true".equalsIgnoreCase(validationCheck);
+ }
+
+ @Override
+ public boolean isValid(Subject subject, X500Principal principal, X509Certificate certificate) {
+ if (validationCheck) {
+ try {
+ certificate.checkValidity();
+ } catch (CertificateExpiredException e) {
+ return false;
+ } catch (CertificateNotYetValidException e) {
+ return false;
+ }
+ }
+ return true;
+ }
+}