org.opensaml.security.httpclient.HttpClientSecuritySupport Maven / Gradle / Ivy
/*
* 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;
import static org.opensaml.security.httpclient.HttpClientSecurityConstants.CONTEXT_KEY_CLIENT_TLS_CREDENTIAL;
import static org.opensaml.security.httpclient.HttpClientSecurityConstants.CONTEXT_KEY_CRITERIA_SET;
import static org.opensaml.security.httpclient.HttpClientSecurityConstants.CONTEXT_KEY_HOSTNAME_VERIFIER;
import static org.opensaml.security.httpclient.HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED;
import static org.opensaml.security.httpclient.HttpClientSecurityConstants.CONTEXT_KEY_TLS_CIPHER_SUITES;
import static org.opensaml.security.httpclient.HttpClientSecurityConstants.CONTEXT_KEY_TLS_PROTOCOLS;
import static org.opensaml.security.httpclient.HttpClientSecurityConstants.CONTEXT_KEY_TRUST_ENGINE;
import static org.opensaml.security.httpclient.HttpClientSecurityConstants.CONTEXT_KEY_SERVER_TLS_FAILURE_IS_FATAL;
import java.util.Collections;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.net.ssl.SSLPeerUnverifiedException;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.opensaml.core.config.ConfigurationService;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.criteria.UsageCriterion;
import org.opensaml.security.x509.TrustedNamesCriterion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
/**
* Support class for working with {@link org.apache.http.client.HttpClient} security features.
*/
public final class HttpClientSecuritySupport {
/** Logger. */
private static final Logger LOG = LoggerFactory.getLogger(HttpClientSecuritySupport.class);
/** Constructor. */
private HttpClientSecuritySupport() {}
/**
* Get the global {@link HttpClientSecurityConfiguration} instance.
*
* @return the global HttpClient security configuration
*/
public static HttpClientSecurityConfiguration getGlobalHttpClientSecurityConfiguration() {
return ConfigurationService.get(HttpClientSecurityConfiguration.class);
}
/**
* Add default trust engine criteria for TLS usage to the {@link HttpClientContext}.
*
* @param context the current HTTP context instance in use
* @param request the current HTTP request
*/
public static void addDefaultTLSTrustEngineCriteria(@Nonnull final HttpClientContext context,
@Nonnull final HttpUriRequest request) {
if ("https".equalsIgnoreCase(request.getURI().getScheme())
&& context.getAttribute(CONTEXT_KEY_TRUST_ENGINE) != null) {
CriteriaSet criteria = (CriteriaSet) context.getAttribute(CONTEXT_KEY_CRITERIA_SET);
if (criteria == null) {
criteria = new CriteriaSet();
context.setAttribute(CONTEXT_KEY_CRITERIA_SET, criteria);
}
if (!criteria.contains(UsageCriterion.class)) {
criteria.add(new UsageCriterion(UsageType.SIGNING));
}
if (!criteria.contains(TrustedNamesCriterion.class)) {
criteria.add(new TrustedNamesCriterion(Collections.singleton(request.getURI().getHost())));
}
}
}
/**
* Check that trust engine evaluation of the server TLS credential was actually performed when the
* scheme is HTTPS.
*
* @param context the current HTTP context instance in use
* @param scheme the HTTP request scheme
* @throws SSLPeerUnverifiedException thrown if the TLS credential was not actually evaluated by the trust engine
*/
public static void checkTLSCredentialEvaluated(@Nonnull final HttpClientContext context,
@Nonnull final String scheme) throws SSLPeerUnverifiedException {
if (context.getAttribute(CONTEXT_KEY_TRUST_ENGINE) != null
&& "https".equalsIgnoreCase(scheme)) {
if (context.getAttribute(CONTEXT_KEY_SERVER_TLS_CREDENTIAL_TRUSTED) == null) {
LOG.warn("Configured TLS trust engine was not used to verify server TLS credential, "
+ "the appropriate socket factory was likely not configured");
throw new SSLPeerUnverifiedException(
"Evaluation of server TLS credential with configured TrustEngine was not performed");
}
}
}
/**
* Marshal the supplied {@link HttpClientSecurityParameters} to the supplied {@link HttpClientContext}.
*
* Existing context values will NOT be replaced by non-null parameter values.
*
* @param context the client context instance
* @param securityParameters the security parameters instance
*/
public static void marshalSecurityParameters(@Nonnull final HttpClientContext context,
@Nullable final HttpClientSecurityParameters securityParameters) {
marshalSecurityParameters(context, securityParameters, false);
}
/**
* Marshal the supplied {@link HttpClientSecurityParameters} to the supplied {@link HttpClientContext}.
*
* @param context the client context instance
* @param securityParameters the security parameters instance
* @param replace whether a non-null security parameter value should replace an existing context value
*/
public static void marshalSecurityParameters(@Nonnull final HttpClientContext context,
@Nullable final HttpClientSecurityParameters securityParameters, final boolean replace) {
if (securityParameters == null) {
return;
}
Constraint.isNotNull(context, "HttpClientContext was null");
if (securityParameters.getCredentialsProvider() != null) {
if (replace || context.getCredentialsProvider() == null) {
context.setCredentialsProvider(securityParameters.getCredentialsProvider());
}
}
if (securityParameters.getAuthCache() != null) {
if (replace || context.getAuthCache() == null) {
context.setAuthCache(securityParameters.getAuthCache());
}
}
setContextValue(context, CONTEXT_KEY_TRUST_ENGINE,
securityParameters.getTLSTrustEngine(), replace);
setContextValue(context, CONTEXT_KEY_CRITERIA_SET,
securityParameters.getTLSCriteriaSet(), replace);
setContextValue(context, CONTEXT_KEY_TLS_PROTOCOLS,
securityParameters.getTLSProtocols(), replace);
setContextValue(context, CONTEXT_KEY_TLS_CIPHER_SUITES,
securityParameters.getTLSCipherSuites(), replace);
setContextValue(context, CONTEXT_KEY_HOSTNAME_VERIFIER,
securityParameters.getHostnameVerifier(), replace);
setContextValue(context, CONTEXT_KEY_CLIENT_TLS_CREDENTIAL,
securityParameters.getClientTLSCredential(), replace);
setContextValue(context, CONTEXT_KEY_SERVER_TLS_FAILURE_IS_FATAL,
securityParameters.isServerTLSFailureFatal(), replace);
}
/**
* Set the supplied attribute value in the client context.
*
* @param context the client context instance
* @param attributeName the context attribute name to
* @param attributeValue the context attribute value to set, may be null
* @param replace whether a non-null argument value should replace an existing context value
*/
public static void setContextValue(@Nonnull final HttpClientContext context,
@Nonnull final String attributeName, @Nullable final Object attributeValue, final boolean replace) {
if (attributeValue == null) {
return;
}
Constraint.isNotNull(context, "HttpClientContext was null");
Constraint.isNotNull(attributeName, "Context attribute name was null");
if (replace || context.getAttribute(attributeName) == null) {
context.setAttribute(attributeName, attributeValue);
}
}
}