...
A common use for advanced configuration is to control the server certificate evaluation process. While the IdP typically relies on SAML metadata for this when communicating with servers, this doesn't work for a lot of non-SAML use cases. However, much of the same machinery can be repurposed to support a variety of trust models, and we have integrated this into the HttpClient library. Another use case is to support HTTP authentication such as a name and password to access a resource.
TLS Server Authentication
By default, the HttpClient will rely on Java's built-in TLS behavior and code built into the HttpClient library, and perform basic hostname/certificate checks and will rely on the Java global trust store to determine whether to trust the certificate (or its issuer). This is where most non-Shibboleth software stops. In testing scenarios, you can turn off this checking via the connectionDisregardTLSCertificate
property, but this should never be used in production.
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 applying advanced security options to components that use an HttpClient.You also need to be very careful 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 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. |
Repeating: you can tell the HttpClient bean that you want to support a more "advanced mode" of processing, but you configure that mode 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 is that if you tell the client you want "advanced mode" but then 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 need to define 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:
- shibboleth.StaticExplicitTrustEngine
- Explicitly compares a set of trusted public keys against the key in the server's certificate, with no regard for the rest of the certificate.
- shibboleth.StaticPKIXTrustEngine
- Provides a full suite of PKIX processing to the server's certificate, but with dedicated trust authorities and policy rules.
The examples below , which are necessarily specific to particular components' use of the HttpClient, demonstrate how this works, but in practice what it means is that you 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 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 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.
The common case of a rule to compare the key against a certificate loaded from a file looks like this:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<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>
<!-- 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" /> |
An example that might be used to support a server rolling over to a new key:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<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:
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) |
Then 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 factories declared factory beans for this purpose, shibboleth.SecurityEnhancedTLSSocketFactory and shibboleth.SecurityEnhancedTLSSocketFactoryWithClientTLS
...
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 Server Authentication
TBD
TLS Client Authentication
...