This is an automated email from the ASF dual-hosted git repository.

adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 271974568d HDDS-11031. Merge BaseApprover abstract class into 
DefaultApprover (#6844)
271974568d is described below

commit 271974568d526400ae6b46b8b0c179f1755f4ded
Author: Istvan Fajth <[email protected]>
AuthorDate: Wed Jun 26 11:07:03 2024 +0200

    HDDS-11031. Merge BaseApprover abstract class into DefaultApprover (#6844)
---
 .../x509/certificate/authority/BaseApprover.java   | 244 ---------------------
 .../certificate/authority/DefaultApprover.java     | 192 +++++++++++++++-
 2 files changed, 186 insertions(+), 250 deletions(-)

diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/BaseApprover.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/BaseApprover.java
deleted file mode 100644
index 0ea2c60f02..0000000000
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/BaseApprover.java
+++ /dev/null
@@ -1,244 +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.certificate.authority;
-
-import org.apache.hadoop.hdds.security.SecurityConfig;
-import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
-import 
org.apache.hadoop.hdds.security.x509.certificate.authority.profile.PKIProfile;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.pkcs.Attribute;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.x500.RDN;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.Extensions;
-import org.bouncycastle.operator.ContentVerifierProvider;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
-import org.bouncycastle.pkcs.PKCS10CertificationRequest;
-import org.bouncycastle.pkcs.PKCSException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * A base approver class for certificate approvals.
- */
-public abstract class BaseApprover implements CertificateApprover {
-  private static final Logger LOG =
-      LoggerFactory.getLogger(BaseApprover.class);
-  private final PKIProfile profile;
-  private final SecurityConfig securityConfig;
-
-  public BaseApprover(PKIProfile pkiProfile, SecurityConfig config) {
-    this.profile = Objects.requireNonNull(pkiProfile);
-    this.securityConfig = Objects.requireNonNull(config);
-  }
-
-  /**
-   * Returns the PKI policy profile.
-   *
-   * @return PKIProfile
-   */
-  public PKIProfile getProfile() {
-    return profile;
-  }
-
-  /**
-   * Returns the Security config.
-   *
-   * @return SecurityConfig
-   */
-  public SecurityConfig getSecurityConfig() {
-    return securityConfig;
-  }
-
-  /**
-   * Returns the Attribute array that encodes extensions.
-   *
-   * @param request - Certificate Request
-   * @return - An Array of Attributes that encode various extensions requested
-   * in this certificate.
-   */
-  Attribute[] getAttributes(PKCS10CertificationRequest request) {
-    Objects.requireNonNull(request);
-    return
-        
request.getAttributes(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest);
-  }
-
-  /**
-   * Returns a list of Extensions encoded in a given attribute.
-   *
-   * @param attribute - Attribute to decode.
-   * @return - List of Extensions.
-   */
-  List<Extensions> getExtensionsList(Attribute attribute) {
-    Objects.requireNonNull(attribute);
-    List<Extensions> extensionsList = new ArrayList<>();
-    for (ASN1Encodable value : attribute.getAttributeValues()) {
-      if (value != null) {
-        Extensions extensions = Extensions.getInstance(value);
-        extensionsList.add(extensions);
-      }
-    }
-    return extensionsList;
-  }
-
-  /**
-   * Returns the Extension decoded into a Java Collection.
-   * @param extensions - A set of Extensions in ASN.1.
-   * @return List of Decoded Extensions.
-   */
-  List<Extension> getIndividualExtension(Extensions extensions) {
-    Objects.requireNonNull(extensions);
-    List<Extension> extenList = new ArrayList<>();
-    for (ASN1ObjectIdentifier id : extensions.getExtensionOIDs()) {
-      if (id != null) {
-        Extension ext = extensions.getExtension(id);
-        if (ext != null) {
-          extenList.add(ext);
-        }
-      }
-    }
-    return extenList;
-  }
-
-
-
-  /**
-   * This function verifies all extensions in the certificate.
-   *
-   * @param request - CSR
-   * @return - true if the extensions are acceptable by the profile, false
-   * otherwise.
-   */
-  boolean verfiyExtensions(PKCS10CertificationRequest request) {
-    Objects.requireNonNull(request);
-    /*
-     * Inside a CSR we have
-     *  1. A list of Attributes
-     *    2. Inside each attribute a list of extensions.
-     *      3. We need to walk thru the each extension and verify they
-     *      are expected and we can put that into a certificate.
-     */
-
-    for (Attribute attr : getAttributes(request)) {
-      for (Extensions extensionsList : getExtensionsList(attr)) {
-        for (Extension extension : getIndividualExtension(extensionsList)) {
-          if (!profile.validateExtension(extension)) {
-            LOG.error("Failed to verify extension. {}",
-                extension.getExtnId().getId());
-            return false;
-          }
-        }
-      }
-    }
-    return true;
-  }
-
-  /**
-   * Verifies the Signature on the CSR is valid.
-   *
-   * @param pkcs10Request - PCKS10 Request.
-   * @return True if it is valid, false otherwise.
-   * @throws OperatorCreationException - On Error.
-   * @throws PKCSException             - on Error.
-   */
-  boolean verifyPkcs10Request(PKCS10CertificationRequest pkcs10Request)
-      throws OperatorCreationException, PKCSException {
-    ContentVerifierProvider verifierProvider = new
-        JcaContentVerifierProviderBuilder()
-        .setProvider(this.securityConfig.getProvider())
-        .build(pkcs10Request.getSubjectPublicKeyInfo());
-    return
-        pkcs10Request.isSignatureValid(verifierProvider);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public CompletableFuture<Void> inspectCSR(PKCS10CertificationRequest csr) {
-    /**
-     * The base approver executes the following algorithm to verify that a
-     * CSR meets the PKI Profile criteria.
-     *
-     * 0. For time being (Until we have SCM HA) we will deny all request to
-     * become an intermediary CA. So we will not need to verify using CA
-     * profile, right now.
-     *
-     * 1. We verify the proof of possession. That is we verify the entity
-     * that sends us the CSR indeed has the private key for the said public 
key.
-     *
-     * 2. Then we will verify the RDNs meet the format and the Syntax that
-     * PKI profile dictates.
-     *
-     * 3. Then we decode each and every extension and  ask if the PKI profile
-     * approves of these extension requests.
-     *
-     * 4. If all of these pass, We will return a Future which will point to
-     * the Certificate when finished.
-     */
-
-    CompletableFuture<Void> response = new CompletableFuture<>();
-    try {
-      // Step 0: Verify this is not a CA Certificate.
-      // Will be done by the Ozone PKI profile for time being.
-      // If there are any basicConstraints, they will flagged as not
-      // supported for time being.
-
-      // Step 1: Let us verify that Certificate is indeed signed by someone
-      // who has access to the private key.
-      if (!verifyPkcs10Request(csr)) {
-        LOG.error("Failed to verify the signature in CSR.");
-        response.completeExceptionally(new SCMSecurityException("Failed to " +
-            "verify the CSR."));
-      }
-
-      // Step 2: Verify the RDNs are in the correct format.
-      // TODO: Ozone Profile does not verify RDN now, so this call will pass.
-      for (RDN rdn : csr.getSubject().getRDNs()) {
-        if (!profile.validateRDN(rdn)) {
-          LOG.error("Failed in verifying RDNs");
-          response.completeExceptionally(new SCMSecurityException("Failed to " 
+
-              "verify the RDNs. Please check the subject name."));
-        }
-      }
-
-      // Step 3: Verify the Extensions.
-      if (!verfiyExtensions(csr)) {
-        LOG.error("failed in verification of extensions.");
-        response.completeExceptionally(new SCMSecurityException("Failed to " +
-            "verify extensions."));
-      }
-
-    } catch (OperatorCreationException | PKCSException e) {
-      LOG.error("Approval Failure.", e);
-      response.completeExceptionally(new SCMSecurityException(e));
-    }
-    return response;
-  }
-
-
-}
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultApprover.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultApprover.java
index e240856eae..5ba7526ec9 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultApprover.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultApprover.java
@@ -19,10 +19,15 @@
 
 package org.apache.hadoop.hdds.security.x509.certificate.authority;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.hdds.security.SecurityConfig;
 import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
 import 
org.apache.hadoop.hdds.security.x509.certificate.authority.profile.PKIProfile;
+import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.Attribute;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.RDN;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x500.style.BCStyle;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -37,11 +42,14 @@ import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.util.PrivateKeyFactory;
 import org.bouncycastle.crypto.util.PublicKeyFactory;
 import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.ContentVerifierProvider;
 import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
 import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.pkcs.PKCSException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -50,8 +58,12 @@ import java.math.BigInteger;
 import java.security.PrivateKey;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
 import java.util.stream.Collectors;
 
 import static 
org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateSignRequest.getDistinguishedNameWithSN;
@@ -60,10 +72,13 @@ import static 
org.apache.hadoop.hdds.security.x509.certificate.utils.Certificate
 /**
  * Default Approver used the by the DefaultCA.
  */
-public class DefaultApprover extends BaseApprover {
+public class DefaultApprover implements CertificateApprover {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(DefaultApprover.class);
+
+  private final PKIProfile profile;
+  private final SecurityConfig securityConfig;
 
-  private static final Logger LOG =
-      LoggerFactory.getLogger(DefaultApprover.class);
   /**
    * Constructs the Default Approver.
    *
@@ -71,7 +86,8 @@ public class DefaultApprover extends BaseApprover {
    * @param config - Security Config
    */
   public DefaultApprover(PKIProfile pkiProfile, SecurityConfig config) {
-    super(pkiProfile, config);
+    this.profile = Objects.requireNonNull(pkiProfile);
+    this.securityConfig = Objects.requireNonNull(config);
   }
 
   /**
@@ -161,10 +177,10 @@ public class DefaultApprover extends BaseApprover {
             .map(ASN1ObjectIdentifier::getId)
             .collect(Collectors.joining(", ")));
     LOG.info("Extensions to add to the certificate if they present in CSR: {}",
-        Arrays.stream(getProfile().getSupportedExtensions())
+        Arrays.stream(profile.getSupportedExtensions())
             .map(oid -> oid == null ? "null" : oid.getId())
             .collect(Collectors.joining(", ")));
-    for (ASN1ObjectIdentifier extId : getProfile().getSupportedExtensions()) {
+    for (ASN1ObjectIdentifier extId : profile.getSupportedExtensions()) {
       Extension ext = exts.getExtension(extId);
       if (ext != null) {
         certificateGenerator.addExtension(ext);
@@ -176,7 +192,171 @@ public class DefaultApprover extends BaseApprover {
 
     //TODO: as part of HDDS-10743 ensure that converter is instantiated only 
once
     return new 
JcaX509CertificateConverter().getCertificate(certificateGenerator.build(sigGen));
+  }
+
+  /**
+   * Returns the Attribute array that encodes extensions.
+   *
+   * @param request - Certificate Request
+   * @return - An Array of Attributes that encode various extensions requested
+   * in this certificate.
+   */
+  private Attribute[] getAttributes(PKCS10CertificationRequest request) {
+    Objects.requireNonNull(request);
+
+    return 
request.getAttributes(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest);
+  }
+
+  /**
+   * Returns a list of Extensions encoded in a given attribute.
+   *
+   * @param attribute - Attribute to decode.
+   * @return - List of Extensions.
+   */
+  private List<Extensions> getExtensionsList(Attribute attribute) {
+    Objects.requireNonNull(attribute);
+    List<Extensions> extensionsList = new ArrayList<>();
+    for (ASN1Encodable value : attribute.getAttributeValues()) {
+      if (value != null) {
+        Extensions extensions = Extensions.getInstance(value);
+        extensionsList.add(extensions);
+      }
+    }
+    return extensionsList;
+  }
+
+  /**
+   * Returns the Extension decoded into a Java Collection.
+   * @param extensions - A set of Extensions in ASN.1.
+   * @return List of Decoded Extensions.
+   */
+  private List<Extension> getIndividualExtension(Extensions extensions) {
+    Objects.requireNonNull(extensions);
+    List<Extension> extenList = new ArrayList<>();
+    for (ASN1ObjectIdentifier id : extensions.getExtensionOIDs()) {
+      if (id != null) {
+        Extension ext = extensions.getExtension(id);
+        if (ext != null) {
+          extenList.add(ext);
+        }
+      }
+    }
+    return extenList;
+  }
+
+  /**
+   * This function verifies all extensions in the certificate.
+   *
+   * @param request - CSR
+   * @return - true if the extensions are acceptable by the profile, false
+   * otherwise.
+   */
+  @VisibleForTesting
+  boolean verfiyExtensions(PKCS10CertificationRequest request) {
+    Objects.requireNonNull(request);
+    /*
+     * Inside a CSR we have
+     *  1. A list of Attributes
+     *    2. Inside each attribute a list of extensions.
+     *      3. We need to walk thru the each extension and verify they
+     *      are expected and we can put that into a certificate.
+     */
+
+    for (Attribute attr : getAttributes(request)) {
+      for (Extensions extensionsList : getExtensionsList(attr)) {
+        for (Extension extension : getIndividualExtension(extensionsList)) {
+          if (!profile.validateExtension(extension)) {
+            LOG.error("Failed to verify extension. {}",
+                extension.getExtnId().getId());
+            return false;
+          }
+        }
+      }
+    }
+    return true;
+  }
 
+  /**
+   * Verifies the Signature on the CSR is valid.
+   *
+   * @param pkcs10Request - PCKS10 Request.
+   * @return True if it is valid, false otherwise.
+   * @throws OperatorCreationException - On Error.
+   * @throws PKCSException             - on Error.
+   */
+  @VisibleForTesting
+  boolean verifyPkcs10Request(PKCS10CertificationRequest pkcs10Request)
+      throws OperatorCreationException, PKCSException {
+    ContentVerifierProvider verifierProvider = new
+        JcaContentVerifierProviderBuilder()
+        .setProvider(this.securityConfig.getProvider())
+        .build(pkcs10Request.getSubjectPublicKeyInfo());
+    return
+        pkcs10Request.isSignatureValid(verifierProvider);
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public CompletableFuture<Void> inspectCSR(PKCS10CertificationRequest csr) {
+    /**
+     * The base approver executes the following algorithm to verify that a
+     * CSR meets the PKI Profile criteria.
+     *
+     * 0. For time being (Until we have SCM HA) we will deny all request to
+     * become an intermediary CA. So we will not need to verify using CA
+     * profile, right now.
+     *
+     * 1. We verify the proof of possession. That is we verify the entity
+     * that sends us the CSR indeed has the private key for the said public 
key.
+     *
+     * 2. Then we will verify the RDNs meet the format and the Syntax that
+     * PKI profile dictates.
+     *
+     * 3. Then we decode each and every extension and  ask if the PKI profile
+     * approves of these extension requests.
+     *
+     * 4. If all of these pass, We will return a Future which will point to
+     * the Certificate when finished.
+     */
+
+    CompletableFuture<Void> response = new CompletableFuture<>();
+    try {
+      // Step 0: Verify this is not a CA Certificate.
+      // Will be done by the Ozone PKI profile for time being.
+      // If there are any basicConstraints, they will flagged as not
+      // supported for time being.
+
+      // Step 1: Let us verify that Certificate is indeed signed by someone
+      // who has access to the private key.
+      if (!verifyPkcs10Request(csr)) {
+        LOG.error("Failed to verify the signature in CSR.");
+        response.completeExceptionally(new SCMSecurityException("Failed to " +
+            "verify the CSR."));
+      }
+
+      // Step 2: Verify the RDNs are in the correct format.
+      // TODO: Ozone Profile does not verify RDN now, so this call will pass.
+      for (RDN rdn : csr.getSubject().getRDNs()) {
+        if (!profile.validateRDN(rdn)) {
+          LOG.error("Failed in verifying RDNs");
+          response.completeExceptionally(new SCMSecurityException("Failed to " 
+
+              "verify the RDNs. Please check the subject name."));
+        }
+      }
+
+      // Step 3: Verify the Extensions.
+      if (!verfiyExtensions(csr)) {
+        LOG.error("failed in verification of extensions.");
+        response.completeExceptionally(new SCMSecurityException("Failed to " +
+            "verify extensions."));
+      }
+
+    } catch (OperatorCreationException | PKCSException e) {
+      LOG.error("Approval Failure.", e);
+      response.completeExceptionally(new SCMSecurityException(e));
+    }
+    return response;
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to