Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The IdP software uses the Apache HttpClient library anywhere more or less anywhere that this functionality is required, which for most deployers is confined to obtaining metadata from remote sources. The necessary settings to control the behavior of the metadata client code can be handled directly in the metadata-providers.xml file in most cases, so this topic is primarily a reference for people who have very advanced needs or are using other components and features that make use of the client.

...

...

Code Block
languagexml
<bean id="ShortTimeoutHttpClient" parent="shibboleth.NonCachingHttpClient" p:socketTimeout="PT5S" />

...

For more advanced control over this process, you need to perform additional steps, and add more complex Spring wiring involving the org.opensaml.security.httpclient.HttpClientSecurityParameters class, which provides a mechanism for using a TrustEngine to evaluate the server's certificate. There are a variety of TrustEngine implementations that can perform simple or advanced checks, but the critical difference is that they're contextual. That is, they can be applied to one component such that the rules it uses can be specific to that component alone and not the whole system.

A particularly useful approach is to abandon the fundamentally flawed use of commercial certificates with non-browser-facing services and use self-signed certificates evaluated on the basis of public key comparisons, much like Shibboleth does with SAML, or even certificates issued on an automated short-term basis by dedicated CAs.

Note

You need to be careful with this, because leaving out a step can introduce security holes. In particular, if If you want an HttpClient bean to use the special TLSSocketFactory we provide that supports a TrustEngine, you MUST provide an HttpClientSecurityParameters instance to the component using the HttpClient bean to configure the security behavior you want.

...

Failure to do so will prevent TLS socket connections from succeeding in V3.4, but only results in warnings on earlier versions, which can lead to security mistakes.

Repeating: you can tell the HttpClient bean that you want to support a more "advanced mode" of processing, but you configure the rules not on the HttpClient bean but rather on the component using the client bean. This is because the injection of the rules you want to apply have to be added at runtime when the client gets used and not into the client's own data structures. It's a consequence of the library's design. The risk in versions of the software prior to V3.4 is that if you tell the client you want this, but don't configure the component that's using it properly, the default Java behavior is also skipped, and you get no security at all. The following explains what you should do, in detail.

TrustEngine Examples

To use a TrustEngine, you , for older versions, because on newer versions it fails closed anyway.

TrustEngine Examples

To use a TrustEngine, you need to define an an HttpClientSecurityParameters bean with a tLSTrustEngine property. While you can define any compatible object, the two most common cases are supported via a pair of factory beans:

...

The examples below are semi-complete, in that they include a portion of the "real" component that the security rules are being supplied for. Most of the components that can support this will carry both httpClient and httpClientSecurityParameters properties. Components defined in custom (non-Spring) XML syntax will usually support httpClientRef and httpClientSecurityParametersRef XML attributes to reference Spring beans of the proper type.

Unsurprisingly, the "explicit" engine is a lot bit simpler to use. You can provide any number of public keys and certificates via resources (file, classpath, even HTTP, though that gets pretty circular here), to drive the engine.

...

Code Block
languagexml
titleExplicit key comparison against two keys
collapsetrue
<bean id="CustomHttpSecurity" class="org.opensaml.security.httpclient.HttpClientSecurityParameters">
	<property name="tLSTrustEngine">
		<bean parent="shibboleth.StaticExplicitTrustEngine">
			<property name="publicKeys">
				<list>
					<value>%{idp.home}/credentials/pubkey1.pem</value>
					<value>%{idp.home}/credentials/pubkey2.pem</value>
				</list>
			</property>
		</bean>
	</property>
</bean>

<!-- Sample feature we're actually trying to use, which we inject custom rules into. -->
<bean id="PushReporter" parent="shibboleth.metrics.HTTPReporter" c:name="MyCollector"
	p:httpClient-ref="CustomHttpClient"
	p:httpClientSecurityParameters-ref="CustomHttpSecurity"
	p:collectorURL="https://log.example.org/cgi-bin/collector.cgi" />

Applying a TrustEngine

You should always start by configuring a component using the HttpClient with the HttpClientSecurityParameters wiring needed to implement your needs, as in the above examples, then configure the HttpClient with the connectionDisregardTLSCertificate flag on, and finally just test it and let it fail and log something like the following:

...

The full range of PKIX options is quite complex, but for basic use cases a factory bean makes it simple. To validate the server's certificate against a fixed CA (name checking is turned off because the HttpClient is already doing this step for us):

