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]