From 727ad6fcd9d2f804d097bf385b50fd099dc45fec Mon Sep 17 00:00:00 2001 From: Andor Molnar Date: Thu, 1 Sep 2022 10:04:06 +0200 Subject: [PATCH 1/6] HBASE-27346. Add keystore file loaders --- .../hbase/io/crypto/tls/BCFKSFileLoader.java | 43 ++++++++++ .../io/crypto/tls/FileKeyStoreLoader.java | 82 +++++++++++++++++++ .../FileKeyStoreLoaderBuilderProvider.java | 54 ++++++++++++ .../hbase/io/crypto/tls/JKSFileLoader.java | 43 ++++++++++ .../hbase/io/crypto/tls/KeyStoreLoader.java | 57 +++++++++++++ .../hbase/io/crypto/tls/PEMFileLoader.java | 68 +++++++++++++++ .../hbase/io/crypto/tls/PKCS12FileLoader.java | 43 ++++++++++ .../tls/StandardTypeFileKeyStoreLoader.java | 80 ++++++++++++++++++ 8 files changed, 470 insertions(+) create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java new file mode 100644 index 000000000000..b8c5338a26b1 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java @@ -0,0 +1,43 @@ +/* + * 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; + +/** + * Implementation of {@link FileKeyStoreLoader} that loads from BCKFS files. + *

+ * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +class BCFKSFileLoader extends StandardTypeFileKeyStoreLoader { + private BCFKSFileLoader(String keyStorePath, + String trustStorePath, + char[] keyStorePassword, + char[] trustStorePassword) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.BCFKS); + } + + static class Builder extends FileKeyStoreLoader.Builder { + @Override + BCFKSFileLoader build() { + return new BCFKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); + } + } +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java new file mode 100644 index 000000000000..f953e3bdf629 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java @@ -0,0 +1,82 @@ +/* + * 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.util.Objects; + +/** + * Base class for instances of {@link KeyStoreLoader} which load the key/trust + * stores from files on a filesystem. + *

+ * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +abstract class FileKeyStoreLoader implements KeyStoreLoader { + final String keyStorePath; + final String trustStorePath; + final char[] keyStorePassword; + final char[] trustStorePassword; + + FileKeyStoreLoader(String keyStorePath, + String trustStorePath, + char[] keyStorePassword, + char[] trustStorePassword) { + this.keyStorePath = keyStorePath; + this.trustStorePath = trustStorePath; + this.keyStorePassword = keyStorePassword; + this.trustStorePassword = trustStorePassword; + } + + /** + * Base class for builder pattern used by subclasses. + * @param the subtype of FileKeyStoreLoader created by the Builder. + */ + static abstract class Builder { + String keyStorePath; + String trustStorePath; + char[] keyStorePassword; + char[] trustStorePassword; + + Builder() {} + + Builder setKeyStorePath(String keyStorePath) { + this.keyStorePath = Objects.requireNonNull(keyStorePath); + return this; + } + + Builder setTrustStorePath(String trustStorePath) { + this.trustStorePath = Objects.requireNonNull(trustStorePath); + return this; + } + + Builder setKeyStorePassword(char[] keyStorePassword) { + this.keyStorePassword = Objects.requireNonNull(keyStorePassword); + return this; + } + + Builder setTrustStorePassword(char[] trustStorePassword) { + this.trustStorePassword = Objects.requireNonNull(trustStorePassword); + return this; + } + + abstract T build(); + } +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java new file mode 100644 index 000000000000..dcf8e56db28d --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java @@ -0,0 +1,54 @@ +/* + * 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.util.Objects; + +/** + * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +public class FileKeyStoreLoaderBuilderProvider { + /** + * Returns a {@link FileKeyStoreLoader.Builder} that can build a loader + * which loads keys and certs from files of the given + * {@link KeyStoreFileType}. + * + * @param type the file type to load keys/certs from. + * @return a new Builder. + */ + static FileKeyStoreLoader.Builder + getBuilderForKeyStoreFileType(KeyStoreFileType type) { + switch (Objects.requireNonNull(type)) { + case JKS: + return new JKSFileLoader.Builder(); + case PEM: + return new PEMFileLoader.Builder(); + case PKCS12: + return new PKCS12FileLoader.Builder(); + case BCFKS: + return new BCFKSFileLoader.Builder(); + default: + throw new AssertionError( + "Unexpected StoreFileType: " + type.name()); + } + } +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java new file mode 100644 index 000000000000..3d2339defa5c --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java @@ -0,0 +1,43 @@ +/* + * 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; + +/** + * Implementation of {@link FileKeyStoreLoader} that loads from JKS files. + *

+ * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +class JKSFileLoader extends StandardTypeFileKeyStoreLoader { + private JKSFileLoader(String keyStorePath, + String trustStorePath, + char[] keyStorePassword, + char[] trustStorePassword) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.JKS); + } + + static class Builder extends FileKeyStoreLoader.Builder { + @Override + JKSFileLoader build() { + return new JKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); + } + } +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java new file mode 100644 index 000000000000..f0c3a8356438 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java @@ -0,0 +1,57 @@ +/* + * 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.GeneralSecurityException; +import java.security.KeyStore; + +/** + * An interface for an object that can load key stores or trust stores. + *

+ * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +interface KeyStoreLoader { + /** + * Loads a KeyStore which contains at least one private key and the + * associated X509 cert chain. + * + * @return a new KeyStore + * @throws IOException if loading the key store fails due to an IO error, + * such as "file not found". + * @throws GeneralSecurityException if loading the key store fails due to + * a security error, such as "unsupported crypto algorithm". + */ + KeyStore loadKeyStore() throws IOException, GeneralSecurityException; + + /** + * Loads a KeyStore which contains at least one X509 cert chain for a + * trusted Certificate Authority (CA). + * + * @return a new KeyStore + * @throws IOException if loading the trust store fails due to an IO error, + * such as "file not found". + * @throws GeneralSecurityException if loading the trust store fails due to + * a security error, such as "unsupported crypto algorithm". + */ + KeyStore loadTrustStore() throws IOException, GeneralSecurityException; +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java new file mode 100644 index 000000000000..479da7ef03d3 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java @@ -0,0 +1,68 @@ +/* + * 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.GeneralSecurityException; +import java.security.KeyStore; +import java.util.Optional; + +import org.apache.zookeeper.util.PemReader; + +/** + * Implementation of {@link FileKeyStoreLoader} that loads from PEM files. + *

+ * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +class PEMFileLoader extends FileKeyStoreLoader { + private PEMFileLoader(String keyStorePath, + String trustStorePath, + char[] keyStorePassword, + char[] trustStorePassword) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); + } + + @Override + public KeyStore loadKeyStore() throws IOException, GeneralSecurityException { + Optional passwordOption; + if (keyStorePassword == null || keyStorePassword.length == 0) { + passwordOption = Optional.empty(); + } else { + passwordOption = Optional.of(String.valueOf(keyStorePassword)); + } + File file = new File(keyStorePath); + return PemReader.loadKeyStore(file, file, passwordOption); + } + + @Override + public KeyStore loadTrustStore() throws IOException, GeneralSecurityException { + return PemReader.loadTrustStore(new File(trustStorePath)); + } + + static class Builder extends FileKeyStoreLoader.Builder { + @Override + PEMFileLoader build() { + return new PEMFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); + } + } +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java new file mode 100644 index 000000000000..0d826fad1957 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java @@ -0,0 +1,43 @@ +/* + * 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; + +/** + * Implementation of {@link FileKeyStoreLoader} that loads from PKCS12 files. + *

+ * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +class PKCS12FileLoader extends StandardTypeFileKeyStoreLoader { + private PKCS12FileLoader(String keyStorePath, + String trustStorePath, + char[] keyStorePassword, + char[] trustStorePassword) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.PKCS12); + } + + static class Builder extends FileKeyStoreLoader.Builder { + @Override + PKCS12FileLoader build() { + return new PKCS12FileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); + } + } +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java new file mode 100644 index 000000000000..63dbc811506a --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java @@ -0,0 +1,80 @@ +/* + * 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.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; + +/** + * Base class for instances of {@link KeyStoreLoader} which load the key/trust + * stores from files on a filesystem using standard {@link KeyStore} types like + * JKS or PKCS12. + *

+ * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +abstract class StandardTypeFileKeyStoreLoader extends FileKeyStoreLoader { + private static final char[] EMPTY_CHAR_ARRAY = new char[0]; + + protected final SupportedStandardKeyFormat format; + + protected enum SupportedStandardKeyFormat { + JKS, PKCS12, BCFKS + } + + StandardTypeFileKeyStoreLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, + char[] trustStorePassword, SupportedStandardKeyFormat format) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); + this.format = format; + } + + @Override + public KeyStore loadKeyStore() throws IOException, GeneralSecurityException { + try (InputStream inputStream = Files.newInputStream(new File(keyStorePath).toPath())) { + KeyStore ks = keyStoreInstance(); + ks.load(inputStream, passwordStringToCharArray(keyStorePassword)); + return ks; + } + } + + @Override + public KeyStore loadTrustStore() throws IOException, GeneralSecurityException { + try (InputStream inputStream = Files.newInputStream(new File(trustStorePath).toPath())) { + KeyStore ts = keyStoreInstance(); + ts.load(inputStream, passwordStringToCharArray(trustStorePassword)); + return ts; + } + } + + private KeyStore keyStoreInstance() throws KeyStoreException { + return KeyStore.getInstance(format.name()); + } + + private static char[] passwordStringToCharArray(char[] password) { + return password == null ? EMPTY_CHAR_ARRAY : password; + } +} From 2aa1204f99f1eb9c2c66a6249365dc4ec74b7168 Mon Sep 17 00:00:00 2001 From: Andor Molnar Date: Thu, 1 Sep 2022 11:48:38 +0200 Subject: [PATCH 2/6] HBASE-27346. Add PEM reader, test and spotless fixes --- .../hbase/io/crypto/tls/BCFKSFileLoader.java | 27 ++- .../io/crypto/tls/FileKeyStoreLoader.java | 90 ++++---- .../FileKeyStoreLoaderBuilderProvider.java | 48 ++-- .../hbase/io/crypto/tls/JKSFileLoader.java | 26 +-- .../hbase/io/crypto/tls/KeyStoreLoader.java | 46 ++-- .../hbase/io/crypto/tls/PEMFileLoader.java | 52 ++--- .../hbase/io/crypto/tls/PKCS12FileLoader.java | 27 ++- .../hadoop/hbase/io/crypto/tls/PemReader.java | 214 ++++++++++++++++++ .../tls/StandardTypeFileKeyStoreLoader.java | 73 +++--- .../hadoop/hbase/io/crypto/tls/X509Util.java | 29 +-- .../hbase/io/crypto/tls/TestX509Util.java | 115 ++++++++-- .../hbase/io/crypto/tls/X509TestHelpers.java | 2 +- 12 files changed, 507 insertions(+), 242 deletions(-) create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java index b8c5338a26b1..a231848d9cd3 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java @@ -15,7 +15,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.hadoop.hbase.io.crypto.tls; /** @@ -23,21 +22,21 @@ *

* This file has been copied from the Apache ZooKeeper project. * @see Base - * revision + * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/BCFKSFileLoader.java">Base + * revision */ class BCFKSFileLoader extends StandardTypeFileKeyStoreLoader { - private BCFKSFileLoader(String keyStorePath, - String trustStorePath, - char[] keyStorePassword, - char[] trustStorePassword) { - super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.BCFKS); - } + private BCFKSFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, + char[] trustStorePassword) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, + SupportedStandardKeyFormat.BCFKS); + } - static class Builder extends FileKeyStoreLoader.Builder { - @Override - BCFKSFileLoader build() { - return new BCFKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); - } + static class Builder extends FileKeyStoreLoader.Builder { + @Override + BCFKSFileLoader build() { + return new BCFKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, + trustStorePassword); } + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java index f953e3bdf629..3a1740b4faf0 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java @@ -15,68 +15,66 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.hadoop.hbase.io.crypto.tls; import java.util.Objects; /** - * Base class for instances of {@link KeyStoreLoader} which load the key/trust - * stores from files on a filesystem. + * Base class for instances of {@link KeyStoreLoader} which load the key/trust stores from files on + * a filesystem. *

* This file has been copied from the Apache ZooKeeper project. * @see Base - * revision + * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoader.java">Base + * revision */ abstract class FileKeyStoreLoader implements KeyStoreLoader { - final String keyStorePath; - final String trustStorePath; - final char[] keyStorePassword; - final char[] trustStorePassword; - - FileKeyStoreLoader(String keyStorePath, - String trustStorePath, - char[] keyStorePassword, - char[] trustStorePassword) { - this.keyStorePath = keyStorePath; - this.trustStorePath = trustStorePath; - this.keyStorePassword = keyStorePassword; - this.trustStorePassword = trustStorePassword; - } + final String keyStorePath; + final String trustStorePath; + final char[] keyStorePassword; + final char[] trustStorePassword; - /** - * Base class for builder pattern used by subclasses. - * @param the subtype of FileKeyStoreLoader created by the Builder. - */ - static abstract class Builder { - String keyStorePath; - String trustStorePath; - char[] keyStorePassword; - char[] trustStorePassword; + FileKeyStoreLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, + char[] trustStorePassword) { + this.keyStorePath = keyStorePath; + this.trustStorePath = trustStorePath; + this.keyStorePassword = keyStorePassword; + this.trustStorePassword = trustStorePassword; + } - Builder() {} + /** + * Base class for builder pattern used by subclasses. + * @param the subtype of FileKeyStoreLoader created by the Builder. + */ + static abstract class Builder { + String keyStorePath; + String trustStorePath; + char[] keyStorePassword; + char[] trustStorePassword; - Builder setKeyStorePath(String keyStorePath) { - this.keyStorePath = Objects.requireNonNull(keyStorePath); - return this; - } + Builder() { + } - Builder setTrustStorePath(String trustStorePath) { - this.trustStorePath = Objects.requireNonNull(trustStorePath); - return this; - } + Builder setKeyStorePath(String keyStorePath) { + this.keyStorePath = Objects.requireNonNull(keyStorePath); + return this; + } - Builder setKeyStorePassword(char[] keyStorePassword) { - this.keyStorePassword = Objects.requireNonNull(keyStorePassword); - return this; - } + Builder setTrustStorePath(String trustStorePath) { + this.trustStorePath = Objects.requireNonNull(trustStorePath); + return this; + } - Builder setTrustStorePassword(char[] trustStorePassword) { - this.trustStorePassword = Objects.requireNonNull(trustStorePassword); - return this; - } + Builder setKeyStorePassword(char[] keyStorePassword) { + this.keyStorePassword = Objects.requireNonNull(keyStorePassword); + return this; + } - abstract T build(); + Builder setTrustStorePassword(char[] trustStorePassword) { + this.trustStorePassword = Objects.requireNonNull(trustStorePassword); + return this; } + + abstract T build(); + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java index dcf8e56db28d..5ab672659f3e 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java @@ -15,40 +15,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.hadoop.hbase.io.crypto.tls; import java.util.Objects; +import org.apache.yetus.audience.InterfaceAudience; /** * This file has been copied from the Apache ZooKeeper project. * @see Base - * revision + * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoaderBuilderProvider.java">Base + * revision */ +@InterfaceAudience.Private public class FileKeyStoreLoaderBuilderProvider { - /** - * Returns a {@link FileKeyStoreLoader.Builder} that can build a loader - * which loads keys and certs from files of the given - * {@link KeyStoreFileType}. - * - * @param type the file type to load keys/certs from. - * @return a new Builder. - */ - static FileKeyStoreLoader.Builder + /** + * Returns a {@link FileKeyStoreLoader.Builder} that can build a loader which loads keys and certs + * from files of the given {@link KeyStoreFileType}. + * @param type the file type to load keys/certs from. + * @return a new Builder. + */ + static FileKeyStoreLoader.Builder getBuilderForKeyStoreFileType(KeyStoreFileType type) { - switch (Objects.requireNonNull(type)) { - case JKS: - return new JKSFileLoader.Builder(); - case PEM: - return new PEMFileLoader.Builder(); - case PKCS12: - return new PKCS12FileLoader.Builder(); - case BCFKS: - return new BCFKSFileLoader.Builder(); - default: - throw new AssertionError( - "Unexpected StoreFileType: " + type.name()); - } + switch (Objects.requireNonNull(type)) { + case JKS: + return new JKSFileLoader.Builder(); + case PEM: + return new PEMFileLoader.Builder(); + case PKCS12: + return new PKCS12FileLoader.Builder(); + case BCFKS: + return new BCFKSFileLoader.Builder(); + default: + throw new AssertionError("Unexpected StoreFileType: " + type.name()); } + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java index 3d2339defa5c..7f52d939a9cd 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java @@ -15,7 +15,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.hadoop.hbase.io.crypto.tls; /** @@ -23,21 +22,20 @@ *

* This file has been copied from the Apache ZooKeeper project. * @see Base - * revision + * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/JKSFileLoader.java">Base + * revision */ class JKSFileLoader extends StandardTypeFileKeyStoreLoader { - private JKSFileLoader(String keyStorePath, - String trustStorePath, - char[] keyStorePassword, - char[] trustStorePassword) { - super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.JKS); - } + private JKSFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, + char[] trustStorePassword) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, + SupportedStandardKeyFormat.JKS); + } - static class Builder extends FileKeyStoreLoader.Builder { - @Override - JKSFileLoader build() { - return new JKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); - } + static class Builder extends FileKeyStoreLoader.Builder { + @Override + JKSFileLoader build() { + return new JKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); } + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java index f0c3a8356438..928b3b9d0469 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java @@ -15,7 +15,6 @@ * 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; @@ -27,31 +26,28 @@ *

* This file has been copied from the Apache ZooKeeper project. * @see Base - * revision + * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/KeyStoreLoader.java">Base + * revision */ interface KeyStoreLoader { - /** - * Loads a KeyStore which contains at least one private key and the - * associated X509 cert chain. - * - * @return a new KeyStore - * @throws IOException if loading the key store fails due to an IO error, - * such as "file not found". - * @throws GeneralSecurityException if loading the key store fails due to - * a security error, such as "unsupported crypto algorithm". - */ - KeyStore loadKeyStore() throws IOException, GeneralSecurityException; + /** + * Loads a KeyStore which contains at least one private key and the associated X509 cert chain. + * @return a new KeyStore + * @throws IOException if loading the key store fails due to an IO error, such as + * "file not found". + * @throws GeneralSecurityException if loading the key store fails due to a security error, such + * as "unsupported crypto algorithm". + */ + KeyStore loadKeyStore() throws IOException, GeneralSecurityException; - /** - * Loads a KeyStore which contains at least one X509 cert chain for a - * trusted Certificate Authority (CA). - * - * @return a new KeyStore - * @throws IOException if loading the trust store fails due to an IO error, - * such as "file not found". - * @throws GeneralSecurityException if loading the trust store fails due to - * a security error, such as "unsupported crypto algorithm". - */ - KeyStore loadTrustStore() throws IOException, GeneralSecurityException; + /** + * Loads a KeyStore which contains at least one X509 cert chain for a trusted Certificate + * Authority (CA). + * @return a new KeyStore + * @throws IOException if loading the trust store fails due to an IO error, such as + * "file not found". + * @throws GeneralSecurityException if loading the trust store fails due to a security error, such + * as "unsupported crypto algorithm". + */ + KeyStore loadTrustStore() throws IOException, GeneralSecurityException; } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java index 479da7ef03d3..4fa8334190af 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java @@ -15,54 +15,42 @@ * 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.GeneralSecurityException; import java.security.KeyStore; -import java.util.Optional; - -import org.apache.zookeeper.util.PemReader; /** * Implementation of {@link FileKeyStoreLoader} that loads from PEM files. *

* This file has been copied from the Apache ZooKeeper project. * @see Base - * revision + * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/PEMFileLoader.java">Base + * revision */ class PEMFileLoader extends FileKeyStoreLoader { - private PEMFileLoader(String keyStorePath, - String trustStorePath, - char[] keyStorePassword, - char[] trustStorePassword) { - super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); - } + private PEMFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, + char[] trustStorePassword) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); + } - @Override - public KeyStore loadKeyStore() throws IOException, GeneralSecurityException { - Optional passwordOption; - if (keyStorePassword == null || keyStorePassword.length == 0) { - passwordOption = Optional.empty(); - } else { - passwordOption = Optional.of(String.valueOf(keyStorePassword)); - } - File file = new File(keyStorePath); - return PemReader.loadKeyStore(file, file, passwordOption); - } + @Override + public KeyStore loadKeyStore() throws IOException, GeneralSecurityException { + File file = new File(keyStorePath); + return PemReader.loadKeyStore(file, file, keyStorePassword); + } - @Override - public KeyStore loadTrustStore() throws IOException, GeneralSecurityException { - return PemReader.loadTrustStore(new File(trustStorePath)); - } + @Override + public KeyStore loadTrustStore() throws IOException, GeneralSecurityException { + return PemReader.loadTrustStore(new File(trustStorePath)); + } - static class Builder extends FileKeyStoreLoader.Builder { - @Override - PEMFileLoader build() { - return new PEMFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); - } + static class Builder extends FileKeyStoreLoader.Builder { + @Override + PEMFileLoader build() { + return new PEMFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); } + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java index 0d826fad1957..b11c948e5877 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java @@ -15,7 +15,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.hadoop.hbase.io.crypto.tls; /** @@ -23,21 +22,21 @@ *

* This file has been copied from the Apache ZooKeeper project. * @see Base - * revision + * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/PKCS12FileLoader.java">Base + * revision */ class PKCS12FileLoader extends StandardTypeFileKeyStoreLoader { - private PKCS12FileLoader(String keyStorePath, - String trustStorePath, - char[] keyStorePassword, - char[] trustStorePassword) { - super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.PKCS12); - } + private PKCS12FileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, + char[] trustStorePassword) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, + SupportedStandardKeyFormat.PKCS12); + } - static class Builder extends FileKeyStoreLoader.Builder { - @Override - PKCS12FileLoader build() { - return new PKCS12FileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); - } + static class Builder extends FileKeyStoreLoader.Builder { + @Override + PKCS12FileLoader build() { + return new PKCS12FileLoader(keyStorePath, trustStorePath, keyStorePassword, + trustStorePassword); } + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java new file mode 100644 index 000000000000..5d40e5c8b437 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java @@ -0,0 +1,214 @@ +/* + * 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 static java.nio.charset.StandardCharsets.US_ASCII; +import static java.util.Base64.getMimeDecoder; +import static java.util.regex.Pattern.CASE_INSENSITIVE; +import static javax.crypto.Cipher.DECRYPT_MODE; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.crypto.Cipher; +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.security.auth.x500.X500Principal; + +/** + * This file has been copied from the Apache ZooKeeper project. + * @see Base + * revision + */ +final class PemReader { + private static final Pattern CERT_PATTERN = + Pattern.compile("-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" + // Header + "([a-z0-9+/=\\r\\n]+)" + // Base64 text + "-+END\\s+.*CERTIFICATE[^-]*-+", // Footer + CASE_INSENSITIVE); + + private static final Pattern PRIVATE_KEY_PATTERN = + Pattern.compile("-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header + "([a-z0-9+/=\\r\\n]+)" + // Base64 text + "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer + CASE_INSENSITIVE); + + private static final Pattern PUBLIC_KEY_PATTERN = + Pattern.compile("-+BEGIN\\s+.*PUBLIC\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header + "([a-z0-9+/=\\r\\n]+)" + // Base64 text + "-+END\\s+.*PUBLIC\\s+KEY[^-]*-+", // Footer + CASE_INSENSITIVE); + + private PemReader() { + } + + public static KeyStore loadTrustStore(File certificateChainFile) + throws IOException, GeneralSecurityException { + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, null); + + List certificateChain = readCertificateChain(certificateChainFile); + for (X509Certificate certificate : certificateChain) { + X500Principal principal = certificate.getSubjectX500Principal(); + keyStore.setCertificateEntry(principal.getName("RFC2253"), certificate); + } + return keyStore; + } + + public static KeyStore loadKeyStore(File certificateChainFile, File privateKeyFile, + char[] keyPassword) throws IOException, GeneralSecurityException { + PrivateKey key = loadPrivateKey(privateKeyFile, keyPassword); + + List certificateChain = readCertificateChain(certificateChainFile); + if (certificateChain.isEmpty()) { + throw new CertificateException( + "Certificate file does not contain any certificates: " + certificateChainFile); + } + + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, null); + keyStore.setKeyEntry("key", key, keyPassword, certificateChain.toArray(new Certificate[0])); + return keyStore; + } + + public static List readCertificateChain(File certificateChainFile) + throws IOException, GeneralSecurityException { + String contents = new String(Files.readAllBytes(certificateChainFile.toPath()), US_ASCII); + return readCertificateChain(contents); + } + + public static List readCertificateChain(String certificateChain) + throws CertificateException { + Matcher matcher = CERT_PATTERN.matcher(certificateChain); + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + List certificates = new ArrayList<>(); + + int start = 0; + while (matcher.find(start)) { + byte[] buffer = base64Decode(matcher.group(1)); + certificates.add( + (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(buffer))); + start = matcher.end(); + } + + return certificates; + } + + public static PrivateKey loadPrivateKey(File privateKeyFile, char[] keyPassword) + throws IOException, GeneralSecurityException { + String privateKey = new String(Files.readAllBytes(privateKeyFile.toPath()), US_ASCII); + return loadPrivateKey(privateKey, keyPassword); + } + + public static PrivateKey loadPrivateKey(String privateKey, char[] keyPassword) + throws IOException, GeneralSecurityException { + Matcher matcher = PRIVATE_KEY_PATTERN.matcher(privateKey); + if (!matcher.find()) { + throw new KeyStoreException("did not find a private key"); + } + byte[] encodedKey = base64Decode(matcher.group(1)); + + PKCS8EncodedKeySpec encodedKeySpec; + if (keyPassword != null && keyPassword.length > 0) { + EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); + SecretKeyFactory keyFactory = + SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); + SecretKey secretKey = keyFactory.generateSecret(new PBEKeySpec(keyPassword)); + + Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); + cipher.init(DECRYPT_MODE, secretKey, encryptedPrivateKeyInfo.getAlgParameters()); + + encodedKeySpec = encryptedPrivateKeyInfo.getKeySpec(cipher); + } else { + encodedKeySpec = new PKCS8EncodedKeySpec(encodedKey); + } + + // this code requires a key in PKCS8 format which is not the default openssl format + // to convert to the PKCS8 format you use : openssl pkcs8 -topk8 ... + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePrivate(encodedKeySpec); + } catch (InvalidKeySpecException ignore) { + } + + try { + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + return keyFactory.generatePrivate(encodedKeySpec); + } catch (InvalidKeySpecException ignore) { + } + + KeyFactory keyFactory = KeyFactory.getInstance("DSA"); + return keyFactory.generatePrivate(encodedKeySpec); + } + + public static PublicKey loadPublicKey(File publicKeyFile) + throws IOException, GeneralSecurityException { + String publicKey = new String(Files.readAllBytes(publicKeyFile.toPath()), US_ASCII); + return loadPublicKey(publicKey); + } + + public static PublicKey loadPublicKey(String publicKey) throws GeneralSecurityException { + Matcher matcher = PUBLIC_KEY_PATTERN.matcher(publicKey); + if (!matcher.find()) { + throw new KeyStoreException("did not find a public key"); + } + String data = matcher.group(1); + byte[] encodedKey = base64Decode(data); + + X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(encodedKey); + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePublic(encodedKeySpec); + } catch (InvalidKeySpecException ignore) { + } + + try { + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + return keyFactory.generatePublic(encodedKeySpec); + } catch (InvalidKeySpecException ignore) { + } + + KeyFactory keyFactory = KeyFactory.getInstance("DSA"); + return keyFactory.generatePublic(encodedKeySpec); + } + + private static byte[] base64Decode(String base64) { + return getMimeDecoder().decode(base64.getBytes(US_ASCII)); + } +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java index 63dbc811506a..67aebdd6c7bc 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java @@ -15,11 +15,9 @@ * 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.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -28,53 +26,54 @@ import java.security.KeyStoreException; /** - * Base class for instances of {@link KeyStoreLoader} which load the key/trust - * stores from files on a filesystem using standard {@link KeyStore} types like - * JKS or PKCS12. + * Base class for instances of {@link KeyStoreLoader} which load the key/trust stores from files on + * a filesystem using standard {@link KeyStore} types like JKS or PKCS12. *

* This file has been copied from the Apache ZooKeeper project. * @see Base - * revision + * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/StandardTypeFileKeyStoreLoader.java">Base + * revision */ abstract class StandardTypeFileKeyStoreLoader extends FileKeyStoreLoader { - private static final char[] EMPTY_CHAR_ARRAY = new char[0]; + private static final char[] EMPTY_CHAR_ARRAY = new char[0]; - protected final SupportedStandardKeyFormat format; + protected final SupportedStandardKeyFormat format; - protected enum SupportedStandardKeyFormat { - JKS, PKCS12, BCFKS - } + protected enum SupportedStandardKeyFormat { + JKS, + PKCS12, + BCFKS + } - StandardTypeFileKeyStoreLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, - char[] trustStorePassword, SupportedStandardKeyFormat format) { - super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); - this.format = format; - } + StandardTypeFileKeyStoreLoader(String keyStorePath, String trustStorePath, + char[] keyStorePassword, char[] trustStorePassword, SupportedStandardKeyFormat format) { + super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); + this.format = format; + } - @Override - public KeyStore loadKeyStore() throws IOException, GeneralSecurityException { - try (InputStream inputStream = Files.newInputStream(new File(keyStorePath).toPath())) { - KeyStore ks = keyStoreInstance(); - ks.load(inputStream, passwordStringToCharArray(keyStorePassword)); - return ks; - } + @Override + public KeyStore loadKeyStore() throws IOException, GeneralSecurityException { + try (InputStream inputStream = Files.newInputStream(new File(keyStorePath).toPath())) { + KeyStore ks = keyStoreInstance(); + ks.load(inputStream, passwordStringToCharArray(keyStorePassword)); + return ks; } + } - @Override - public KeyStore loadTrustStore() throws IOException, GeneralSecurityException { - try (InputStream inputStream = Files.newInputStream(new File(trustStorePath).toPath())) { - KeyStore ts = keyStoreInstance(); - ts.load(inputStream, passwordStringToCharArray(trustStorePassword)); - return ts; - } + @Override + public KeyStore loadTrustStore() throws IOException, GeneralSecurityException { + try (InputStream inputStream = Files.newInputStream(new File(trustStorePath).toPath())) { + KeyStore ts = keyStoreInstance(); + ts.load(inputStream, passwordStringToCharArray(trustStorePassword)); + return ts; } + } - private KeyStore keyStoreInstance() throws KeyStoreException { - return KeyStore.getInstance(format.name()); - } + private KeyStore keyStoreInstance() throws KeyStoreException { + return KeyStore.getInstance(format.name()); + } - private static char[] passwordStringToCharArray(char[] password) { - return password == null ? EMPTY_CHAR_ARRAY : password; - } + private static char[] passwordStringToCharArray(char[] password) { + return password == null ? EMPTY_CHAR_ARRAY : password; + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java index 76b7fad4c596..471ad41d06fb 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java @@ -17,10 +17,7 @@ */ package org.apache.hadoop.hbase.io.crypto.tls; -import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.Security; @@ -227,19 +224,16 @@ public static SslContext createSslContextForServer(Configuration config) static X509KeyManager createKeyManager(String keyStoreLocation, char[] keyStorePassword, String keyStoreType) throws KeyManagerException { - if (keyStoreType == null) { - keyStoreType = "jks"; - } - if (keyStorePassword == null) { keyStorePassword = EMPTY_CHAR_ARRAY; } try { - KeyStore ks = KeyStore.getInstance(keyStoreType); - try (InputStream inputStream = Files.newInputStream(new File(keyStoreLocation).toPath())) { - ks.load(inputStream, keyStorePassword); - } + KeyStoreFileType storeFileType = + KeyStoreFileType.fromPropertyValueOrFileName(keyStoreType, keyStoreLocation); + KeyStore ks = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(storeFileType) + .setKeyStorePath(keyStoreLocation).setKeyStorePassword(keyStorePassword).build() + .loadKeyStore(); KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); kmf.init(ks, keyStorePassword); @@ -272,19 +266,16 @@ static X509KeyManager createKeyManager(String keyStoreLocation, char[] keyStoreP static X509TrustManager createTrustManager(String trustStoreLocation, char[] trustStorePassword, String trustStoreType, boolean crlEnabled, boolean ocspEnabled) throws TrustManagerException { - if (trustStoreType == null) { - trustStoreType = "jks"; - } - if (trustStorePassword == null) { trustStorePassword = EMPTY_CHAR_ARRAY; } try { - KeyStore ts = KeyStore.getInstance(trustStoreType); - try (InputStream inputStream = Files.newInputStream(new File(trustStoreLocation).toPath())) { - ts.load(inputStream, trustStorePassword); - } + KeyStoreFileType storeFileType = + KeyStoreFileType.fromPropertyValueOrFileName(trustStoreType, trustStoreLocation); + KeyStore ts = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(storeFileType) + .setTrustStorePath(trustStoreLocation).setTrustStorePassword(trustStorePassword).build() + .loadTrustStore(); PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); if (crlEnabled || ocspEnabled) { 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 61134390e8a2..bb70c02946fb 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 @@ -36,6 +36,8 @@ 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; @@ -204,6 +206,69 @@ public void testCRLDisabled() throws Exception { assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable"))); } + @Test + public void testLoadPEMKeyStore() throws Exception { + // Make sure we can instantiate a key manager from the PEM file on disk + X509KeyManager km = X509Util.createKeyManager( + x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), + x509TestContext.getKeyStorePassword(), KeyStoreFileType.PEM.getPropertyValue()); + } + + @Test + public void testLoadPEMKeyStoreNullPassword() throws Exception { + assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); + // Make sure that empty password and null password are treated the same + X509KeyManager km = X509Util.createKeyManager( + x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, + KeyStoreFileType.PEM.getPropertyValue()); + } + + @Test + public void testLoadPEMKeyStoreAutodetectStoreFileType() throws Exception { + // Make sure we can instantiate a key manager from the PEM file on disk + X509KeyManager km = X509Util.createKeyManager( + x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), + x509TestContext.getKeyStorePassword(), + null /* null StoreFileType means 'autodetect from file extension' */); + } + + @Test(expected = KeyManagerException.class) + public void testLoadPEMKeyStoreWithWrongPassword() throws Exception { + // Attempting to load with the wrong key password should fail + X509KeyManager km = X509Util.createKeyManager( + x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), + "wrong password".toCharArray(), // intentionally use the wrong password + KeyStoreFileType.PEM.getPropertyValue()); + } + + @Test + public void testLoadPEMTrustStore() throws Exception { + // Make sure we can instantiate a trust manager from the PEM file on disk + X509TrustManager tm = X509Util.createTrustManager( + x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), + x509TestContext.getTrustStorePassword(), KeyStoreFileType.PEM.getPropertyValue(), false, + false); + } + + @Test + public void testLoadPEMTrustStoreNullPassword() throws Exception { + assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); + // Make sure that empty password and null password are treated the same + X509TrustManager tm = X509Util.createTrustManager( + x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, + KeyStoreFileType.PEM.getPropertyValue(), false, false); + } + + @Test + public void testLoadPEMTrustStoreAutodetectStoreFileType() throws Exception { + // Make sure we can instantiate a trust manager from the PEM file on disk + X509TrustManager tm = X509Util.createTrustManager( + x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), + x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from + // file extension' + false, false); + } + @Test public void testLoadJKSKeyStore() throws Exception { // Make sure we can instantiate a key manager from the JKS file on disk @@ -222,9 +287,9 @@ public void testLoadJKSKeyStoreNullPassword() throws Exception { } @Test - public void testLoadJKSKeyStoreFileTypeDefaultToJks() throws Exception { + public void testLoadJKSKeyStoreAutodetectStoreFileType() throws Exception { // Make sure we can instantiate a key manager from the JKS file on disk - X509Util.createKeyManager( + X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), x509TestContext.getKeyStorePassword(), null /* null StoreFileType means 'autodetect from file extension' */); @@ -258,16 +323,17 @@ public void testLoadJKSTrustStoreNullPassword() throws Exception { } @Test - public void testLoadJKSTrustStoreFileTypeDefaultToJks() throws Exception { + public void testLoadJKSTrustStoreAutodetectStoreFileType() throws Exception { // Make sure we can instantiate a trust manager from the JKS file on disk - X509Util.createTrustManager( + X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), - // null StoreFileType means 'autodetect from file extension' - x509TestContext.getTrustStorePassword(), null, true, true); + x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from + // file extension' + true, true); } @Test - public void testLoadJKSTrustStoreWithWrongPassword() throws Exception { + public void testLoadJKSTrustStoreWithWrongPassword() { assertThrows(TrustManagerException.class, () -> { // Attempting to load with the wrong key password should fail X509Util.createTrustManager( @@ -294,7 +360,16 @@ public void testLoadPKCS12KeyStoreNullPassword() throws Exception { } @Test - public void testLoadPKCS12KeyStoreWithWrongPassword() throws Exception { + public void testLoadPKCS12KeyStoreAutodetectStoreFileType() throws Exception { + // Make sure we can instantiate a key manager from the PKCS12 file on disk + X509KeyManager km = X509Util.createKeyManager( + x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), + x509TestContext.getKeyStorePassword(), + null /* null StoreFileType means 'autodetect from file extension' */); + } + + @Test + public void testLoadPKCS12KeyStoreWithWrongPassword() { assertThrows(KeyManagerException.class, () -> { // Attempting to load with the wrong key password should fail X509Util.createKeyManager( @@ -322,7 +397,17 @@ public void testLoadPKCS12TrustStoreNullPassword() throws Exception { } @Test - public void testLoadPKCS12TrustStoreWithWrongPassword() throws Exception { + public void testLoadPKCS12TrustStoreAutodetectStoreFileType() throws Exception { + // Make sure we can instantiate a trust manager from the PKCS12 file on disk + X509TrustManager tm = X509Util.createTrustManager( + x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), + x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from + // file extension' + true, true); + } + + @Test + public void testLoadPKCS12TrustStoreWithWrongPassword() { assertThrows(TrustManagerException.class, () -> { // Attempting to load with the wrong key password should fail X509Util.createTrustManager( @@ -332,42 +417,42 @@ public void testLoadPKCS12TrustStoreWithWrongPassword() throws Exception { } @Test - public void testGetDefaultCipherSuitesJava8() throws Exception { + public void testGetDefaultCipherSuitesJava8() { String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("1.8"); // Java 8 default should have the CBC suites first assertThat(cipherSuites[0], containsString("CBC")); } @Test - public void testGetDefaultCipherSuitesJava9() throws Exception { + public void testGetDefaultCipherSuitesJava9() { String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("9"); // Java 9+ default should have the GCM suites first assertThat(cipherSuites[0], containsString("GCM")); } @Test - public void testGetDefaultCipherSuitesJava10() throws Exception { + public void testGetDefaultCipherSuitesJava10() { String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("10"); // Java 9+ default should have the GCM suites first assertThat(cipherSuites[0], containsString("GCM")); } @Test - public void testGetDefaultCipherSuitesJava11() throws Exception { + public void testGetDefaultCipherSuitesJava11() { String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("11"); // Java 9+ default should have the GCM suites first assertThat(cipherSuites[0], containsString("GCM")); } @Test - public void testGetDefaultCipherSuitesUnknownVersion() throws Exception { + public void testGetDefaultCipherSuitesUnknownVersion() { String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("notaversion"); // If version can't be parsed, use the more conservative Java 8 default assertThat(cipherSuites[0], containsString("CBC")); } @Test - public void testGetDefaultCipherSuitesNullVersion() throws Exception { + public void testGetDefaultCipherSuitesNullVersion() { assertThrows(NullPointerException.class, () -> { X509Util.getDefaultCipherSuitesForJavaVersion(null); }); 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 1697dca8669b..42c798abcd9e 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 @@ -281,7 +281,7 @@ public static String pemEncodePrivateKey(PrivateKey key, char[] password) StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter); OutputEncryptor encryptor = null; - if (password != null) { + if (password != null && password.length > 0) { encryptor = new JceOpenSSLPKCS8EncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC) .setProvider(BouncyCastleProvider.PROVIDER_NAME).setRandom(PRNG).setPasssword(password) From 9dee9cf176cb8aae8eddaa3a911a9367fedf4b58 Mon Sep 17 00:00:00 2001 From: Andor Molnar Date: Thu, 1 Sep 2022 14:18:32 +0200 Subject: [PATCH 3/6] 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); From 52f4a82feef3c688b5c2a225e2874616cfe5c377 Mon Sep 17 00:00:00 2001 From: Andor Molnar Date: Thu, 1 Sep 2022 14:26:06 +0200 Subject: [PATCH 4/6] HBASE-27346. Removed unnecessary interface audience annotiations --- .../io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java index 5ab672659f3e..bcaccfebea07 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java @@ -18,7 +18,6 @@ package org.apache.hadoop.hbase.io.crypto.tls; import java.util.Objects; -import org.apache.yetus.audience.InterfaceAudience; /** * This file has been copied from the Apache ZooKeeper project. @@ -26,8 +25,7 @@ * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoaderBuilderProvider.java">Base * revision */ -@InterfaceAudience.Private -public class FileKeyStoreLoaderBuilderProvider { +class FileKeyStoreLoaderBuilderProvider { /** * Returns a {@link FileKeyStoreLoader.Builder} that can build a loader which loads keys and certs * from files of the given {@link KeyStoreFileType}. From 1a6a306a58d5049a6600906d5ca7ab9b205799d4 Mon Sep 17 00:00:00 2001 From: Andor Molnar Date: Sun, 4 Sep 2022 15:45:56 +0200 Subject: [PATCH 5/6] HBASE-27346. Checkstyle, error prone fixes --- .../hbase/io/crypto/tls/BCFKSFileLoader.java | 2 +- .../FileKeyStoreLoaderBuilderProvider.java | 6 ++++- .../hbase/io/crypto/tls/JKSFileLoader.java | 2 +- .../hbase/io/crypto/tls/PEMFileLoader.java | 2 +- .../hbase/io/crypto/tls/PKCS12FileLoader.java | 2 +- .../hadoop/hbase/io/crypto/tls/PemReader.java | 4 ++++ .../hbase/io/crypto/tls/TestX509Util.java | 22 +++++++++---------- 7 files changed, 24 insertions(+), 16 deletions(-) diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java index a231848d9cd3..cefa4135c90e 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java @@ -25,7 +25,7 @@ * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/BCFKSFileLoader.java">Base * revision */ -class BCFKSFileLoader extends StandardTypeFileKeyStoreLoader { +final class BCFKSFileLoader extends StandardTypeFileKeyStoreLoader { private BCFKSFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, char[] trustStorePassword) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java index bcaccfebea07..432c8a06d114 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java @@ -25,7 +25,7 @@ * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoaderBuilderProvider.java">Base * revision */ -class FileKeyStoreLoaderBuilderProvider { +final class FileKeyStoreLoaderBuilderProvider { /** * Returns a {@link FileKeyStoreLoader.Builder} that can build a loader which loads keys and certs * from files of the given {@link KeyStoreFileType}. @@ -47,4 +47,8 @@ class FileKeyStoreLoaderBuilderProvider { throw new AssertionError("Unexpected StoreFileType: " + type.name()); } } + + private FileKeyStoreLoaderBuilderProvider() { + // disabled + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java index 7f52d939a9cd..36e9643cbfe6 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java @@ -25,7 +25,7 @@ * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/JKSFileLoader.java">Base * revision */ -class JKSFileLoader extends StandardTypeFileKeyStoreLoader { +final class JKSFileLoader extends StandardTypeFileKeyStoreLoader { private JKSFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, char[] trustStorePassword) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java index 4fa8334190af..06d264b67da2 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java @@ -30,7 +30,7 @@ * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/PEMFileLoader.java">Base * revision */ -class PEMFileLoader extends FileKeyStoreLoader { +final class PEMFileLoader extends FileKeyStoreLoader { private PEMFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, char[] trustStorePassword) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java index b11c948e5877..ab5a532787e0 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java @@ -25,7 +25,7 @@ * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/PKCS12FileLoader.java">Base * revision */ -class PKCS12FileLoader extends StandardTypeFileKeyStoreLoader { +final class PKCS12FileLoader extends StandardTypeFileKeyStoreLoader { private PKCS12FileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword, char[] trustStorePassword) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java index 5d40e5c8b437..b4f7aa5565a2 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java @@ -165,12 +165,14 @@ public static PrivateKey loadPrivateKey(String privateKey, char[] keyPassword) KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(encodedKeySpec); } catch (InvalidKeySpecException ignore) { + // ignore } try { KeyFactory keyFactory = KeyFactory.getInstance("EC"); return keyFactory.generatePrivate(encodedKeySpec); } catch (InvalidKeySpecException ignore) { + // ignore } KeyFactory keyFactory = KeyFactory.getInstance("DSA"); @@ -196,12 +198,14 @@ public static PublicKey loadPublicKey(String publicKey) throws GeneralSecurityEx KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(encodedKeySpec); } catch (InvalidKeySpecException ignore) { + // ignore } try { KeyFactory keyFactory = KeyFactory.getInstance("EC"); return keyFactory.generatePublic(encodedKeySpec); } catch (InvalidKeySpecException ignore) { + // ignore } KeyFactory keyFactory = KeyFactory.getInstance("DSA"); 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 f98950381157..b61e314e4fd2 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 @@ -128,7 +128,7 @@ public void testCRLDisabled() throws Exception { @Test public void testLoadPEMKeyStore() throws Exception { // Make sure we can instantiate a key manager from the PEM file on disk - X509KeyManager km = X509Util.createKeyManager( + X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), x509TestContext.getKeyStorePassword(), KeyStoreFileType.PEM.getPropertyValue()); } @@ -137,7 +137,7 @@ public void testLoadPEMKeyStore() throws Exception { public void testLoadPEMKeyStoreNullPassword() throws Exception { assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); // Make sure that empty password and null password are treated the same - X509KeyManager km = X509Util.createKeyManager( + X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, KeyStoreFileType.PEM.getPropertyValue()); } @@ -145,7 +145,7 @@ public void testLoadPEMKeyStoreNullPassword() throws Exception { @Test public void testLoadPEMKeyStoreAutodetectStoreFileType() throws Exception { // Make sure we can instantiate a key manager from the PEM file on disk - X509KeyManager km = X509Util.createKeyManager( + X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), x509TestContext.getKeyStorePassword(), null /* null StoreFileType means 'autodetect from file extension' */); @@ -154,7 +154,7 @@ public void testLoadPEMKeyStoreAutodetectStoreFileType() throws Exception { @Test(expected = KeyManagerException.class) public void testLoadPEMKeyStoreWithWrongPassword() throws Exception { // Attempting to load with the wrong key password should fail - X509KeyManager km = X509Util.createKeyManager( + X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), "wrong password".toCharArray(), // intentionally use the wrong password KeyStoreFileType.PEM.getPropertyValue()); @@ -163,7 +163,7 @@ public void testLoadPEMKeyStoreWithWrongPassword() throws Exception { @Test public void testLoadPEMTrustStore() throws Exception { // Make sure we can instantiate a trust manager from the PEM file on disk - X509TrustManager tm = X509Util.createTrustManager( + X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), x509TestContext.getTrustStorePassword(), KeyStoreFileType.PEM.getPropertyValue(), false, false); @@ -173,7 +173,7 @@ public void testLoadPEMTrustStore() throws Exception { public void testLoadPEMTrustStoreNullPassword() throws Exception { assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY)); // Make sure that empty password and null password are treated the same - X509TrustManager tm = X509Util.createTrustManager( + X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, KeyStoreFileType.PEM.getPropertyValue(), false, false); } @@ -181,7 +181,7 @@ public void testLoadPEMTrustStoreNullPassword() throws Exception { @Test public void testLoadPEMTrustStoreAutodetectStoreFileType() throws Exception { // Make sure we can instantiate a trust manager from the PEM file on disk - X509TrustManager tm = X509Util.createTrustManager( + X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from // file extension' @@ -208,7 +208,7 @@ public void testLoadJKSKeyStoreNullPassword() throws Exception { @Test public void testLoadJKSKeyStoreAutodetectStoreFileType() throws Exception { // Make sure we can instantiate a key manager from the JKS file on disk - X509KeyManager km = X509Util.createKeyManager( + X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), x509TestContext.getKeyStorePassword(), null /* null StoreFileType means 'autodetect from file extension' */); @@ -244,7 +244,7 @@ public void testLoadJKSTrustStoreNullPassword() throws Exception { @Test public void testLoadJKSTrustStoreAutodetectStoreFileType() throws Exception { // Make sure we can instantiate a trust manager from the JKS file on disk - X509TrustManager tm = X509Util.createTrustManager( + X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from // file extension' @@ -281,7 +281,7 @@ public void testLoadPKCS12KeyStoreNullPassword() throws Exception { @Test public void testLoadPKCS12KeyStoreAutodetectStoreFileType() throws Exception { // Make sure we can instantiate a key manager from the PKCS12 file on disk - X509KeyManager km = X509Util.createKeyManager( + X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), x509TestContext.getKeyStorePassword(), null /* null StoreFileType means 'autodetect from file extension' */); @@ -318,7 +318,7 @@ public void testLoadPKCS12TrustStoreNullPassword() throws Exception { @Test public void testLoadPKCS12TrustStoreAutodetectStoreFileType() throws Exception { // Make sure we can instantiate a trust manager from the PKCS12 file on disk - X509TrustManager tm = X509Util.createTrustManager( + X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from // file extension' From c89d5d05535e120ec87d299a44601c7fe625acd5 Mon Sep 17 00:00:00 2001 From: Andor Molnar Date: Sun, 4 Sep 2022 21:32:01 +0200 Subject: [PATCH 6/6] HBASE-27346. Spotless fix --- .../org/apache/hadoop/hbase/io/crypto/tls/TestX509Util.java | 2 -- 1 file changed, 2 deletions(-) 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 b61e314e4fd2..a847db98a04a 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 @@ -31,8 +31,6 @@ import java.security.Security; import java.util.Arrays; import java.util.Collections; -import javax.net.ssl.X509KeyManager; -import javax.net.ssl.X509TrustManager; import org.apache.hadoop.hbase.HBaseClassTestRule; import org.apache.hadoop.hbase.exceptions.KeyManagerException; import org.apache.hadoop.hbase.exceptions.SSLContextException;