1616
1717package org .springframework .amqp .rabbit .connection ;
1818
19+ import java .lang .reflect .Method ;
1920import java .net .URI ;
2021import java .net .URISyntaxException ;
2122import java .security .KeyManagementException ;
2223import java .security .KeyStore ;
23- import java .security .KeyStoreException ;
2424import java .security .NoSuchAlgorithmException ;
2525import java .security .SecureRandom ;
2626import java .util .Arrays ;
2727import java .util .Map ;
2828import java .util .Properties ;
2929import java .util .concurrent .ExecutorService ;
3030import java .util .concurrent .ThreadFactory ;
31+ import java .util .concurrent .atomic .AtomicReference ;
3132
3233import javax .net .SocketFactory ;
34+ import javax .net .ssl .HostnameVerifier ;
3335import javax .net .ssl .KeyManager ;
3436import javax .net .ssl .KeyManagerFactory ;
3537import javax .net .ssl .SSLContext ;
4244import org .springframework .beans .factory .config .AbstractFactoryBean ;
4345import org .springframework .core .io .Resource ;
4446import 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 ;
4551import org .springframework .util .StringUtils ;
4652
4753import com .rabbitmq .client .ConnectionFactory ;
7581 */
7682public 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}
0 commit comments