You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by xy...@apache.org on 2020/07/25 00:07:15 UTC
[hadoop-ozone] branch master updated: HDDS-3997. Ozone certificate needs additional flags and SAN extension… (#1235)
This is an automated email from the ASF dual-hosted git repository.
xyao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hadoop-ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 0bb3e24 HDDS-3997. Ozone certificate needs additional flags and SAN extension… (#1235)
0bb3e24 is described below
commit 0bb3e247ae7939ddbe9a18af835dd57ff44f904e
Author: Xiaoyu Yao <xy...@apache.org>
AuthorDate: Fri Jul 24 17:07:05 2020 -0700
HDDS-3997. Ozone certificate needs additional flags and SAN extension… (#1235)
---
.../x509/certificate/authority/BaseApprover.java | 9 ++
.../certificate/authority/DefaultApprover.java | 11 +++
.../certificate/authority/DefaultCAServer.java | 42 ++++++---
.../certificate/client/DNCertificateClient.java | 5 +-
.../certificates/utils/SelfSignedCertificate.java | 105 +++++++++++++++++----
.../x509/certificates/TestRootCertificate.java | 42 ++++++++-
.../org/apache/hadoop/ozone/om/OzoneManager.java | 3 +-
7 files changed, 180 insertions(+), 37 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
index 12ececd..26cb491 100644
--- 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
@@ -60,6 +60,15 @@ public abstract class BaseApprover implements CertificateApprover {
}
/**
+ * Returns the PKI policy profile.
+ *
+ * @return PKIProfile
+ */
+ public PKIProfile getProfile() {
+ return profile;
+ }
+
+ /**
* Returns the Security config.
*
* @return SecurityConfig
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 c7f37c1..0098fa5 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
@@ -24,9 +24,12 @@ import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.security.x509.certificate.authority.PKIProfiles.PKIProfile;
import org.apache.hadoop.hdds.security.x509.keys.SecurityUtil;
import org.apache.hadoop.util.Time;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
@@ -136,6 +139,14 @@ public class DefaultApprover extends BaseApprover {
validTill,
x500Name, keyInfo);
+ Extensions exts = SecurityUtil.getPkcs9Extensions(certificationRequest);
+ for (ASN1ObjectIdentifier extId : getProfile().getSupportedExtensions()) {
+ Extension ext = exts.getExtension(extId);
+ if (ext != null) {
+ certificateGenerator.addExtension(ext);
+ }
+ }
+
ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
.build(asymmetricKP);
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
index c583c19..2378260 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java
@@ -21,6 +21,7 @@ package org.apache.hadoop.hdds.security.x509.certificate.authority;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import org.apache.commons.validator.routines.DomainValidator;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.security.x509.certificate.authority.PKIProfiles.DefaultProfile;
@@ -29,6 +30,7 @@ import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
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.apache.hadoop.ozone.OzoneSecurityUtil;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
@@ -54,6 +56,7 @@ import java.util.concurrent.Future;
import java.util.function.Consumer;
import static org.apache.hadoop.hdds.security.x509.certificates.utils.CertificateSignRequest.*;
+import static org.apache.hadoop.hdds.security.x509.exceptions.CertificateException.ErrorCode.CSR_ERROR;
/**
* The default CertificateServer used by SCM. This has no dependencies on any
@@ -459,18 +462,33 @@ public class DefaultCAServer implements CertificateServer {
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();
+ SelfSignedCertificate.Builder builder = SelfSignedCertificate.newBuilder()
+ .setSubject(this.subject)
+ .setScmID(this.scmID)
+ .setClusterID(this.clusterID)
+ .setBeginDate(beginDate)
+ .setEndDate(endDate)
+ .makeCA()
+ .setConfiguration(securityConfig.getConfiguration())
+ .setKey(key);
+
+ try {
+ DomainValidator validator = DomainValidator.getInstance();
+ // Add all valid ips.
+ OzoneSecurityUtil.getValidInetsForCurrentHost().forEach(
+ ip -> {
+ builder.addIpAddress(ip.getHostAddress());
+ if(validator.isValid(ip.getCanonicalHostName())) {
+ builder.addDnsName(ip.getCanonicalHostName());
+ }
+ });
+ } catch (IOException e) {
+ throw new org.apache.hadoop.hdds.security.x509
+ .exceptions.CertificateException(
+ "Error while adding ip to CA self signed certificate", e,
+ CSR_ERROR);
+ }
+ X509CertificateHolder selfSignedCertificate = builder.build();
CertificateCodec certCodec =
new CertificateCodec(config, componentName);
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DNCertificateClient.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DNCertificateClient.java
index 7698658..e95a4a7 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DNCertificateClient.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DNCertificateClient.java
@@ -48,6 +48,7 @@ public class DNCertificateClient extends DefaultCertificateClient {
/**
* Returns a CSR builder that can be used to creates a Certificate signing
* request.
+ * The default flag is added to allow basic SSL handshake.
*
* @return CertificateSignRequest.Builder
*/
@@ -55,8 +56,8 @@ public class DNCertificateClient extends DefaultCertificateClient {
public CertificateSignRequest.Builder getCSRBuilder()
throws CertificateException {
return super.getCSRBuilder()
- .setDigitalEncryption(false)
- .setDigitalSignature(false);
+ .setDigitalEncryption(true)
+ .setDigitalSignature(true);
}
public Logger getLogger() {
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/SelfSignedCertificate.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/SelfSignedCertificate.java
index 7ecc161..a7edfde 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/SelfSignedCertificate.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/SelfSignedCertificate.java
@@ -26,7 +26,9 @@ import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneOffset;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
@@ -37,10 +39,18 @@ import org.apache.hadoop.util.Time;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.logging.log4j.util.Strings;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.CertIOException;
@@ -64,28 +74,23 @@ public final class SelfSignedCertificate {
private LocalDate endDate;
private KeyPair key;
private SecurityConfig config;
+ private List<GeneralName> altNames;
/**
* 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 builder - builder
*/
- 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;
+
+ private SelfSignedCertificate(Builder builder) {
+ this.subject = builder.subject;
+ this.clusterID = builder.clusterID;
+ this.scmID = builder.scmID;
+ this.beginDate = builder.beginDate;
+ this.endDate = builder.endDate;
+ this.config = builder.config;
+ this.key = builder.key;
+ this.altNames = builder.altNames;
}
@VisibleForTesting
@@ -142,6 +147,11 @@ public final class SelfSignedCertificate {
KeyUsage keyUsage = new KeyUsage(keyUsageFlag);
builder.addExtension(Extension.keyUsage, false,
new DEROctetString(keyUsage));
+ if (altNames != null && altNames.size() >= 1) {
+ builder.addExtension(new Extension(Extension.subjectAlternativeName,
+ false, new GeneralNames(altNames.toArray(
+ new GeneralName[altNames.size()])).getEncoded()));
+ }
}
return builder.build(contentSigner);
}
@@ -158,6 +168,7 @@ public final class SelfSignedCertificate {
private KeyPair key;
private SecurityConfig config;
private boolean isCA;
+ private List<GeneralName> altNames;
public Builder setConfiguration(ConfigurationSource configuration) {
this.config = new SecurityConfig(configuration);
@@ -199,6 +210,62 @@ public final class SelfSignedCertificate {
return this;
}
+ // Support SAN extension with DNS and RFC822 Name
+ // other name type will be added as needed.
+ public Builder addDnsName(String dnsName) {
+ Preconditions.checkNotNull(dnsName, "dnsName cannot be null");
+ this.addAltName(GeneralName.dNSName, dnsName);
+ return this;
+ }
+
+ // IP address is subject to change which is optional for now.
+ public Builder addIpAddress(String ip) {
+ Preconditions.checkNotNull(ip, "Ip address cannot be null");
+ this.addAltName(GeneralName.iPAddress, ip);
+ return this;
+ }
+
+ public Builder addServiceName(
+ String serviceName) {
+ Preconditions.checkNotNull(
+ serviceName, "Service Name cannot be null");
+
+ this.addAltName(GeneralName.otherName, serviceName);
+ return this;
+ }
+
+ private Builder addAltName(int tag, String name) {
+ if (altNames == null) {
+ altNames = new ArrayList<>();
+ }
+ if (tag == GeneralName.otherName) {
+ ASN1Object ono = addOtherNameAsn1Object(name);
+
+ altNames.add(new GeneralName(tag, ono));
+ } else {
+ altNames.add(new GeneralName(tag, name));
+ }
+ return this;
+ }
+
+ /**
+ * addOtherNameAsn1Object requires special handling since
+ * Bouncy Castle does not support othername as string.
+ * @param name
+ * @return
+ */
+ private ASN1Object addOtherNameAsn1Object(String name) {
+ // Below oid is copied from this URL:
+ // https://docs.microsoft.com/en-us/windows/win32/adschema/a-middlename
+ final String otherNameOID = "2.16.840.1.113730.3.1.34";
+ ASN1EncodableVector otherName = new ASN1EncodableVector();
+ otherName.add(new ASN1ObjectIdentifier(otherNameOID));
+ otherName.add(new DERTaggedObject(
+ true, GeneralName.otherName, new DERUTF8String(name)));
+ return new DERTaggedObject(
+ false, 0, new DERSequence(otherName));
+ }
+
public X509CertificateHolder build()
throws SCMSecurityException, IOException {
Preconditions.checkNotNull(key, "Key cannot be null");
@@ -225,9 +292,7 @@ public final class SelfSignedCertificate {
}
SelfSignedCertificate rootCertificate =
- new SelfSignedCertificate(this.subject,
- this.scmID, this.clusterID, this.beginDate, this.endDate,
- this.config, key);
+ new SelfSignedCertificate(this);
try {
return rootCertificate.generateCertificate(isCA);
} catch (OperatorCreationException | CertIOException e) {
diff --git a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java
index 02d0078..1e3a8f4 100644
--- a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java
+++ b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java
@@ -19,11 +19,14 @@
package org.apache.hadoop.hdds.security.x509.certificates;
+import org.apache.commons.validator.routines.DomainValidator;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
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.SelfSignedCertificate;
import org.apache.hadoop.hdds.security.x509.keys.HDDSKeyGenerator;
+import org.apache.hadoop.ozone.OzoneSecurityUtil;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
@@ -33,6 +36,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
@@ -48,6 +52,9 @@ import java.util.Date;
import java.util.UUID;
import static org.apache.hadoop.hdds.HddsConfigKeys.OZONE_METADATA_DIRS;
+import static org.apache.hadoop.hdds.security.x509.exceptions.CertificateException.ErrorCode.CSR_ERROR;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* Test Class for Root Certificate generation.
@@ -131,7 +138,7 @@ public class TestRootCertificate {
@Test
public void testCACert()
throws SCMSecurityException, NoSuchProviderException,
- NoSuchAlgorithmException, IOException {
+ NoSuchAlgorithmException, IOException, CertificateException {
LocalDate notBefore = LocalDate.now();
LocalDate notAfter = notBefore.plus(365, ChronoUnit.DAYS);
String clusterID = UUID.randomUUID().toString();
@@ -152,6 +159,23 @@ public class TestRootCertificate {
.setConfiguration(conf)
.makeCA();
+ try {
+ DomainValidator validator = DomainValidator.getInstance();
+ // Add all valid ips.
+ OzoneSecurityUtil.getValidInetsForCurrentHost().forEach(
+ ip -> {
+ builder.addIpAddress(ip.getHostAddress());
+ if(validator.isValid(ip.getCanonicalHostName())) {
+ builder.addDnsName(ip.getCanonicalHostName());
+ }
+ });
+ } catch (IOException e) {
+ throw new org.apache.hadoop.hdds.security.x509
+ .exceptions.CertificateException(
+ "Error while adding ip to CA self signed certificate", e,
+ CSR_ERROR);
+ }
+
X509CertificateHolder certificateHolder = builder.build();
// This time we asked for a CertificateServer Certificate, make sure that
// extension is
@@ -165,6 +189,22 @@ public class TestRootCertificate {
// Since this code assigns ONE for the root certificate, we check if the
// serial number is the expected number.
Assert.assertEquals(certificateHolder.getSerialNumber(), BigInteger.ONE);
+
+ CertificateCodec codec = new CertificateCodec(securityConfig, "scm");
+ String pemString = codec.getPEMEncodedString(certificateHolder);
+
+ File basePath = temporaryFolder.newFolder();
+ if (!basePath.exists()) {
+ Assert.assertTrue(basePath.mkdirs());
+ }
+ codec.writeCertificate(basePath.toPath(), "pemcertificate.crt",
+ pemString, false);
+
+ X509CertificateHolder loadedCert =
+ codec.readCertificate(basePath.toPath(), "pemcertificate.crt");
+ assertNotNull(loadedCert);
+ assertEquals(certificateHolder.getSerialNumber(),
+ loadedCert.getSerialNumber());
}
@Test
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index 16d83da..41527a8 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -1363,8 +1363,7 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
.setConfiguration(config)
.setScmID(omStore.getScmId())
.setClusterID(omStore.getClusterID())
- .setSubject(subject)
- .addIpAddress(ip);
+ .setSubject(subject);
OMHANodeDetails haOMHANodeDetails = OMHANodeDetails.loadOMHAConfig(config);
String serviceName =
---------------------------------------------------------------------
To unsubscribe, e-mail: ozone-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: ozone-commits-help@hadoop.apache.org