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 <aengin...@apache.org>
Authored: Tue Nov 27 15:02:07 2018 -0800
Committer: Xiaoyu Yao <x...@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-unsubscr...@hadoop.apache.org
For additional commands, e-mail: common-commits-h...@hadoop.apache.org

Reply via email to