HBASE-27346 Autodetect key/truststore file type from file extension (#4757)

Signed-off-by: Duo Zhang <zhangduo@apache.org>
Signed-off-by: Bryan Beaudreault <bbeaudreault@apache.org>
This commit is contained in:
Andor Molnár 2022-09-06 21:43:41 +02:00 committed by GitHub
parent ee58f177f0
commit d2cc840edf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1647 additions and 126 deletions

View File

@ -0,0 +1,42 @@
/*
* 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.
* <p/>
* This file has been copied from the Apache ZooKeeper project.
* @see <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/BCFKSFileLoader.java">Base
* revision</a>
*/
final 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<BCFKSFileLoader> {
@Override
BCFKSFileLoader build() {
return new BCFKSFileLoader(keyStorePath, trustStorePath, keyStorePassword,
trustStorePassword);
}
}
}

View File

@ -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.util.Objects;
/**
* Base class for instances of {@link KeyStoreLoader} which load the key/trust stores from files on
* a filesystem.
* <p/>
* This file has been copied from the Apache ZooKeeper project.
* @see <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoader.java">Base
* revision</a>
*/
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 <T> the subtype of FileKeyStoreLoader created by the Builder.
*/
static abstract class Builder<T extends FileKeyStoreLoader> {
String keyStorePath;
String trustStorePath;
char[] keyStorePassword;
char[] trustStorePassword;
Builder() {
}
Builder<T> setKeyStorePath(String keyStorePath) {
this.keyStorePath = Objects.requireNonNull(keyStorePath);
return this;
}
Builder<T> setTrustStorePath(String trustStorePath) {
this.trustStorePath = Objects.requireNonNull(trustStorePath);
return this;
}
Builder<T> setKeyStorePassword(char[] keyStorePassword) {
this.keyStorePassword = Objects.requireNonNull(keyStorePassword);
return this;
}
Builder<T> setTrustStorePassword(char[] trustStorePassword) {
this.trustStorePassword = Objects.requireNonNull(trustStorePassword);
return this;
}
abstract T build();
}
}

View File

@ -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 <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoaderBuilderProvider.java">Base
* revision</a>
*/
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}.
* @param type the file type to load keys/certs from.
* @return a new Builder.
*/
static FileKeyStoreLoader.Builder<? extends FileKeyStoreLoader>
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());
}
}
private FileKeyStoreLoaderBuilderProvider() {
// disabled
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.
* <p/>
* This file has been copied from the Apache ZooKeeper project.
* @see <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/JKSFileLoader.java">Base
* revision</a>
*/
final 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<JKSFileLoader> {
@Override
JKSFileLoader build() {
return new JKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword);
}
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.
* <p/>
* This file has been copied from the Apache ZooKeeper project.
* @see <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/KeyStoreLoader.java">Base
* revision</a>
*/
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;
}

View File

@ -0,0 +1,56 @@
/*
* 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;
/**
* Implementation of {@link FileKeyStoreLoader} that loads from PEM files.
* <p/>
* This file has been copied from the Apache ZooKeeper project.
* @see <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/PEMFileLoader.java">Base
* revision</a>
*/
final 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 {
File file = new File(keyStorePath);
return PemReader.loadKeyStore(file, file, keyStorePassword);
}
@Override
public KeyStore loadTrustStore() throws IOException, GeneralSecurityException {
return PemReader.loadTrustStore(new File(trustStorePath));
}
static class Builder extends FileKeyStoreLoader.Builder<PEMFileLoader> {
@Override
PEMFileLoader build() {
return new PEMFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword);
}
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.
* <p/>
* This file has been copied from the Apache ZooKeeper project.
* @see <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/PKCS12FileLoader.java">Base
* revision</a>
*/
final 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<PKCS12FileLoader> {
@Override
PKCS12FileLoader build() {
return new PKCS12FileLoader(keyStorePath, trustStorePath, keyStorePassword,
trustStorePassword);
}
}
}

View File

