Skip to content

Commit aff4d0a

Browse files
garyrussellartembilan
authored andcommitted
AMQP-830 Enable Hostname Verification by default
JIRA: https://jira.spring.io/browse/AMQP-830
1 parent 159d2b4 commit aff4d0a

File tree

2 files changed

+113
-14
lines changed

2 files changed

+113
-14
lines changed

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/RabbitConnectionFactoryBean.java

+107-14
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,22 @@
1616

1717
package org.springframework.amqp.rabbit.connection;
1818

19+
import java.lang.reflect.Method;
1920
import java.net.URI;
2021
import java.net.URISyntaxException;
2122
import java.security.KeyManagementException;
2223
import java.security.KeyStore;
23-
import java.security.KeyStoreException;
2424
import java.security.NoSuchAlgorithmException;
2525
import java.security.SecureRandom;
2626
import java.util.Arrays;
2727
import java.util.Map;
2828
import java.util.Properties;
2929
import java.util.concurrent.ExecutorService;
3030
import java.util.concurrent.ThreadFactory;
31+
import java.util.concurrent.atomic.AtomicReference;
3132

3233
import javax.net.SocketFactory;
34+
import javax.net.ssl.HostnameVerifier;
3335
import javax.net.ssl.KeyManager;
3436
import javax.net.ssl.KeyManagerFactory;
3537
import javax.net.ssl.SSLContext;
@@ -42,6 +44,10 @@
4244
import org.springframework.beans.factory.config.AbstractFactoryBean;
4345
import org.springframework.core.io.Resource;
4446
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
47+
import org.springframework.util.Assert;
48+
import org.springframework.util.ReflectionUtils;
49+
import org.springframework.util.ReflectionUtils.MethodCallback;
50+
import org.springframework.util.ReflectionUtils.MethodFilter;
4551
import org.springframework.util.StringUtils;
4652

4753
import com.rabbitmq.client.ConnectionFactory;
@@ -75,6 +81,37 @@
7581
*/
7682
public class RabbitConnectionFactoryBean extends AbstractFactoryBean<ConnectionFactory> {
7783

84+
public static final Method enableHostnameVerificationNoArgMethod;
85+
86+
public static final Method enableHostnameVerificationOneArgMethod;
87+
88+
static {
89+
final AtomicReference<Method> method1 = new AtomicReference<Method>();
90+
final AtomicReference<Method> method2 = new AtomicReference<Method>();
91+
ReflectionUtils.doWithMethods(ConnectionFactory.class, new MethodCallback() {
92+
93+
@Override
94+
public void doWith(Method m) throws IllegalArgumentException, IllegalAccessException {
95+
if (m.getParameterTypes().length == 0) {
96+
method1.set(m);
97+
}
98+
else if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0].equals(HostnameVerifier.class)) {
99+
method2.set(m);
100+
}
101+
}
102+
103+
}, new MethodFilter() {
104+
105+
@Override
106+
public boolean matches(Method m) {
107+
return m.getName().equals("enableHostnameVerification");
108+
}
109+
110+
});
111+
enableHostnameVerificationNoArgMethod = method1.get();
112+
enableHostnameVerificationOneArgMethod = method2.get();
113+
}
114+
78115
private final Log logger = LogFactory.getLog(getClass());
79116

80117
private static final String KEY_STORE = "keyStore";
@@ -103,30 +140,34 @@ public class RabbitConnectionFactoryBean extends AbstractFactoryBean<ConnectionF
103140

104141
private Resource sslPropertiesLocation;
105142

106-
private volatile String keyStore;
143+
private String keyStore;
107144

108-
private volatile String trustStore;
145+
private String trustStore;
109146

110-
private volatile Resource keyStoreResource;
147+
private Resource keyStoreResource;
111148

112-
private volatile Resource trustStoreResource;
149+
private Resource trustStoreResource;
113150

114-
private volatile String keyStorePassphrase;
151+
private String keyStorePassphrase;
115152

116-
private volatile String trustStorePassphrase;
153+
private String trustStorePassphrase;
117154

118-
private volatile String keyStoreType;
155+
private String keyStoreType;
119156

120-
private volatile String trustStoreType;
157+
private String trustStoreType;
121158

122-
private volatile String sslAlgorithm = TLS_V1_1;
159+
private String sslAlgorithm = TLS_V1_1;
123160

124-
private volatile boolean sslAlgorithmSet;
161+
private boolean sslAlgorithmSet;
125162

126-
private volatile SecureRandom secureRandom;
163+
private SecureRandom secureRandom;
127164

128165
private boolean skipServerCertificateValidation = true;
129166

