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.junit.jupiter.api.Assertions.assertArrayEquals; 021import static org.junit.jupiter.api.Assertions.assertEquals; 022import static org.junit.jupiter.api.Assertions.assertFalse; 023import static org.junit.jupiter.api.Assertions.assertThrows; 024import static org.junit.jupiter.api.Assertions.assertTrue; 025import static org.junit.jupiter.api.Assumptions.assumeTrue; 026import static org.mockito.Mockito.mock; 027 028import java.security.Security; 029import java.util.Arrays; 030import java.util.Collections; 031import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate; 032import org.apache.hadoop.hbase.exceptions.KeyManagerException; 033import org.apache.hadoop.hbase.exceptions.SSLContextException; 034import org.apache.hadoop.hbase.exceptions.TrustManagerException; 035import org.apache.hadoop.hbase.testclassification.SecurityTests; 036import org.apache.hadoop.hbase.testclassification.SmallTests; 037import org.junit.jupiter.api.Tag; 038import org.junit.jupiter.api.TestTemplate; 039 040import org.apache.hbase.thirdparty.io.netty.buffer.ByteBufAllocator; 041import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslContext; 042 043/** 044 * This file has been copied from the Apache ZooKeeper project. 045 * @see <a href= 046 * "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java">Base 047 * revision</a> 048 */ 049@Tag(SecurityTests.TAG) 050@Tag(SmallTests.TAG) 051@HBaseParameterizedTestTemplate(name = "{index}: caKeyType={0}, certKeyType={1}, keyPassword={2}") 052public class TestX509Util extends AbstractTestX509Parameterized { 053 054 private static final char[] EMPTY_CHAR_ARRAY = new char[0]; 055 056 public TestX509Util(X509KeyType caKeyType, X509KeyType certKeyType, char[] keyPassword) { 057 super(caKeyType, certKeyType, keyPassword); 058 } 059 060 @TestTemplate 061 public void testCreateSSLContextWithClientAuthDefault() throws Exception { 062 SslContext sslContext = X509Util.createSslContextForServer(conf); 063 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 064 assertTrue(sslContext.newEngine(byteBufAllocatorMock).getNeedClientAuth()); 065 } 066 067 @TestTemplate 068 public void testCreateSSLContextWithClientAuthNEED() throws Exception { 069 conf.set(X509Util.HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE, X509Util.ClientAuth.NEED.name()); 070 SslContext sslContext = X509Util.createSslContextForServer(conf); 071 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 072 assertTrue(sslContext.newEngine(byteBufAllocatorMock).getNeedClientAuth()); 073 } 074 075 @TestTemplate 076 public void testCreateSSLContextWithClientAuthWANT() throws Exception { 077 conf.set(X509Util.HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE, X509Util.ClientAuth.WANT.name()); 078 SslContext sslContext = X509Util.createSslContextForServer(conf); 079 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 080 assertTrue(sslContext.newEngine(byteBufAllocatorMock).getWantClientAuth()); 081 } 082 083 @TestTemplate 084 public void testCreateSSLContextWithClientAuthNONE() throws Exception { 085 conf.set(X509Util.HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE, X509Util.ClientAuth.NONE.name()); 086 SslContext sslContext = X509Util.createSslContextForServer(conf); 087 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 088 assertFalse(sslContext.newEngine(byteBufAllocatorMock).getNeedClientAuth()); 089 assertFalse(sslContext.newEngine(byteBufAllocatorMock).getWantClientAuth()); 090 } 091 092 @TestTemplate 093 public void testCreateSSLContextWithoutCustomProtocol() throws Exception { 094 SslContext sslContext = X509Util.createSslContextForClient(conf); 095 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 096 assertArrayEquals(new String[] { "TLSv1.3", "TLSv1.2" }, 097 sslContext.newEngine(byteBufAllocatorMock).getEnabledProtocols()); 098 } 099 100 @TestTemplate 101 public void testCreateTcNativeSSLContextWithoutCustomProtocol() throws Exception { 102 conf.set(X509Util.TLS_USE_OPENSSL, "true"); 103 SslContext sslContext = X509Util.createSslContextForClient(conf); 104 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 105 assertArrayEquals(new String[] { "TLSv1.3", "TLSv1.2" }, 106 sslContext.newEngine(byteBufAllocatorMock).getEnabledProtocols()); 107 } 108 109 @TestTemplate 110 public void testCreateSSLContextWithCustomProtocol() throws Exception { 111 final String protocol = "TLSv1.1"; 112 conf.set(X509Util.TLS_CONFIG_PROTOCOL, protocol); 113 ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class); 114 SslContext sslContext = X509Util.createSslContextForServer(conf); 115 assertEquals(Collections.singletonList(protocol), 116 Arrays.asList(sslContext.newEngine(byteBufAllocatorMock).getEnabledProtocols())); 117 } 118 119 @TestTemplate 120 public void testCreateSSLContextWithoutKeyStoreLocationServer() { 121 conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION); 122 assertThrows(SSLContextException.class, () -> { 123 X509Util.createSslContextForServer(conf); 124 }); 125 } 126 127 @TestTemplate 128 public void testCreateSSLContextWithoutKeyStoreLocationClient() throws Exception { 129 conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION); 130 X509Util.createSslContextForClient(conf); 131 } 132 133 @TestTemplate 134 public void testCreateSSLContextWithoutTrustStoreLocationClient() throws Exception { 135 conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION); 136 X509Util.createSslContextForClient(conf); 137 } 138 139 @TestTemplate 140 public void testCreateSSLContextWithoutTrustStoreLocationServer() throws Exception { 141 conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION); 142 X509Util.createSslContextForServer(conf); 143 } 144 145 // It would be great to test the value of PKIXBuilderParameters#setRevocationEnabled, 146 // but it does not appear to be possible 147 @TestTemplate 148 public void testCRLEnabled() throws Exception { 149 conf.setBoolean(X509Util.TLS_CONFIG_CLR, true); 150 X509Util.createSslContextForServer(conf); 151 assertTrue(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation"))); 152 assertTrue(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP"))); 153 assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable"))); 154 } 155 156 @TestTemplate 157 public void testCRLDisabled() throws Exception { 158 X509Util.createSslContextForServer(conf); 159 assertFalse(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation"))); 160 assertFalse(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP"))); 161 assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable"))); 162 } 163 164 @TestTemplate 165 public void testLoadPEMKeyStore() throws Exception { 166 // Make sure we can instantiate a key manager from the PEM file on disk 167 X509Util.createKeyManager( 168 x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 169 x509TestContext.getKeyStorePassword(), KeyStoreFileType.PEM.getPropertyValue()); 170 } 171 172 @TestTemplate 173 public void testLoadPEMKeyStoreNullPassword() throws Exception { 174 assumeTrue(Arrays.equals(x509TestContext.getKeyStorePassword(), EMPTY_CHAR_ARRAY)); 175 // Make sure that empty password and null password are treated the same 176 X509Util.createKeyManager( 177 x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, 178 KeyStoreFileType.PEM.getPropertyValue()); 179 } 180 181 @TestTemplate 182 public void testLoadPEMKeyStoreAutodetectStoreFileType() throws Exception { 183 // Make sure we can instantiate a key manager from the PEM file on disk 184 X509Util.createKeyManager( 185 x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 186 x509TestContext.getKeyStorePassword(), 187 null /* null StoreFileType means 'autodetect from file extension' */); 188 } 189 190 @TestTemplate 191 public void testLoadPEMKeyStoreWithWrongPassword() { 192 assertThrows(KeyManagerException.class, () -> { 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 201 @TestTemplate 202 public void testLoadPEMTrustStore() throws Exception { 203 // Make sure we can instantiate a trust manager from the PEM file on disk 204 X509Util.createTrustManager( 205 x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 206 x509TestContext.getTrustStorePassword(), KeyStoreFileType.PEM.getPropertyValue(), false, 207 false, true, true); 208 } 209 210 @TestTemplate 211 public void testLoadPEMTrustStoreNullPassword() throws Exception { 212 assumeTrue(Arrays.equals(x509TestContext.getTrustStorePassword(), EMPTY_CHAR_ARRAY)); 213 // Make sure that empty password and null password are treated the same 214 X509Util.createTrustManager( 215 x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, 216 KeyStoreFileType.PEM.getPropertyValue(), false, false, true, true); 217 } 218 219 @TestTemplate 220 public void testLoadPEMTrustStoreAutodetectStoreFileType() throws Exception { 221 // Make sure we can instantiate a trust manager from the PEM file on disk 222 X509Util.createTrustManager( 223 x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), 224 x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from 225 // file extension' 226 false, false, true, true); 227 } 228 229 @TestTemplate 230 public void testLoadJKSKeyStore() throws Exception { 231 // Make sure we can instantiate a key manager from the JKS file on disk 232 X509Util.createKeyManager( 233 x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 234 x509TestContext.getKeyStorePassword(), KeyStoreFileType.JKS.getPropertyValue()); 235 } 236 237 @TestTemplate 238 public void testLoadJKSKeyStoreNullPassword() throws Exception { 239 assumeTrue(Arrays.equals(x509TestContext.getKeyStorePassword(), EMPTY_CHAR_ARRAY)); 240 // Make sure that empty password and null password are treated the same 241 X509Util.createKeyManager( 242 x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null, 243 KeyStoreFileType.JKS.getPropertyValue()); 244 } 245 246 @TestTemplate 247 public void testLoadJKSKeyStoreAutodetectStoreFileType() throws Exception { 248 // Make sure we can instantiate a key manager from the JKS file on disk 249 X509Util.createKeyManager( 250 x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 251 x509TestContext.getKeyStorePassword(), 252 null /* null StoreFileType means 'autodetect from file extension' */); 253 } 254 255 @TestTemplate 256 public void testLoadJKSKeyStoreWithWrongPassword() { 257 assertThrows(KeyManagerException.class, () -> { 258 // Attempting to load with the wrong key password should fail 259 X509Util.createKeyManager( 260 x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 261 "wrong password".toCharArray(), KeyStoreFileType.JKS.getPropertyValue()); 262 }); 263 } 264 265 @TestTemplate 266 public void testLoadJKSTrustStore() throws Exception { 267 // Make sure we can instantiate a trust manager from the JKS file on disk 268 X509Util.createTrustManager( 269 x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 270 x509TestContext.getTrustStorePassword(), KeyStoreFileType.JKS.getPropertyValue(), true, true, 271 true, true); 272 } 273 274 @TestTemplate 275 public void testLoadJKSTrustStoreNullPassword() throws Exception { 276 assumeTrue(Arrays.equals(x509TestContext.getTrustStorePassword(), EMPTY_CHAR_ARRAY)); 277 // Make sure that empty password and null password are treated the same 278 X509Util.createTrustManager( 279 x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null, 280 KeyStoreFileType.JKS.getPropertyValue(), false, false, true, true); 281 } 282 283 @TestTemplate 284 public void testLoadJKSTrustStoreAutodetectStoreFileType() throws Exception { 285 // Make sure we can instantiate a trust manager from the JKS file on disk 286 // null StoreFileType means 'autodetect from file extension' 287 X509Util.createTrustManager( 288 x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), 289 x509TestContext.getTrustStorePassword(), null, true, true, true, true); 290 } 291 292 @TestTemplate 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 @TestTemplate 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 @TestTemplate 312 public void testLoadPKCS12KeyStoreNullPassword() throws Exception { 313 assumeTrue(Arrays.equals(x509TestContext.getKeyStorePassword(), 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 @TestTemplate 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 @TestTemplate 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 @TestTemplate 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 @TestTemplate 349 public void testLoadPKCS12TrustStoreNullPassword() throws Exception { 350 assumeTrue(Arrays.equals(x509TestContext.getTrustStorePassword(), 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 @TestTemplate 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 @TestTemplate 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}