You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by xy...@apache.org on 2018/11/29 20:45:05 UTC
[49/50] [abbrv] hadoop git commit: HDDS-696. Bootstrap genesis
SCM(CA) with self-signed certificate. Contributed by Anu Engineer.
HDDS-696. Bootstrap genesis SCM(CA) with self-signed certificate.
Contributed by Anu Engineer.
Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/87f51d23
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/87f51d23
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/87f51d23
Branch: refs/heads/HDDS-4
Commit: 87f51d23d9899e46ac31058b726b272f1a7880ba
Parents: 9b9b8e4
Author: Anu Engineer <ae...@apache.org>
Authored: Tue Nov 27 15:02:07 2018 -0800
Committer: Xiaoyu Yao <xy...@apache.org>
Committed: Thu Nov 29 11:58:54 2018 -0800
----------------------------------------------------------------------
.../org/apache/hadoop/hdds/HddsConfigKeys.java | 57 ++-
.../hdds/security/x509/SecurityConfig.java | 84 ++++-
.../authority/CertificateServer.java | 11 +-
.../certificate/authority/DefaultCAServer.java | 373 +++++++++++++++++++
.../certificate/client/CertificateClient.java | 18 +-
.../certificate/utils/CertificateCodec.java | 280 ++++++++++++++
.../x509/certificate/utils/package-info.java | 22 ++
.../certificates/CertificateSignRequest.java | 245 ------------
.../certificates/SelfSignedCertificate.java | 212 -----------
.../x509/certificates/package-info.java | 22 --
.../utils/CertificateSignRequest.java | 245 ++++++++++++
.../utils/SelfSignedCertificate.java | 238 ++++++++++++
.../x509/certificates/utils/package-info.java | 22 ++
.../security/x509/keys/HDDSKeyGenerator.java | 32 +-
.../security/x509/keys/HDDSKeyPEMWriter.java | 255 -------------
.../hdds/security/x509/keys/KeyCodec.java | 337 +++++++++++++++++
.../apache/hadoop/ozone/common/StorageInfo.java | 2 +-
.../authority/TestDefaultCAServer.java | 118 ++++++
.../certificate/authority/package-info.java | 22 ++
.../certificate/utils/TestCertificateCodec.java | 218 +++++++++++
.../TestCertificateSignRequest.java | 19 +-
.../x509/certificates/TestRootCertificate.java | 46 +--
.../x509/certificates/package-info.java | 4 +-
.../x509/keys/TestHDDSKeyPEMWriter.java | 216 -----------
.../hdds/security/x509/keys/TestKeyCodec.java | 216 +++++++++++
.../hadoop/utils/db/TestDBStoreBuilder.java | 5 +-
.../hadoop/ozone/TestSecureOzoneCluster.java | 15 +-
27 files changed, 2267 insertions(+), 1067 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java
index 553f4aa..a02152d 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java
@@ -1,19 +1,18 @@
/**
- * 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
+ * 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
+ * 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.hdds;
@@ -25,61 +24,45 @@ import org.apache.hadoop.utils.db.DBProfile;
*/
public final class HddsConfigKeys {
- /**
- * Do not instantiate.
- */
- private HddsConfigKeys() {
- }
-
public static final String HDDS_HEARTBEAT_INTERVAL =
"hdds.heartbeat.interval";
public static final String HDDS_HEARTBEAT_INTERVAL_DEFAULT =
"30s";
-
public static final String HDDS_NODE_REPORT_INTERVAL =
"hdds.node.report.interval";
public static final String HDDS_NODE_REPORT_INTERVAL_DEFAULT =
"60s";
-
public static final String HDDS_CONTAINER_REPORT_INTERVAL =
"hdds.container.report.interval";
public static final String HDDS_CONTAINER_REPORT_INTERVAL_DEFAULT =
"60s";
-
public static final String HDDS_PIPELINE_REPORT_INTERVAL =
"hdds.pipeline.report.interval";
public static final String HDDS_PIPELINE_REPORT_INTERVAL_DEFAULT =
"60s";
-
public static final String HDDS_COMMAND_STATUS_REPORT_INTERVAL =
"hdds.command.status.report.interval";
public static final String HDDS_COMMAND_STATUS_REPORT_INTERVAL_DEFAULT =
"60s";
-
public static final String HDDS_CONTAINER_ACTION_MAX_LIMIT =
"hdds.container.action.max.limit";
public static final int HDDS_CONTAINER_ACTION_MAX_LIMIT_DEFAULT =
20;
-
public static final String HDDS_PIPELINE_ACTION_MAX_LIMIT =
"hdds.pipeline.action.max.limit";
public static final int HDDS_PIPELINE_ACTION_MAX_LIMIT_DEFAULT =
20;
-
// Configuration to allow volume choosing policy.
public static final String HDDS_DATANODE_VOLUME_CHOOSING_POLICY =
"hdds.datanode.volume.choosing.policy";
-
// DB Profiles used by ROCKDB instances.
public static final String HDDS_DB_PROFILE = "hdds.db.profile";
public static final DBProfile HDDS_DEFAULT_DB_PROFILE = DBProfile.DISK;
-
// Once a container usage crosses this threshold, it is eligible for
// closing.
public static final String HDDS_CONTAINER_CLOSE_THRESHOLD =
"hdds.container.close.threshold";
public static final float HDDS_CONTAINER_CLOSE_THRESHOLD_DEFAULT = 0.9f;
-
public static final String HDDS_SCM_CHILLMODE_ENABLED =
"hdds.scm.chillmode.enabled";
public static final boolean HDDS_SCM_CHILLMODE_ENABLED_DEFAULT = true;
@@ -97,11 +80,9 @@ public final class HddsConfigKeys {
public static final String HDDS_SCM_CHILLMODE_THRESHOLD_PCT =
"hdds.scm.chillmode.threshold.pct";
public static final double HDDS_SCM_CHILLMODE_THRESHOLD_PCT_DEFAULT = 0.99;
-
public static final String HDDS_LOCK_MAX_CONCURRENCY =
"hdds.lock.max.concurrency";
public static final int HDDS_LOCK_MAX_CONCURRENCY_DEFAULT = 100;
-
// This configuration setting is used as a fallback location by all
// Ozone/HDDS services for their metadata. It is useful as a single
// config point for test/PoC clusters.
@@ -121,7 +102,6 @@ public final class HddsConfigKeys {
public static final String HDDS_DEFAULT_SECURITY_PROVIDER = "BC";
public static final String HDDS_KEY_DIR_NAME = "hdds.key.dir.name";
public static final String HDDS_KEY_DIR_NAME_DEFAULT = "keys";
-
// TODO : Talk to StorageIO classes and see if they can return a secure
// storage location for each node.
public static final String HDDS_METADATA_DIR_NAME = "hdds.metadata.dir";
@@ -131,7 +111,6 @@ public final class HddsConfigKeys {
public static final String HDDS_PUBLIC_KEY_FILE_NAME = "hdds.public.key.file"
+ ".name";
public static final String HDDS_PUBLIC_KEY_FILE_NAME_DEFAULT = "public.pem";
-
/**
* Maximum duration of certificates issued by SCM including Self-Signed Roots.
* The formats accepted are based on the ISO-8601 duration format PnDTnHnMn.nS
@@ -140,12 +119,20 @@ public final class HddsConfigKeys {
public static final String HDDS_X509_MAX_DURATION = "hdds.x509.max.duration";
// Limit Certificate duration to a max value of 5 years.
public static final String HDDS_X509_MAX_DURATION_DEFAULT= "P1865D";
-
public static final String HDDS_X509_SIGNATURE_ALGO =
"hdds.x509.signature.algorithm";
public static final String HDDS_X509_SIGNATURE_ALGO_DEFAULT = "SHA256withRSA";
-
- public static final String HDDS_GRPC_BLOCK_TOKEN_ENABLED = "hdds.grpc.block" +
- ".token.enabled";
+ public static final String HDDS_GRPC_BLOCK_TOKEN_ENABLED =
+ "hdds.grpc.block.token.enabled";
public static final boolean HDDS_GRPC_BLOCK_TOKEN_ENABLED_DEFAULT = false;
+ public static final String HDDS_X509_DIR_NAME = "hdds.x509.dir.name";
+ public static final String HDDS_X509_DIR_NAME_DEFAULT = "certs";
+ public static final String HDDS_X509_FILE_NAME = "hdds.x509.file.name";
+ public static final String HDDS_X509_FILE_NAME_DEFAULT = "certificate.crt";
+
+ /**
+ * Do not instantiate.
+ */
+ private HddsConfigKeys() {
+ }
}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
index 2826e55..ee20a21 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
@@ -46,6 +46,10 @@ import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_PRIVATE_KEY_FILE_NAME_D
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_PUBLIC_KEY_FILE_NAME;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_PUBLIC_KEY_FILE_NAME_DEFAULT;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_SECURITY_PROVIDER;
+import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_DIR_NAME;
+import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_DIR_NAME_DEFAULT;
+import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_FILE_NAME;
+import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_FILE_NAME_DEFAULT;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_MAX_DURATION;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_MAX_DURATION_DEFAULT;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_SIGNATURE_ALGO;
@@ -54,7 +58,7 @@ import static org.apache.hadoop.hdds.HddsConfigKeys.OZONE_METADATA_DIRS;
/**
* A class that deals with all Security related configs in HDDS.
- *
+ * <p>
* This class allows security configs to be read and used consistently across
* all of security related code base.
*/
@@ -73,6 +77,8 @@ public class SecurityConfig {
private final Duration certDuration;
private final String x509SignatureAlgo;
private final Boolean grpcBlockTokenEnabled;
+ private final String certificateDir;
+ private final String certificateFileName;
/**
* Constructs a SecurityConfig.
@@ -108,6 +114,10 @@ public class SecurityConfig {
this.certDuration = Duration.parse(durationString);
this.x509SignatureAlgo = this.configuration.get(HDDS_X509_SIGNATURE_ALGO,
HDDS_X509_SIGNATURE_ALGO_DEFAULT);
+ this.certificateDir = this.configuration.get(HDDS_X509_DIR_NAME,
+ HDDS_X509_DIR_NAME_DEFAULT);
+ this.certificateFileName = this.configuration.get(HDDS_X509_FILE_NAME,
+ HDDS_X509_FILE_NAME_DEFAULT);
this.grpcBlockTokenEnabled = this.configuration.getBoolean(
HDDS_GRPC_BLOCK_TOKEN_ENABLED,
@@ -127,8 +137,17 @@ public class SecurityConfig {
}
/**
- * Returns the public key file name, This is used for storing the public
- * keys on disk.
+ * Returns the Standard Certificate file name.
+ *
+ * @return String - Name of the Certificate File.
+ */
+ public String getCertificateFileName() {
+ return certificateFileName;
+ }
+
+ /**
+ * Returns the public key file name, This is used for storing the public keys
+ * on disk.
*
* @return String, File name used for public keys.
*/
@@ -137,8 +156,8 @@ public class SecurityConfig {
}
/**
- * Returns the private key file name.This is used for storing the private
- * keys on disk.
+ * Returns the private key file name.This is used for storing the private keys
+ * on disk.
*
* @return String, File name used for private keys.
*/
@@ -149,16 +168,47 @@ public class SecurityConfig {
/**
* Returns the File path to where keys are stored.
*
- * @return String Key location.
+ * @return path Key location.
*/
public Path getKeyLocation() {
return Paths.get(metadatDir, keyDir);
}
/**
+ * Returns the File path to where keys are stored with an additional component
+ * name inserted in between.
+ *
+ * @param component - Component Name - String.
+ * @return Path location.
+ */
+ public Path getKeyLocation(String component) {
+ return Paths.get(metadatDir, component, keyDir);
+ }
+
+ /**
+ * Returns the File path to where keys are stored.
+ *
+ * @return path Key location.
+ */
+ public Path getCertificateLocation() {
+ return Paths.get(metadatDir, certificateDir);
+ }
+
+ /**
+ * Returns the File path to where keys are stored with an addition component
+ * name inserted in between.
+ *
+ * @param component - Component Name - String.
+ * @return Path location.
+ */
+ public Path getCertificateLocation(String component) {
+ return Paths.get(metadatDir, component, certificateDir);
+ }
+
+ /**
* Gets the Key Size, The default key size is 2048, since the default
- * algorithm used is RSA. User can change this by setting the "hdds.key
- * .len" in configuration.
+ * algorithm used is RSA. User can change this by setting the "hdds.key.len"
+ * in configuration.
*
* @return key size.
*/
@@ -177,8 +227,8 @@ public class SecurityConfig {
}
/**
- * Returns the Key generation Algorithm used. User can change this by
- * setting the "hdds.key.algo" in configuration.
+ * Returns the Key generation Algorithm used. User can change this by setting
+ * the "hdds.key.algo" in configuration.
*
* @return String Algo.
*/
@@ -188,8 +238,8 @@ public class SecurityConfig {
/**
* Returns the X.509 Signature Algorithm used. This can be changed by setting
- * "hdds.x509.signature.algorithm" to the new name. The default algorithm
- * is SHA256withRSA.
+ * "hdds.x509.signature.algorithm" to the new name. The default algorithm is
+ * SHA256withRSA.
*
* @return String
*/
@@ -207,11 +257,11 @@ public class SecurityConfig {
}
/**
- * Returns the maximum length a certificate can be valid in SCM. The
- * default value is 5 years. This can be changed by setting
- * "hdds.x509.max.duration" in configuration. The formats accepted are
- * based on the ISO-8601 duration format PnDTnHnMn.nS
- *
+ * Returns the maximum length a certificate can be valid in SCM. The default
+ * value is 5 years. This can be changed by setting "hdds.x509.max.duration"
+ * in configuration. The formats accepted are based on the ISO-8601 duration
+ * format PnDTnHnMn.nS
+ * <p>
* Default value is 5 years and written as P1865D.
*
* @return Duration.
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java
index 5b5deef..ee685c8 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java
@@ -20,10 +20,12 @@
package org.apache.hadoop.hdds.security.x509.certificate.authority;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
-import org.apache.hadoop.hdds.security.x509.certificates.CertificateSignRequest;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.hdds.security.x509.certificates.utils.CertificateSignRequest;
import org.bouncycastle.cert.X509CertificateHolder;
+import java.io.IOException;
+import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.Future;
@@ -47,16 +49,19 @@ public interface CertificateServer {
* Returns the CA Certificate for this CA.
*
* @return X509CertificateHolder - Certificate for this CA.
- * @throws SCMSecurityException -- usually thrown if this CA is not
+ * @throws CertificateException - usually thrown if this CA is not
* initialized.
+ * @throws IOException - on Error.
*/
X509CertificateHolder getCACertificate()
- throws SCMSecurityException;
+ throws CertificateException, IOException;
/**
* Request a Certificate based on Certificate Signing Request.
*
* @param csr - Certificate Signing Request.
+ * @param approver - An Enum which says what kind of approval process to
+ * follow.
* @return A future that will have this certificate when this request is
* approved.
* @throws SCMSecurityException - on Error.
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
new file mode 100644
index 0000000..f6e6e5c
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
@@ -0,0 +1,373 @@
+/*
+ * 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.hdds.security.x509.certificate.authority;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
+import org.apache.hadoop.hdds.security.x509.certificates.utils.CertificateSignRequest;
+import org.apache.hadoop.hdds.security.x509.certificates.utils.SelfSignedCertificate;
+import org.apache.hadoop.hdds.security.x509.keys.HDDSKeyGenerator;
+import org.apache.hadoop.hdds.security.x509.keys.KeyCodec;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.concurrent.Future;
+import java.util.function.Consumer;
+
+/**
+ * The default CertificateServer used by SCM. This has no dependencies on any
+ * external system, this allows us to bootstrap a CertificateServer from
+ * Scratch.
+ * <p>
+ * Details =======
+ * <p>
+ * The Default CA server is one of the many possible implementations of an SCM
+ * Certificate Authority.
+ * <p>
+ * A certificate authority needs the Root Certificates and its private key to
+ * operate. The init function of the DefaultCA Server detects four possible
+ * states the System can be in.
+ * <p>
+ * 1. Success - This means that the expected Certificates and Keys are in
+ * place, and the CA was able to read those files into memory.
+ * <p>
+ * 2. Missing Keys - This means that private keys are missing. This is an error
+ * state which SCM CA cannot recover from. The cluster might have been
+ * initialized earlier and for some reason, we are not able to find the private
+ * keys for the CA. Eventually we will have 2 ways to recover from this state,
+ * first one is to copy the SCM CA private keys from a backup. Second one is to
+ * rekey the whole cluster. Both of these are improvements we will support in
+ * future.
+ * <p>
+ * 3. Missing Certificate - Similar to Missing Keys, but the root certificates
+ * are missing.
+ * <p>
+ * 4. Initialize - We don't have keys or certificates. DefaultCA assumes that
+ * this is a system bootup and will generate the keys and certificates
+ * automatically.
+ * <p>
+ * The init() follows the following logic,
+ * <p>
+ * 1. Compute the Verification Status -- Success, Missing Keys, Missing Certs or
+ * Initialize.
+ * <p>
+ * 2. ProcessVerificationStatus - Returns a Lambda, based on the Verification
+ * Status.
+ * <p>
+ * 3. Invoke the Lambda function.
+ * <p>
+ * At the end of the init function, we have functional CA. This function can be
+ * invoked as many times since we will regenerate the keys and certs only if
+ * both of them are missing.
+ */
+public class DefaultCAServer implements CertificateServer {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(DefaultCAServer.class);
+ private final String subject;
+ private final String clusterID;
+ private final String scmID;
+ private String componentName = Paths.get("scm", "ca").toString();
+ private Path caKeysPath;
+ private Path caRootX509Path;
+ private SecurityConfig config;
+
+ /**
+ * Create an Instance of DefaultCAServer.
+ *
+ * @param subject - String Subject
+ * @param clusterID - String ClusterID
+ * @param scmID - String SCMID.
+ */
+ public DefaultCAServer(String subject, String clusterID, String scmID) {
+ this.subject = subject;
+ this.clusterID = clusterID;
+ this.scmID = scmID;
+ }
+
+ @Override
+ public void init(SecurityConfig securityConfig, CAType type)
+ throws SCMSecurityException {
+ caKeysPath = securityConfig.getKeyLocation(componentName);
+ caRootX509Path = securityConfig.getCertificateLocation(componentName);
+ this.config = securityConfig;
+
+ /* In future we will spilt this code to have different kind of CAs.
+ * Right now, we have only self-signed CertificateServer.
+ */
+
+ if (type == CAType.SELF_SIGNED_CA) {
+ VerificationStatus status = verifySelfSignedCA(securityConfig);
+ Consumer<SecurityConfig> caInitializer =
+ processVerificationStatus(status);
+ caInitializer.accept(securityConfig);
+ return;
+ }
+
+ LOG.error("We support only Self-Signed CAs for now.");
+ throw new IllegalStateException("Not implemented functionality requested.");
+ }
+
+ @Override
+ public X509CertificateHolder getCACertificate() throws
+ CertificateException, IOException {
+ CertificateCodec certificateCodec =
+ new CertificateCodec(config, componentName);
+ return certificateCodec.readCertificate();
+ }
+
+ @Override
+ public Future<X509CertificateHolder> requestCertificate(
+ CertificateSignRequest csr, CertificateApprover approver)
+ throws SCMSecurityException {
+ return null;
+ }
+
+ @Override
+ public Future<Boolean> revokeCertificate(X509Certificate certificate,
+ CertificateApprover approver) throws SCMSecurityException {
+ return null;
+ }
+
+ /**
+ * Generates a Self Signed CertificateServer. These are the steps in
+ * generating a Self-Signed CertificateServer.
+ * <p>
+ * 1. Generate a Private/Public Key Pair. 2. Persist to a protected location.
+ * 3. Generate a SelfSigned Root CertificateServer certificate.
+ *
+ * @param securityConfig - Config.
+ */
+ private void generateSelfSignedCA(SecurityConfig securityConfig) throws
+ NoSuchAlgorithmException, NoSuchProviderException, IOException {
+ KeyPair keyPair = generateKeys(securityConfig);
+ generateRootCertificate(securityConfig, keyPair);
+ }
+
+ /**
+ * Verify Self-Signed CertificateServer. 1. Check if the Certificate exist. 2.
+ * Check if the key pair exists.
+ *
+ * @param securityConfig -- Config
+ * @return Verification Status
+ */
+ private VerificationStatus verifySelfSignedCA(SecurityConfig securityConfig) {
+ /*
+ The following is the truth table for the States.
+ True means we have that file False means it is missing.
+ +--------------+--------+--------+--------------+
+ | Certificates | Keys | Result | Function |
+ +--------------+--------+--------+--------------+
+ | True | True | True | Success |
+ | False | False | True | Initialize |
+ | True | False | False | Missing Key |
+ | False | True | False | Missing Cert |
+ +--------------+--------+--------+--------------+
+
+ This truth table maps to ~(certs xor keys) or certs == keys
+ */
+ boolean keyStatus = checkIfKeysExist();
+ boolean certStatus = checkIfCertificatesExist();
+
+ if ((certStatus == keyStatus) && (certStatus)) {
+ return VerificationStatus.SUCCESS;
+ }
+
+ if ((certStatus == keyStatus) && (!certStatus)) {
+ return VerificationStatus.INITIALIZE;
+ }
+
+ // At this point certStatus is not equal to keyStatus.
+ if (certStatus) {
+ return VerificationStatus.MISSING_KEYS;
+ }
+
+ return VerificationStatus.MISSING_CERTIFICATE;
+ }
+
+ /**
+ * Returns Keys status.
+ *
+ * @return True if the key files exist.
+ */
+ private boolean checkIfKeysExist() {
+ if (!Files.exists(caKeysPath)) {
+ return false;
+ }
+
+ if (!Files.exists(Paths.get(caKeysPath.toString(),
+ this.config.getPrivateKeyFileName()))) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns certificate Status.
+ *
+ * @return True if the Certificate files exist.
+ */
+ private boolean checkIfCertificatesExist() {
+ if (!Files.exists(caRootX509Path)) {
+ return false;
+ }
+ if (!Files.exists(Paths.get(caRootX509Path.toString(),
+ this.config.getCertificateFileName()))) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Based on the Status of the verification, we return a lambda that gets
+ * executed by the init function of the CA.
+ *
+ * @param status - Verification Status.
+ */
+ @VisibleForTesting
+ Consumer<SecurityConfig> processVerificationStatus(
+ VerificationStatus status) {
+ Consumer<SecurityConfig> consumer = null;
+ switch (status) {
+ case SUCCESS:
+ consumer = (arg) -> LOG.info("CertificateServer validation is " +
+ "successful");
+ break;
+ case MISSING_KEYS:
+ consumer = (arg) -> {
+ LOG.error("We have found the Certificate for this CertificateServer, " +
+ "but keys used by this CertificateServer is missing. This is a " +
+ "non-recoverable error. Please restart the system after locating " +
+ "the Keys used by the CertificateServer.");
+ LOG.error("Exiting due to unrecoverable CertificateServer error.");
+ throw new IllegalStateException("Missing Keys, cannot continue.");
+ };
+ break;
+ case MISSING_CERTIFICATE:
+ consumer = (arg) -> {
+ LOG.error("We found the keys, but the root certificate for this " +
+ "CertificateServer is missing. Please restart SCM after locating " +
+ "the " +
+ "Certificates.");
+ LOG.error("Exiting due to unrecoverable CertificateServer error.");
+ throw new IllegalStateException("Missing Root Certs, cannot continue.");
+ };
+ break;
+ case INITIALIZE:
+ consumer = (arg) -> {
+ try {
+ generateSelfSignedCA(arg);
+ } catch (NoSuchProviderException | NoSuchAlgorithmException
+ | IOException e) {
+ LOG.error("Unable to initialize CertificateServer.", e);
+ }
+ VerificationStatus newStatus = verifySelfSignedCA(arg);
+ if (newStatus != VerificationStatus.SUCCESS) {
+ LOG.error("Unable to initialize CertificateServer, failed in " +
+ "verification.");
+ }
+ };
+ break;
+ default:
+ /* Make CheckStyle happy */
+ break;
+ }
+ return consumer;
+ }
+
+ /**
+ * Generates a KeyPair for the Certificate.
+ *
+ * @param securityConfig - SecurityConfig.
+ * @return Key Pair.
+ * @throws NoSuchProviderException - on Error.
+ * @throws NoSuchAlgorithmException - on Error.
+ * @throws IOException - on Error.
+ */
+ private KeyPair generateKeys(SecurityConfig securityConfig)
+ throws NoSuchProviderException, NoSuchAlgorithmException, IOException {
+ HDDSKeyGenerator keyGenerator = new HDDSKeyGenerator(securityConfig);
+ KeyPair keys = keyGenerator.generateKey();
+ KeyCodec keyPEMWriter = new KeyCodec(securityConfig,
+ componentName);
+ keyPEMWriter.writeKey(keys);
+ return keys;
+ }
+
+ /**
+ * Generates a self-signed Root Certificate for CA.
+ *
+ * @param securityConfig - SecurityConfig
+ * @param key - KeyPair.
+ * @throws IOException - on Error.
+ * @throws SCMSecurityException - on Error.
+ */
+ private void generateRootCertificate(SecurityConfig securityConfig,
+ KeyPair key) throws IOException, SCMSecurityException {
+ Preconditions.checkNotNull(this.config);
+ LocalDate beginDate = LocalDate.now().atStartOfDay().toLocalDate();
+ LocalDateTime temp = LocalDateTime.of(beginDate, LocalTime.MIDNIGHT);
+ LocalDate endDate =
+ temp.plus(securityConfig.getMaxCertificateDuration()).toLocalDate();
+ X509CertificateHolder selfSignedCertificate =
+ SelfSignedCertificate
+ .newBuilder()
+ .setSubject(this.subject)
+ .setScmID(this.scmID)
+ .setClusterID(this.clusterID)
+ .setBeginDate(beginDate)
+ .setEndDate(endDate)
+ .makeCA()
+ .setConfiguration(securityConfig.getConfiguration())
+ .setKey(key)
+ .build();
+
+ CertificateCodec certCodec =
+ new CertificateCodec(config, componentName);
+ certCodec.writeCertificate(selfSignedCertificate);
+ }
+
+ /**
+ * This represents the verification status of the CA. Based on this enum
+ * appropriate action is taken in the Init.
+ */
+ @VisibleForTesting
+ enum VerificationStatus {
+ SUCCESS, /* All artifacts needed by CertificateServer is present */
+ MISSING_KEYS, /* Private key is missing, certificate Exists.*/
+ MISSING_CERTIFICATE, /* Keys exist, but root certificate missing.*/
+ INITIALIZE /* All artifacts are missing, we should init the system. */
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
index bfc0576..e33c9b6 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
@@ -19,7 +19,7 @@
package org.apache.hadoop.hdds.security.x509.certificate.client;
-import org.apache.hadoop.hdds.security.x509.certificates.CertificateSignRequest;
+import org.apache.hadoop.hdds.security.x509.certificates.utils.CertificateSignRequest;
import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
import java.io.InputStream;
@@ -64,7 +64,7 @@ public interface CertificateClient {
/**
* Verifies if this certificate is part of a trusted chain.
- *
+ * @param certificate - certificate.
* @return true if it trusted, false otherwise.
*/
boolean verifyCertificate(X509Certificate certificate);
@@ -74,7 +74,9 @@ public interface CertificateClient {
* key.
*
* @param stream - Data stream to sign.
+ * @param component - name of the component.
* @return byte array - containing the signature.
+ * @throws CertificateException - on Error.
*/
byte[] signDataStream(InputStream stream, String component)
throws CertificateException;
@@ -82,6 +84,7 @@ public interface CertificateClient {
/**
* Verifies a digital Signature, given the signature and the certificate of
* the signer.
+ *
* @param stream - Data Stream.
* @param signature - Byte Array containing the signature.
* @param cert - Certificate of the Signer.
@@ -123,7 +126,7 @@ public interface CertificateClient {
*
* @param key - private key
* @param component - name of the component.
- * @throws CertificateException
+ * @throws CertificateException - on Error.
*/
void storePrivateKey(PrivateKey key, String component)
throws CertificateException;
@@ -132,7 +135,8 @@ public interface CertificateClient {
* Stores the public key of a specified component.
*
* @param key - public key
- * @throws CertificateException
+ * @param component - name of the component.
+ * @throws CertificateException - on Error.
*/
void storePublicKey(PublicKey key, String component)
throws CertificateException;
@@ -142,7 +146,7 @@ public interface CertificateClient {
*
* @param certificate - X509 Certificate
* @param component - Name of the component.
- * @throws CertificateException
+ * @throws CertificateException - on Error.
*/
void storeCertificate(X509Certificate certificate, String component)
throws CertificateException;
@@ -152,7 +156,7 @@ public interface CertificateClient {
*
* @param certStore - Cert Store.
* @param component - Trust Chain.
- * @throws CertificateException
+ * @throws CertificateException - on Error.
*/
void storeTrustChain(CertStore certStore,
String component) throws CertificateException;
@@ -162,7 +166,7 @@ public interface CertificateClient {
*
* @param certificates - List of Certificates.
* @param component - String component.
- * @throws CertificateException
+ * @throws CertificateException - on Error.
*/
void storeTrustChain(List<X509Certificate> certificates,
String component) throws CertificateException;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/CertificateCodec.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/CertificateCodec.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/CertificateCodec.java
new file mode 100644
index 0000000..b2a37b5
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/CertificateCodec.java
@@ -0,0 +1,280 @@
+/*
+ * 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.hdds.security.x509.certificate.utils;
+
+import com.google.common.base.Preconditions;
+import org.apache.commons.io.IOUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
+
+/**
+ * A class used to read and write X.509 certificates PEM encoded Streams.
+ */
+public class CertificateCodec {
+ public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
+ public static final String END_CERT = "-----END CERTIFICATE-----";
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(CertificateCodec.class);
+ private static final JcaX509CertificateConverter CERTIFICATE_CONVERTER
+ = new JcaX509CertificateConverter();
+ private final SecurityConfig securityConfig;
+ private final Path location;
+ private Set<PosixFilePermission> permissionSet =
+ Stream.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)
+ .collect(Collectors.toSet());
+ /**
+ * Creates an CertificateCodec.
+ *
+ * @param config - Security Config.
+ * @param component - Component String.
+ */
+ public CertificateCodec(SecurityConfig config, String component) {
+ this.securityConfig = config;
+ this.location = securityConfig.getCertificateLocation(component);
+ }
+
+ /**
+ * Creates an CertificateCodec.
+ *
+ * @param configuration - Configuration
+ */
+ public CertificateCodec(Configuration configuration) {
+ Preconditions.checkNotNull(configuration, "Config cannot be null");
+ this.securityConfig = new SecurityConfig(configuration);
+ this.location = securityConfig.getCertificateLocation();
+ }
+
+ /**
+ * Returns a X509 Certificate from the Certificate Holder.
+ *
+ * @param holder - Holder
+ * @return X509Certificate.
+ * @throws CertificateException - on Error.
+ */
+ public static X509Certificate getX509Certificate(X509CertificateHolder holder)
+ throws CertificateException {
+ return CERTIFICATE_CONVERTER.getCertificate(holder);
+ }
+
+ /**
+ * Get Certificate location.
+ *
+ * @return Path
+ */
+ public Path getLocation() {
+ return location;
+ }
+
+ /**
+ * Returns the Certificate as a PEM encoded String.
+ *
+ * @param x509CertHolder - X.509 Certificate Holder.
+ * @return PEM Encoded Certificate String.
+ * @throws SCMSecurityException - On failure to create a PEM String.
+ */
+ public String getPEMEncodedString(X509CertificateHolder x509CertHolder)
+ throws SCMSecurityException {
+ try {
+ StringWriter stringWriter = new StringWriter();
+ try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
+ pemWriter.writeObject(getX509Certificate(x509CertHolder));
+ }
+ return stringWriter.toString();
+ } catch (CertificateException | IOException e) {
+ LOG.error("Error in encoding certificate." + x509CertHolder
+ .getSubject().toString(), e);
+ throw new SCMSecurityException("PEM Encoding failed for certificate." +
+ x509CertHolder.getSubject().toString(), e);
+ }
+ }
+
+ /**
+ * Gets the X.509 Certificate from PEM encoded String.
+ *
+ * @param pemEncodedString - PEM encoded String.
+ * @return X509Certificate - Certificate.
+ * @throws CertificateException - Thrown on Failure.
+ * @throws IOException - Thrown on Failure.
+ */
+ public X509Certificate getX509Certificate(String pemEncodedString)
+ throws CertificateException, IOException {
+ CertificateFactory fact = CertificateFactory.getInstance("X.509");
+ try (InputStream input = IOUtils.toInputStream(pemEncodedString, UTF_8)) {
+ return (X509Certificate) fact.generateCertificate(input);
+ }
+ }
+
+ /**
+ * Write the Certificate pointed to the location by the configs.
+ *
+ * @param xCertificate - Certificate to write.
+ * @throws SCMSecurityException - on Error.
+ * @throws IOException - on Error.
+ */
+ public void writeCertificate(X509CertificateHolder xCertificate)
+ throws SCMSecurityException, IOException {
+ String pem = getPEMEncodedString(xCertificate);
+ writeCertificate(location.toAbsolutePath(),
+ this.securityConfig.getCertificateFileName(), pem, false);
+ }
+
+ /**
+ * Write the Certificate to the specific file.
+ *
+ * @param xCertificate - Certificate to write.
+ * @param fileName - file name to write to.
+ * @param overwrite - boolean value, true means overwrite an existing
+ * certificate.
+ * @throws SCMSecurityException - On Error.
+ * @throws IOException - On Error.
+ */
+ public void writeCertificate(X509CertificateHolder xCertificate,
+ String fileName, boolean overwrite)
+ throws SCMSecurityException, IOException {
+ String pem = getPEMEncodedString(xCertificate);
+ writeCertificate(location.toAbsolutePath(), fileName, pem, overwrite);
+ }
+
+ /**
+ * Helper function that writes data to the file.
+ *
+ * @param basePath - Base Path where the file needs to written to.
+ * @param fileName - Certificate file name.
+ * @param pemEncodedCertificate - pemEncoded Certificate file.
+ * @param force - Overwrite if the file exists.
+ * @throws IOException - on Error.
+ */
+ public synchronized void writeCertificate(Path basePath, String fileName,
+ String pemEncodedCertificate, boolean force)
+ throws IOException {
+ File certificateFile =
+ Paths.get(basePath.toString(), fileName).toFile();
+ if (certificateFile.exists() && !force) {
+ throw new SCMSecurityException("Specified certificate file already " +
+ "exists.Please use force option if you want to overwrite it.");
+ }
+ if (!basePath.toFile().exists()) {
+ if (!basePath.toFile().mkdirs()) {
+ LOG.error("Unable to create file path. Path: {}", basePath);
+ throw new IOException("Creation of the directories failed."
+ + basePath.toString());
+ }
+ }
+ try (FileOutputStream file = new FileOutputStream(certificateFile)) {
+ IOUtils.write(pemEncodedCertificate, file, UTF_8);
+ }
+
+ Files.setPosixFilePermissions(certificateFile.toPath(), permissionSet);
+ }
+
+ /**
+ * Rertuns a default certificate using the default paths for this component.
+ *
+ * @return X509CertificateHolder.
+ * @throws SCMSecurityException - on Error.
+ * @throws CertificateException - on Error.
+ * @throws IOException - on Error.
+ */
+ public X509CertificateHolder readCertificate() throws
+ CertificateException, IOException {
+ return readCertificate(this.location.toAbsolutePath(),
+ this.securityConfig.getCertificateFileName());
+ }
+
+ /**
+ * Returns the certificate from the specific PEM encoded file.
+ *
+ * @param basePath - base path
+ * @param fileName - fileName
+ * @return X%09 Certificate
+ * @throws IOException - on Error.
+ * @throws SCMSecurityException - on Error.
+ * @throws CertificateException - on Error.
+ */
+ public synchronized X509CertificateHolder readCertificate(Path basePath,
+ String fileName) throws IOException, CertificateException {
+ File certificateFile = Paths.get(basePath.toString(), fileName).toFile();
+ return getX509CertificateHolder(certificateFile);
+ }
+
+ /**
+ * Helper function to read certificate.
+ *
+ * @param certificateFile - Full path to certificate file.
+ * @return X509CertificateHolder
+ * @throws IOException - On Error.
+ * @throws CertificateException - On Error.
+ */
+ private X509CertificateHolder getX509CertificateHolder(File certificateFile)
+ throws IOException, CertificateException {
+ if (!certificateFile.exists()) {
+ throw new IOException("Unable to find the requested certificate. Path: "
+ + certificateFile.toString());
+ }
+ CertificateFactory fact = CertificateFactory.getInstance("X.509");
+ try (FileInputStream is = new FileInputStream(certificateFile)) {
+ return getCertificateHolder(
+ (X509Certificate) fact.generateCertificate(is));
+ }
+ }
+
+ /**
+ * Returns the Certificate holder from X509Ceritificate class.
+ *
+ * @param x509cert - Certificate class.
+ * @return X509CertificateHolder
+ * @throws CertificateEncodingException - on Error.
+ * @throws IOException - on Error.
+ */
+ public X509CertificateHolder getCertificateHolder(X509Certificate x509cert)
+ throws CertificateEncodingException, IOException {
+ return new X509CertificateHolder(x509cert.getEncoded());
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/package-info.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/package-info.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/package-info.java
new file mode 100644
index 0000000..4971d4a
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ *
+ */
+/**
+ * Certificate Utils.
+ */
+package org.apache.hadoop.hdds.security.x509.certificate.utils;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java
deleted file mode 100644
index 0e762a5..0000000
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * 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.hdds.security.x509.certificates;
-
-import com.google.common.base.Preconditions;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
-import org.apache.hadoop.hdds.security.x509.SecurityConfig;
-import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
-import org.apache.hadoop.hdds.security.x509.keys.SecurityUtil;
-import org.apache.logging.log4j.util.Strings;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.Extensions;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.KeyUsage;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.pkcs.PKCS10CertificationRequest;
-import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
-import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
-
-import java.io.IOException;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * A certificate sign request object that wraps operations to build a
- * PKCS10CertificationRequest to CA.
- */
-public final class CertificateSignRequest {
- private final KeyPair keyPair;
- private final SecurityConfig config;
- private final Extensions extensions;
- private String subject;
- private String clusterID;
- private String scmID;
-
- /**
- * Private Ctor for CSR.
- *
- * @param subject - Subject
- * @param scmID - SCM ID
- * @param clusterID - Cluster ID
- * @param keyPair - KeyPair
- * @param config - SCM Config
- * @param extensions - CSR extensions
- */
- private CertificateSignRequest(String subject, String scmID, String clusterID,
- KeyPair keyPair, SecurityConfig config, Extensions extensions) {
- this.subject = subject;
- this.clusterID = clusterID;
- this.scmID = scmID;
- this.keyPair = keyPair;
- this.config = config;
- this.extensions = extensions;
- }
-
- private PKCS10CertificationRequest generateCSR() throws
- OperatorCreationException {
- X500Name dnName = SecurityUtil.getDistinguishedName(subject, scmID,
- clusterID);
- PKCS10CertificationRequestBuilder p10Builder =
- new JcaPKCS10CertificationRequestBuilder(dnName, keyPair.getPublic());
-
- ContentSigner contentSigner =
- new JcaContentSignerBuilder(config.getSignatureAlgo())
- .setProvider(config.getProvider())
- .build(keyPair.getPrivate());
-
- if (extensions != null) {
- p10Builder.addAttribute(
- PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extensions);
- }
- return p10Builder.build(contentSigner);
- }
-
- /**
- * Builder class for Certificate Sign Request.
- */
- public static class Builder {
- private String subject;
- private String clusterID;
- private String scmID;
- private KeyPair key;
- private SecurityConfig config;
- private List<GeneralName> altNames;
- private Boolean ca = false;
-
- public CertificateSignRequest.Builder setConfiguration(
- Configuration configuration) {
- this.config = new SecurityConfig(configuration);
- return this;
- }
-
- public CertificateSignRequest.Builder setKey(KeyPair keyPair) {
- this.key = keyPair;
- return this;
- }
-
- public CertificateSignRequest.Builder setSubject(String subjectString) {
- this.subject = subjectString;
- return this;
- }
-
- public CertificateSignRequest.Builder setClusterID(String s) {
- this.clusterID = s;
- return this;
- }
-
- public CertificateSignRequest.Builder setScmID(String s) {
- this.scmID = s;
- return this;
- }
-
- // Support SAN extenion with DNS and RFC822 Name
- // other name type will be added as needed.
- public CertificateSignRequest.Builder addDnsName(String dnsName) {
- Preconditions.checkNotNull(dnsName, "dnsName cannot be null");
- this.addAltName(GeneralName.dNSName, dnsName);
- return this;
- }
-
- public CertificateSignRequest.Builder addRfc822Name(String name) {
- Preconditions.checkNotNull(name, "Rfc822Name cannot be null");
- this.addAltName(GeneralName.rfc822Name, name);
- return this;
- }
-
- // IP address is subject to change which is optional for now.
- public CertificateSignRequest.Builder addIpAddress(String ip) {
- Preconditions.checkNotNull(ip, "Ip address cannot be null");
- this.addAltName(GeneralName.iPAddress, ip);
- return this;
- }
-
- private CertificateSignRequest.Builder addAltName(int tag, String name) {
- if (altNames == null) {
- altNames = new ArrayList<>();
- }
- altNames.add(new GeneralName(tag, name));
- return this;
- }
-
- public CertificateSignRequest.Builder setCA(Boolean isCA) {
- this.ca = isCA;
- return this;
- }
-
- private Extension getKeyUsageExtension() throws IOException {
- int keyUsageFlag = KeyUsage.digitalSignature | KeyUsage.keyEncipherment
- | KeyUsage.dataEncipherment | KeyUsage.keyAgreement;
-
- if (ca) {
- keyUsageFlag |= KeyUsage.keyCertSign | KeyUsage.cRLSign;
- }
- KeyUsage keyUsage = new KeyUsage(keyUsageFlag);
- return new Extension(Extension.keyUsage, true,
- new DEROctetString(keyUsage));
- }
-
- private Optional<Extension> getSubjectAltNameExtension() throws
- IOException {
- if (altNames != null) {
- return Optional.of(new Extension(Extension.subjectAlternativeName,
- true, new DEROctetString(new GeneralNames(
- altNames.toArray(new GeneralName[altNames.size()])))));
- }
- return Optional.empty();
- }
-
- private Extension getBasicExtension() throws IOException {
- // We don't set pathLenConstraint means no limit is imposed.
- return new Extension(Extension.basicConstraints,
- true, new DEROctetString(new BasicConstraints(ca)));
- }
-
- private Extensions createExtensions() throws IOException {
- List<Extension> extensions = new ArrayList<>();
-
- // Add basic extension
- extensions.add(getBasicExtension());
-
- // Add key usage extension
- extensions.add(getKeyUsageExtension());
-
- // Add subject alternate name extension
- Optional<Extension> san = getSubjectAltNameExtension();
- if (san.isPresent()) {
- extensions.add(san.get());
- }
-
- return new Extensions(
- extensions.toArray(new Extension[extensions.size()]));
- }
-
- public PKCS10CertificationRequest build() throws SCMSecurityException {
- Preconditions.checkNotNull(key, "KeyPair cannot be null");
- Preconditions.checkArgument(Strings.isNotBlank(subject), "Subject " +
- "cannot be blank");
- Preconditions.checkArgument(Strings.isNotBlank(clusterID), "Cluster ID " +
- "cannot be blank");
- Preconditions.checkArgument(Strings.isNotBlank(scmID), "SCM ID cannot " +
- "be blank");
-
- try {
- CertificateSignRequest csr = new CertificateSignRequest(subject, scmID,
- clusterID, key, config, createExtensions());
- return csr.generateCSR();
- } catch (IOException ioe) {
- throw new CertificateException(String.format("Unable to create " +
- "extension for certificate sign request for %s.", SecurityUtil
- .getDistinguishedName(subject, scmID, clusterID)), ioe.getCause());
- } catch (OperatorCreationException ex) {
- throw new CertificateException(String.format("Unable to create " +
- "certificate sign request for %s.", SecurityUtil
- .getDistinguishedName(subject, scmID, clusterID)),
- ex.getCause());
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java
deleted file mode 100644
index 85fba9b..0000000
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * 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.hdds.security.x509.certificates;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
-import org.apache.hadoop.hdds.security.x509.SecurityConfig;
-import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
-import org.apache.hadoop.util.Time;
-import org.apache.logging.log4j.util.Strings;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.cert.CertIOException;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.time.Duration;
-import java.util.Date;
-
-/**
- * A Self Signed Certificate with CA basic constraint can be used to boot-strap
- * a certificate infra-structure, if no external certificate is provided.
- */
-public final class SelfSignedCertificate {
- private static final String NAME_FORMAT = "CN=%s,OU=%s,O=%s";
- private String subject;
- private String clusterID;
- private String scmID;
- private Date beginDate;
- private Date endDate;
- private KeyPair key;
- private SecurityConfig config;
- private boolean isCA;
-
- /**
- * Private Ctor invoked only via Builder Interface.
- * @param subject - Subject
- * @param scmID - SCM ID
- * @param clusterID - Cluster ID
- * @param beginDate - NotBefore
- * @param endDate - Not After
- * @param configuration - SCM Config
- * @param keyPair - KeyPair
- * @param ca - isCA?
- */
- private SelfSignedCertificate(String subject, String scmID, String clusterID,
- Date beginDate, Date endDate, SecurityConfig configuration,
- KeyPair keyPair, boolean ca) {
- this.subject = subject;
- this.clusterID = clusterID;
- this.scmID = scmID;
- this.beginDate = beginDate;
- this.endDate = endDate;
- config = configuration;
- this.key = keyPair;
- this.isCA = ca;
- }
-
- @VisibleForTesting
- public static String getNameFormat() {
- return NAME_FORMAT;
- }
-
- public static Builder newBuilder() {
- return new Builder();
- }
-
- private X509CertificateHolder generateCertificate()
- throws OperatorCreationException, CertIOException {
- // For the Root Certificate we form the name from Subject, SCM ID and
- // Cluster ID.
- String dnName = String.format(getNameFormat(), subject, scmID, clusterID);
- X500Name name = new X500Name(dnName);
- byte[] encoded = key.getPublic().getEncoded();
- SubjectPublicKeyInfo publicKeyInfo =
- SubjectPublicKeyInfo.getInstance(encoded);
-
-
- ContentSigner contentSigner =
- new JcaContentSignerBuilder(config.getSignatureAlgo())
- .setProvider(config.getProvider()).build(key.getPrivate());
-
- // Please note: Since this is a root certificate we use "ONE" as the
- // serial number. Also note that skip enforcing locale or UTC. We are
- // trying to operate at the Days level, hence Time zone is also skipped for
- // now.
- BigInteger serial = BigInteger.ONE;
- if (!isCA) {
- serial = new BigInteger(Long.toString(Time.monotonicNow()));
- }
-
- X509v3CertificateBuilder builder = new X509v3CertificateBuilder(name,
- serial, beginDate, endDate, name, publicKeyInfo);
-
- if (isCA) {
- builder.addExtension(Extension.basicConstraints, true,
- new BasicConstraints(true));
- }
- return builder.build(contentSigner);
- }
-
- /**
- * Builder class for Root Certificates.
- */
- public static class Builder {
- private String subject;
- private String clusterID;
- private String scmID;
- private Date beginDate;
- private Date endDate;
- private KeyPair key;
- private SecurityConfig config;
- private boolean isCA;
-
- public Builder setConfiguration(Configuration configuration) {
- this.config = new SecurityConfig(configuration);
- return this;
- }
-
- public Builder setKey(KeyPair keyPair) {
- this.key = keyPair;
- return this;
- }
-
- public Builder setSubject(String subjectString) {
- this.subject = subjectString;
- return this;
- }
-
- public Builder setClusterID(String s) {
- this.clusterID = s;
- return this;
- }
-
- public Builder setScmID(String s) {
- this.scmID = s;
- return this;
- }
-
- public Builder setBeginDate(Date date) {
- this.beginDate = new Date(date.toInstant().toEpochMilli());
- return this;
- }
-
- public Builder setEndDate(Date date) {
- this.endDate = new Date(date.toInstant().toEpochMilli());
- return this;
- }
-
- public Builder makeCA() {
- isCA = true;
- return this;
- }
-
- public X509CertificateHolder build() throws SCMSecurityException {
- Preconditions.checkNotNull(key, "Key cannot be null");
- Preconditions.checkArgument(Strings.isNotBlank(subject), "Subject " +
- "cannot be blank");
- Preconditions.checkArgument(Strings.isNotBlank(clusterID), "Cluster ID " +
- "cannot be blank");
- Preconditions.checkArgument(Strings.isNotBlank(scmID), "SCM ID cannot " +
- "be blank");
-
- Preconditions.checkArgument(beginDate.before(endDate), "Certificate " +
- "begin date should be before end date");
-
- Duration certDuration = Duration.between(beginDate.toInstant(),
- endDate.toInstant());
- Preconditions.checkArgument(
- certDuration.compareTo(config.getMaxCertificateDuration()) < 0,
- "Certificate life time cannot be greater than max configured value.");
-
-
- SelfSignedCertificate rootCertificate =
- new SelfSignedCertificate(this.subject,
- this.scmID, this.clusterID, this.beginDate, this.endDate,
- this.config, key, isCA);
- try {
- return rootCertificate.generateCertificate();
- } catch (OperatorCreationException | CertIOException e) {
- throw new CertificateException("Unable to create root certificate.",
- e.getCause());
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/package-info.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/package-info.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/package-info.java
deleted file mode 100644
index e88737c..0000000
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.
- *
- */
-/**
- * Utils for Certificates.
- */
-package org.apache.hadoop.hdds.security.x509.certificates;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/CertificateSignRequest.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/CertificateSignRequest.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/CertificateSignRequest.java
new file mode 100644
index 0000000..3624b32
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/CertificateSignRequest.java
@@ -0,0 +1,245 @@
+/*
+ * 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.hdds.security.x509.certificates.utils;
+
+import com.google.common.base.Preconditions;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
+import org.apache.hadoop.hdds.security.x509.keys.SecurityUtil;
+import org.apache.logging.log4j.util.Strings;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
+import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * A certificate sign request object that wraps operations to build a
+ * PKCS10CertificationRequest to CertificateServer.
+ */
+public final class CertificateSignRequest {
+ private final KeyPair keyPair;
+ private final SecurityConfig config;
+ private final Extensions extensions;
+ private String subject;
+ private String clusterID;
+ private String scmID;
+
+ /**
+ * Private Ctor for CSR.
+ *
+ * @param subject - Subject
+ * @param scmID - SCM ID
+ * @param clusterID - Cluster ID
+ * @param keyPair - KeyPair
+ * @param config - SCM Config
+ * @param extensions - CSR extensions
+ */
+ private CertificateSignRequest(String subject, String scmID, String clusterID,
+ KeyPair keyPair, SecurityConfig config, Extensions extensions) {
+ this.subject = subject;
+ this.clusterID = clusterID;
+ this.scmID = scmID;
+ this.keyPair = keyPair;
+ this.config = config;
+ this.extensions = extensions;
+ }
+
+ private PKCS10CertificationRequest generateCSR() throws
+ OperatorCreationException {
+ X500Name dnName = SecurityUtil.getDistinguishedName(subject, scmID,
+ clusterID);
+ PKCS10CertificationRequestBuilder p10Builder =
+ new JcaPKCS10CertificationRequestBuilder(dnName, keyPair.getPublic());
+
+ ContentSigner contentSigner =
+ new JcaContentSignerBuilder(config.getSignatureAlgo())
+ .setProvider(config.getProvider())
+ .build(keyPair.getPrivate());
+
+ if (extensions != null) {
+ p10Builder.addAttribute(
+ PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extensions);
+ }
+ return p10Builder.build(contentSigner);
+ }
+
+ /**
+ * Builder class for Certificate Sign Request.
+ */
+ public static class Builder {
+ private String subject;
+ private String clusterID;
+ private String scmID;
+ private KeyPair key;
+ private SecurityConfig config;
+ private List<GeneralName> altNames;
+ private Boolean ca = false;
+
+ public CertificateSignRequest.Builder setConfiguration(
+ Configuration configuration) {
+ this.config = new SecurityConfig(configuration);
+ return this;
+ }
+
+ public CertificateSignRequest.Builder setKey(KeyPair keyPair) {
+ this.key = keyPair;
+ return this;
+ }
+
+ public CertificateSignRequest.Builder setSubject(String subjectString) {
+ this.subject = subjectString;
+ return this;
+ }
+
+ public CertificateSignRequest.Builder setClusterID(String s) {
+ this.clusterID = s;
+ return this;
+ }
+
+ public CertificateSignRequest.Builder setScmID(String s) {
+ this.scmID = s;
+ return this;
+ }
+
+ // Support SAN extenion with DNS and RFC822 Name
+ // other name type will be added as needed.
+ public CertificateSignRequest.Builder addDnsName(String dnsName) {
+ Preconditions.checkNotNull(dnsName, "dnsName cannot be null");
+ this.addAltName(GeneralName.dNSName, dnsName);
+ return this;
+ }
+
+ public CertificateSignRequest.Builder addRfc822Name(String name) {
+ Preconditions.checkNotNull(name, "Rfc822Name cannot be null");
+ this.addAltName(GeneralName.rfc822Name, name);
+ return this;
+ }
+
+ // IP address is subject to change which is optional for now.
+ public CertificateSignRequest.Builder addIpAddress(String ip) {
+ Preconditions.checkNotNull(ip, "Ip address cannot be null");
+ this.addAltName(GeneralName.iPAddress, ip);
+ return this;
+ }
+
+ private CertificateSignRequest.Builder addAltName(int tag, String name) {
+ if (altNames == null) {
+ altNames = new ArrayList<>();
+ }
+ altNames.add(new GeneralName(tag, name));
+ return this;
+ }
+
+ public CertificateSignRequest.Builder setCA(Boolean isCA) {
+ this.ca = isCA;
+ return this;
+ }
+
+ private Extension getKeyUsageExtension() throws IOException {
+ int keyUsageFlag = KeyUsage.digitalSignature | KeyUsage.keyEncipherment
+ | KeyUsage.dataEncipherment | KeyUsage.keyAgreement;
+
+ if (ca) {
+ keyUsageFlag |= KeyUsage.keyCertSign | KeyUsage.cRLSign;
+ }
+ KeyUsage keyUsage = new KeyUsage(keyUsageFlag);
+ return new Extension(Extension.keyUsage, true,
+ new DEROctetString(keyUsage));
+ }
+
+ private Optional<Extension> getSubjectAltNameExtension() throws
+ IOException {
+ if (altNames != null) {
+ return Optional.of(new Extension(Extension.subjectAlternativeName,
+ true, new DEROctetString(new GeneralNames(
+ altNames.toArray(new GeneralName[altNames.size()])))));
+ }
+ return Optional.empty();
+ }
+
+ private Extension getBasicExtension() throws IOException {
+ // We don't set pathLenConstraint means no limit is imposed.
+ return new Extension(Extension.basicConstraints,
+ true, new DEROctetString(new BasicConstraints(ca)));
+ }
+
+ private Extensions createExtensions() throws IOException {
+ List<Extension> extensions = new ArrayList<>();
+
+ // Add basic extension
+ extensions.add(getBasicExtension());
+
+ // Add key usage extension
+ extensions.add(getKeyUsageExtension());
+
+ // Add subject alternate name extension
+ Optional<Extension> san = getSubjectAltNameExtension();
+ if (san.isPresent()) {
+ extensions.add(san.get());
+ }
+
+ return new Extensions(
+ extensions.toArray(new Extension[extensions.size()]));
+ }
+
+ public PKCS10CertificationRequest build() throws SCMSecurityException {
+ Preconditions.checkNotNull(key, "KeyPair cannot be null");
+ Preconditions.checkArgument(Strings.isNotBlank(subject), "Subject " +
+ "cannot be blank");
+ Preconditions.checkArgument(Strings.isNotBlank(clusterID), "Cluster ID " +
+ "cannot be blank");
+ Preconditions.checkArgument(Strings.isNotBlank(scmID), "SCM ID cannot " +
+ "be blank");
+
+ try {
+ CertificateSignRequest csr = new CertificateSignRequest(subject, scmID,
+ clusterID, key, config, createExtensions());
+ return csr.generateCSR();
+ } catch (IOException ioe) {
+ throw new CertificateException(String.format("Unable to create " +
+ "extension for certificate sign request for %s.", SecurityUtil
+ .getDistinguishedName(subject, scmID, clusterID)), ioe.getCause());
+ } catch (OperatorCreationException ex) {
+ throw new CertificateException(String.format("Unable to create " +
+ "certificate sign request for %s.", SecurityUtil
+ .getDistinguishedName(subject, scmID, clusterID)),
+ ex.getCause());
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/SelfSignedCertificate.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/SelfSignedCertificate.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/SelfSignedCertificate.java
new file mode 100644
index 0000000..1fd6d7c
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/SelfSignedCertificate.java
@@ -0,0 +1,238 @@
+/*
+ * 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.hdds.security.x509.certificates.utils;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
+import org.apache.hadoop.util.Time;
+import org.apache.logging.log4j.util.Strings;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.CertIOException;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneOffset;
+import java.util.Date;
+
+/**
+ * A Self Signed Certificate with CertificateServer basic constraint can be used
+ * to bootstrap a certificate infrastructure, if no external certificate is
+ * provided.
+ */
+public final class SelfSignedCertificate {
+ private static final String NAME_FORMAT = "CN=%s,OU=%s,O=%s";
+ private String subject;
+ private String clusterID;
+ private String scmID;
+ private LocalDate beginDate;
+ private LocalDate endDate;
+ private KeyPair key;
+ private SecurityConfig config;
+
+ /**
+ * Private Ctor invoked only via Builder Interface.
+ *
+ * @param subject - Subject
+ * @param scmID - SCM ID
+ * @param clusterID - Cluster ID
+ * @param beginDate - NotBefore
+ * @param endDate - Not After
+ * @param configuration - SCM Config
+ * @param keyPair - KeyPair
+ */
+ private SelfSignedCertificate(String subject, String scmID, String clusterID,
+ LocalDate beginDate, LocalDate endDate, SecurityConfig configuration,
+ KeyPair keyPair) {
+ this.subject = subject;
+ this.clusterID = clusterID;
+ this.scmID = scmID;
+ this.beginDate = beginDate;
+ this.endDate = endDate;
+ config = configuration;
+ this.key = keyPair;
+ }
+
+ @VisibleForTesting
+ public static String getNameFormat() {
+ return NAME_FORMAT;
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ private X509CertificateHolder generateCertificate(boolean isCA)
+ throws OperatorCreationException, IOException {
+ // For the Root Certificate we form the name from Subject, SCM ID and
+ // Cluster ID.
+ String dnName = String.format(getNameFormat(), subject, scmID, clusterID);
+ X500Name name = new X500Name(dnName);
+ byte[] encoded = key.getPublic().getEncoded();
+ SubjectPublicKeyInfo publicKeyInfo =
+ SubjectPublicKeyInfo.getInstance(encoded);
+
+
+ ContentSigner contentSigner =
+ new JcaContentSignerBuilder(config.getSignatureAlgo())
+ .setProvider(config.getProvider()).build(key.getPrivate());
+
+ // Please note: Since this is a root certificate we use "ONE" as the
+ // serial number. Also note that skip enforcing locale or UTC. We are
+ // trying to operate at the Days level, hence Time zone is also skipped for
+ // now.
+ BigInteger serial = BigInteger.ONE;
+ if (!isCA) {
+ serial = new BigInteger(Long.toString(Time.monotonicNow()));
+ }
+
+ ZoneOffset zoneOffset =
+ beginDate.atStartOfDay(ZoneOffset.systemDefault()).getOffset();
+
+ // Valid from the Start of the day when we generate this Certificate.
+ Date validFrom =
+ Date.from(beginDate.atTime(LocalTime.MIN).toInstant(zoneOffset));
+
+ // Valid till end day finishes.
+ Date validTill =
+ Date.from(endDate.atTime(LocalTime.MAX).toInstant(zoneOffset));
+
+ X509v3CertificateBuilder builder = new X509v3CertificateBuilder(name,
+ serial, validFrom, validTill, name, publicKeyInfo);
+
+ if (isCA) {
+ builder.addExtension(Extension.basicConstraints, true,
+ new BasicConstraints(true));
+ int keyUsageFlag = KeyUsage.keyCertSign | KeyUsage.cRLSign;
+ KeyUsage keyUsage = new KeyUsage(keyUsageFlag);
+ builder.addExtension(Extension.keyUsage, false,
+ new DEROctetString(keyUsage));
+ }
+ return builder.build(contentSigner);
+ }
+
+ /**
+ * Builder class for Root Certificates.
+ */
+ public static class Builder {
+ private String subject;
+ private String clusterID;
+ private String scmID;
+ private LocalDate beginDate;
+ private LocalDate endDate;
+ private KeyPair key;
+ private SecurityConfig config;
+ private boolean isCA;
+
+ public Builder setConfiguration(Configuration configuration) {
+ this.config = new SecurityConfig(configuration);
+ return this;
+ }
+
+ public Builder setKey(KeyPair keyPair) {
+ this.key = keyPair;
+ return this;
+ }
+
+ public Builder setSubject(String subjectString) {
+ this.subject = subjectString;
+ return this;
+ }
+
+ public Builder setClusterID(String s) {
+ this.clusterID = s;
+ return this;
+ }
+
+ public Builder setScmID(String s) {
+ this.scmID = s;
+ return this;
+ }
+
+ public Builder setBeginDate(LocalDate date) {
+ this.beginDate = date;
+ return this;
+ }
+
+ public Builder setEndDate(LocalDate date) {
+ this.endDate = date;
+ return this;
+ }
+
+ public Builder makeCA() {
+ isCA = true;
+ return this;
+ }
+
+ public X509CertificateHolder build()
+ throws SCMSecurityException, IOException {
+ Preconditions.checkNotNull(key, "Key cannot be null");
+ Preconditions.checkArgument(Strings.isNotBlank(subject), "Subject " +
+ "cannot be blank");
+ Preconditions.checkArgument(Strings.isNotBlank(clusterID), "Cluster ID " +
+ "cannot be blank");
+ Preconditions.checkArgument(Strings.isNotBlank(scmID), "SCM ID cannot " +
+ "be blank");
+
+ Preconditions.checkArgument(beginDate.isBefore(endDate), "Certificate " +
+ "begin date should be before end date");
+
+ // We just read the beginDate and EndDate as Start of the Day and
+ // confirm that we do not violate the maxDuration Config.
+ Duration certDuration = Duration.between(beginDate.atStartOfDay(),
+ endDate.atStartOfDay());
+ Duration maxDuration = config.getMaxCertificateDuration();
+ if (certDuration.compareTo(maxDuration) > 0) {
+ throw new SCMSecurityException("The cert duration violates the " +
+ "maximum configured value. Please check the hdds.x509.max" +
+ ".duration config key. Current Value: " + certDuration +
+ " config: " + maxDuration);
+ }
+
+ SelfSignedCertificate rootCertificate =
+ new SelfSignedCertificate(this.subject,
+ this.scmID, this.clusterID, this.beginDate, this.endDate,
+ this.config, key);
+ try {
+ return rootCertificate.generateCertificate(isCA);
+ } catch (OperatorCreationException | CertIOException e) {
+ throw new CertificateException("Unable to create root certificate.",
+ e.getCause());
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/87f51d23/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/package-info.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/package-info.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/package-info.java
new file mode 100644
index 0000000..945adc9
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ *
+ */
+/**
+ * Utils for Certificates.
+ */
+package org.apache.hadoop.hdds.security.x509.certificates.utils;
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org