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[] { "TLSv1.3", "TLSv1.2" }, 100 sslContext.newEngine(byteBufAllocatorMock).getEnabledProtocols()); 101 } 102 103 @Test 104 public void testCreateTcNativeSSLContextWithoutCustomProtocol() throws Exception { 105 conf.set(X509Util.TLS_USE_OPENSSL, "true"); 106 SslContext sslContext = X509Util.createSslContextForClient(conf); 107 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 108 assertArrayEquals(new String[] { "TLSv1.3", "TLSv1.2" }, 109 sslContext.newEngine(byteBufAllocatorMock).getEnabledProtocols()); 110 } 111 112 @Test 113 public void testCreateSSLContextWithCustomProtocol() throws Exception { 114 final String protocol = "TLSv1.1"; 115 conf.set(X509Util.TLS_CONFIG_PROTOCOL, protocol); 116 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 117 SslContext sslContext = X509Util.createSslContextForServer(conf); 118 assertEquals(Collections.singletonList(protocol), 119 Arrays.asList(sslContext.newEngine(byteBufAllocatorMock).getEnabledProtocols())); 120 } 121 122 @Test(expected = SSLContextException.class) 123 public void testCreateSSLContextWithoutKeyStoreLocationServer() throws Exception { 124 conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION); 125 X509Util.createSslContextForServer(conf); 126 } 127 128 @Test 129 public void testCreateSSLContextWithoutKeyStoreLocationClient() throws Exception { 130 conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION); 131 X509Util.createSslContextForClient(conf); 132 } 133 134 @Test 135 public void testCreateSSLContextWithoutTrustStoreLocationClient() throws Exception { 136 conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION); 137 X509Util.createSslContextForClient(conf); 138 } 139 140 @Test 141 public void testCreateSSLContextWithoutTrustStoreLocationServer() throws Exception { 142 conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION); 143 X509Util.createSslContextForServer(conf); 144 } 145 146 // It would be great to test the value of PKIXBuilderParameters#setRevocationEnabled, 147 // but it does not appear to be possible 148 @Test 149 public void testCRLEnabled() throws Exception { 150 conf.setBoolean(X509Util.TLS_CONFIG_CLR, true); 151 X509Util.createSslContextForServer(conf); 152 assertTrue(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation"))); 153 assertTrue(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP"))); 154 assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable"))); 155 } 156 157 @Test 158 public void testCRLDisabled() throws Exception { 159 X509Util.createSslContextForServer(conf); 160 assertFalse(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation"))); 161 assertFalse(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP"))); 162 assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable"))); 163 } 164 165 @Test 166 public void testLoadPEMKeyStore() throws Exception { 167 // Make sure we can instantiate a key manager from the PEM file on disk 168 X509Util.createKeyManager( 169 x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 170 x509TestContext.getKeyStorePassword(), KeyStoreFileType.PEM.getPropertyValue()); 171 } 172 173 @Test 174 public void testLoadPEMKeyStoreNullPassword() throws Exception { 175 assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); 176 // Make sure that empty password and null password are treated the same 177 X509Util.createKeyManager( 178 x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, 179 KeyStoreFileType.PEM.getPropertyValue()); 180 } 181 182 @Test 183 public void testLoadPEMKeyStoreAutodetectStoreFileType() throws Exception { 184 // Make sure we can instantiate a key manager from the PEM file on disk 185 X509Util.createKeyManager( 186 x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 187 x509TestContext.getKeyStorePassword(), 188 null /* null StoreFileType means 'autodetect from file extension' */); 189 } 190 191 @Test(expected = KeyManagerException.class) 192 public void testLoadPEMKeyStoreWithWrongPassword() throws Exception { 193 // Attempting to load with the wrong key password should fail 194 X509Util.createKeyManager( 195 x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 196 "wrong password".toCharArray(), // intentionally use the wrong password 197 KeyStoreFileType.PEM.getPropertyValue()); 198 } 199 200 @Test 201 public void testLoadPEMTrustStore() throws Exception { 202 // Make sure we can instantiate a trust manager from the PEM file on disk 203 X509Util.createTrustManager( 204 x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 205 x509TestContext.getTrustStorePassword(), KeyStoreFileType.PEM.getPropertyValue(), false, 206 false, true, true); 207 } 208 209 @Test 210 public void testLoadPEMTrustStoreNullPassword() throws Exception { 211 assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); 212 // Make sure that empty password and null password are treated the same 213 X509Util.createTrustManager( 214 x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, 215 KeyStoreFileType.PEM.getPropertyValue(), false, false, true, true); 216 } 217 218 @Test 219 public void testLoadPEMTrustStoreAutodetectStoreFileType() throws Exception { 220 // Make sure we can instantiate a trust manager from the PEM file on disk 221 X509Util.createTrustManager( 222 x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 223 x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from 224 // file extension' 225 false, false, true, true); 226 } 227 228 @Test 229 public void testLoadJKSKeyStore() throws Exception { 230 // Make sure we can instantiate a key manager from the JKS file on disk 231 X509Util.createKeyManager( 232 x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 233 x509TestContext.getKeyStorePassword(), KeyStoreFileType.JKS.getPropertyValue()); 234 } 235 236 @Test 237 public void testLoadJKSKeyStoreNullPassword() throws Exception { 238 assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); 239 // Make sure that empty password and null password are treated the same 240 X509Util.createKeyManager( 241 x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null, 242 KeyStoreFileType.JKS.getPropertyValue()); 243 } 244 245 @Test 246 public void testLoadJKSKeyStoreAutodetectStoreFileType() throws Exception { 247 // Make sure we can instantiate a key manager from the JKS file on disk 248 X509Util.createKeyManager( 249 x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 250 x509TestContext.getKeyStorePassword(), 251 null /* null StoreFileType means 'autodetect from file extension' */); 252 } 253 254 @Test 255 public void testLoadJKSKeyStoreWithWrongPassword() { 256 assertThrows(KeyManagerException.class, () -> { 257 // Attempting to load with the wrong key password should fail 258 X509Util.createKeyManager( 259 x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 260 "wrong password".toCharArray(), KeyStoreFileType.JKS.getPropertyValue()); 261 }); 262 } 263 264 @Test 265 public void testLoadJKSTrustStore() throws Exception { 266 // Make sure we can instantiate a trust manager from the JKS file on disk 267 X509Util.createTrustManager( 268 x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 269 x509TestContext.getTrustStorePassword(), KeyStoreFileType.JKS.getPropertyValue(), true, true, 270 true, true); 271 } 272 273 @Test 274 public void testLoadJKSTrustStoreNullPassword() throws Exception { 275 assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); 276 // Make sure that empty password and null password are treated the same 277 X509Util.createTrustManager( 278 x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null, 279 KeyStoreFileType.JKS.getPropertyValue(), false, false, true, true); 280 } 281 282 @Test 283 public void testLoadJKSTrustStoreAutodetectStoreFileType() throws Exception { 284 // Make sure we can instantiate a trust manager from the JKS file on disk 285 X509Util.createTrustManager( 286 x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 287 x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from 288 // file extension' 289 true, true, true, true); 290 } 291 292 @Test 293 public void testLoadJKSTrustStoreWithWrongPassword() { 294 assertThrows(TrustManagerException.class, () -> { 295 // Attempting to load with the wrong key password should fail 296 X509Util.createTrustManager( 297 x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 298 "wrong password".toCharArray(), KeyStoreFileType.JKS.getPropertyValue(), true, true, true, 299 true); 300 }); 301 } 302 303 @Test 304 public void testLoadPKCS12KeyStore() throws Exception { 305 // Make sure we can instantiate a key manager from the PKCS12 file on disk 306 X509Util.createKeyManager( 307 x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), 308 x509TestContext.getKeyStorePassword(), KeyStoreFileType.PKCS12.getPropertyValue()); 309 } 310 311 @Test 312 public void testLoadPKCS12KeyStoreNullPassword() throws Exception { 313 assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); 314 // Make sure that empty password and null password are treated the same 315 X509Util.createKeyManager( 316 x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), null, 317 KeyStoreFileType.PKCS12.getPropertyValue()); 318 } 319 320 @Test 321 public void testLoadPKCS12KeyStoreAutodetectStoreFileType() throws Exception { 322 // Make sure we can instantiate a key manager from the PKCS12 file on disk 323 X509Util.createKeyManager( 324 x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), 325 x509TestContext.getKeyStorePassword(), 326 null /* null StoreFileType means 'autodetect from file extension' */); 327 } 328 329 @Test 330 public void testLoadPKCS12KeyStoreWithWrongPassword() { 331 assertThrows(KeyManagerException.class, () -> { 332 // Attempting to load with the wrong key password should fail 333 X509Util.createKeyManager( 334 x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), 335 "wrong password".toCharArray(), KeyStoreFileType.PKCS12.getPropertyValue()); 336 }); 337 } 338 339 @Test 340 public void testLoadPKCS12TrustStore() throws Exception { 341 // Make sure we can instantiate a trust manager from the PKCS12 file on disk 342 X509Util.createTrustManager( 343 x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), 344 x509TestContext.getTrustStorePassword(), KeyStoreFileType.PKCS12.getPropertyValue(), true, 345 true, true, true); 346 } 347 348 @Test 349 public void testLoadPKCS12TrustStoreNullPassword() throws Exception { 350 assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); 351 // Make sure that empty password and null password are treated the same 352 X509Util.createTrustManager( 353 x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), null, 354 KeyStoreFileType.PKCS12.getPropertyValue(), false, false, true, true); 355 } 356 357 @Test 358 public void testLoadPKCS12TrustStoreAutodetectStoreFileType() throws Exception { 359 // Make sure we can instantiate a trust manager from the PKCS12 file on disk 360 X509Util.createTrustManager( 361 x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), 362 x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from 363 // file extension' 364 true, true, true, true); 365 } 366 367 @Test 368 public void testLoadPKCS12TrustStoreWithWrongPassword() { 369 assertThrows(TrustManagerException.class, () -> { 370 // Attempting to load with the wrong key password should fail 371 X509Util.createTrustManager( 372 x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), 373 "wrong password".toCharArray(), KeyStoreFileType.PKCS12.getPropertyValue(), true, true, 374 true, true); 375 }); 376 } 377 378}