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}