Code Block
languagexml
titlePKIX verification with root CA
collapsetrue
<bean id="CustomHttpSecurity" class="org.opensaml.security.httpclient.HttpClientSecuritySupport:98] - Configured TLS trust engine was not used to verify server TLS credential, the appropriate socket factory was likely not configured
19:04:53.366 - 127.0.0.1 - ERROR [net.shibboleth.idp.profile.impl.ResolveAttributes:299] - Profile Action ResolveAttributes: Error resolving attributes
net.shibboleth.idp.attribute.resolver.ResolutionException: Data Connector 'webservice': HTTP request failed
 at net.shibboleth.idp.attribute.resolver.dc.http.impl.HTTPDataConnector.retrieveAttributes(HTTPDataConnector.java:90)
Caused by: javax.net.ssl.SSLPeerUnverifiedException: Evaluation of server TLS credential with configured TrustEngine was not performed
 at org.opensaml.security.httpclient.HttpClientSecuritySupport.checkTLSCredentialEvaluated(HttpClientSecuritySupport.java:100)

The category and details in the ERROR will vary by component, but the WARN and SSLPeerUnverifiedException messages will consistently indicate that the component was configured to apply a TrustEngine to the connection but couldn't do so. This will happen after the connection is established, possibly even after data is sent and consumed, but it should happen at the end. If not, something is wrong and has to be corrected to be confident that the eventual configuration will work.

