[java-opensaml] branch master updated: OSJ-272: Move HttpClient TLS TrustEngine use to a X509TrustManager impl
Brent Putman
putmanb at georgetown.edu
Sun Feb 23 21:45:52 EST 2020
This is an automated email from the git hooks/post-receive script.
putmanb pushed a commit to branch master
in repository java-opensaml.
View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=0a66c13e6658cf813064bfd67e9e64597d83bd59
The following commit(s) were added to refs/heads/master by this push:
new 0a66c13 OSJ-272: Move HttpClient TLS TrustEngine use to a X509TrustManager impl
0a66c13 is described below
commit 0a66c13e6658cf813064bfd67e9e64597d83bd59
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Wed Apr 3 19:20:08 2019 -0400
OSJ-272: Move HttpClient TLS TrustEngine use to a X509TrustManager impl
---
.../resolver/filter/impl/EntityRoleFilterTest.java | 2 +-
.../impl/FileBackedHTTPMetadataResolverTest.java | 47 +--
...ctionDrivenDynamicHTTPMetadataResolverTest.java | 81 +++---
.../resolver/impl/HTTPMetadataResolverTest.java | 62 ++--
.../src/test/resources/logback-test.xml | 20 ++
.../metadata/resolver/impl/repo.truststore.jks | Bin 0 -> 1754 bytes
.../metadata/resolver/impl/repo.truststore.txt | 1 +
.../metadata/resolver/impl/test.shibboleth.net.crt | 37 +++
.../opensaml/security/trust/MockTrustEngine.java | 55 ++++
opensaml-security-impl/pom.xml | 8 +
.../impl/SecurityEnhancedHttpClientSupport.java | 18 +-
.../impl/SecurityEnhancedTLSSocketFactory.java | 315 ++++++---------------
.../impl/ThreadLocalServerTLSHandler.java | 62 ++++
.../impl/ThreadLocalX509TrustEngineContext.java | 146 ++++++++++
.../x509/tls/impl/ThreadLocalX509TrustManager.java | 155 ++++++++++
.../impl/SecurityEnhancedTLSSocketFactoryTest.java | 262 ++++++++---------
.../tls/impl/ThreadLocalX509TrustManagerTest.java | 170 +++++++++++
17 files changed, 992 insertions(+), 449 deletions(-)
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityRoleFilterTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityRoleFilterTest.java
index d143661..bf2d485 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityRoleFilterTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/filter/impl/EntityRoleFilterTest.java
@@ -57,7 +57,7 @@ public class EntityRoleFilterTest extends XMLObjectBaseTestCase {
protected void setUpClass() throws Exception {
httpClientBuilder = new HttpClientBuilder();
httpClientBuilder.setConnectionTimeout(Duration.ofSeconds(5));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
httpClient = httpClientBuilder.buildClient();
httpClientParams = new HttpClientSecurityParameters();
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/FileBackedHTTPMetadataResolverTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/FileBackedHTTPMetadataResolverTest.java
index 4c9ea9d..e87938b 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/FileBackedHTTPMetadataResolverTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/FileBackedHTTPMetadataResolverTest.java
@@ -385,9 +385,9 @@ public class FileBackedHTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testTrustEngineSocketFactoryNoHTTPSNoTrustEngine() throws Exception {
// Make sure resolver works when TrustEngine socket factory is configured but just using an HTTP URL.
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory(false));
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory(true));
- metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
+ metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttp, backupFilePath);
metadataProvider.setParserPool(parserPool);
metadataProvider.setId("test");
metadataProvider.initialize();
@@ -400,7 +400,7 @@ public class FileBackedHTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testTrustEngineSocketFactoryNoHTTPSWithTrustEngine() throws Exception {
// Make sure resolver works when TrustEngine socket factory is configured but just using an HTTP URL.
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
metadataProvider.setParserPool(parserPool);
@@ -417,21 +417,30 @@ public class FileBackedHTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testHTTPSNoTrustEngine() throws Exception {
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory(false));
-
- metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
- metadataProvider.setParserPool(parserPool);
- metadataProvider.setId("test");
- metadataProvider.initialize();
-
- EntityDescriptor descriptor = metadataProvider.resolveSingle(criteriaSet);
- Assert.assertNotNull(descriptor, "Retrieved entity descriptor was null");
- Assert.assertEquals(descriptor.getEntityID(), entityID, "Entity's ID does not match requested ID");
+ try {
+ System.setProperty("javax.net.ssl.trustStore", getClass().getResource("repo.truststore.jks").getFile());
+ System.setProperty("javax.net.ssl.trustStorePassword", "shibboleth");
+
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory(false));
+
+ metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
+ metadataProvider.setParserPool(parserPool);
+ metadataProvider.setId("test");
+ metadataProvider.initialize();
+
+ EntityDescriptor descriptor = metadataProvider.resolveSingle(criteriaSet);
+ Assert.assertNotNull(descriptor, "Retrieved entity descriptor was null");
+ Assert.assertEquals(descriptor.getEntityID(), entityID, "Entity's ID does not match requested ID");
+ } finally {
+ System.setProperty("javax.net.ssl.trustStore", "");
+ System.setProperty("javax.net.ssl.trustStorePassword", "");
+
+ }
}
@Test
public void testHTTPSTrustEngineExplicitKey() throws Exception {
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
metadataProvider.setParserPool(parserPool);
@@ -449,7 +458,7 @@ public class FileBackedHTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test(expectedExceptions=ComponentInitializationException.class)
public void testHTTPSTrustEngineInvalidKey() throws Exception {
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
metadataProvider.setParserPool(parserPool);
@@ -466,7 +475,7 @@ public class FileBackedHTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testHTTPSTrustEngineValidPKIX() throws Exception {
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
metadataProvider.setParserPool(parserPool);
@@ -485,7 +494,7 @@ public class FileBackedHTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testHTTPSTrustEngineValidPKIXExplicitName() throws Exception {
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
metadataProvider.setParserPool(parserPool);
@@ -503,7 +512,7 @@ public class FileBackedHTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test(expectedExceptions=ComponentInitializationException.class)
public void testHTTPSTrustEngineInvalidPKIX() throws Exception {
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
metadataProvider.setParserPool(parserPool);
@@ -521,7 +530,7 @@ public class FileBackedHTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test(expectedExceptions=ComponentInitializationException.class)
public void testHTTPSTrustEngineValidPKIXInvalidName() throws Exception {
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
metadataProvider = new FileBackedHTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps, backupFilePath);
metadataProvider.setParserPool(parserPool);
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/FunctionDrivenDynamicHTTPMetadataResolverTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/FunctionDrivenDynamicHTTPMetadataResolverTest.java
index 2e6dbcf..2b6c30b 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/FunctionDrivenDynamicHTTPMetadataResolverTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/FunctionDrivenDynamicHTTPMetadataResolverTest.java
@@ -197,12 +197,17 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
HTTPEntityIDRequestURLBuilder requestURLBuilder = new HTTPEntityIDRequestURLBuilder();
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory(false));
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
resolver.setParserPool(parserPool);
resolver.setRequestURLBuilder(requestURLBuilder);
+
+ final HttpClientSecurityParameters params = new HttpClientSecurityParameters();
+ params.setTLSTrustEngine(HTTPMetadataResolverTest.buildExplicitKeyTrustEngine("test.shibboleth.net.crt"));
+ resolver.setHttpClientSecurityParameters(params);
+
resolver.initialize();
CriteriaSet criteriaSet = new CriteriaSet( new EntityIdCriterion(entityID));
@@ -265,7 +270,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
@Test
public void testTrustEngineSocketFactoryNoHTTPSNoTrustEngine() throws Exception {
- String template = RepositorySupport.buildHTTPSResourceURL("java-opensaml", "opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/${entityID}.xml");
+ String template = RepositorySupport.buildHTTPResourceURL("java-opensaml", "opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/${entityID}.xml", false);
String entityID = "https://www.example.org/sp";
// Digesting the entityID is a little artificial for the test, but means we can test more easily against a path in the repo.
@@ -275,7 +280,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
EncodingStyle.path,
new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory(false));
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
@@ -303,7 +308,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
EncodingStyle.path,
new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
@@ -326,30 +331,38 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
@Test
public void testHTTPSNoTrustEngine() throws Exception {
- String template = RepositorySupport.buildHTTPSResourceURL("java-opensaml", "opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/${entityID}.xml");
- String entityID = "https://www.example.org/sp";
-
- // Digesting the entityID is a little artificial for the test, but means we can test more easily against a path in the repo.
- TemplateRequestURLBuilder requestURLBuilder = new TemplateRequestURLBuilder(
- VelocityEngine.newVelocityEngine(),
- template,
- EncodingStyle.path,
- new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
-
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory(false));
-
- resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
- resolver.setId("myDynamicResolver");
- resolver.setParserPool(parserPool);
- resolver.setRequestURLBuilder(requestURLBuilder);
- resolver.initialize();
-
- CriteriaSet criteriaSet = new CriteriaSet( new EntityIdCriterion(entityID));
-
- EntityDescriptor ed = resolver.resolveSingle(criteriaSet);
- Assert.assertNotNull(ed);
- Assert.assertEquals(ed.getEntityID(), entityID);
- Assert.assertNull(ed.getDOM());
+ try {
+ System.setProperty("javax.net.ssl.trustStore", getClass().getResource("repo.truststore.jks").getFile());
+ System.setProperty("javax.net.ssl.trustStorePassword", "shibboleth");
+
+ String template = RepositorySupport.buildHTTPSResourceURL("java-opensaml", "opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/${entityID}.xml");
+ String entityID = "https://www.example.org/sp";
+
+ // Digesting the entityID is a little artificial for the test, but means we can test more easily against a path in the repo.
+ TemplateRequestURLBuilder requestURLBuilder = new TemplateRequestURLBuilder(
+ VelocityEngine.newVelocityEngine(),
+ template,
+ EncodingStyle.path,
+ new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
+
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory(false));
+
+ resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
+ resolver.setId("myDynamicResolver");
+ resolver.setParserPool(parserPool);
+ resolver.setRequestURLBuilder(requestURLBuilder);
+ resolver.initialize();
+
+ CriteriaSet criteriaSet = new CriteriaSet( new EntityIdCriterion(entityID));
+
+ EntityDescriptor ed = resolver.resolveSingle(criteriaSet);
+ Assert.assertNotNull(ed);
+ Assert.assertEquals(ed.getEntityID(), entityID);
+ Assert.assertNull(ed.getDOM());
+ } finally {
+ System.setProperty("javax.net.ssl.trustStore", "");
+ System.setProperty("javax.net.ssl.trustStorePassword", "");
+ }
}
@Test
@@ -364,7 +377,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
EncodingStyle.path,
new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
@@ -397,7 +410,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
EncodingStyle.path,
new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
@@ -428,7 +441,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
EncodingStyle.path,
new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
@@ -461,7 +474,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
EncodingStyle.path,
new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
@@ -494,7 +507,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
EncodingStyle.path,
new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
@@ -525,7 +538,7 @@ public class FunctionDrivenDynamicHTTPMetadataResolverTest extends XMLObjectBase
EncodingStyle.path,
new StringDigester("SHA-1", OutputFormat.HEX_LOWER));
- httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(HTTPMetadataResolverTest.buildSocketFactory());
resolver = new FunctionDrivenDynamicHTTPMetadataResolver(httpClientBuilder.buildClient());
resolver.setId("myDynamicResolver");
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/HTTPMetadataResolverTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/HTTPMetadataResolverTest.java
index c37c242..d105217 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/HTTPMetadataResolverTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/metadata/resolver/impl/HTTPMetadataResolverTest.java
@@ -32,6 +32,7 @@ import org.opensaml.core.xml.XMLObjectBaseTestCase;
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.opensaml.security.credential.impl.StaticCredentialResolver;
import org.opensaml.security.httpclient.HttpClientSecurityParameters;
+import org.opensaml.security.httpclient.impl.SecurityEnhancedHttpClientSupport;
import org.opensaml.security.httpclient.impl.SecurityEnhancedTLSSocketFactory;
import org.opensaml.security.trust.TrustEngine;
import org.opensaml.security.trust.impl.ExplicitKeyTrustEngine;
@@ -45,6 +46,7 @@ import org.opensaml.security.x509.impl.CertPathPKIXTrustEvaluator;
import org.opensaml.security.x509.impl.PKIXX509CredentialTrustEngine;
import org.opensaml.security.x509.impl.StaticPKIXValidationInformationResolver;
import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -55,6 +57,7 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
import net.shibboleth.utilities.java.support.httpclient.HttpClientBuilder;
import net.shibboleth.utilities.java.support.httpclient.HttpClientSupport;
import net.shibboleth.utilities.java.support.httpclient.TLSSocketFactory;
+import net.shibboleth.utilities.java.support.httpclient.TLSSocketFactoryBuilder;
import net.shibboleth.utilities.java.support.repository.RepositorySupport;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
@@ -156,9 +159,9 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testTrustEngineSocketFactoryNoHTTPSNoTrustEngine() throws Exception {
// Make sure resolver works when TrustEngine socket factory is configured but just using an HTTP URL.
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory(false));
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory(true));
- metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
+ metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttp);
metadataProvider.setParserPool(parserPool);
metadataProvider.setId("test");
metadataProvider.initialize();
@@ -171,7 +174,7 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testTrustEngineSocketFactoryNoHTTPSWithTrustEngine() throws Exception {
// Make sure resolver works when TrustEngine socket factory is configured but just using an HTTP URL.
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory());
metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
metadataProvider.setParserPool(parserPool);
@@ -189,21 +192,29 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testHTTPSNoTrustEngine() throws Exception {
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory(false));
-
- metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
- metadataProvider.setParserPool(parserPool);
- metadataProvider.setId("test");
- metadataProvider.initialize();
-
- EntityDescriptor descriptor = metadataProvider.resolveSingle(criteriaSet);
- Assert.assertNotNull(descriptor, "Retrieved entity descriptor was null");
- Assert.assertEquals(descriptor.getEntityID(), entityID, "Entity's ID does not match requested ID");
+ try {
+ System.setProperty("javax.net.ssl.trustStore", getClass().getResource("repo.truststore.jks").getFile());
+ System.setProperty("javax.net.ssl.trustStorePassword", "shibboleth");
+
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory(false));
+
+ metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
+ metadataProvider.setParserPool(parserPool);
+ metadataProvider.setId("test");
+ metadataProvider.initialize();
+
+ EntityDescriptor descriptor = metadataProvider.resolveSingle(criteriaSet);
+ Assert.assertNotNull(descriptor, "Retrieved entity descriptor was null");
+ Assert.assertEquals(descriptor.getEntityID(), entityID, "Entity's ID does not match requested ID");
+ } finally {
+ System.setProperty("javax.net.ssl.trustStore", "");
+ System.setProperty("javax.net.ssl.trustStorePassword", "");
+ }
}
@Test
public void testHTTPSTrustEngineExplicitKey() throws Exception {
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory());
metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
metadataProvider.setParserPool(parserPool);
@@ -223,7 +234,7 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test(expectedExceptions=ComponentInitializationException.class)
public void testHTTPSTrustEngineInvalidKey() throws Exception {
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory());
metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
metadataProvider.setParserPool(parserPool);
@@ -242,7 +253,7 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testHTTPSTrustEngineValidPKIX() throws Exception {
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory());
metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
metadataProvider.setParserPool(parserPool);
@@ -261,7 +272,7 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test
public void testHTTPSTrustEngineValidPKIXExplicitName() throws Exception {
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory());
metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
metadataProvider.setParserPool(parserPool);
@@ -280,7 +291,7 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test(expectedExceptions=ComponentInitializationException.class)
public void testHTTPSTrustEngineInvalidPKIX() throws Exception {
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory());
metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
metadataProvider.setParserPool(parserPool);
@@ -299,7 +310,7 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
@Test(expectedExceptions=ComponentInitializationException.class)
public void testHTTPSTrustEngineValidPKIXInvalidName() throws Exception {
- httpClientBuilder.setTLSSocketFactory(buildTrustEngineSocketFactory());
+ httpClientBuilder.setTLSSocketFactory(buildSocketFactory());
metadataProvider = new HTTPMetadataResolver(httpClientBuilder.buildClient(), metadataURLHttps);
metadataProvider.setParserPool(parserPool);
@@ -355,17 +366,12 @@ public class HTTPMetadataResolverTest extends XMLObjectBaseTestCase {
}
- public static LayeredConnectionSocketFactory buildTrustEngineSocketFactory() {
- return buildTrustEngineSocketFactory(true);
+ public static LayeredConnectionSocketFactory buildSocketFactory() {
+ return buildSocketFactory(true);
}
- public static LayeredConnectionSocketFactory buildTrustEngineSocketFactory(boolean trustEngineRequired) {
- SecurityEnhancedTLSSocketFactory factory = new SecurityEnhancedTLSSocketFactory(
- HttpClientSupport.buildNoTrustTLSSocketFactory(),
- TLSSocketFactory.STRICT_HOSTNAME_VERIFIER,
- trustEngineRequired
- );
- return factory;
+ public static LayeredConnectionSocketFactory buildSocketFactory(boolean supportTrustEngine) {
+ return SecurityEnhancedHttpClientSupport.buildTLSSocketFactory(supportTrustEngine, false);
}
}
diff --git a/opensaml-saml-impl/src/test/resources/logback-test.xml b/opensaml-saml-impl/src/test/resources/logback-test.xml
index 232a61c..ac9ab81 100644
--- a/opensaml-saml-impl/src/test/resources/logback-test.xml
+++ b/opensaml-saml-impl/src/test/resources/logback-test.xml
@@ -8,6 +8,26 @@
</encoder>
</appender>
+ <logger name="org.apache.http">
+ <level value="WARN"/>
+ </logger>
+
+ <logger name="org.apache.http.wire">
+ <level value="WARN"/>
+ </logger>
+
+ <logger name="org.opensaml.security.x509.tls.impl.ThreadLocalX509TrustManager">
+ <level value="INFO"/>
+ </logger>
+
+ <logger name="org.opensaml.security.httpclient.impl.SecurityEnhancedTLSSocketFactory">
+ <level value="INFO"/>
+ </logger>
+
+ <logger name="org.opensaml.security.httpclient.impl.ThreadLocalServerTLSHandler">
+ <level value="INFO"/>
+ </logger>
+
<logger name="org.opensaml.messaging">
<level value="INFO"/>
</logger>
diff --git a/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/repo.truststore.jks b/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/repo.truststore.jks
new file mode 100644
index 0000000..5f12f9d
Binary files /dev/null and b/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/repo.truststore.jks differ
diff --git a/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/repo.truststore.txt b/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/repo.truststore.txt
new file mode 100644
index 0000000..2d4153e
--- /dev/null
+++ b/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/repo.truststore.txt
@@ -0,0 +1 @@
+store password is: shibboleth
diff --git a/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/test.shibboleth.net.crt b/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/test.shibboleth.net.crt
new file mode 100644
index 0000000..739de83
--- /dev/null
+++ b/opensaml-saml-impl/src/test/resources/org/opensaml/saml/metadata/resolver/impl/test.shibboleth.net.crt
@@ -0,0 +1,37 @@
+-----BEGIN CERTIFICATE-----
+MIIGazCCBFOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMx
+DTALBgNVBAgMBE9oaW8xHjAcBgNVBAoMFVNoaWJib2xldGggQ29uc29ydGl1bTEr
+MCkGA1UEAwwiU2hpYmJvbGV0aCBQcm9qZWN0IEludGVybWVkaWF0ZSBDQTAeFw0x
+ODEyMTExNTE5NDlaFw0zODEyMDYxNTE5NDlaMFoxCzAJBgNVBAYTAlVTMQ0wCwYD
+VQQIDARPaGlvMR4wHAYDVQQKDBVTaGliYm9sZXRoIENvbnNvcnRpdW0xHDAaBgNV
+BAMME3Rlc3Quc2hpYmJvbGV0aC5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDI39TBFEbOkf0Bb2h/qnG4bbJRF5Ga9OBgSAxPsx2PNgVmf1cvJYlq
+uRCzATu3O4PPclomow5VU7hB+umpk2s03FHejUtO8w85qQx7bkSeyfLpMYloX3SD
+AWDFSpw54Tg2Dfja6jYLnE0aYWUCTXjcFJEIrT0QP1Azdk8b9PeKLqGIagECUMzC
+UjmJxEcyqhbe6mB7tdSsBng+R4DRdVdouN4rtHknNuEyAc5eEppGxY33KXhElYol
+4IK6YxAqXI6iQIDdcVxrH061/iKh7qT1bCAEmzeeoS4st4v4oyIhxUk7yUxYOuFc
+NhYUd5U93Ws4P8xPFzcPhD2TIZDAS/bnm2EJ+JxLG6Gp1jhBN5qTpdTPWiG9+Tmm
+HFuYOrSefgZk0edkJhe0dycfV/FWFX2fNrmkpQhyPQzb3ZyZ5mACn020TXDaF5o3
+fX8ndLl4RghTORJs5n51TuLX85DEeZWkJe5f8Hsip1mpmxlMTl9QMqHrN70n5gBi
+uCYo9g/Jw5xoropCq7Jri9K9FWtbORncUIXutsTVo+fXeHZ6IDRoovR004gHHEJ7
+ks46fZQYNbd8bB/mPlCdYiFyJnfUiOu89O5aLqvhrJNgPUhMt/gmhSV7zw+/3/cd
+o7pwWH2h2ObuS8v8gVUZAE04tefqRJZEB0YsWA3DASDT7nVqndOp2QIDAQABo4IB
+KjCCASYwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgEN
+BCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4E
+FgQUG3Vg4ubsJVaylBUCPtqVc9mHWfYwgYwGA1UdIwSBhDCBgYAUELzfpij2mkrs
+9B4CEsXYmoN6cKuhZaRjMGExCzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARPaGlvMR4w
+HAYDVQQKDBVTaGliYm9sZXRoIENvbnNvcnRpdW0xIzAhBgNVBAMMGlNoaWJib2xl
+dGggUHJvamVjdCBUZXN0IENBggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAww
+CgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggIBAFZQF7KzKGSPieV2eQWbFt30
+kVZzVe/T5UAvrr8n7mRqMfye4QtHHOVM3MHvZv93AXVUQU4PH9KPGtv7poEtwNWk
+7hNbq74z5x5tSvE0EYYI9UMolDL6il+QR5AgLw7YqgMmPPhtm+crmLg8+uMoQDyU
+uSH8ej4rMrdhL2xKlgvL/rhWycpYu1cFLmeolljOZGqr7ITwWJ06BQsLtt4/cyYj
+WiVldHQRZMGAuHLHlX+ukaEg7Gc/N7o936bS5d7AqXwtmtkXiA8An5q5rhncEK1G
+kwUUQN2y5iyx/nD4B2k1IcgFlu/bM4iXZQtmMUtLptUqRssuxS45ukiweiM9UU8n
+WxyxJMzFTctJJ2f/y4Bg5ggsr4WQU/YhoDaQVYxyjXiBt3oT+7eqsoep9HK9xwbG
+xriLzrlao+K3EcKA3vjKGYh15gpEDUbn0Cr5V74TUSdkHjhG7ocaJ5u9/vCS4+AR
+iU5ge2zN6QcwoZKT8+8XGKeXqVc/3hXeXTn3FyvMitPDZsmg8wUhnb/pq5MyKqUA
+bNse4a7oGAeAUGkLf4Q+eLCLSZmL5udrGXdHIffFYqZVcZS+zVWQ1TRfodTDPkFb
+KTz1mcr4KjLNCtplu4CfFpHwC20uk3hPEslOUd+ugj0+HGNH93L7H0WL1GDQoCPh
+RcdRISVwORcD/dit15zD
+-----END CERTIFICATE-----
diff --git a/opensaml-security-api/src/test/java/org/opensaml/security/trust/MockTrustEngine.java b/opensaml-security-api/src/test/java/org/opensaml/security/trust/MockTrustEngine.java
new file mode 100644
index 0000000..a837443
--- /dev/null
+++ b/opensaml-security-api/src/test/java/org/opensaml/security/trust/MockTrustEngine.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.security.trust;
+
+import org.opensaml.security.SecurityException;
+
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public class MockTrustEngine<TokenType> implements TrustEngine<TokenType> {
+
+ private boolean trusted;
+
+ private Throwable throwable;
+
+ public MockTrustEngine(boolean flag) {
+ trusted = flag;
+ }
+
+ public MockTrustEngine(Throwable t) {
+ throwable = t;
+ }
+
+ /** {@inheritDoc} */
+ public boolean validate(TokenType token, CriteriaSet trustBasisCriteria) throws SecurityException {
+ if (throwable != null) {
+ if (SecurityException.class.isInstance(throwable)) {
+ throw SecurityException.class.cast(throwable);
+ } else if (RuntimeException.class.isInstance(throwable)) {
+ throw RuntimeException.class.cast(throwable);
+ } else if (Error.class.isInstance(throwable)){
+ throw Error.class.cast(throwable);
+ } else {
+ throw new RuntimeException(throwable);
+ }
+ }
+
+ return trusted;
+ }
+
+}
diff --git a/opensaml-security-impl/pom.xml b/opensaml-security-impl/pom.xml
index 8fd2fea..be2a00a 100644
--- a/opensaml-security-impl/pom.xml
+++ b/opensaml-security-impl/pom.xml
@@ -90,6 +90,14 @@
</dependency>
<dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>opensaml-security-api</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.ldaptive</groupId>
<artifactId>ldaptive</artifactId>
<scope>test</scope>
diff --git a/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/SecurityEnhancedHttpClientSupport.java b/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/SecurityEnhancedHttpClientSupport.java
index f18ed5b..98c7878 100644
--- a/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/SecurityEnhancedHttpClientSupport.java
+++ b/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/SecurityEnhancedHttpClientSupport.java
@@ -27,13 +27,14 @@ import javax.net.ssl.X509TrustManager;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
-import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.opensaml.security.trust.TrustEngine;
import org.opensaml.security.x509.tls.impl.ThreadLocalX509CredentialKeyManager;
+import org.opensaml.security.x509.tls.impl.ThreadLocalX509TrustManager;
import net.shibboleth.utilities.java.support.httpclient.HttpClientSupport;
import net.shibboleth.utilities.java.support.httpclient.TLSSocketFactoryBuilder;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
/**
* Support class for working with security-enhanced components related to use of {@link HttpClient}.
@@ -104,12 +105,17 @@ public final class SecurityEnhancedHttpClientSupport {
*
* <p>
* If <code>supportTrustEngine</code> is true, then the wrapped factory will be configured
- * with a "no trust" {@link X509TrustManager}, to allow the actual server TLS trust evaluation
- * to be performed by a mandatory {@link TrustEngine} supplied at runtime,
+ * with a {@link X509TrustManager} that supports per-request specification of a mandatory
+ * server TLS {@TrustEngine} and optional {@link CriteriaSet},
* as documented in {@link SecurityEnhancedTLSSocketFactory}.
* </p>
*
* <p>
+ * If <code>supportTrustEngine</code> is false, then the wrapped factory will be configured
+ * for server TLS based on the default JSSE trust mechanism.
+ * </p>
+ *
+ * <p>
* If <code>supportClientTLS</code> is true, then the wrapped factory will be configured
* with a {@link X509KeyManager} that supports per-request specification of a client TLS
* credential, as documented in {@link SecurityEnhancedTLSSocketFactory}.
@@ -126,11 +132,10 @@ public final class SecurityEnhancedHttpClientSupport {
final TLSSocketFactoryBuilder wrappedFactoryBuilder = new TLSSocketFactoryBuilder();
if (supportTrustEngine || supportClientTLS) {
- wrappedFactoryBuilder.setHostnameVerifier(new NoopHostnameVerifier());
if (supportTrustEngine) {
wrappedFactoryBuilder.setTrustManagers(
- Collections.<TrustManager>singletonList(HttpClientSupport.buildNoTrustX509TrustManager()));
+ Collections.<TrustManager>singletonList(new ThreadLocalX509TrustManager()));
}
if (supportClientTLS) {
@@ -138,8 +143,7 @@ public final class SecurityEnhancedHttpClientSupport {
Collections.<KeyManager>singletonList(new ThreadLocalX509CredentialKeyManager()));
}
- return new SecurityEnhancedTLSSocketFactory(wrappedFactoryBuilder.build(), new DefaultHostnameVerifier(),
- supportTrustEngine);
+ return new SecurityEnhancedTLSSocketFactory(wrappedFactoryBuilder.build());
}
diff --git a/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/SecurityEnhancedTLSSocketFactory.java b/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/SecurityEnhancedTLSSocketFactory.java
index 9bb0aab..34840e3 100644
--- a/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/SecurityEnhancedTLSSocketFactory.java
+++ b/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/SecurityEnhancedTLSSocketFactory.java
@@ -20,35 +20,29 @@ package org.opensaml.security.httpclient.impl;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
import java.util.Collections;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpHost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.protocol.HttpContext;
-import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.criteria.UsageCriterion;
import org.opensaml.security.httpclient.HttpClientSecurityConstants;
import org.opensaml.security.trust.TrustEngine;
-import org.opensaml.security.x509.BasicX509Credential;
import org.opensaml.security.x509.TrustedNamesCriterion;
import org.opensaml.security.x509.X509Credential;
import org.opensaml.security.x509.tls.impl.ThreadLocalX509CredentialContext;
+import org.opensaml.security.x509.tls.impl.ThreadLocalX509TrustEngineContext;
+import org.opensaml.security.x509.tls.impl.ThreadLocalX509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.httpclient.HttpClientSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@@ -60,9 +54,9 @@ import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
* This implementation wraps an existing TLS socket factory instance, decorating it with additional support for:
* </p>
* <ul>
- * <li>Verifying the server TLS certificate and chain via a
+ * <li>Loading and clearing thread-local instances of
* {@link TrustEngine}<code><</code>{@link org.opensaml.security.credential.Credential}<code>></code>
- * and {@link CriteriaSet} supplied by the HttpClient caller via the {@link HttpContext}.</li>
+ * and {@link CriteriaSet} used for server TLS.</li>
*
* <li>Loading and clearing a thread-local instance of {@link X509Credential} used for client TLS.</li>
* </ul>
@@ -86,37 +80,37 @@ import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
* </ul>
*
* <p>
- * If the trust engine context attribute is not populated by the caller and {@link #isTrustEngineRequired()}
- * is <code>true</code> (the default), then an {@link SSLPeerUnverifiedException} is thrown.
- * </p>
- *
- * <p>
- * If the trust engine context attribute is not populated by the caller and {@link #isTrustEngineRequired()}
- * is <code>false</code>, then no trust evaluation is performed. This allows use of this implementation
- * with use cases where, given a particular HttpClient instance, sometimes TLS trust engine evaluation is to
- * be performed, and sometimes not. The caller is then responsible for ensuring they supply a trust engine or not,
- * as appropriate.
+ * Support for server TLS via trust engine evaluation requires use of a compatible {@link javax.net.ssl.TrustManager}
+ * implementation configured in the
+ * {@link javax.net.ssl.SSLContext} of the wrapped {@link LayeredConnectionSocketFactory}, such as
+ * {@link org.opensaml.security.x509.tls.impl.ThreadLocalX509TrustManager}.
* </p>
*
* <p>
- * Since this implementation may typically be used with and wrap a "no trust" SSL socket factory,
- * an optional instance of {@link javax.net.ssl.HostnameVerifier} may also be supplied.
+ * Support for client TLS requires use of a compatible {@link javax.net.ssl.KeyManager}
+ * implementation configured in the
+ * {@link javax.net.ssl.SSLContext} of the wrapped {@link LayeredConnectionSocketFactory}, such as
+ * {@link org.opensaml.security.x509.tls.impl.ThreadLocalX509CredentialKeyManager}.
* </p>
*
* <p>
- * If the client TLS credential context attribute is not populated by the caller, then client TLS is not attempted.
+ * If the trust engine context attribute is not populated by the caller, then no server TLS thread-local
+ * data is populated. If the wrapped socket factory's {@link X509TrustManager} implementation requires
+ * this data (for example {@link ThreadLocalX509TrustManager}), then a fatal exception is expected to be thrown.
* </p>
*
* <p>
- * Client TLS support requires use of a compatible {@link javax.net.ssl.KeyManager} implementation configured in the
- * {@link javax.net.ssl.SSLContext} of the wrapped {@link LayeredConnectionSocketFactory}, such as
- * {@link org.opensaml.security.x509.tls.impl.ThreadLocalX509CredentialKeyManager}.
+ * If the client TLS credential context attribute is not populated by the caller, then no client TLS thread-local data
+ * is populated, and client TLS will not be attempted.
* </p>
- *
*/
public class SecurityEnhancedTLSSocketFactory implements LayeredConnectionSocketFactory {
/** Instance of {@link ThreadLocalClientTLSCredentialHandler} to use. */
+ private static final ThreadLocalServerTLSHandler SERVER_TLS_HANDLER =
+ new ThreadLocalServerTLSHandler();
+
+ /** Instance of {@link ThreadLocalClientTLSCredentialHandler} to use. */
private static final ThreadLocalClientTLSCredentialHandler CLIENT_TLS_HANDLER =
new ThreadLocalClientTLSCredentialHandler();
@@ -126,74 +120,13 @@ public class SecurityEnhancedTLSSocketFactory implements LayeredConnectionSocket
/** The HttpClient socket factory instance wrapped by this implementation. */
@Nonnull private LayeredConnectionSocketFactory wrappedFactory;
- /** The hostname verifier evaluated by this implementation. */
- @Nullable private HostnameVerifier hostnameVerifier;
-
- /** Flag indicating whether a context trust engine attribute is required for TLS server validation.
- * Default: true. */
- private boolean engineRequired = true;
-
/**
* Constructor.
*
- * <p>No hostname verifier is configured in this implementation. (Does not affect whether hostname
- * is or is not evaluated by the wrapped socket factory).</p>
- *
- * @param factory the underlying HttpClient socket factory wrapped by this implementation.
- * @param trustEngineRequired flag indicating whether a context trust engine attribute is required
- * for TLS server validation.
- */
- public SecurityEnhancedTLSSocketFactory(@Nonnull final LayeredConnectionSocketFactory factory,
- final boolean trustEngineRequired) {
- this(factory, null, trustEngineRequired);
- }
-
- /**
- * Constructor.
- *
- * <p>No hostname verifier is configured in this implementation. (Does not affect whether hostname
- * is or is not evaluated by the wrapped socket factory).</p>
- *
* @param factory the underlying HttpClient socket factory wrapped by this implementation.
*/
public SecurityEnhancedTLSSocketFactory(@Nonnull final LayeredConnectionSocketFactory factory) {
- this(factory, null, true);
- }
-
- /**
- * Constructor.
- *
- * @param factory the underlying HttpClient socket factory wrapped by this implementation.
- * @param verifier the hostname verifier evaluated by this implementation
- */
- public SecurityEnhancedTLSSocketFactory(@Nonnull final LayeredConnectionSocketFactory factory,
- @Nullable final HostnameVerifier verifier) {
- this(factory, verifier, true);
- }
- /**
- * Constructor.
- *
- * @param factory the underlying HttpClient socket factory wrapped by this implementation.
- * @param verifier the hostname verifier evaluated by this implementation
- * @param trustEngineRequired flag indicating whether a context trust engine attribute is required
- * for TLS server validation.
- */
- public SecurityEnhancedTLSSocketFactory(@Nonnull final LayeredConnectionSocketFactory factory,
- @Nullable final HostnameVerifier verifier, final boolean trustEngineRequired) {
wrappedFactory = Constraint.isNotNull(factory, "Socket factory was null");
- hostnameVerifier = verifier;
- engineRequired = trustEngineRequired;
- }
-
- /**
- * Get the flag indicating whether a context trust engine attribute is required for TLS server validation.
- *
- * <p>Default: true.</p>
- *
- * @return true if trust engine is required, false if not
- */
- public boolean isTrustEngineRequired() {
- return engineRequired;
}
/** {@inheritDoc} */
@@ -210,12 +143,8 @@ public class SecurityEnhancedTLSSocketFactory implements LayeredConnectionSocket
log.trace("In connectSocket");
try {
- setup(context);
- final Socket socket =
- wrappedFactory.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
- performTrustEval(socket, host.getHostName(), context);
- performHostnameVerification(socket, host.getHostName(), context);
- return socket;
+ setup(context, host.getHostName());
+ return wrappedFactory.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
} finally {
teardown(context);
}
@@ -227,163 +156,96 @@ public class SecurityEnhancedTLSSocketFactory implements LayeredConnectionSocket
final HttpContext context) throws IOException {
log.trace("In createLayeredSocket");
try {
- setup(context);
- final Socket layeredSocket = wrappedFactory.createLayeredSocket(socket, target, port, context);
- performTrustEval(layeredSocket, target, context);
- performHostnameVerification(layeredSocket, target, context);
- return layeredSocket;
+ setup(context, target);
+ return wrappedFactory.createLayeredSocket(socket, target, port, context);
} finally {
teardown(context);
}
}
/**
- * Perform trust evaluation by extracting the server TLS {@link X509Credential} from the
- * {@link SSLSession} and evaluating it via a
- * {@link TrustEngine}<code><</code>{@link org.opensaml.security.credential.Credential}<code>></code>
- * and {@link CriteriaSet} supplied by the caller via the {@link HttpContext}.
+ * Setup calling execution environment for server TLS and client TLS based on information supplied in the
+ * {@link HttpContext}.
*
- * @param socket the socket instance being processed
- * @param hostname the hostname being processed
- * @param context the HttpClient context being processed
- *
- * @throws IOException if the server TLS credential is untrusted, or if there is a fatal error
- * attempting trust evaluation.
+ * @param context the HttpContext instance
+ * @param hostname the hostname for the connection
+ *
+ * @throws SSLPeerUnverifiedException if required data is not available from the context
*/
- protected void performTrustEval(@Nonnull final Socket socket, @Nonnull @NotEmpty final String hostname,
- @Nonnull final HttpContext context) throws IOException {
+ protected void setup(@Nullable final HttpContext context, @Nonnull final String hostname)
+ throws SSLPeerUnverifiedException {
- if (!(socket instanceof SSLSocket)) {
- log.debug("Socket was not an instance of SSLSocket, skipping trust eval");
- return;
- }
- final SSLSocket sslSocket = (SSLSocket) socket;
-
- log.debug("Attempting to evaluate server TLS credential against supplied TrustEngine and CriteriaSet");
-
- @SuppressWarnings("unchecked")
- final TrustEngine<? super X509Credential> trustEngine =
- (TrustEngine<? super X509Credential>) context.getAttribute(
- HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE);
- if (trustEngine == null) {
- if (isTrustEngineRequired()) {
- log.warn("The required trust engine was not supplied by the caller, failing socket TLS creation");
- throw new SSLPeerUnverifiedException("The required trust engine was not supplied by the caller");
- }
- log.debug("No trust engine supplied by caller, skipping trust eval");
+ log.trace("Attempting to setup thread-local data for TLS evaluation");
+ if (context == null) {
+ log.trace("HttpContext was null, skipping thread-local setup");
return;
}
- log.trace("Saw trust engine of type: {}", trustEngine.getClass().getName());
-
- CriteriaSet criteriaSet = (CriteriaSet) context.getAttribute(
- HttpClientSecurityConstants.CONTEXT_KEY_CRITERIA_SET);
- if (criteriaSet == null) {
- log.debug("No criteria set supplied by caller, building new criteria set with signing "
- + "and trusted names criteria");
- criteriaSet = new CriteriaSet(new UsageCriterion(UsageType.SIGNING));
- criteriaSet.add(new TrustedNamesCriterion(Collections.singleton(hostname)));
- } else {
- log.trace("Saw CriteriaSet: {}", criteriaSet);
- }
+ setupServerTLS(context, hostname);
- final X509Credential credential = extractCredential(sslSocket);
-
- try {
- if (trustEngine.validate(credential, criteriaSet)) {
- log.debug("Credential evaluated as trusted");
- context.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED,
- Boolean.TRUE);
- } else {
- context.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED,
- Boolean.FALSE);
- final Boolean fatal = (Boolean)context.getAttribute(
- HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_FAILURE_IS_FATAL);
- if (fatal == null || fatal) {
- log.debug("Credential evaluated as untrusted, failure indicated as fatal");
- throw new SSLPeerUnverifiedException(
- "Trust engine could not establish trust of server TLS credential");
- }
- log.debug("Credential evaluated as untrusted, failure indicated as non-fatal");
- }
- } catch (final SecurityException e) {
- log.error("Trust engine error evaluating credential: {}", e.getMessage());
- throw new IOException("Trust engine error evaluating credential", e);
- }
-
+ setupClientTLS(context);
}
/**
- * Extract the server TLS {@link X509Credential} from the supplied {@link SSLSocket}.
- *
- * @param sslSocket the SSL socket instance to process
- * @return an X509Credential representing the server TLS entity certificate as well as the
- * supplied supporting intermediate certificate chain (if any)
- * @throws IOException if credential data can not be extracted from the socket
- */
- @Nonnull protected X509Credential extractCredential(@Nonnull final SSLSocket sslSocket) throws IOException {
- final SSLSession session = sslSocket.getSession();
- final Certificate[] peerCertificates = session.getPeerCertificates();
- if (peerCertificates == null || peerCertificates.length < 1) {
- throw new SSLPeerUnverifiedException("SSLSession peer certificates array was null or empty");
- }
-
- final ArrayList<X509Certificate> certChain = new ArrayList<>();
- for (final Certificate cert : peerCertificates) {
- certChain.add((X509Certificate) cert);
- }
-
- final X509Certificate entityCert = certChain.get(0);
-
- final BasicX509Credential credential = new BasicX509Credential(entityCert);
- credential.setEntityCertificateChain(certChain);
-
- return credential;
- }
-
- /**
- * Perform hostname verification on the connection represented by the supplied socket.
- *
- * @param socket the socket instance being processed
- * @param hostname the hostname against which to verify
- * @param context the current HttpClient context instance
- * @throws IOException if an I/O error occurs or the verification process fails
- */
- protected void performHostnameVerification(final Socket socket, final String hostname, final HttpContext context)
- throws IOException {
- if (hostnameVerifier != null && socket instanceof SSLSocket) {
- if (! hostnameVerifier.verify(hostname, ((SSLSocket) socket).getSession())) {
- throw new SSLPeerUnverifiedException("TLS hostname verification failed for hostname: " + hostname);
- }
- }
- }
-
- /**
- * Load the {@link ThreadLocalX509CredentialContext} with the client TLS credential obtained from
- * the {@link HttpContext}.
+ * Setup thread-local data for server TLS and client TLS based on information supplied in the
+ * {@link HttpContext}.
*
* @param context the HttpContext instance
*/
- protected void setup(@Nullable final HttpContext context) {
- log.trace("Attempting to setup thread-local client TLS X509Credential");
- if (context == null) {
- log.trace("HttpContext was null, skipping thread-local setup");
- return;
- }
-
+ protected void setupClientTLS(@Nonnull final HttpContext context) {
final X509Credential credential =
(X509Credential) context.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_CLIENT_TLS_CREDENTIAL);
+
if (credential != null) {
log.trace("Loading ThreadLocalX509CredentialContext with client TLS credential: {}", credential);
if (ThreadLocalX509CredentialContext.haveCurrent()) {
log.trace("ThreadLocalX509CredentialContext was already loaded with client TLS credential, "
- + "will be overwritten with credential from HttpContext");
+ + "will be overwritten with data from HttpContext");
}
ThreadLocalX509CredentialContext.loadCurrent(credential);
} else {
- log.trace("HttpContext did not contain a client TLS credential, nothing to do");
+ log.trace("X509Credential not supplied by caller, skipping ThreadLocalX509CredentialContext population");
}
+ }
+ /**
+ * Setup thread-local data for server TLS.
+ *
+ * @param context the HttpContext instance
+ * @param hostname the hostname for the connection
+ */
+ protected void setupServerTLS(@Nonnull final HttpContext context, @Nonnull final String hostname) {
+
+ @SuppressWarnings("unchecked")
+ final TrustEngine<? super X509Credential> trustEngine =
+ (TrustEngine<? super X509Credential>) context.getAttribute(
+ HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE);
+
+ if (trustEngine != null) {
+ CriteriaSet criteriaSet = (CriteriaSet) context.getAttribute(
+ HttpClientSecurityConstants.CONTEXT_KEY_CRITERIA_SET);
+ if (criteriaSet == null) {
+ log.debug("No CriteriaSet supplied by caller, building new instance with signing "
+ + "and trusted names criteria");
+ criteriaSet = new CriteriaSet(new UsageCriterion(UsageType.SIGNING));
+ criteriaSet.add(new TrustedNamesCriterion(Collections.singleton(hostname)));
+ } else {
+ log.trace("Saw CriteriaSet: {}", criteriaSet);
+ }
+
+ final Boolean isFailureFatal = (Boolean) context.getAttribute(
+ HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_FAILURE_IS_FATAL);
+
+ if (ThreadLocalX509TrustEngineContext.haveCurrent()) {
+ log.trace("ThreadLocalX509TrustEngineContext was already loaded with trust engine and criteria, "
+ + "will be overwritten with data from HttpContext");
+ }
+
+ ThreadLocalX509TrustEngineContext.loadCurrent(trustEngine, criteriaSet, isFailureFatal);
+
+ } else {
+ log.debug("TrustEngine not supplied by the caller, skipping ThreadLocalX509TrustEngineContext population");
+ }
}
/**
@@ -393,9 +255,16 @@ public class SecurityEnhancedTLSSocketFactory implements LayeredConnectionSocket
* @param context the HttpContext instance
*/
protected void teardown(@Nullable final HttpContext context) {
+ final HttpClientContext clientContext = HttpClientContext.adapt(context);
+
+ if (ThreadLocalX509TrustEngineContext.haveCurrent()) {
+ log.trace("Scheduling deferred clearing of thread-local server TLS TrustEngine and CriteriaSet");
+ HttpClientSupport.addDynamicContextHandlerLast(clientContext, SERVER_TLS_HANDLER, true);
+ }
+
if (ThreadLocalX509CredentialContext.haveCurrent()) {
log.trace("Scheduling deferred clearing of thread-local client TLS X509Credential");
- HttpClientSupport.addDynamicContextHandlerLast(HttpClientContext.adapt(context), CLIENT_TLS_HANDLER);
+ HttpClientSupport.addDynamicContextHandlerLast(clientContext, CLIENT_TLS_HANDLER, true);
}
}
diff --git a/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/ThreadLocalServerTLSHandler.java b/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/ThreadLocalServerTLSHandler.java
new file mode 100644
index 0000000..5122f2a
--- /dev/null
+++ b/opensaml-security-impl/src/main/java/org/opensaml/security/httpclient/impl/ThreadLocalServerTLSHandler.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.security.httpclient.impl;
+
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.opensaml.security.httpclient.HttpClientSecurityConstants;
+import org.opensaml.security.x509.tls.impl.ThreadLocalX509TrustEngineContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.httpclient.HttpClientContextHandler;
+
+/**
+ * An implementation of {@link HttpClientContextHandler} which handles cleanup and transfer of
+ * data used for server TLS held by {@link ThreadLocalX509TrustEngineContext}.
+ */
+public class ThreadLocalServerTLSHandler implements HttpClientContextHandler {
+
+ /** Logger. */
+ private final Logger log = LoggerFactory.getLogger(ThreadLocalServerTLSHandler.class);
+
+ /** {@inheritDoc} */
+ public void invokeBefore(@Nonnull final HttpClientContext context, @Nonnull final HttpUriRequest request)
+ throws IOException {
+ // Do nothing here
+
+ }
+
+ /** {@inheritDoc} */
+ public void invokeAfter(@Nonnull final HttpClientContext context, @Nonnull final HttpUriRequest request)
+ throws IOException {
+
+ log.trace("Saw ThreadLocalX509TrustEngineContext.getTrusted: {}",
+ ThreadLocalX509TrustEngineContext.getTrusted());
+ context.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED,
+ ThreadLocalX509TrustEngineContext.getTrusted());
+
+ log.trace("Clearing ThreadLocalX509TrustEngineContext");
+ ThreadLocalX509TrustEngineContext.clearCurrent();
+ }
+
+}
diff --git a/opensaml-security-impl/src/main/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustEngineContext.java b/opensaml-security-impl/src/main/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustEngineContext.java
new file mode 100644
index 0000000..6f6801c
--- /dev/null
+++ b/opensaml-security-impl/src/main/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustEngineContext.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.security.x509.tls.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.trust.TrustEngine;
+import org.opensaml.security.x509.X509Credential;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * Class which holds and makes available instances of
+ * {@link TrustEngine}<code><? super </code>{@link org.opensaml.security.x509.X509Credential}<code>></code>
+ * and {@link CriteriaSet} via ThreadLocal storage, typically used for server TLS authentication
+ * via {@link ThreadLocalX509TrustManager}.
+ */
+public final class ThreadLocalX509TrustEngineContext {
+
+ private static final class Data {
+
+ /** Trust engine. */
+ private TrustEngine<? super X509Credential> trustEngine;
+
+ /** Criteria. */
+ private CriteriaSet criteriaSet;
+
+ /** Whether evaluation target was trusted. */
+ private Boolean trusted;
+
+ /** Whether trust engine evaluation failure should be treated as fatal. Defaults to true. */
+ private Boolean failureFatal;
+
+ private Data(@Nonnull final TrustEngine<? super X509Credential> engine, @Nonnull final CriteriaSet criteria,
+ @Nullable final Boolean fatal) {
+
+ trustEngine = engine;
+ criteriaSet = criteria;
+ failureFatal = fatal != null ? fatal : Boolean.TRUE;
+ }
+
+ }
+
+ /** ThreadLocal storage for trust engine. */
+ private static ThreadLocal<Data> current = new ThreadLocal<>();
+
+ /** Constructor. */
+ private ThreadLocalX509TrustEngineContext() { }
+
+ /**
+ * Load the thread-local storage with the current credential.
+ *
+ * @param trustEngine the current trust engine
+ * @param criteria the current criteria
+ * @param fatal whether trust engine evaluation failure should be treated as fatal. Defaults to true.
+ */
+ public static void loadCurrent(@Nonnull final TrustEngine<? super X509Credential> trustEngine,
+ @Nonnull final CriteriaSet criteria, @Nullable final Boolean fatal) {
+
+ Constraint.isNotNull(trustEngine, "TrustEngine may not be null");
+ Constraint.isNotNull(criteria, "CriteriaSet may not be null");
+
+ current.set(new Data(trustEngine, criteria, fatal));
+ }
+
+ /**
+ * Clear the current thread-local credential.
+ */
+ public static void clearCurrent() {
+ current.remove();
+ }
+
+ /**
+ * Get whether the current thread-local is populated with non-null data.
+ *
+ * @return true if thread-local has non-null data, false otherwise
+ */
+ public static boolean haveCurrent() {
+ return current.get() != null;
+ }
+
+ /**
+ * Return the current thread-local trust engine instance.
+ *
+ * @return the current trust engine
+ */
+ @Nullable public static TrustEngine<? super X509Credential> getTrustEngine() {
+ return current.get() != null ? current.get().trustEngine : null;
+ }
+
+ /**
+ * Return the current thread-local criteria instance.
+ *
+ * @return the current criteria
+ */
+ @Nullable public static CriteriaSet getCriteria() {
+ return current.get() != null ? current.get().criteriaSet : null;
+ }
+
+ /**
+ * Return whether failure of server TLS is to be treated as fatal.
+ *
+ * @return true if fatal, false if not
+ */
+ @Nonnull public static Boolean isFailureFatal() {
+ return current.get() != null ? current.get().failureFatal : Boolean.TRUE;
+ }
+
+ /**
+ * Return the current thread-local trust evaluation data.
+ *
+ * @return the current trust evaluation data
+ */
+ @Nullable public static Boolean getTrusted() {
+ return current.get() != null ? current.get().trusted : null;
+ }
+
+ /**
+ * Set the current thread-local trust evaluation data.
+ *
+ * @param trusted the current trust evaluation data
+ */
+ public static void setTrusted(@Nullable final Boolean trusted) {
+ if (current.get() != null) {
+ current.get().trusted = trusted;
+ }
+ }
+
+}
diff --git a/opensaml-security-impl/src/main/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustManager.java b/opensaml-security-impl/src/main/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustManager.java
new file mode 100644
index 0000000..9e05903
--- /dev/null
+++ b/opensaml-security-impl/src/main/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustManager.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.security.x509.tls.impl;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509TrustManager;
+
+import org.opensaml.security.trust.TrustEngine;
+import org.opensaml.security.x509.BasicX509Credential;
+import org.opensaml.security.x509.X509Credential;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * An implementation of {@link X509TrustManager} which performs its evaluation using trust engine and criteria
+ * instances available from {@link ThreadLocalX509TrustEngineContext}.
+ */
+public class ThreadLocalX509TrustManager implements X509TrustManager {
+
+ /** Logger. */
+ private Logger log = LoggerFactory.getLogger(ThreadLocalX509TrustManager.class);
+
+ /** {@inheritDoc} */
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[]{};
+ }
+
+ /** {@inheritDoc} */
+ public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
+ performTrustEval(chain, authType);
+ }
+
+ /** {@inheritDoc} */
+ public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
+ performTrustEval(chain, authType);
+ }
+
+ /**
+ * Perform trust evaluation on the specified certificate chain using the trust engine and criteria
+ * available from {@link ThreadLocalX509TrustEngineContext}.
+ *
+ * @param chain the peer certificate chain
+ * @param authType the authentication type based on the client certificate
+ *
+ * @throws CertificateException if the certificate chain is not trusted by this TrustManager.
+ */
+ protected void performTrustEval(final X509Certificate[] chain, final String authType) throws CertificateException {
+ // These checks are per the documentation for this interface
+ if (chain == null || chain.length == 0) {
+ throw new IllegalArgumentException("Certificate chain was null or empty");
+ }
+ if (authType == null || authType.isEmpty()) {
+ throw new IllegalArgumentException("AuthType was null or empty");
+ }
+
+ if (!ThreadLocalX509TrustEngineContext.haveCurrent()) {
+ throw new CertificateException("Trust of X509Certificate could not be established, "
+ + "ThreadLocalX509TrustEngineContext is not populated");
+ }
+
+ if (performTrustEval(chain,
+ ThreadLocalX509TrustEngineContext.getTrustEngine(),
+ ThreadLocalX509TrustEngineContext.getCriteria())) {
+ ThreadLocalX509TrustEngineContext.setTrusted(true);
+ } else {
+ ThreadLocalX509TrustEngineContext.setTrusted(false);
+ if (ThreadLocalX509TrustEngineContext.isFailureFatal()) {
+ log.debug("Credential evaluated as untrusted, failure indicated as fatal");
+ throw new CertificateException("Trust engine could not establish trust of presented TLS credential");
+ }
+ log.debug("Credential evaluated as untrusted, failure indicated as non-fatal");
+ }
+ }
+
+ /**
+ * Perform trust evaluation on the specified certificate chain using the supplied trust engine and criteria.
+ *
+ * @param chain the certificate chain to be evaluated
+ * @param trustEngine the trust engine
+ * @param criteriaSet the criteria set
+ *
+ * @return true if certificate was established as trusted, false if not
+ *
+ * @throws CertificateException if the trust of the certificate
+ */
+ protected boolean performTrustEval(@Nonnull final X509Certificate[] chain,
+ @Nonnull final TrustEngine<? super X509Credential> trustEngine,
+ @Nonnull final CriteriaSet criteriaSet) throws CertificateException {
+
+ log.debug("Attempting to evaluate server TLS credential against supplied TrustEngine and CriteriaSet");
+
+ final X509Credential credential = extractCredential(chain);
+
+ log.trace("Saw trust engine of type: {}", trustEngine.getClass().getName());
+
+ try {
+ if (trustEngine.validate(credential, criteriaSet)) {
+ log.debug("Credential evaluated as trusted");
+ return true;
+ }
+ log.debug("Credential evaluated as untrusted");
+ return false;
+ } catch (final Throwable t) {
+ log.error("Fatal trust engine error evaluating credential", t);
+ return false;
+ }
+
+ }
+
+ /**
+ * Extract the server TLS {@link X509Credential} from the supplied {@link SSLSocket}.
+ *
+ * @param chain the chain of X509 certificates
+ * @return an X509Credential representing the entity certificate as well as the
+ * supplied supporting intermediate certificate chain (if any)
+ * @throws CertificateException if credential data can not be extracted from the socket
+ */
+ @Nonnull protected X509Credential extractCredential(@Nonnull @NotEmpty final X509Certificate[] chain)
+ throws CertificateException {
+
+ final List<X509Certificate> certChain = Arrays.asList(chain);
+
+ final X509Certificate entityCert = certChain.get(0);
+
+ final BasicX509Credential credential = new BasicX509Credential(entityCert);
+ credential.setEntityCertificateChain(certChain);
+
+ return credential;
+ }
+
+}
diff --git a/opensaml-security-impl/src/test/java/org/opensaml/security/httpclient/impl/SecurityEnhancedTLSSocketFactoryTest.java b/opensaml-security-impl/src/test/java/org/opensaml/security/httpclient/impl/SecurityEnhancedTLSSocketFactoryTest.java
index 206a2cd..f1e1f95 100644
--- a/opensaml-security-impl/src/test/java/org/opensaml/security/httpclient/impl/SecurityEnhancedTLSSocketFactoryTest.java
+++ b/opensaml-security-impl/src/test/java/org/opensaml/security/httpclient/impl/SecurityEnhancedTLSSocketFactoryTest.java
@@ -22,30 +22,30 @@ import java.io.InputStream;
import java.net.Socket;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLPeerUnverifiedException;
-
import org.apache.http.HttpHost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
-import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.protocol.HttpContext;
-import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.impl.StaticCredentialResolver;
import org.opensaml.security.httpclient.HttpClientSecurityConstants;
import org.opensaml.security.trust.impl.ExplicitKeyTrustEngine;
import org.opensaml.security.x509.BasicX509Credential;
import org.opensaml.security.x509.X509Credential;
import org.opensaml.security.x509.X509Support;
+import org.opensaml.security.x509.tls.impl.ThreadLocalX509CredentialContext;
+import org.opensaml.security.x509.tls.impl.ThreadLocalX509TrustEngineContext;
import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import net.shibboleth.utilities.java.support.httpclient.HttpClientSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
public class SecurityEnhancedTLSSocketFactoryTest {
private static final String DATA_PATH = "/org/opensaml/security/x509/impl/";
@@ -61,156 +61,144 @@ public class SecurityEnhancedTLSSocketFactoryTest {
httpContext = new HttpClientContext();
}
+ @AfterMethod
+ public void clearThreadLocals() {
+ ThreadLocalX509TrustEngineContext.clearCurrent();
+ ThreadLocalX509CredentialContext.clearCurrent();
+ }
+
@Test
- public void testNonSSL() throws IOException {
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(null, hostname), null);
+ public void testNoContextParametersHTTP() throws IOException {
+ securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(null, hostname));
Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
+
securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 80, "http"), null, null, httpContext);
+
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.haveCurrent());
+ Assert.assertFalse(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalServerTLSHandler.class.isInstance(h)));
- Assert.assertNull(httpContext.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED));
- }
-
- @Test(expectedExceptions=SSLPeerUnverifiedException.class)
- public void testDefaultFailNoTrustEngine() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
-
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname), null);
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
+ Assert.assertFalse(ThreadLocalX509CredentialContext.haveCurrent());
+ Assert.assertFalse(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalClientTLSCredentialHandler.class.isInstance(h)));
}
@Test
- public void testSuccessNoTrustEngine() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
-
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname), null, false);
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
-
- Assert.assertNull(httpContext.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED));
+ public void testNoContextParametersHTTPS() throws IOException {
+ X509Credential cred = getCredential("foo-1A1-good.crt");
+
+ securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
+ Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname));
+ Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
+
+ securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
+
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.haveCurrent());
+ Assert.assertFalse(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalServerTLSHandler.class.isInstance(h)));
+
+ Assert.assertFalse(ThreadLocalX509CredentialContext.haveCurrent());
+ Assert.assertFalse(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalClientTLSCredentialHandler.class.isInstance(h)));
}
@Test
- public void testSuccessWithEngine() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
- ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(cred));
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
-
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname), null);
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
-
- Assert.assertEquals(httpContext.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED), Boolean.TRUE);
+ public void testEngineParamWithDefaultCriteria() throws IOException {
+ X509Credential cred = getCredential("foo-1A1-good.crt");
+ ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(cred));
+ httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
+
+ securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
+ Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname));
+ Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
+
+ securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
+
+ Assert.assertTrue(ThreadLocalX509TrustEngineContext.haveCurrent());
+ Assert.assertSame(ThreadLocalX509TrustEngineContext.getTrustEngine(), trustEngine);
+ Assert.assertNotNull(ThreadLocalX509TrustEngineContext.getCriteria());
+ Assert.assertTrue(ThreadLocalX509TrustEngineContext.isFailureFatal());
+ Assert.assertTrue(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalServerTLSHandler.class.isInstance(h)));
+
+ Assert.assertFalse(ThreadLocalX509CredentialContext.haveCurrent());
+ Assert.assertFalse(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalClientTLSCredentialHandler.class.isInstance(h)));
}
@Test
- public void testSuccessWithEngineAndVerifier() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
- ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(cred));
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
-
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname), new DefaultHostnameVerifier());
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
-
- Assert.assertEquals(httpContext.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED), Boolean.TRUE);
- }
-
- @Test(expectedExceptions=SSLPeerUnverifiedException.class)
- public void testFailUntrustedCert() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
- List<Credential> emptyCreds = new ArrayList<>();
- ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(emptyCreds));
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
-
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname), new DefaultHostnameVerifier());
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
- try {
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
- } catch (Exception e) {
- Assert.assertEquals(httpContext.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED), Boolean.FALSE);
- throw e;
- }
- }
-
- @Test(expectedExceptions=SSLPeerUnverifiedException.class)
- public void testFailUntrustedCertExplicitFatal() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
- List<Credential> emptyCreds = new ArrayList<>();
- ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(emptyCreds));
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_FAILURE_IS_FATAL, Boolean.TRUE);
-
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname), new DefaultHostnameVerifier());
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
- try {
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
- } catch (Exception e) {
- Assert.assertEquals(httpContext.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED), Boolean.FALSE);
- throw e;
- }
+ public void testEngineParamWithExplicitCriteria() throws IOException {
+ X509Credential cred = getCredential("foo-1A1-good.crt");
+ ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(cred));
+ httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
+
+ CriteriaSet criteria = new CriteriaSet();
+ httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_CRITERIA_SET, criteria);
+
+ securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
+ Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname));
+ Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
+
+ securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
+
+ Assert.assertTrue(ThreadLocalX509TrustEngineContext.haveCurrent());
+ Assert.assertSame(ThreadLocalX509TrustEngineContext.getTrustEngine(), trustEngine);
+ Assert.assertSame(ThreadLocalX509TrustEngineContext.getCriteria(), criteria);
+ Assert.assertTrue(ThreadLocalX509TrustEngineContext.isFailureFatal());
+ Assert.assertTrue(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalServerTLSHandler.class.isInstance(h)));
+
+ Assert.assertFalse(ThreadLocalX509CredentialContext.haveCurrent());
+ Assert.assertFalse(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalClientTLSCredentialHandler.class.isInstance(h)));
}
@Test
- public void testFailUntrustedCertNonFatal() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
- List<Credential> emptyCreds = new ArrayList<>();
- ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(emptyCreds));
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_FAILURE_IS_FATAL, Boolean.FALSE);
-
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname), new DefaultHostnameVerifier());
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
- Assert.assertEquals(httpContext.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED), Boolean.FALSE);
+ public void testEngineParamWithFailureNonFatal() throws IOException {
+ X509Credential cred = getCredential("foo-1A1-good.crt");
+ ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(cred));
+ httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
+ httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_FAILURE_IS_FATAL, Boolean.FALSE);
+
+ CriteriaSet criteria = new CriteriaSet();
+ httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_CRITERIA_SET, criteria);
+
+ securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
+ Collections.singletonList((Certificate)cred.getEntityCertificate()), hostname));
+ Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
+
+ securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
+
+ Assert.assertTrue(ThreadLocalX509TrustEngineContext.haveCurrent());
+ Assert.assertSame(ThreadLocalX509TrustEngineContext.getTrustEngine(), trustEngine);
+ Assert.assertNotNull(ThreadLocalX509TrustEngineContext.getCriteria());
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.isFailureFatal());
+ Assert.assertTrue(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalServerTLSHandler.class.isInstance(h)));
+
+ Assert.assertFalse(ThreadLocalX509CredentialContext.haveCurrent());
+ Assert.assertFalse(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalClientTLSCredentialHandler.class.isInstance(h)));
}
- @Test(expectedExceptions=SSLException.class)
- public void testFailBadHostname() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
- ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(cred));
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
-
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- Collections.singletonList((Certificate)cred.getEntityCertificate()), "bogus.example.com"), new DefaultHostnameVerifier());
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
-
- try {
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost("bogus.example.com", 443, "https"), null, null, httpContext);
- } catch (Exception e) {
- Assert.assertEquals(httpContext.getAttribute(HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED), Boolean.TRUE);
- throw e;
- }
- }
+ @Test
+ public void testClientTLSParam() throws IOException {
+ X509Credential cred = getCredential("foo-1A1-good.crt");
+ httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_CLIENT_TLS_CREDENTIAL, cred);
+
+ securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(null, hostname));
+ Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
- @Test(expectedExceptions=SSLPeerUnverifiedException.class)
- public void testFailNoCertsInSession() throws IOException {
- X509Credential cred = getCredential("foo-1A1-good.crt");
- ExplicitKeyTrustEngine trustEngine = new ExplicitKeyTrustEngine(new StaticCredentialResolver(cred));
- httpContext.setAttribute(HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE, trustEngine);
-
- // Pass an empty cert list, to simulate unlikely condition of SSLSession not having any peerCertificates
- securityEnhancedSocketFactory = new SecurityEnhancedTLSSocketFactory(buildInnerSSLFactory(
- new ArrayList<Certificate>(), hostname), new DefaultHostnameVerifier());
- Socket socket = securityEnhancedSocketFactory.createSocket(httpContext);
-
- securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
+ securityEnhancedSocketFactory.connectSocket(0, socket, new HttpHost(hostname, 443, "https"), null, null, httpContext);
+
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.haveCurrent());
+ Assert.assertFalse(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalServerTLSHandler.class.isInstance(h)));
+
+ Assert.assertTrue(ThreadLocalX509CredentialContext.haveCurrent());
+ Assert.assertSame(ThreadLocalX509CredentialContext.getCredential(), cred);
+ Assert.assertTrue(HttpClientSupport.getDynamicContextHandlerList(
+ HttpClientContext.adapt(httpContext)).stream().anyMatch(h -> ThreadLocalClientTLSCredentialHandler.class.isInstance(h)));
}
// Helper methods
diff --git a/opensaml-security-impl/src/test/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustManagerTest.java b/opensaml-security-impl/src/test/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustManagerTest.java
new file mode 100644
index 0000000..b4cdecc
--- /dev/null
+++ b/opensaml-security-impl/src/test/java/org/opensaml/security/x509/tls/impl/ThreadLocalX509TrustManagerTest.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.security.x509.tls.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.trust.MockTrustEngine;
+import org.opensaml.security.x509.X509Support;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public class ThreadLocalX509TrustManagerTest {
+
+ private ThreadLocalX509TrustManager trustManager = new ThreadLocalX509TrustManager();
+
+ private X509Certificate[] chain;
+
+ private CriteriaSet criteria;
+
+ @BeforeClass
+ public void beforeClass() throws CertificateException, IOException {
+ try (final InputStream is = this.getClass().getResourceAsStream("/data/certificate.pem")) {
+ final Collection<X509Certificate> certs = X509Support.decodeCertificates(is);
+ chain = certs.stream().toArray(X509Certificate[]::new);
+ }
+ }
+
+ @BeforeMethod
+ public void beforeMethod() {
+ ThreadLocalX509TrustEngineContext.clearCurrent();
+ criteria = new CriteriaSet();
+ }
+
+ @AfterMethod
+ public void afterMethod() {
+ ThreadLocalX509TrustEngineContext.clearCurrent();
+ }
+
+ @Test
+ public void trusted() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(true), criteria, true);
+
+ trustManager.checkServerTrusted(chain, "RSA");
+
+ Assert.assertTrue(ThreadLocalX509TrustEngineContext.getTrusted());
+ }
+
+ @Test
+ public void notTrusted() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(false), criteria, true);
+
+ try {
+ trustManager.checkServerTrusted(chain, "RSA");
+ Assert.fail("Trust manager should have thrown");
+ } catch (CertificateException e) {
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.getTrusted());
+ }
+ }
+
+ @Test
+ public void notTrustedNotFatal() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(false), criteria, false);
+
+ trustManager.checkServerTrusted(chain, "RSA");
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.getTrusted());
+ }
+
+ @Test
+ public void trustEngineThrowsSecurityException() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(new SecurityException()), criteria, true);
+
+ try {
+ trustManager.checkServerTrusted(chain, "RSA");
+ Assert.fail("Trust manager should have thrown");
+ } catch (CertificateException e) {
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.getTrusted());
+ }
+ }
+
+ @Test
+ public void trustEngineThrowsSecurityExceptionNotFatal() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(new SecurityException()), criteria, false);
+
+ trustManager.checkServerTrusted(chain, "RSA");
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.getTrusted());
+ }
+
+ @Test
+ public void trustEngineThrowsRuntimeException() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(new RuntimeException()), criteria, true);
+
+ try {
+ trustManager.checkServerTrusted(chain, "RSA");
+ Assert.fail("Trust manager should have thrown");
+ } catch (CertificateException e) {
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.getTrusted());
+ }
+
+ }
+
+ @Test
+ public void trustEngineThrowsRuntimeExceptionNotFatal() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(new RuntimeException()), criteria, false);
+
+ trustManager.checkServerTrusted(chain, "RSA");
+ Assert.assertFalse(ThreadLocalX509TrustEngineContext.getTrusted());
+
+ }
+
+ @Test
+ public void threadLocalNotLoaded() throws CertificateException {
+
+ try {
+ trustManager.checkServerTrusted(chain, "RSA");
+ Assert.fail("Trust manager should have thrown");
+ } catch (CertificateException e) {
+ Assert.assertNull(ThreadLocalX509TrustEngineContext.getTrusted());
+ }
+ }
+
+ @Test(expectedExceptions=IllegalArgumentException.class)
+ public void nullChain() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(true), criteria, true);
+ trustManager.checkServerTrusted(null, "RSA");
+ }
+
+ @Test(expectedExceptions=IllegalArgumentException.class)
+ public void emptyChain() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(true), criteria, true);
+ trustManager.checkServerTrusted(new X509Certificate[] {}, "RSA");
+ }
+
+ @Test(expectedExceptions=IllegalArgumentException.class)
+ public void nullAuthType() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(true), criteria, true);
+ trustManager.checkServerTrusted(chain, null);
+ }
+
+ @Test(expectedExceptions=IllegalArgumentException.class)
+ public void emptyAuthType() throws CertificateException {
+ ThreadLocalX509TrustEngineContext.loadCurrent(new MockTrustEngine<>(true), criteria, true);
+ trustManager.checkServerTrusted(chain, "");
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list