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/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new c06851e  HDDS-4571. DN handle expired certificates when validate block 
token. (#1754)
c06851e is described below

commit c06851ecae88290d47c54abd5baf36e181b2ecde
Author: Xiaoyu Yao <[email protected]>
AuthorDate: Wed Jan 20 08:52:31 2021 -0800

    HDDS-4571. DN handle expired certificates when validate block token. (#1754)
---
 .../hdds/security/token/BlockTokenVerifier.java    |  13 ++
 .../x509/certificate/client/CertificateClient.java |  12 ++
 .../client/DefaultCertificateClient.java           |  30 ++--
 .../ozone/client/CertificateClientTestImpl.java    |  11 ++
 .../security/TestOzoneBlockTokenSecretManager.java | 159 ++++++++++++++++-----
 5 files changed, 182 insertions(+), 43 deletions(-)

diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenVerifier.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenVerifier.java
index 0c2249a..46dec5a 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenVerifier.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenVerifier.java
@@ -34,6 +34,8 @@ import org.slf4j.LoggerFactory;
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.IOException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
 
 import static 
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Type.GetBlock;
@@ -106,6 +108,17 @@ public class BlockTokenVerifier implements TokenVerifier {
           "(OmCertSerialId: " + tokenId.getOmCertSerialId() +
           ") of the block token for user: " + tokenUser);
     }
+
+    try {
+      signerCert.checkValidity();
+    } catch (CertificateExpiredException exExp) {
+      throw new BlockTokenException("Block token can't be verified due to " +
+          "expired certificate " + tokenId.getOmCertSerialId());
+    } catch (CertificateNotYetValidException exNyv) {
+      throw new BlockTokenException("Block token can't be verified due to " +
+          "not yet valid certificate " + tokenId.getOmCertSerialId());
+    }
+
     boolean validToken = caClient.verifySignature(tokenId.getBytes(),
         token.getPassword(), signerCert);
     if (!validToken) {
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
index 34b4930..c776398 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
@@ -200,4 +200,16 @@ public interface CertificateClient {
     RECOVER
   }
 
+  /**
+   * Get signature algorithm used by signer.
+   * @return signature algorithm
+   */
+  String getSignatureAlgorithm();
+
+  /**
+   * Get security provider.
+   * @return security provider
+   */
+  String getSecurityProvider();
+
 }
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
index 6c077ba..8ee0019 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
@@ -317,8 +317,8 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
   public byte[] signDataStream(InputStream stream)
       throws CertificateException {
     try {
-      Signature sign = Signature.getInstance(securityConfig.getSignatureAlgo(),
-          securityConfig.getProvider());
+      Signature sign = Signature.getInstance(getSignatureAlgorithm(),
+          getSecurityProvider());
       sign.initSign(getPrivateKey());
       byte[] buffer = new byte[1024 * 4];
 
@@ -335,6 +335,11 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
     }
   }
 
+  @Override
+  public String getSecurityProvider() {
+    return securityConfig.getProvider();
+  }
+
   /**
    * Creates digital signature over the data stream using the s private key.
    *
@@ -344,8 +349,8 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
   @Override
   public byte[] signData(byte[] data) throws CertificateException {
     try {
-      Signature sign = Signature.getInstance(securityConfig.getSignatureAlgo(),
-          securityConfig.getProvider());
+      Signature sign = Signature.getInstance(getSignatureAlgorithm(),
+          getSecurityProvider());
 
       sign.initSign(getPrivateKey());
       sign.update(data);
@@ -359,6 +364,11 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
     }
   }
 
+  @Override
+  public String getSignatureAlgorithm() {
+    return securityConfig.getSignatureAlgo();
+  }
+
   /**
    * Verifies a digital Signature, given the signature and the certificate of
    * the signer.
@@ -372,8 +382,8 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
   public boolean verifySignature(InputStream stream, byte[] signature,
       X509Certificate cert) throws CertificateException {
     try {
-      Signature sign = Signature.getInstance(securityConfig.getSignatureAlgo(),
-          securityConfig.getProvider());
+      Signature sign = Signature.getInstance(getSignatureAlgorithm(),
+          getSecurityProvider());
       sign.initVerify(cert);
       byte[] buffer = new byte[1024 * 4];
 
@@ -403,8 +413,8 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
   public boolean verifySignature(byte[] data, byte[] signature,
       X509Certificate cert) throws CertificateException {
     try {
-      Signature sign = Signature.getInstance(securityConfig.getSignatureAlgo(),
-          securityConfig.getProvider());
+      Signature sign = Signature.getInstance(getSignatureAlgorithm(),
+          getSecurityProvider());
       sign.initVerify(cert);
       sign.update(data);
       return sign.verify(signature);
@@ -428,8 +438,8 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
   private boolean verifySignature(byte[] data, byte[] signature,
       PublicKey pubKey) throws CertificateException {
     try {
-      Signature sign = Signature.getInstance(securityConfig.getSignatureAlgo(),
-          securityConfig.getProvider());
+      Signature sign = Signature.getInstance(getSignatureAlgorithm(),
+          getSecurityProvider());
       sign.initVerify(pubKey);
       sign.update(data);
       return sign.verify(signature);
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/CertificateClientTestImpl.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/CertificateClientTestImpl.java
index 7664d75..5910b2c 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/CertificateClientTestImpl.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/CertificateClientTestImpl.java
@@ -172,4 +172,15 @@ public class CertificateClientTestImpl implements 
CertificateClient {
   public InitResponse init() throws CertificateException {
     return null;
   }
+
+  @Override
+  public String getSignatureAlgorithm(){
+    return securityConfig.getSignatureAlgo();
+  }
+
+  @Override
+  public String getSecurityProvider(){
+    return securityConfig.getProvider();
+  }
+
 }
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/TestOzoneBlockTokenSecretManager.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/TestOzoneBlockTokenSecretManager.java
index 1895aa7..29ba083 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/TestOzoneBlockTokenSecretManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/TestOzoneBlockTokenSecretManager.java
@@ -28,26 +28,42 @@ import 
org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
 import org.apache.hadoop.hdds.security.x509.SecurityConfig;
 import 
org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
 import 
org.apache.hadoop.hdds.security.x509.certificate.client.OMCertificateClient;
-import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
 import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.test.LambdaTestUtils;
 import org.apache.hadoop.util.Time;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v1CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.mockito.Mockito;
 
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
 import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
+import java.security.SecureRandom;
 import java.security.Signature;
+import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.util.Date;
 import java.util.EnumSet;
 
 /**
@@ -64,6 +80,7 @@ public class TestOzoneBlockTokenSecretManager {
   private static final String BASEDIR = GenericTestUtils
       .getTempPath(TestOzoneBlockTokenSecretManager.class.getSimpleName());
   private BlockTokenVerifier tokenVerifier;
+  private final static String ALGORITHM = "SHA256withRSA";
 
   @Rule
   public ExpectedException exception = ExpectedException.none();
@@ -79,41 +96,25 @@ public class TestOzoneBlockTokenSecretManager {
     // Create Ozone Master certificate (SCM CA issued cert) and key store.
     SecurityConfig securityConfig = new SecurityConfig(conf);
     x509Certificate = KeyStoreTestUtil
-        .generateCertificate("CN=OzoneMaster", keyPair, 30, "SHA256withRSA");
+        .generateCertificate("CN=OzoneMaster", keyPair, 30, ALGORITHM);
     omCertSerialId = x509Certificate.getSerialNumber().toString();
     secretManager = new OzoneBlockTokenSecretManager(securityConfig,
         expiryTime, omCertSerialId);
-    client = getCertificateClient(securityConfig);
-    client.init();
+    client = Mockito.mock(OMCertificateClient.class);
+    Mockito.when(client.getCertificate()).thenReturn(x509Certificate);
+    Mockito.when(client.getCertificate(Mockito.anyString())).
+        thenReturn(x509Certificate);
+    Mockito.when(client.getPublicKey()).thenReturn(keyPair.getPublic());
+    Mockito.when(client.getPrivateKey()).thenReturn(keyPair.getPrivate());
+    Mockito.when(client.getSignatureAlgorithm()).thenReturn(
+        securityConfig.getSignatureAlgo());
+    Mockito.when(client.getSecurityProvider()).thenReturn(
+        securityConfig.getProvider());
+    Mockito.when(client.verifySignature((byte[]) Mockito.any(),
+        Mockito.any(), Mockito.any())).thenCallRealMethod();
+
     secretManager.start(client);
     tokenVerifier = new BlockTokenVerifier(securityConfig, client);
-
-  }
-
-  private CertificateClient getCertificateClient(SecurityConfig secConf)
-      throws Exception {
-    return new OMCertificateClient(secConf){
-      @Override
-      public X509Certificate getCertificate() {
-        return x509Certificate;
-      }
-
-      @Override
-      public X509Certificate getCertificate(String certSerialId)
-          throws CertificateException {
-        return x509Certificate;
-      }
-
-      @Override
-      public PrivateKey getPrivateKey() {
-        return keyPair.getPrivate();
-      }
-
-      @Override
-      public PublicKey getPublicKey() {
-        return keyPair.getPublic();
-      }
-    };
   }
 
   @After
@@ -269,4 +270,96 @@ public class TestOzoneBlockTokenSecretManager {
     tokenVerifier.verify(testUser2, writeToken.encodeToUrlString(),
         ContainerProtos.Type.ReadChunk, testBlockId2);
   }
+
+  @Test
+  public void testExpiredCertificate() throws Exception {
+    String tokenBlockID = "102";
+    Token<OzoneBlockTokenIdentifier> blockToken =
+        secretManager.generateToken("testUser2", tokenBlockID,
+            EnumSet.allOf(AccessModeProto.class), 100);
+
+    // Mock client with an expired cert
+    X509Certificate expiredCert = generateExpiredCert(
+        "CN=OzoneMaster", keyPair, ALGORITHM);
+    Mockito.when(client.getCertificate(Mockito.anyString())).
+        thenReturn(expiredCert);
+
+    exception.expect(BlockTokenException.class);
+    exception.expectMessage("Block token can't be verified due to expired" +
+        " certificate");
+    tokenVerifier.verify("testUser", blockToken.encodeToUrlString(),
+        ContainerProtos.Type.PutBlock, "102");
+
+    // Reset to original valid certificate
+    Mockito.when(client.getCertificate(Mockito.anyString())).
+        thenReturn(x509Certificate);
+
+    tokenVerifier.verify("testUser", blockToken.encodeToUrlString(),
+        ContainerProtos.Type.PutBlock, "102");
+  }
+
+  @Test
+  public void testNetYetValidCertificate() throws Exception {
+    String tokenBlockID = "103";
+    Token<OzoneBlockTokenIdentifier> blockToken =
+        secretManager.generateToken("testUser3", tokenBlockID,
+            EnumSet.allOf(AccessModeProto.class), 100);
+
+    // Mock client with an expired cert
+    X509Certificate netYetValidCert = generateNotValidYetCert(
+        "CN=OzoneMaster", keyPair, ALGORITHM);
+    Mockito.when(client.getCertificate(Mockito.anyString())).
+        thenReturn(netYetValidCert);
+
+    exception.expect(BlockTokenException.class);
+    exception.expectMessage("Block token can't be verified due to not" +
+        " yet valid certificate");
+    tokenVerifier.verify("testUser", blockToken.encodeToUrlString(),
+        ContainerProtos.Type.PutBlock, "103");
+
+    // Reset to original valid certificate
+    Mockito.when(client.getCertificate(Mockito.anyString())).
+        thenReturn(x509Certificate);
+
+    tokenVerifier.verify("testUser", blockToken.encodeToUrlString(),
+        ContainerProtos.Type.PutBlock, "103");
+  }
+
+  private X509Certificate generateExpiredCert(String dn,
+      KeyPair pair, String algorithm) throws CertificateException,
+      IllegalStateException, IOException, OperatorCreationException {
+    Date from = new Date();
+    Date to = new Date(from.getTime() + 100L);
+    return generateTestCert(dn, pair, algorithm, from, to);
+  }
+
+  private X509Certificate generateNotValidYetCert(String dn,
+      KeyPair pair, String algorithm) throws CertificateException,
+      IllegalStateException, IOException, OperatorCreationException {
+    Date from = new Date(Instant.now().toEpochMilli() + 100000L);
+    Date to = new Date(from.getTime() + 200000L);
+    return generateTestCert(dn, pair, algorithm, from, to);
+  }
+
+  private X509Certificate generateTestCert(String dn,
+      KeyPair pair, String algorithm, Date from, Date to)
+      throws CertificateException, IllegalStateException,
+      IOException, OperatorCreationException {
+    BigInteger sn = new BigInteger(64, new SecureRandom());
+    SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(
+        pair.getPublic().getEncoded());
+    X500Name subjectDN = new X500Name(dn);
+    X509v1CertificateBuilder builder = new X509v1CertificateBuilder(
+        subjectDN, sn, from, to, subjectDN, subPubKeyInfo);
+
+    AlgorithmIdentifier sigAlgId =
+        new DefaultSignatureAlgorithmIdentifierFinder().find(algorithm);
+    AlgorithmIdentifier digAlgId =
+        new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
+    ContentSigner signer =
+        new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
+            
.build(PrivateKeyFactory.createKey(pair.getPrivate().getEncoded()));
+    X509CertificateHolder holder = builder.build(signer);
+    return new JcaX509CertificateConverter().getCertificate(holder);
+  }
 }
\ No newline at end of file


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

Reply via email to