16
16
17
17
package org .springframework .amqp .rabbit .connection ;
18
18
19
+ import java .lang .reflect .Method ;
19
20
import java .net .URI ;
20
21
import java .net .URISyntaxException ;
21
22
import java .security .KeyManagementException ;
22
23
import java .security .KeyStore ;
23
- import java .security .KeyStoreException ;
24
24
import java .security .NoSuchAlgorithmException ;
25
25
import java .security .SecureRandom ;
26
26
import java .util .Arrays ;
27
27
import java .util .Map ;
28
28
import java .util .Properties ;
29
29
import java .util .concurrent .ExecutorService ;
30
30
import java .util .concurrent .ThreadFactory ;
31
+ import java .util .concurrent .atomic .AtomicReference ;
31
32
32
33
import javax .net .SocketFactory ;
34
+ import javax .net .ssl .HostnameVerifier ;
33
35
import javax .net .ssl .KeyManager ;
34
36
import javax .net .ssl .KeyManagerFactory ;
35
37
import javax .net .ssl .SSLContext ;
42
44
import org .springframework .beans .factory .config .AbstractFactoryBean ;
43
45
import org .springframework .core .io .Resource ;
44
46
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 ;
45
51
import org .springframework .util .StringUtils ;
46
52
47
53
import com .rabbitmq .client .ConnectionFactory ;
75
81
*/
76
82
public class RabbitConnectionFactoryBean extends AbstractFactoryBean <ConnectionFactory > {
77
83
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
+
78
115
private final Log logger = LogFactory .getLog (getClass ());
79
116
80
117
private static final String KEY_STORE = "keyStore" ;
@@ -103,30 +140,34 @@ public class RabbitConnectionFactoryBean extends AbstractFactoryBean<ConnectionF
103
140
104
141
private Resource sslPropertiesLocation ;
105
142
106
- private volatile String keyStore ;
143
+ private String keyStore ;
107
144
108
- private volatile String trustStore ;
145
+ private String trustStore ;
109
146
110
- private volatile Resource keyStoreResource ;
147
+ private Resource keyStoreResource ;
111
148
112
- private volatile Resource trustStoreResource ;
149
+ private Resource trustStoreResource ;
113
150
114
- private volatile String keyStorePassphrase ;
151
+ private String keyStorePassphrase ;
115
152
116
- private volatile String trustStorePassphrase ;
153
+ private String trustStorePassphrase ;
117
154
118
- private volatile String keyStoreType ;
155
+ private String keyStoreType ;
119
156
120
- private volatile String trustStoreType ;
157
+ private String trustStoreType ;
121
158
122
- private volatile String sslAlgorithm = TLS_V1_1 ;
159
+ private String sslAlgorithm = TLS_V1_1 ;
123
160
124
- private volatile boolean sslAlgorithmSet ;
161
+ private boolean sslAlgorithmSet ;
125
162
126
- private volatile SecureRandom secureRandom ;
163
+ private SecureRandom secureRandom ;
127
164
128
165
private boolean skipServerCertificateValidation = true ;
129
166
167
+ private boolean enableHostnameVerification ;
168
+
169
+ private HostnameVerifier hostnameVerifier ;
170
+
130
171
public RabbitConnectionFactoryBean () {
131
172
this .connectionFactory .setAutomaticRecoveryEnabled (false );
132
173
}
@@ -592,6 +633,46 @@ public void setTopologyRecoveryEnabled(boolean topologyRecoveryEnabled) {
592
633
this .connectionFactory .setTopologyRecoveryEnabled (topologyRecoveryEnabled );
593
634
}
594
635
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
+
595
676
@ Override
596
677
public Class <?> getObjectType () {
597
678
return ConnectionFactory .class ;
@@ -674,6 +755,7 @@ protected void setUpSSL() throws Exception {
674
755
SSLContext context = createSSLContext ();
675
756
context .init (keyManagers , trustManagers , this .secureRandom );
676
757
this .connectionFactory .useSslProtocol (context );
758
+ checkHostVerification ();
677
759
}
678
760
}
679
761
@@ -689,14 +771,25 @@ protected SSLContext createSSLContext() throws NoSuchAlgorithmException {
689
771
}
690
772
691
773
692
- private void useDefaultTrustStoreMechanism ()
693
- throws NoSuchAlgorithmException , KeyManagementException , KeyStoreException {
774
+ private void useDefaultTrustStoreMechanism () throws Exception {
694
775
SSLContext sslContext = SSLContext .getInstance (this .sslAlgorithm );
695
776
TrustManagerFactory trustManagerFactory =
696
777
TrustManagerFactory .getInstance (TrustManagerFactory .getDefaultAlgorithm ());
697
778
trustManagerFactory .init ((KeyStore ) null );
698
779
sslContext .init (null , trustManagerFactory .getTrustManagers (), null );
699
780
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
+ }
700
793
}
701
794
702
795
}
0 commit comments