167+
private boolean enableHostnameVerification;
168+
169+
private HostnameVerifier hostnameVerifier;
170+
130171
public RabbitConnectionFactoryBean() {
131172
this.connectionFactory.setAutomaticRecoveryEnabled(false);
132173
}
@@ -592,6 +633,46 @@ public void setTopologyRecoveryEnabled(boolean topologyRecoveryEnabled) {
592633
this.connectionFactory.setTopologyRecoveryEnabled(topologyRecoveryEnabled);
593634
}
594635

636+
/**
637+
* Enable server hostname verification for TLS connections.
638+
* <p>
639+
* This enables hostname verification regardless of the IO mode used (blocking or
640+
* non-blocking IO).
641+
* <p>
642+
* If <strong>using Java 7 or more</strong>, the hostname verification will be
643+
* performed by Java, as part of the TLS handshake.
644+
* <p>
645+
* If <strong>using Java 6</strong>, the hostname verification will be handled after
646+
* the TLS handshake, using the {@link HostnameVerifier} from the Commons HttpClient
647+
* project. This requires to add Commons HttpClient and its dependencies to the
648+
* classpath. To use a custom {@link HostnameVerifier}, use
649+
* {@link #setHostnameVerifier(HostnameVerifier)}. Requires
650+
* amqp-client 4.8.0 or later.
651+
* @param enable true to enable.
652+
* @since 1.7.10
653+
* @see #setHostnameVerifier(HostnameVerifier)
654+
*/
655+
public void setEnableHostnameVerification(boolean enable) {
656+
Assert.notNull(enableHostnameVerificationNoArgMethod,
657+
"Host name verification requires amqp-client 4.8.0 or later");
658+
this.enableHostnameVerification = enable;
659+
}
660+
661+
/**
662+
* Set a custom {@link HostnameVerifier} for use with
663+
* {@link #setEnableHostnameVerification(boolean)}.
664+
* @param hostnameVerifier the verifier.
665+
* @see #setEnableHostnameVerification(boolean)
666+
* @deprecated only used with Java 6
667+
*/
668+
@Deprecated
669+
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
670+
Assert.notNull(enableHostnameVerificationOneArgMethod,
671+
"Host name verification requires amqp-client 4.8.0 or later, "
672+
+ "when using 5.8.0 or later, a custom verifier is not required");
673+
this.hostnameVerifier = hostnameVerifier;
674+
}
675+
595676
@Override
596677
public Class<?> getObjectType() {
597678
return ConnectionFactory.class;
@@ -674,6 +755,7 @@ protected void setUpSSL() throws Exception {
674755
SSLContext context = createSSLContext();
675756
context.init(keyManagers, trustManagers, this.secureRandom);
676757
this.connectionFactory.useSslProtocol(context);
758+
checkHostVerification();
677759
}
678760
}
679761

@@ -689,14 +771,25 @@ protected SSLContext createSSLContext() throws NoSuchAlgorithmException {
689771
}
690772

691773

692-
private void useDefaultTrustStoreMechanism()
693-
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
774+
private void useDefaultTrustStoreMechanism() throws Exception {
694775
SSLContext sslContext = SSLContext.getInstance(this.sslAlgorithm);
695776
TrustManagerFactory trustManagerFactory =
696777
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
697778
trustManagerFactory.init((KeyStore) null);
698779
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
699780
this.connectionFactory.useSslProtocol(sslContext);
781+
checkHostVerification();
782+
}
783+
784+
private void checkHostVerification() throws Exception {
785+
if (this.enableHostnameVerification) {
786+
if (this.hostnameVerifier == null) {
787+
enableHostnameVerificationNoArgMethod.invoke(this.connectionFactory);
788+
}
789+
else {
790+
enableHostnameVerificationOneArgMethod.invoke(this.connectionFactory, this.hostnameVerifier);
791+
}
792+
}
700793
}
701794

702795
}

src/reference/asciidoc/whats-new.adoc

+6
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ See <<connections>> for more information.
6262
Message requeue on transaction rollback can now be configured to be consistent, regardless of whether or not a transaction manager is configured.
6363
See <<transaction-rollback>> for more information.
6464

65+
===== Connection Factory Bean Changes
66+
67+
The `RabbitConnectionFactoryBean` now provides an `enabaleHostnameVerification` property; set it to `true` to enable host name verification.
68+
Also `setHostnameVerifier` is provided for when using Java 6; see the connection factory javadocs for more information.
69+
Host name verification requires overriding the `amqp-client` to 4.8.0 or later.
70+
6571
==== Earlier Releases
6672

6773
See <<previous-whats-new>> for changes in previous versions.

0 commit comments

Comments
 (0)