001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.io.crypto.tls;
019
020import static org.hamcrest.Matchers.equalTo;
021import static org.junit.Assert.assertArrayEquals;
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertFalse;
024import static org.junit.Assert.assertThrows;
025import static org.junit.Assert.assertTrue;
026import static org.junit.Assume.assumeThat;
027import static org.mockito.Mockito.mock;
028
029import java.security.Security;
030import java.util.Arrays;
031import java.util.Collections;
032import org.apache.hadoop.hbase.HBaseClassTestRule;
033import org.apache.hadoop.hbase.exceptions.KeyManagerException;
034import org.apache.hadoop.hbase.exceptions.SSLContextException;
035import org.apache.hadoop.hbase.exceptions.TrustManagerException;
036import org.apache.hadoop.hbase.testclassification.SecurityTests;
037import org.apache.hadoop.hbase.testclassification.SmallTests;
038import org.junit.ClassRule;
039import org.junit.Test;
040import org.junit.experimental.categories.Category;
041import org.junit.runner.RunWith;
042import org.junit.runners.Parameterized;
043
044import org.apache.hbase.thirdparty.io.netty.buffer.ByteBufAllocator;
045import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslContext;
046
047/**
048 * This file has been copied from the Apache ZooKeeper project.
049 * @see <a href=
050 *      "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java">Base
051 *      revision</a>
052 */
053@RunWith(Parameterized.class)
054@Category({ SecurityTests.class, SmallTests.class })
055public class TestX509Util extends AbstractTestX509Parameterized {
056
057  @ClassRule
058  public static final HBaseClassTestRule CLASS_RULE =
059    HBaseClassTestRule.forClass(TestX509Util.class);
060
061  private static final char[] EMPTY_CHAR_ARRAY = new char[0];
062
063  @Test
064  public void testCreateSSLContextWithClientAuthDefault() throws Exception {
065    SslContext sslContext = X509Util.createSslContextForServer(conf);
066    ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
067    assertTrue(sslContext.newEngine(byteBufAllocatorMock).getNeedClientAuth());
068  }
069
070  @Test
071  public void testCreateSSLContextWithClientAuthNEED() throws Exception {
072    conf.set(X509Util.HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE, X509Util.ClientAuth.NEED.name());
073    SslContext sslContext = X509Util.createSslContextForServer(conf);
074    ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
075    assertTrue(sslContext.newEngine(byteBufAllocatorMock).getNeedClientAuth());
076  }
077
078  @Test
079  public void testCreateSSLContextWithClientAuthWANT() throws Exception {
080    conf.set(X509Util.HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE, X509Util.ClientAuth.WANT.name());
081    SslContext sslContext = X509Util.createSslContextForServer(conf);
082    ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
083    assertTrue(sslContext.newEngine(byteBufAllocatorMock).getWantClientAuth());
084  }
085
086  @Test
087  public void testCreateSSLContextWithClientAuthNONE() throws Exception {
088    conf.set(X509Util.HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE, X509Util.ClientAuth.NONE.name());
089    SslContext sslContext = X509Util.createSslContextForServer(conf);
090    ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
091    assertFalse(sslContext.newEngine(byteBufAllocatorMock).getNeedClientAuth());
092    assertFalse(sslContext.newEngine(byteBufAllocatorMock).getWantClientAuth());
093  }
094
095  @Test
096  public void testCreateSSLContextWithoutCustomProtocol() throws Exception {
097    SslContext sslContext = X509Util.createSslContextForClient(conf);
098    ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
099    assertArrayEquals(new String[] { X509Util.DEFAULT_PROTOCOL },
100      sslContext.newEngine(byteBufAllocatorMock).getEnabledProtocols());
101  }
102
103  @Test
104  public void testCreateSSLContextWithCustomProtocol() throws Exception {
105    final String protocol = "TLSv1.1";
106    conf.set(X509Util.TLS_CONFIG_PROTOCOL, protocol);
107    ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
108    SslContext sslContext = X509Util.createSslContextForServer(conf);
109    assertEquals(Collections.singletonList(protocol),
110      Arrays.asList(sslContext.newEngine(byteBufAllocatorMock).getEnabledProtocols()));
111  }
112
113  @Test(expected = SSLContextException.class)
114  public void testCreateSSLContextWithoutKeyStoreLocationServer() throws Exception {
115    conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
116    X509Util.createSslContextForServer(conf);
117  }
118
119  @Test
120  public void testCreateSSLContextWithoutKeyStoreLocationClient() throws Exception {
121    conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
122    X509Util.createSslContextForClient(conf);
123  }
124
125  @Test
126  public void testCreateSSLContextWithoutTrustStoreLocationClient() throws Exception {
127    conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION);
128    X509Util.createSslContextForClient(conf);
129  }
130
131  @Test
132  public void testCreateSSLContextWithoutTrustStoreLocationServer() throws Exception {
133    conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION);
134    X509Util.createSslContextForServer(conf);
135  }
136
137  // It would be great to test the value of PKIXBuilderParameters#setRevocationEnabled,
138  // but it does not appear to be possible
139  @Test
140  public void testCRLEnabled() throws Exception {
141    conf.setBoolean(X509Util.TLS_CONFIG_CLR, true);
142    X509Util.createSslContextForServer(conf);
143    assertTrue(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation")));
144    assertTrue(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP")));
145    assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable")));
146  }
147
148  @Test
149  public void testCRLDisabled() throws Exception {
150    X509Util.createSslContextForServer(conf);
151    assertFalse(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation")));
152    assertFalse(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP")));
153    assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable")));
154  }
155
156  @Test
157  public void testLoadPEMKeyStore() throws Exception {
158    // Make sure we can instantiate a key manager from the PEM file on disk
159    X509Util.createKeyManager(
160      x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(),
161      x509TestContext.getKeyStorePassword(), KeyStoreFileType.PEM.getPropertyValue());
162  }
163
164  @Test
165  public void testLoadPEMKeyStoreNullPassword() throws Exception {
166    assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
167    // Make sure that empty password and null password are treated the same
168    X509Util.createKeyManager(
169      x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null,
170      KeyStoreFileType.PEM.getPropertyValue());
171  }
172
173  @Test
174  public void testLoadPEMKeyStoreAutodetectStoreFileType() throws Exception {
175    // Make sure we can instantiate a key manager from the PEM file on disk
176    X509Util.createKeyManager(
177      x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(),
178      x509TestContext.getKeyStorePassword(),
179      null /* null StoreFileType means 'autodetect from file extension' */);
180  }
181
182  @Test(expected = KeyManagerException.class)
183  public void testLoadPEMKeyStoreWithWrongPassword() throws Exception {
184    // Attempting to load with the wrong key password should fail
185    X509Util.createKeyManager(
186      x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(),
187      "wrong password".toCharArray(), // intentionally use the wrong password
188      KeyStoreFileType.PEM.getPropertyValue());
189  }
190
191  @Test
192  public void testLoadPEMTrustStore() throws Exception {
193    // Make sure we can instantiate a trust manager from the PEM file on disk
194    X509Util.createTrustManager(
195      x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(),
196      x509TestContext.getTrustStorePassword(), KeyStoreFileType.PEM.getPropertyValue(), false,
197      false, true, true);
198  }
199
200  @Test
201  public void testLoadPEMTrustStoreNullPassword() throws Exception {
202    assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
203    // Make sure that empty password and null password are treated the same
204    X509Util.createTrustManager(
205      x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null,
206      KeyStoreFileType.PEM.getPropertyValue(), false, false, true, true);
207  }
208
209  @Test
210  public void testLoadPEMTrustStoreAutodetectStoreFileType() throws Exception {
211    // Make sure we can instantiate a trust manager from the PEM file on disk
212    X509Util.createTrustManager(
213      x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(),
214      x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from
215                                                     // file extension'
216      false, false, true, true);
217  }
218
219  @Test
220  public void testLoadJKSKeyStore() throws Exception {
221    // Make sure we can instantiate a key manager from the JKS file on disk
222    X509Util.createKeyManager(
223      x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
224      x509TestContext.getKeyStorePassword(), KeyStoreFileType.JKS.getPropertyValue());
225  }
226
227  @Test
228  public void testLoadJKSKeyStoreNullPassword() throws Exception {
229    assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
230    // Make sure that empty password and null password are treated the same
231    X509Util.createKeyManager(
232      x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null,
233      KeyStoreFileType.JKS.getPropertyValue());
234  }
235
236  @Test
237  public void testLoadJKSKeyStoreAutodetectStoreFileType() throws Exception {
238    // Make sure we can instantiate a key manager from the JKS file on disk
239    X509Util.createKeyManager(
240      x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
241      x509TestContext.getKeyStorePassword(),
242      null /* null StoreFileType means 'autodetect from file extension' */);
243  }
244
245  @Test
246  public void testLoadJKSKeyStoreWithWrongPassword() {
247    assertThrows(KeyManagerException.class, () -> {
248      // Attempting to load with the wrong key password should fail
249      X509Util.createKeyManager(
250        x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
251        "wrong password".toCharArray(), KeyStoreFileType.JKS.getPropertyValue());
252    });
253  }
254
255  @Test
256  public void testLoadJKSTrustStore() throws Exception {
257    // Make sure we can instantiate a trust manager from the JKS file on disk
258    X509Util.createTrustManager(
259      x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
260      x509TestContext.getTrustStorePassword(), KeyStoreFileType.JKS.getPropertyValue(), true, true,
261      true, true);
262  }
263
264  @Test
265  public void testLoadJKSTrustStoreNullPassword() throws Exception {
266    assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
267    // Make sure that empty password and null password are treated the same
268    X509Util.createTrustManager(
269      x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null,
270      KeyStoreFileType.JKS.getPropertyValue(), false, false, true, true);
271  }
272
273  @Test
274  public void testLoadJKSTrustStoreAutodetectStoreFileType() throws Exception {
275    // Make sure we can instantiate a trust manager from the JKS file on disk
276    X509Util.createTrustManager(
277      x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
278      x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from
279                                                     // file extension'
280      true, true, true, true);
281  }
282
283  @Test
284  public void testLoadJKSTrustStoreWithWrongPassword() {
285    assertThrows(TrustManagerException.class, () -> {
286      // Attempting to load with the wrong key password should fail
287      X509Util.createTrustManager(
288        x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
289        "wrong password".toCharArray(), KeyStoreFileType.JKS.getPropertyValue(), true, true, true,
290        true);
291    });
292  }
293
294  @Test
295  public void testLoadPKCS12KeyStore() throws Exception {
296    // Make sure we can instantiate a key manager from the PKCS12 file on disk
297    X509Util.createKeyManager(
298      x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(),
299      x509TestContext.getKeyStorePassword(), KeyStoreFileType.PKCS12.getPropertyValue());
300  }
301
302  @Test
303  public void testLoadPKCS12KeyStoreNullPassword() throws Exception {
304    assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
305    // Make sure that empty password and null password are treated the same
306    X509Util.createKeyManager(
307      x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), null,
308      KeyStoreFileType.PKCS12.getPropertyValue());
309  }
310
311  @Test
312  public void testLoadPKCS12KeyStoreAutodetectStoreFileType() throws Exception {
313    // Make sure we can instantiate a key manager from the PKCS12 file on disk
314    X509Util.createKeyManager(
315      x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(),
316      x509TestContext.getKeyStorePassword(),
317      null /* null StoreFileType means 'autodetect from file extension' */);
318  }
319
320  @Test
321  public void testLoadPKCS12KeyStoreWithWrongPassword() {
322    assertThrows(KeyManagerException.class, () -> {
323      // Attempting to load with the wrong key password should fail
324      X509Util.createKeyManager(
325        x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(),
326        "wrong password".toCharArray(), KeyStoreFileType.PKCS12.getPropertyValue());
327    });
328  }
329
330  @Test
331  public void testLoadPKCS12TrustStore() throws Exception {
332    // Make sure we can instantiate a trust manager from the PKCS12 file on disk
333    X509Util.createTrustManager(
334      x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(),
335      x509TestContext.getTrustStorePassword(), KeyStoreFileType.PKCS12.getPropertyValue(), true,
336      true, true, true);
337  }
338
339  @Test
340  public void testLoadPKCS12TrustStoreNullPassword() throws Exception {
341    assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
342    // Make sure that empty password and null password are treated the same
343    X509Util.createTrustManager(
344      x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), null,
345      KeyStoreFileType.PKCS12.getPropertyValue(), false, false, true, true);
346  }
347
348  @Test
349  public void testLoadPKCS12TrustStoreAutodetectStoreFileType() throws Exception {
350    // Make sure we can instantiate a trust manager from the PKCS12 file on disk
351    X509Util.createTrustManager(
352      x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(),
353      x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from
354                                                     // file extension'
355      true, true, true, true);
356  }
357
358  @Test
359  public void testLoadPKCS12TrustStoreWithWrongPassword() {
360    assertThrows(TrustManagerException.class, () -> {
361      // Attempting to load with the wrong key password should fail
362      X509Util.createTrustManager(
363        x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(),
364        "wrong password".toCharArray(), KeyStoreFileType.PKCS12.getPropertyValue(), true, true,
365        true, true);
366    });
367  }
368
369}