@ -0,0 +1,218 @@
/*
* 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 <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/util/PemReader.java">Base
* revision</a>
*/
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<X509Certificate> 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<X509Certificate> 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<X509Certificate> readCertificateChain(File certificateChainFile)
throws IOException, GeneralSecurityException {
String contents = new String(Files.readAllBytes(certificateChainFile.toPath()), US_ASCII);
return readCertificateChain(contents);
}
public static List<X509Certificate> readCertificateChain(String certificateChain)
throws CertificateException {
Matcher matcher = CERT_PATTERN.matcher(certificateChain);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
List<X509Certificate> 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) {
// ignore
}
try {
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePrivate(encodedKeySpec);
} catch (InvalidKeySpecException ignore) {
// 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) {
// ignore
}
try {
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePublic(encodedKeySpec);
} catch (InvalidKeySpecException ignore) {
// ignore
}
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
return keyFactory.generatePublic(encodedKeySpec);
}
private static byte[] base64Decode(String base64) {
return getMimeDecoder().decode(base64.getBytes(US_ASCII));
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.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.
* <p/>
* This file has been copied from the Apache ZooKeeper project.
* @see <a href=
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/StandardTypeFileKeyStoreLoader.java">Base
* revision</a>
*/
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;
}
}

View File

@ -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 final class X509Util {
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 @@ public final class X509Util {
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) {

View File

@ -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).
* <p/>
* This base class takes care of setting up / cleaning up the test environment, and caching the
* X509TestContext objects used by the tests.
* <p/>
* This file has been copied from the Apache ZooKeeper project.
* @see <a href=
* "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/BaseX509ParameterizedTestCase.java">Base
* revision</a>
*/
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<Object[]> defaultParams() {
List<Object[]> 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());
}
}

View File

@ -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 <a href=
* "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/BCFKSFileLoaderTest.java">Base
* revision</a>
*/
@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();
}
}

View File

@ -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 <a href=
* "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/FileKeyStoreLoaderBuilderProviderTest.java">Base
* revision</a>
*/
@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);
}
}

View File

@ -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 <a href=
* "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/JKSFileLoaderTest.java">Base
* revision</a>
*/
@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();
}
}

View File

@ -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 <a href=
* "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/KeyStoreFileTypeTest.java">Base
* revision</a>
*/
@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");
}
}

View File

@ -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 <a href=
* "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/PEMFileLoaderTest.java">Base
* revision</a>
*/
@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());
}
}

View File

@ -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 <a href=
* "https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/test/java/org/apache/zookeeper/common/PKCS12FileLoaderTest.java">Base
* revision</a>
*/
@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();
}
}

View File

@ -28,28 +28,15 @@ import static org.junit.Assert.assertTrue;
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 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;
@ -66,83 +53,15 @@ import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslContext;
* revision</a>
*/
@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<Object[]> data() {
List<Object[]> 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);
@ -204,6 +123,69 @@ public class TestX509Util {
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
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
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
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
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
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
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
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,7 +204,7 @@ public class TestX509Util {
}
@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(
x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
@ -258,16 +240,17 @@ public class TestX509Util {
}
@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(
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 +277,16 @@ public class TestX509Util {
}
@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
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 +314,17 @@ public class TestX509Util {
}
@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
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 +334,42 @@ public class TestX509Util {
}
@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);
});

View File

@ -31,7 +31,6 @@ import java.security.cert.X509Certificate;
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 final class X509TestContext {
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 @@ public final class X509TestContext {
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 final class X509TestContext {
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 @@ public final class X509TestContext {
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 final class X509TestContext {
}
}
/**
* 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.

View File

@ -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 class X509TestContextProvider {
public X509TestContext get(X509KeyType caKeyType, X509KeyType certKeyType, char[] keyPassword) {
return ctxs.getUnchecked(new CacheKey(caKeyType, certKeyType, keyPassword));
}
static Collection<Object[]> defaultParams() {
List<Object[]> 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;
}
}

View File

@ -281,7 +281,7 @@ final class X509TestHelpers {
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)
@ -341,6 +341,23 @@ final class X509TestHelpers {
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 @@ final class X509TestHelpers {
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);