Once the warning/error occurs, only then make the final change to the HttpClient bean to fix the error by overriding the tLSSocketFactory property (note the weird mixed case, that's due to the usual "first character is lower case" property convention in Java beans). The IdP includes a pair of socket factory beans for this purpose, shibboleth.SecurityEnhancedTLSSocketFactory and shibboleth.SecurityEnhancedTLSSocketFactoryWithClientTLS

So the final step to adding advanced TLS support is, for example:

Code Block
languagexml
<bean id="SecurityEnhancedHttpClient parent="shibboleth.NonCachingHttpClient"
	p:tLSSocketFactory-ref="shibboleth.SecurityEnhancedTLSSocketFactory" />

You won't generally need to configure your own instance of the socket factories, because all the interesting settings are part of the other component's configuration, via HttpClientSecurityParameters.

TLS Client Authentication

TBD

HTTP Authentication

TBD

Reference

Beans

...

shibboleth.NonCachingHttpClient

...

shibboleth.MemoryCachingHttpClient

...

HttpClientSecurityParameters">
	<property name="tLSTrustEngine">
		<bean parent="shibboleth.StaticPKIXTrustEngine"
			p:certificates="%{idp.home}/credentials/rootca.pem"
			p:checkNames="false" />
	</property>
</bean>

<!-- Sample feature we're actually trying to use, which we inject custom rules into. -->
<bean id="PushReporter" parent="shibboleth.metrics.HTTPReporter" c:name="MyCollector"
	p:httpClient-ref="CustomHttpClient"
	p:httpClientSecurityParameters-ref="CustomHttpSecurity"
	p:collectorURL="https://log.example.org/cgi-bin/collector.cgi" />

Other PKIX options include the verifyDepth property to control the chain length, and the cRLs property to supply certificate revocation lists.

Applying a TrustEngine to HttpClient

You should start by configuring a component using the HttpClient with the HttpClientSecurityParameters wiring needed to implement your needs, as in the above examples.

Once that's in place, temporarily configure the HttpClient with the connectionDisregardTLSCertificate flag on, and finally just test it and let it fail and log something like the following:

Code Block
19:04:53.364 - 127.0.0.1 - WARN [org.opensaml.security.httpclient.HttpClientSecuritySupport:98] - Configured TLS trust engine was not used to verify server TLS credential, the appropriate socket factory was likely not configured
19:04:53.366 - 127.0.0.1 - ERROR [net.shibboleth.idp.profile.impl.ResolveAttributes:299] - Profile Action ResolveAttributes: Error resolving attributes
net.shibboleth.idp.attribute.resolver.ResolutionException: Data Connector 'webservice': HTTP request failed
 at net.shibboleth.idp.attribute.resolver.dc.http.impl.HTTPDataConnector.retrieveAttributes(HTTPDataConnector.java:90)
Caused by: javax.net.ssl.SSLPeerUnverifiedException: Evaluation of server TLS credential with configured TrustEngine was not performed
 at org.opensaml.security.httpclient.HttpClientSecuritySupport.checkTLSCredentialEvaluated(HttpClientSecuritySupport.java:100)

The category and details in the ERROR will vary by component, but the WARN and SSLPeerUnverifiedException messages will consistently indicate that the component was configured to apply a TrustEngine to the connection but couldn't do so. This may happen after the connection is established, possibly even after data is sent and consumed, but it should happen at the end. If not, something is wrong and has to be corrected to be confident that the eventual configuration will work.

Once the warning/error occurs, only then make the final change to the HttpClient bean to fix the error by overriding the tLSSocketFactory property (note the weird mixed case, that's due to the usual "first character is lower case" property convention in Java beans). The IdP includes a pair of socket factory beans for this purpose, shibboleth.SecurityEnhancedTLSSocketFactory and shibboleth.SecurityEnhancedTLSSocketFactoryWithClientTLS

So the final step to adding advanced TLS support is, for example:

Code Block
languagexml
<bean id="SecurityEnhancedHttpClient" parent="shibboleth.NonCachingHttpClient"
	p:tLSSocketFactory-ref="shibboleth.SecurityEnhancedTLSSocketFactory" />

You won't generally need to configure your own instance of the socket factories, because all the interesting settings are part of the other component's configuration, via HttpClientSecurityParameters.

TLS Client Authentication

Note

The code as it stands does not generally support TLS Renegotiation, which is most commonly encountered when using a virtual host that applies client TLS to only a subset of paths and not the host as a whole.

Configuring a component using the HttpClient with a private key and certificate for authenticating itself to a server is a simple two step process:

  • Make sure the HttpClient bean's tLSSocketFactory-ref property points to the shibboleth.SecurityEnhancedTLSSocketFactoryWithClientTLS bean.
  • Configure the component's injected HttpClientSecurityParameters instance's clientTLSCredential property with an X.509 credential.

The syntax for supplying a keypair can be essentially copied from the credentials.xml file that contains the more "usual" keys and certificates used by the IdP. Note that the beans defined in that file are not visible outside the RelyingPartyConfiguration so if you try to reuse them elsewhere by reference, you'll get errors. The example builds on the previous one and includes server authentication.

Code Block
languagexml
titleClient TLS example
collapsetrue
<bean id="CustomHttpSecurity" class="org.opensaml.security.httpclient.HttpClientSecurityParameters">
	<property name="tLSTrustEngine">
		<bean parent="shibboleth.StaticExplicitTrustEngine"
			p:certificates="%{idp.home}/credentials/server.pem" />
	</property>
	<property name="clientTLSCredential">
		<bean class="net.shibboleth.idp.profile.spring.factory.BasicX509CredentialFactoryBean"
			p:privateKeyResource="%{idp.home}/credentials/tlsclient.key"
			p:certificateResource="%{idp.home}/credentials/tlsclient.crt" />
	</property>
</bean>

<!-- Sample feature we're actually trying to use, which we inject custom rules into. -->
<bean id="PushReporter" parent="shibboleth.metrics.HTTPReporter" c:name="MyCollector"
	p:httpClient-ref="CustomHttpClient"
	p:httpClientSecurityParameters-ref="CustomHttpSecurity"
	p:collectorURL="https://log.example.org/cgi-bin/collector.cgi" />

HTTP Authentication

Currently it is less than straightforward to configure more typical HTTP credentials such as a basic-auth username and password, due to a lack of abstraction methods in our code to hide some of the gory details of the HttpClient's data model. In particular, some of the methods that need to be called take multiple parameters, which violates the bean convention for a setter. It's possible to invoke more complex methods in Spring, but it takes some extra wiring. We intend to supply some additional code for this in a future release.

Note that some of the older custom schemas such as the metadata configuration schema may support shorthand for supplying username/password credentials, and while these do work, they're deprecated in favor of the more generic httpClientSecurityParameters-ref syntax.

At the moment, it's fairly simple to supply a username and password that gets used unilaterally with a given component's requests. That is, it's not "scoped" to limit its use to a particular server. This implies that you have a working configuration in place to authenticate the server's certificate so that the password isn't sent inadvertently to a malicious location. An example follows (again, building on the server authentication case):

Code Block
languagexml
titleUse of Basic Authentication
collapsetrue
<bean id="CustomHttpSecurity" class="org.opensaml.security.httpclient.HttpClientSecurityParameters">
	<property name="tLSTrustEngine">
		<bean parent="shibboleth.StaticExplicitTrustEngine"
			p:certificates="%{idp.home}/credentials/server.pem" />
	</property>
	<property name="basicCredentials">
		<bean class="org.apache.http.auth.UsernamePasswordCredentials"
			c:_0="webauth" c:_1="%{idp.collector.password}" />
	</property>
</bean>

<!-- Sample feature we're actually trying to use, which we inject custom rules into. -->
<bean id="PushReporter" parent="shibboleth.metrics.HTTPReporter" c:name="MyCollector"
	p:httpClient-ref="CustomHttpClient"
	p:httpClientSecurityParameters-ref="CustomHttpSecurity"
	p:collectorURL="https://log.example.org/cgi-bin/collector.cgi" />

The next level up in complexity is the desirable ability to limit the scope of the credentials for safety's sake. The example relies on the hostname and port of the server to scope the password. There are more advanced ways to build the AuthScope object being passed into the API such as including the Realm challenge from the server.

Code Block
languagexml
titleBasic Authentication with host/port AuthScope
collapsetrue
<bean id="CustomHttpSecurity" class="org.opensaml.security.httpclient.HttpClientSecurityParameters">
	<property name="tLSTrustEngine">
		<bean parent="shibboleth.StaticExplicitTrustEngine"
			p:certificates="%{idp.home}/credentials/server.pem" />
	</property>
</bean>

<bean id="ScopedBasicAuth" class="org.springframework.beans.factory.config.MethodInvokingBean"
		p:targetObject-ref="CustomHttpSecurity"
		p:targetMethod="setBasicCredentialsWithScope">
	<property name="arguments">
		<list>
			<bean class="org.apache.http.auth.UsernamePasswordCredentials"
				c:_0="webauth" c:_1="%{idp.collector.password}" />
			<bean class="org.apache.http.auth.AuthScope"
				c:_0="log.example.org" c:_1="443" />
		</list>
	</property>
</bean>

<!-- Sample feature we're actually trying to use, which we inject custom rules into. -->
<bean id="PushReporter" parent="shibboleth.metrics.HTTPReporter" c:name="MyCollector"
	p:httpClient-ref="CustomHttpClient"
	p:httpClientSecurityParameters-ref="CustomHttpSecurity"
	p:collectorURL="https://log.example.org/cgi-bin/collector.cgi" />

A more advanced example would be to configure multiple sets of credentials for different servers, assuming a component that potentially contacts different servers. Since this is not a common case with any of our components, it's not likely to be needed much.

Reference

Beans

NameTypeDescription

shibboleth.NonCachingHttpClient                                                           

HttpClientFactoryBeanFactory bean for creating non-caching HTTPClient
shibboleth.FileCachingHttpClientFileCachingHttpClientFactoryBeanFactory bean for creating file-based-caching HTTPClient

shibboleth.MemoryCachingHttpClient

InMemoryCachingHttpClientFactoryBeanFactory bean for creating in-memory-caching HTTPClient
shibboleth.StaticExplicitTrustEngine 3.3StaticExplicitKeyFactoryBeanFactory bean for creating ExplicitKeyTrustEngine
shibboleth.StaticPKIXTrustEngine 3.3StaticPKIXFactoryBeanFactory bean for creating PKIXX509CredentialTrustEngine
shibboleth.SecurityEnhancedTLSSocketFactory 3.2SecurityEnhancedTLSSocketFactorySocket factory that supports HttpClientSecurityParameters-aware components
shibboleth.SecurityEnhancedTLSSocketFactoryWithClientTLS 3.3SecurityEnhancedTLSSocketFactoryClient-TLS-capable socket factory that supports HttpClientSecurityParameters-aware components
shibboleth.SecurityEnhancedTLSSocketFactoryWithClientTLSOnly 3.4SecurityEnhancedTLSSocketFactoryClient-TLS-capable socket factory that supports HttpClientSecurityParameters-aware components but does not accept a pluggable TrustEngine

Properties

NameTypeDefaultDescription
idp.httpclient.useSecurityEnhancedTLSSocketFactory 3.2booleanBooleanfalseIf true, causes the default clients to be injected with a special socket factory that supports advanced TLS features (requires substantial additional configuration)
idp.httpclient.connectionDisregardTLSCertificateconnectionDisregardTLSCertificate              booleanBooleanfalseIf the previous property is false, this allows the default TLS behavior of the client to ignore the TLS server certificate entirely (use with obvious caution, typically only while testing)
idp.httpclient.connectionRequestTimeoutDurationPT1M (one min)TIme to wait for a connection to be returned from the pool (can be 0 for no imposed value)
idp.httpclient.connectionTimeoutDurationPT1M (one min)TIme to wait for a connection to be established (can be 0 for no imposed value)
idp.httpclient.socketTimeoutDurationPT1M (one min)Time to allow between packets on a connection (can be 0 for no imposed value)
idp.httpclient.maxConnectionsTotalintegerInteger100Caps the number of simultaneous connections created by the pooling connection manager
idp.httpclient.maxConnectionsPerRouteintegerInteger100Caps the number of simultaneous connections per route created by the pooling connection manager
idp.httpclient.memorycaching.maxCacheEntriesintegerInteger50Size of the in-memory result cache
idp.httpclient.memorycaching.maxCacheEntrySizelongLong1048576 (1MB)Largest size to allow for an in-memory cache entry
idp.httpclient.filecaching.maxCacheEntriesintegerInteger100Size of the nonon-disk result cache
idp.httpclient.filecaching.maxCacheEntrySizelongLong10485760 (10MB)Largest sze to allow for an on-disk cache entry
idp.httpclient.filecaching.cacheDirectorylocal Local directory
Location of on-disk cache