From 9dee9cf176cb8aae8eddaa3a911a9367fedf4b58 Mon Sep 17 00:00:00 2001 From: Andor Molnar Date: Thu, 1 Sep 2022 14:18:32 +0200 Subject: [PATCH] HBASE-27346. Add more unit tests for new file loaders --- .../tls/AbstractTestX509Parameterized.java | 128 ++++++++++++++++++ .../io/crypto/tls/TestBCFKSFileLoader.java | 118 ++++++++++++++++ ...TestFileKeyStoreLoaderBuilderProvider.java | 66 +++++++++ .../io/crypto/tls/TestJKSFileLoader.java | 117 ++++++++++++++++ .../io/crypto/tls/TestKeyStoreFileType.java | 120 ++++++++++++++++ .../io/crypto/tls/TestPEMFileLoader.java | 112 +++++++++++++++ .../io/crypto/tls/TestPKCS12FileLoader.java | 117 ++++++++++++++++ .../hbase/io/crypto/tls/TestX509Util.java | 87 +----------- .../hbase/io/crypto/tls/X509TestContext.java | 52 +++++-- .../crypto/tls/X509TestContextProvider.java | 17 +++ .../hbase/io/crypto/tls/X509TestHelpers.java | 34 +++++ 11 files changed, 875 insertions(+), 93 deletions(-) create mode 100644 hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/AbstractTestX509Parameterized.java create mode 100644 hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestBCFKSFileLoader.java create mode 100644 hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestFileKeyStoreLoaderBuilderProvider.java create mode 100644 hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestJKSFileLoader.java create mode 100644 hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestKeyStoreFileType.java create mode 100644 hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestPEMFileLoader.java create mode 100644 hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestPKCS12FileLoader.java diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/AbstractTestX509Parameterized.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/AbstractTestX509Parameterized.java new file mode 100644 index 000000000000..821a68541358 --- /dev/null +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/AbstractTestX509Parameterized.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.io.crypto.tls; + +import java.io.File; +import java.io.IOException; +import java.security.Security; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.commons.io.FileUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseCommonTestingUtil; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runners.Parameterized; + +/** + * Base class for parameterized unit tests that use X509TestContext for testing different X509 + * parameter combinations (CA key type, cert key type, with/without a password, with/without + * hostname verification, etc). + *

+ * This base class takes care of setting up / cleaning up the test environment, and caching the + * X509TestContext objects used by the tests. + *

+ * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +public abstract class AbstractTestX509Parameterized { + + private static final HBaseCommonTestingUtil UTIL = new HBaseCommonTestingUtil(); + private static X509TestContextProvider PROVIDER; + + @Parameterized.Parameter() + public X509KeyType caKeyType; + + @Parameterized.Parameter(value = 1) + public X509KeyType certKeyType; + + @Parameterized.Parameter(value = 2) + public char[] keyPassword; + + @Parameterized.Parameter(value = 3) + public Integer paramIndex; + + /** + * Default parameters suitable for most subclasses. See example usage in {@link TestX509Util}. + * @return an array of parameter combinations to test with. + */ + @Parameterized.Parameters( + name = "{index}: caKeyType={0}, certKeyType={1}, keyPassword={2}, paramIndex={3}") + public static Collection defaultParams() { + List result = new ArrayList<>(); + int paramIndex = 0; + for (X509KeyType caKeyType : X509KeyType.values()) { + for (X509KeyType certKeyType : X509KeyType.values()) { + for (char[] keyPassword : new char[][] { "".toCharArray(), "pa$$w0rd".toCharArray() }) { + result.add(new Object[] { caKeyType, certKeyType, keyPassword, paramIndex++ }); + } + } + } + return result; + } + + /** + * Because key generation and writing / deleting files is kind of expensive, we cache the certs + * and on-disk files between test cases. None of the test cases modify any of this data so it's + * safe to reuse between tests. This caching makes all test cases after the first one for a given + * parameter combination complete almost instantly. + */ + protected static Configuration conf; + + protected X509TestContext x509TestContext; + + @BeforeClass + public static void setUpBaseClass() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + File dir = new File(UTIL.getDataTestDir(TestX509Util.class.getSimpleName()).toString()) + .getCanonicalFile(); + FileUtils.forceMkdir(dir); + PROVIDER = new X509TestContextProvider(UTIL.getConfiguration(), dir); + } + + @AfterClass + public static void cleanUpBaseClass() { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + UTIL.cleanupTestDir(); + } + + @Before + public void setUp() throws IOException { + x509TestContext = PROVIDER.get(caKeyType, certKeyType, keyPassword); + x509TestContext.setConfigurations(KeyStoreFileType.JKS, KeyStoreFileType.JKS); + conf = new Configuration(UTIL.getConfiguration()); + } + + @After + public void cleanUp() { + x509TestContext.clearConfigurations(); + x509TestContext.getConf().unset(X509Util.TLS_CONFIG_OCSP); + x509TestContext.getConf().unset(X509Util.TLS_CONFIG_CLR); + x509TestContext.getConf().unset(X509Util.TLS_CONFIG_PROTOCOL); + System.clearProperty("com.sun.net.ssl.checkRevocation"); + System.clearProperty("com.sun.security.enableCRLDP"); + Security.setProperty("ocsp.enable", Boolean.FALSE.toString()); + Security.setProperty("com.sun.security.enableCRLDP", Boolean.FALSE.toString()); + } +} diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestBCFKSFileLoader.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestBCFKSFileLoader.java new file mode 100644 index 000000000000..060c60a7a0ca --- /dev/null +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestBCFKSFileLoader.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.io.crypto.tls; + +import java.io.IOException; +import java.security.KeyStore; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +@RunWith(Parameterized.class) +@Category({ SecurityTests.class, SmallTests.class }) +public class TestBCFKSFileLoader extends AbstractTestX509Parameterized { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestBCFKSFileLoader.class); + + @Test + public void testLoadKeyStore() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); + KeyStore ks = new BCFKSFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + Assert.assertEquals(1, ks.size()); + } + + @Test(expected = Exception.class) + public void testLoadKeyStoreWithWrongPassword() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); + new BCFKSFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword("wrong password".toCharArray()).build().loadKeyStore(); + } + + @Test(expected = IOException.class) + public void testLoadKeyStoreWithWrongFilePath() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); + new BCFKSFileLoader.Builder().setKeyStorePath(path + ".does_not_exist") + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + } + + @Test(expected = NullPointerException.class) + public void testLoadKeyStoreWithNullFilePath() throws Exception { + new BCFKSFileLoader.Builder().setKeyStorePassword(x509TestContext.getKeyStorePassword()).build() + .loadKeyStore(); + } + + @Test(expected = IOException.class) + public void testLoadKeyStoreWithWrongFileType() throws Exception { + // Trying to load a PEM file with BCFKS loader should fail + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new BCFKSFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + } + + @Test + public void testLoadTrustStore() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); + KeyStore ts = new BCFKSFileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + Assert.assertEquals(1, ts.size()); + } + + @Test(expected = Exception.class) + public void testLoadTrustStoreWithWrongPassword() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); + new BCFKSFileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword("wrong password".toCharArray()).build().loadTrustStore(); + } + + @Test(expected = IOException.class) + public void testLoadTrustStoreWithWrongFilePath() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); + new BCFKSFileLoader.Builder().setTrustStorePath(path + ".does_not_exist") + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + } + + @Test(expected = NullPointerException.class) + public void testLoadTrustStoreWithNullFilePath() throws Exception { + new BCFKSFileLoader.Builder().setTrustStorePassword(x509TestContext.getTrustStorePassword()) + .build().loadTrustStore(); + } + + @Test(expected = IOException.class) + public void testLoadTrustStoreWithWrongFileType() throws Exception { + // Trying to load a PEM file with BCFKS loader should fail + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new BCFKSFileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + } + +} diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestFileKeyStoreLoaderBuilderProvider.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestFileKeyStoreLoaderBuilderProvider.java new file mode 100644 index 000000000000..a80103483345 --- /dev/null +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestFileKeyStoreLoaderBuilderProvider.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.io.crypto.tls; + +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +@Category({ SecurityTests.class, SmallTests.class }) +public class TestFileKeyStoreLoaderBuilderProvider { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestFileKeyStoreLoaderBuilderProvider.class); + + @Test + public void testGetBuilderForJKSFileType() { + FileKeyStoreLoader.Builder builder = + FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(KeyStoreFileType.JKS); + Assert.assertTrue(builder instanceof JKSFileLoader.Builder); + } + + @Test + public void testGetBuilderForPEMFileType() { + FileKeyStoreLoader.Builder builder = + FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(KeyStoreFileType.PEM); + Assert.assertTrue(builder instanceof PEMFileLoader.Builder); + } + + @Test + public void testGetBuilderForPKCS12FileType() { + FileKeyStoreLoader.Builder builder = + FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(KeyStoreFileType.PKCS12); + Assert.assertTrue(builder instanceof PKCS12FileLoader.Builder); + } + + @Test(expected = NullPointerException.class) + public void testGetBuilderForNullFileType() { + FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(null); + } +} diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestJKSFileLoader.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestJKSFileLoader.java new file mode 100644 index 000000000000..6640e3b22f98 --- /dev/null +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestJKSFileLoader.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.io.crypto.tls; + +import java.io.IOException; +import java.security.KeyStore; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +@RunWith(Parameterized.class) +@Category({ SecurityTests.class, SmallTests.class }) +public class TestJKSFileLoader extends AbstractTestX509Parameterized { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestJKSFileLoader.class); + + @Test + public void testLoadKeyStore() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); + KeyStore ks = new JKSFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + Assert.assertEquals(1, ks.size()); + } + + @Test(expected = Exception.class) + public void testLoadKeyStoreWithWrongPassword() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); + new JKSFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword("wrong password".toCharArray()).build().loadKeyStore(); + } + + @Test(expected = IOException.class) + public void testLoadKeyStoreWithWrongFilePath() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); + new JKSFileLoader.Builder().setKeyStorePath(path + ".does_not_exist") + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + } + + @Test(expected = NullPointerException.class) + public void testLoadKeyStoreWithNullFilePath() throws Exception { + new JKSFileLoader.Builder().setKeyStorePassword(x509TestContext.getKeyStorePassword()).build() + .loadKeyStore(); + } + + @Test(expected = IOException.class) + public void testLoadKeyStoreWithWrongFileType() throws Exception { + // Trying to load a PEM file with JKS loader should fail + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new JKSFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + } + + @Test + public void testLoadTrustStore() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); + KeyStore ts = new JKSFileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + Assert.assertEquals(1, ts.size()); + } + + @Test(expected = Exception.class) + public void testLoadTrustStoreWithWrongPassword() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); + new JKSFileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword("wrong password".toCharArray()).build().loadTrustStore(); + } + + @Test(expected = IOException.class) + public void testLoadTrustStoreWithWrongFilePath() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); + new JKSFileLoader.Builder().setTrustStorePath(path + ".does_not_exist") + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + } + + @Test(expected = NullPointerException.class) + public void testLoadTrustStoreWithNullFilePath() throws Exception { + new JKSFileLoader.Builder().setTrustStorePassword(x509TestContext.getTrustStorePassword()) + .build().loadTrustStore(); + } + + @Test(expected = IOException.class) + public void testLoadTrustStoreWithWrongFileType() throws Exception { + // Trying to load a PEM file with JKS loader should fail + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new JKSFileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + } +} diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestKeyStoreFileType.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestKeyStoreFileType.java new file mode 100644 index 000000000000..d3f457fb4d33 --- /dev/null +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestKeyStoreFileType.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.io.crypto.tls; + +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +@Category({ SecurityTests.class, SmallTests.class }) +public class TestKeyStoreFileType { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestKeyStoreFileType.class); + + @Test + public void testGetPropertyValue() { + Assert.assertEquals("PEM", KeyStoreFileType.PEM.getPropertyValue()); + Assert.assertEquals("JKS", KeyStoreFileType.JKS.getPropertyValue()); + Assert.assertEquals("PKCS12", KeyStoreFileType.PKCS12.getPropertyValue()); + Assert.assertEquals("BCFKS", KeyStoreFileType.BCFKS.getPropertyValue()); + } + + @Test + public void testFromPropertyValue() { + Assert.assertEquals(KeyStoreFileType.PEM, KeyStoreFileType.fromPropertyValue("PEM")); + Assert.assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromPropertyValue("JKS")); + Assert.assertEquals(KeyStoreFileType.PKCS12, KeyStoreFileType.fromPropertyValue("PKCS12")); + Assert.assertEquals(KeyStoreFileType.BCFKS, KeyStoreFileType.fromPropertyValue("BCFKS")); + Assert.assertNull(KeyStoreFileType.fromPropertyValue("")); + Assert.assertNull(KeyStoreFileType.fromPropertyValue(null)); + } + + @Test + public void testFromPropertyValueIgnoresCase() { + Assert.assertEquals(KeyStoreFileType.PEM, KeyStoreFileType.fromPropertyValue("pem")); + Assert.assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromPropertyValue("jks")); + Assert.assertEquals(KeyStoreFileType.PKCS12, KeyStoreFileType.fromPropertyValue("pkcs12")); + Assert.assertEquals(KeyStoreFileType.BCFKS, KeyStoreFileType.fromPropertyValue("bcfks")); + Assert.assertNull(KeyStoreFileType.fromPropertyValue("")); + Assert.assertNull(KeyStoreFileType.fromPropertyValue(null)); + } + + @Test(expected = IllegalArgumentException.class) + public void testFromPropertyValueThrowsOnBadPropertyValue() { + KeyStoreFileType.fromPropertyValue("foobar"); + } + + @Test + public void testFromFilename() { + Assert.assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromFilename("mykey.jks")); + Assert.assertEquals(KeyStoreFileType.JKS, + KeyStoreFileType.fromFilename("/path/to/key/dir/mykey.jks")); + Assert.assertEquals(KeyStoreFileType.PEM, KeyStoreFileType.fromFilename("mykey.pem")); + Assert.assertEquals(KeyStoreFileType.PEM, + KeyStoreFileType.fromFilename("/path/to/key/dir/mykey.pem")); + Assert.assertEquals(KeyStoreFileType.PKCS12, KeyStoreFileType.fromFilename("mykey.p12")); + Assert.assertEquals(KeyStoreFileType.PKCS12, + KeyStoreFileType.fromFilename("/path/to/key/dir/mykey.p12")); + Assert.assertEquals(KeyStoreFileType.BCFKS, KeyStoreFileType.fromFilename("mykey.bcfks")); + Assert.assertEquals(KeyStoreFileType.BCFKS, + KeyStoreFileType.fromFilename("/path/to/key/dir/mykey.bcfks")); + } + + @Test(expected = IllegalArgumentException.class) + public void testFromFilenameThrowsOnBadFileExtension() { + KeyStoreFileType.fromFilename("prod.key"); + } + + @Test + public void testFromPropertyValueOrFileName() { + // Property value takes precedence if provided + Assert.assertEquals(KeyStoreFileType.JKS, + KeyStoreFileType.fromPropertyValueOrFileName("JKS", "prod.key")); + Assert.assertEquals(KeyStoreFileType.PEM, + KeyStoreFileType.fromPropertyValueOrFileName("PEM", "prod.key")); + Assert.assertEquals(KeyStoreFileType.PKCS12, + KeyStoreFileType.fromPropertyValueOrFileName("PKCS12", "prod.key")); + Assert.assertEquals(KeyStoreFileType.BCFKS, + KeyStoreFileType.fromPropertyValueOrFileName("BCFKS", "prod.key")); + // Falls back to filename detection if no property value + Assert.assertEquals(KeyStoreFileType.JKS, + KeyStoreFileType.fromPropertyValueOrFileName("", "prod.jks")); + } + + @Test(expected = IllegalArgumentException.class) + public void testFromPropertyValueOrFileNameThrowsOnBadPropertyValue() { + KeyStoreFileType.fromPropertyValueOrFileName("foobar", "prod.jks"); + } + + @Test(expected = IllegalArgumentException.class) + public void testFromPropertyValueOrFileNameThrowsOnBadFileExtension() { + KeyStoreFileType.fromPropertyValueOrFileName("", "prod.key"); + } +} diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestPEMFileLoader.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestPEMFileLoader.java new file mode 100644 index 000000000000..0c9924f09075 --- /dev/null +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestPEMFileLoader.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.io.crypto.tls; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +@RunWith(Parameterized.class) +@Category({ SecurityTests.class, SmallTests.class }) +public class TestPEMFileLoader extends AbstractTestX509Parameterized { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestPEMFileLoader.class); + + @Test + public void testLoadKeyStore() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + KeyStore ks = new PEMFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + Assert.assertEquals(1, ks.size()); + } + + @Test(expected = Exception.class) + public void testLoadKeyStoreWithWrongPassword() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new PEMFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword("wrong password".toCharArray()).build().loadKeyStore(); + } + + @Test(expected = IOException.class) + public void testLoadKeyStoreWithWrongFilePath() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new PEMFileLoader.Builder().setKeyStorePath(path + ".does_not_exist") + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + } + + @Test(expected = NullPointerException.class) + public void testLoadKeyStoreWithNullFilePath() throws Exception { + new PEMFileLoader.Builder().setKeyStorePassword(x509TestContext.getKeyStorePassword()).build() + .loadKeyStore(); + } + + @Test(expected = KeyStoreException.class) + public void testLoadKeyStoreWithWrongFileType() throws Exception { + // Trying to load a JKS file with PEM loader should fail + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); + new PEMFileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + } + + @Test + public void testLoadTrustStore() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + KeyStore ts = new PEMFileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + Assert.assertEquals(1, ts.size()); + } + + @Test(expected = IOException.class) + public void testLoadTrustStoreWithWrongFilePath() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new PEMFileLoader.Builder().setTrustStorePath(path + ".does_not_exist") + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + } + + @Test(expected = NullPointerException.class) + public void testLoadTrustStoreWithNullFilePath() throws Exception { + new PEMFileLoader.Builder().setTrustStorePassword(x509TestContext.getTrustStorePassword()) + .build().loadTrustStore(); + } + + @Test + public void testLoadTrustStoreWithWrongFileType() throws Exception { + // Trying to load a JKS file with PEM loader should fail + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); + KeyStore ts = new PEMFileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + Assert.assertEquals(0, ts.size()); + } +} diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestPKCS12FileLoader.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestPKCS12FileLoader.java new file mode 100644 index 000000000000..a0ff83833e22 --- /dev/null +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestPKCS12FileLoader.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.io.crypto.tls; + +import java.io.IOException; +import java.security.KeyStore; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +@RunWith(Parameterized.class) +@Category({ SecurityTests.class, SmallTests.class }) +public class TestPKCS12FileLoader extends AbstractTestX509Parameterized { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestPKCS12FileLoader.class); + + @Test + public void testLoadKeyStore() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); + KeyStore ks = new PKCS12FileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + Assert.assertEquals(1, ks.size()); + } + + @Test(expected = Exception.class) + public void testLoadKeyStoreWithWrongPassword() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); + new PKCS12FileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword("wrong password".toCharArray()).build().loadKeyStore(); + } + + @Test(expected = IOException.class) + public void testLoadKeyStoreWithWrongFilePath() throws Exception { + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); + new PKCS12FileLoader.Builder().setKeyStorePath(path + ".does_not_exist") + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + } + + @Test(expected = NullPointerException.class) + public void testLoadKeyStoreWithNullFilePath() throws Exception { + new PKCS12FileLoader.Builder().setKeyStorePassword(x509TestContext.getKeyStorePassword()) + .build().loadKeyStore(); + } + + @Test(expected = IOException.class) + public void testLoadKeyStoreWithWrongFileType() throws Exception { + // Trying to load a PEM file with PKCS12 loader should fail + String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new PKCS12FileLoader.Builder().setKeyStorePath(path) + .setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); + } + + @Test + public void testLoadTrustStore() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); + KeyStore ts = new PKCS12FileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + Assert.assertEquals(1, ts.size()); + } + + @Test(expected = Exception.class) + public void testLoadTrustStoreWithWrongPassword() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); + new PKCS12FileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword("wrong password".toCharArray()).build().loadTrustStore(); + } + + @Test(expected = IOException.class) + public void testLoadTrustStoreWithWrongFilePath() throws Exception { + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); + new PKCS12FileLoader.Builder().setTrustStorePath(path + ".does_not_exist") + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + } + + @Test(expected = NullPointerException.class) + public void testLoadTrustStoreWithNullFilePath() throws Exception { + new PKCS12FileLoader.Builder().setTrustStorePassword(x509TestContext.getTrustStorePassword()) + .build().loadTrustStore(); + } + + @Test(expected = IOException.class) + public void testLoadTrustStoreWithWrongFileType() throws Exception { + // Trying to load a PEM file with PKCS12 loader should fail + String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); + new PKCS12FileLoader.Builder().setTrustStorePath(path) + .setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); + } +} diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestX509Util.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestX509Util.java index bb70c02946fb..f98950381157 100644 --- a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestX509Util.java +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestX509Util.java @@ -28,30 +28,17 @@ import static org.junit.Assume.assumeThat; import static org.mockito.Mockito.mock; -import java.io.File; -import java.io.IOException; import java.security.Security; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.List; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; -import org.apache.commons.io.FileUtils; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseClassTestRule; -import org.apache.hadoop.hbase.HBaseCommonTestingUtil; import org.apache.hadoop.hbase.exceptions.KeyManagerException; import org.apache.hadoop.hbase.exceptions.SSLContextException; import org.apache.hadoop.hbase.exceptions.TrustManagerException; -import org.apache.hadoop.hbase.testclassification.MiscTests; +import org.apache.hadoop.hbase.testclassification.SecurityTests; import org.apache.hadoop.hbase.testclassification.SmallTests; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -68,83 +55,15 @@ * revision */ @RunWith(Parameterized.class) -@Category({ MiscTests.class, SmallTests.class }) -public class TestX509Util { +@Category({ SecurityTests.class, SmallTests.class }) +public class TestX509Util extends AbstractTestX509Parameterized { @ClassRule public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestX509Util.class); - private static final HBaseCommonTestingUtil UTIL = new HBaseCommonTestingUtil(); private static final char[] EMPTY_CHAR_ARRAY = new char[0]; - private static X509TestContextProvider PROVIDER; - - @Parameterized.Parameter() - public X509KeyType caKeyType; - - @Parameterized.Parameter(value = 1) - public X509KeyType certKeyType; - - @Parameterized.Parameter(value = 2) - public char[] keyPassword; - - @Parameterized.Parameter(value = 3) - public Integer paramIndex; - - private X509TestContext x509TestContext; - - private Configuration conf; - - @Parameterized.Parameters( - name = "{index}: caKeyType={0}, certKeyType={1}, keyPassword={2}, paramIndex={3}") - public static Collection data() { - List params = new ArrayList<>(); - int paramIndex = 0; - for (X509KeyType caKeyType : X509KeyType.values()) { - for (X509KeyType certKeyType : X509KeyType.values()) { - for (char[] keyPassword : new char[][] { "".toCharArray(), "pa$$w0rd".toCharArray() }) { - params.add(new Object[] { caKeyType, certKeyType, keyPassword, paramIndex++ }); - } - } - } - return params; - } - - @BeforeClass - public static void setUpBeforeClass() throws IOException { - Security.addProvider(new BouncyCastleProvider()); - File dir = new File(UTIL.getDataTestDir(TestX509Util.class.getSimpleName()).toString()) - .getCanonicalFile(); - FileUtils.forceMkdir(dir); - PROVIDER = new X509TestContextProvider(UTIL.getConfiguration(), dir); - } - - @AfterClass - public static void tearDownAfterClass() { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - UTIL.cleanupTestDir(); - } - - @Before - public void setUp() throws IOException { - x509TestContext = PROVIDER.get(caKeyType, certKeyType, keyPassword); - x509TestContext.setConfigurations(KeyStoreFileType.JKS, KeyStoreFileType.JKS); - conf = new Configuration(UTIL.getConfiguration()); - } - - @After - public void cleanUp() { - x509TestContext.clearConfigurations(); - x509TestContext.getConf().unset(X509Util.TLS_CONFIG_OCSP); - x509TestContext.getConf().unset(X509Util.TLS_CONFIG_CLR); - x509TestContext.getConf().unset(X509Util.TLS_CONFIG_PROTOCOL); - System.clearProperty("com.sun.net.ssl.checkRevocation"); - System.clearProperty("com.sun.security.enableCRLDP"); - Security.setProperty("ocsp.enable", Boolean.FALSE.toString()); - Security.setProperty("com.sun.security.enableCRLDP", Boolean.FALSE.toString()); - } - @Test public void testCreateSSLContextWithoutCustomProtocol() throws Exception { SslContext sslContext = X509Util.createSslContextForClient(conf); diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestContext.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestContext.java index b20850788603..27cb5bde3aca 100644 --- a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestContext.java +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestContext.java @@ -31,7 +31,6 @@ import java.util.Arrays; import org.apache.commons.io.FileUtils; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.yetus.audience.InterfaceAudience; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; @@ -60,6 +59,7 @@ public final class X509TestContext { private File trustStoreJksFile; private File trustStorePemFile; private File trustStorePkcs12File; + private File trustStoreBcfksFile; private final KeyPair keyStoreKeyPair; private final X509Certificate keyStoreCertificate; @@ -67,6 +67,7 @@ public final class X509TestContext { private File keyStoreJksFile; private File keyStorePemFile; private File keyStorePkcs12File; + private File keyStoreBcfksFile; /** * Constructor is intentionally private, use the Builder class instead. @@ -137,6 +138,8 @@ public File getTrustStoreFile(KeyStoreFileType storeFileType) throws IOException return getTrustStorePemFile(); case PKCS12: return getTrustStorePkcs12File(); + case BCFKS: + return getTrustStoreBcfksFile(); default: throw new IllegalArgumentException("Invalid trust store type: " + storeFileType + ", must be one of: " + Arrays.toString(KeyStoreFileType.values())); @@ -194,6 +197,25 @@ private File getTrustStorePkcs12File() throws IOException { return trustStorePkcs12File; } + private File getTrustStoreBcfksFile() throws IOException { + if (trustStoreBcfksFile == null) { + File trustStoreBcfksFile = File.createTempFile(TRUST_STORE_PREFIX, + KeyStoreFileType.BCFKS.getDefaultFileExtension(), tempDir); + trustStoreBcfksFile.deleteOnExit(); + try ( + final FileOutputStream trustStoreOutputStream = new FileOutputStream(trustStoreBcfksFile)) { + byte[] bytes = + X509TestHelpers.certToBCFKSTrustStoreBytes(trustStoreCertificate, trustStorePassword); + trustStoreOutputStream.write(bytes); + trustStoreOutputStream.flush(); + } catch (GeneralSecurityException e) { + throw new IOException(e); + } + this.trustStoreBcfksFile = trustStoreBcfksFile; + } + return trustStoreBcfksFile; + } + public X509Certificate getKeyStoreCertificate() { return keyStoreCertificate; } @@ -226,6 +248,8 @@ public File getKeyStoreFile(KeyStoreFileType storeFileType) throws IOException { return getKeyStorePemFile(); case PKCS12: return getKeyStorePkcs12File(); + case BCFKS: + return getKeyStoreBcfksFile(); default: throw new IllegalArgumentException("Invalid key store type: " + storeFileType + ", must be one of: " + Arrays.toString(KeyStoreFileType.values())); @@ -286,6 +310,24 @@ private File getKeyStorePkcs12File() throws IOException { return keyStorePkcs12File; } + private File getKeyStoreBcfksFile() throws IOException { + if (keyStoreBcfksFile == null) { + File keyStoreBcfksFile = File.createTempFile(KEY_STORE_PREFIX, + KeyStoreFileType.BCFKS.getDefaultFileExtension(), tempDir); + keyStoreBcfksFile.deleteOnExit(); + try (final FileOutputStream keyStoreOutputStream = new FileOutputStream(keyStoreBcfksFile)) { + byte[] bytes = X509TestHelpers.certAndPrivateKeyToBCFKSBytes(keyStoreCertificate, + keyStoreKeyPair.getPrivate(), keyStorePassword); + keyStoreOutputStream.write(bytes); + keyStoreOutputStream.flush(); + } catch (GeneralSecurityException e) { + throw new IOException(e); + } + this.keyStoreBcfksFile = keyStoreBcfksFile; + } + return keyStoreBcfksFile; + } + /** * Sets the SSL system properties such that the given X509Util object can be used to create SSL * Contexts that will use the trust store and key store files created by this test context. @@ -413,14 +455,6 @@ public Builder setKeyStorePassword(char[] password) { } } - /** - * Returns a new default-constructed Builder. - * @return a new Builder. - */ - public static Builder newBuilder() { - return newBuilder(HBaseConfiguration.create()); - } - /** * Returns a new default-constructed Builder. * @return a new Builder. diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestContextProvider.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestContextProvider.java index 3024755a2e3e..d65cdbe689dd 100644 --- a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestContextProvider.java +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestContextProvider.java @@ -18,7 +18,10 @@ package org.apache.hadoop.hbase.io.crypto.tls; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.List; import java.util.Objects; import org.apache.hadoop.conf.Configuration; @@ -83,4 +86,18 @@ public X509TestContextProvider(Configuration conf, File tempDir) { public X509TestContext get(X509KeyType caKeyType, X509KeyType certKeyType, char[] keyPassword) { return ctxs.getUnchecked(new CacheKey(caKeyType, certKeyType, keyPassword)); } + + static Collection defaultParams() { + List params = new ArrayList<>(); + int paramIndex = 0; + for (X509KeyType caKeyType : X509KeyType.values()) { + for (X509KeyType certKeyType : X509KeyType.values()) { + for (char[] keyPassword : new char[][] { "".toCharArray(), "pa$$w0rd".toCharArray() }) { + params.add(new Object[] { caKeyType, certKeyType, keyPassword, paramIndex++ }); + } + } + } + return params; + } + } diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestHelpers.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestHelpers.java index 42c798abcd9e..968e616ef569 100644 --- a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestHelpers.java +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/X509TestHelpers.java @@ -341,6 +341,23 @@ public static byte[] certToPKCS12TrustStoreBytes(X509Certificate cert, char[] ke return certToTrustStoreBytes(cert, keyPassword, trustStore); } + /** + * Encodes the given X509Certificate as a BCFKS TrustStore, optionally protecting the cert with a + * password (though it's unclear why one would do this since certificates only contain public + * information and do not need to be kept secret). Returns the byte array encoding of the trust + * store, which may be written to a file and loaded to instantiate the trust store at a later + * point or in another process. + * @param cert the certificate to serialize. + * @param keyPassword an optional password to encrypt the trust store. If empty or null, the cert + * will not be encrypted. + * @return the serialized bytes of the BCFKS trust store. nn + */ + public static byte[] certToBCFKSTrustStoreBytes(X509Certificate cert, char[] keyPassword) + throws IOException, GeneralSecurityException { + KeyStore trustStore = KeyStore.getInstance("BCFKS"); + return certToTrustStoreBytes(cert, keyPassword, trustStore); + } + private static byte[] certToTrustStoreBytes(X509Certificate cert, char[] keyPassword, KeyStore trustStore) throws IOException, GeneralSecurityException { trustStore.load(null, keyPassword); @@ -387,6 +404,23 @@ public static byte[] certAndPrivateKeyToPKCS12Bytes(X509Certificate cert, Privat return certAndPrivateKeyToBytes(cert, privateKey, keyPassword, keyStore); } + /** + * Encodes the given X509Certificate and private key as a BCFKS KeyStore, optionally protecting + * the private key (and possibly the cert?) with a password. Returns the byte array encoding of + * the key store, which may be written to a file and loaded to instantiate the key store at a + * later point or in another process. + * @param cert the X509 certificate to serialize. + * @param privateKey the private key to serialize. + * @param keyPassword an optional key password. If empty or null, the private key will not be + * encrypted. + * @return the serialized bytes of the BCFKS key store. nn + */ + public static byte[] certAndPrivateKeyToBCFKSBytes(X509Certificate cert, PrivateKey privateKey, + char[] keyPassword) throws IOException, GeneralSecurityException { + KeyStore keyStore = KeyStore.getInstance("BCFKS"); + return certAndPrivateKeyToBytes(cert, privateKey, keyPassword, keyStore); + } + private static byte[] certAndPrivateKeyToBytes(X509Certificate cert, PrivateKey privateKey, char[] keyPassword, KeyStore keyStore) throws IOException, GeneralSecurityException { keyStore.load(null, keyPassword);