CertificateValidatorTest.java
package org.keycloak.authentication.authenticators.x509;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.crypto.KeyType;
import org.keycloak.rule.CryptoInitRule;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.cert.X509Certificate;
import java.util.Date;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.CERTIFICATE_POLICY_MODE_ALL;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.CERTIFICATE_POLICY_MODE_ANY;
/**
* author Pascal Knueppel <br>
* created at: 07.11.2019 - 16:24 <br>
* <br>
*
*/
public class CertificateValidatorTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
/**
* will validate that the certificate validation succeeds if the certificate is currently valid
*/
@Test
public void testValidityOfCertificatesSuccess() throws GeneralSecurityException {
KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair();
X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils()
.createServicesTestCertificate("CN=keycloak-test", new Date(),
new Date(System.currentTimeMillis() + 1000L * 60), keyPair);
CertificateValidator.CertificateValidatorBuilder builder =
new CertificateValidator.CertificateValidatorBuilder();
CertificateValidator validator = builder
.timestampValidation()
.enabled(true)
.build(new X509Certificate[] { certificate });
try {
validator.validateTimestamps();
} catch (Exception ex) {
ex.printStackTrace();
Assert.fail(ex.getMessage());
}
}
/**
* will validate that the certificate validation throws an exception if the certificate is not valid yet
*/
@Test
public void testValidityOfCertificatesNotValidYet() throws GeneralSecurityException {
KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair();
X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils()
.createServicesTestCertificate("CN=keycloak-test", new Date(System.currentTimeMillis() + 1000L * 60),
new Date(System.currentTimeMillis() + 1000L * 60), keyPair);
CertificateValidator.CertificateValidatorBuilder builder =
new CertificateValidator.CertificateValidatorBuilder();
CertificateValidator validator = builder
.timestampValidation()
.enabled(true)
.build(new X509Certificate[] { certificate });
try {
validator.validateTimestamps();
Assert.fail("certificate validation must fail for certificate is not valid yet");
} catch (Exception ex) {
MatcherAssert.assertThat(ex.getMessage(), Matchers.containsString("not valid yet"));
Assert.assertEquals(GeneralSecurityException.class, ex.getClass());
}
}
/**
* will validate that the certificate validation throws an exception if the certificate has expired
*/
@Test
public void testValidityOfCertificatesHasExpired() throws GeneralSecurityException {
KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair();
X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils()
.createServicesTestCertificate("CN=keycloak-test",
new Date(System.currentTimeMillis() - 1000L * 60 * 2),
new Date(System.currentTimeMillis() - 1000L * 60), keyPair);
CertificateValidator.CertificateValidatorBuilder builder =
new CertificateValidator.CertificateValidatorBuilder();
CertificateValidator validator = builder
.timestampValidation()
.enabled(true)
.build(new X509Certificate[] { certificate });
try {
validator.validateTimestamps();
Assert.fail("certificate validation must fail for certificate has expired");
} catch (Exception ex) {
MatcherAssert.assertThat(ex.getMessage(), Matchers.containsString("has expired"));
Assert.assertEquals(GeneralSecurityException.class, ex.getClass());
}
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ALL, no policies are requested and the cert does not contain any policy
*/
@Test
public void testCertificatePolicyModeAllNotRequestedAndNotPresent() throws GeneralSecurityException {
testCertificatePolicyValidation(null, CERTIFICATE_POLICY_MODE_ALL);
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ALL, no policies are requested and the cert does contains a policy
*/
@Test
public void testCertificatePolicyModeAllNotRequestedAndOnePresent() throws GeneralSecurityException {
testCertificatePolicyValidation(null, CERTIFICATE_POLICY_MODE_ALL, "1.3.76.16.2.1");
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ALL, no policies are requested and the cert contains two policies
*/
@Test
public void testCertificatePolicyModeAllNotRequestedAndTwoPresent() throws GeneralSecurityException {
testCertificatePolicyValidation(null, CERTIFICATE_POLICY_MODE_ALL, "1.3.76.16.2.1", "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ALL, one policy is requested and the cert does not contain any policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAllOneRequestedAndNotPresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ALL);
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ALL, one policy is requested and the cert contains that policy
*/
@Test
public void testCertificatePolicyModeAllOneRequestedAndOnePresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ALL, "1.3.76.16.2.1");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ALL, one policy is requested and the cert contains a different policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAllOneRequestedAndOnePresentDifferent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ALL, "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ALL, one policy is requested and the cert contains that policy and one more
*/
@Test
public void testCertificatePolicyModeAllOneRequestedAndTwoPresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ALL, "1.3.76.16.2.1", "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ALL, one policy is requested and the cert contains a different policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAllOneRequestedAndTwoPresentDifferent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ALL, "1.2.3.4.5", "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ALL, two policies are requested and the cert does not contain any policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAllTwoRequestedAndNotPresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1,1.2.3.4.5.6", CERTIFICATE_POLICY_MODE_ALL);
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ALL, two policies are requested and the cert contains one different policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAllTwoRequestedAndOnePresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1,1.2.3.4.5.6", CERTIFICATE_POLICY_MODE_ALL, "1.3.76.16.2.1");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ALL, two policies are requested and the cert contains one different policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAllTwoRequestedAndOnePresentDifferent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1,1.2.3.4.5.6", CERTIFICATE_POLICY_MODE_ALL, "1.2.3.4");
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ALL, two policies are requested and the cert contains those two policies
*/
@Test
public void testCertificatePolicyModeAllTwoRequestedAndTwoPresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1,1.2.3.4.5.6", CERTIFICATE_POLICY_MODE_ALL, "1.3.76.16.2.1", "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ANY, no policies are requested and the cert does not contain any policy
*/
@Test
public void testCertificatePolicyModeAnyNotRequestedAndNotPresent() throws GeneralSecurityException {
testCertificatePolicyValidation(null, CERTIFICATE_POLICY_MODE_ANY);
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ANY, no policies are requested and the cert does contains a policy
*/
@Test
public void testCertificatePolicyModeAnyNotRequestedAndOnePresent() throws GeneralSecurityException {
testCertificatePolicyValidation(null, CERTIFICATE_POLICY_MODE_ANY, "1.3.76.16.2.1");
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ANY, no policies are requested and the cert contains two policies
*/
@Test
public void testCertificatePolicyModeAnyNotRequestedAndTwoPresent() throws GeneralSecurityException {
testCertificatePolicyValidation(null, CERTIFICATE_POLICY_MODE_ANY, "1.3.76.16.2.1", "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ANY, one policy is requested and the cert does not contain any policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAnyOneRequestedAndNotPresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ANY);
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ANY, one policy is requested and the cert contains that policy
*/
@Test
public void testCertificatePolicyModeAnyOneRequestedAndOnePresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ANY, "1.3.76.16.2.1");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ANY, one policy is requested and the cert contains a different policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAnyOneRequestedAndOnePresentDifferent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ANY, "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ANY, one policy is requested and the cert contains that policy and one more
*/
@Test
public void testCertificatePolicyModeAnyOneRequestedAndTwoPresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ANY, "1.3.76.16.2.1", "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ANY, one policy is requested and the cert contains a different policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAnyOneRequestedAndTwoPresentDifferent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1", CERTIFICATE_POLICY_MODE_ANY, "1.2.3.4.5", "1.2.3.4.5.6");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ANY, two policies are requested and the cert does not contain any policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAnyTwoRequestedAndNotPresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1,1.2.3.4.5.6", CERTIFICATE_POLICY_MODE_ANY);
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ANY, two policies are requested and the cert contains one policy
*/
@Test
public void testCertificatePolicyModeAnyTwoRequestedAndOnePresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1,1.2.3.4.5.6", CERTIFICATE_POLICY_MODE_ANY, "1.3.76.16.2.1");
}
/**
* will validate that the certificate policy validation WILL throw exceptions
* if mode=ANY, two policies are requested and the cert contains one different policy
*/
@Test(expected = GeneralSecurityException.class)
public void testCertificatePolicyModeAnyTwoRequestedAndOnePresentDifferent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1,1.2.3.4.5.6", CERTIFICATE_POLICY_MODE_ANY, "1.2.3.4");
}
/**
* will validate that the certificate policy validation won't throw exceptions
* if mode=ANY, two policies are requested and the cert contains those two policies
*/
@Test
public void testCertificatePolicyModeAnyTwoRequestedAndTwoPresent() throws GeneralSecurityException {
testCertificatePolicyValidation("1.3.76.16.2.1,1.2.3.4.5.6", CERTIFICATE_POLICY_MODE_ANY, "1.3.76.16.2.1", "1.2.3.4.5.6");
}
// Helper to test various certificate policy validation combinations
private void testCertificatePolicyValidation(String expectedPolicy, String mode, String... certificatePolicyOid)
throws GeneralSecurityException
{
KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair();
X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils()
.createServicesTestCertificate("CN=keycloak-test",
new Date(System.currentTimeMillis() - 1000L * 60 * 2),
new Date(System.currentTimeMillis() - 1000L * 60), keyPair, certificatePolicyOid);
CertificateValidator.CertificateValidatorBuilder builder =
new CertificateValidator.CertificateValidatorBuilder();
CertificateValidator validator = builder
.certificatePolicy()
.mode(mode)
.parse(expectedPolicy)
.build(new X509Certificate[] { certificate });
validator.validatePolicy();
}
}