Skip to content

Commit 94a3016

Browse files
authored
HDDS-13781. Certificate expiry date should consider time zone daylight saving impact (#9209)
1 parent 5322ff1 commit 94a3016

File tree

10 files changed

+136
-86
lines changed

10 files changed

+136
-86
lines changed

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/SelfSignedCertificate.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@
2828
import java.security.KeyPair;
2929
import java.security.cert.X509Certificate;
3030
import java.time.Duration;
31-
import java.time.LocalDateTime;
32-
import java.time.ZoneId;
31+
import java.time.ZonedDateTime;
3332
import java.util.ArrayList;
3433
import java.util.Date;
3534
import java.util.List;
@@ -71,8 +70,8 @@ public final class SelfSignedCertificate {
7170
private String subject;
7271
private String clusterID;
7372
private String scmID;
74-
private LocalDateTime beginDate;
75-
private LocalDateTime endDate;
73+
private ZonedDateTime beginDate;
74+
private ZonedDateTime endDate;
7675
private KeyPair key;
7776
private SecurityConfig config;
7877
private List<GeneralName> altNames;
@@ -128,12 +127,10 @@ private X509Certificate generateCertificate(BigInteger caCertSerialId) throws Op
128127
X500Name name = new X500Name(dnName);
129128

130129
// Valid from the Start of the day when we generate this Certificate.
131-
Date validFrom =
132-
Date.from(beginDate.atZone(ZoneId.systemDefault()).toInstant());
130+
Date validFrom = Date.from(beginDate.toInstant());
133131

134132
// Valid till end day finishes.
135-
Date validTill =
136-
Date.from(endDate.atZone(ZoneId.systemDefault()).toInstant());
133+
Date validTill = Date.from(endDate.toInstant());
137134

138135
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(name,
139136
serial, validFrom, validTill, name, publicKeyInfo);
@@ -168,8 +165,8 @@ public static class Builder {
168165
private String subject;
169166
private String clusterID;
170167
private String scmID;
171-
private LocalDateTime beginDate;
172-
private LocalDateTime endDate;
168+
private ZonedDateTime beginDate;
169+
private ZonedDateTime endDate;
173170
private KeyPair key;
174171
private SecurityConfig config;
175172
private BigInteger caCertSerialId;
@@ -200,12 +197,12 @@ public Builder setScmID(String s) {
200197
return this;
201198
}
202199

203-
public Builder setBeginDate(LocalDateTime date) {
200+
public Builder setBeginDate(ZonedDateTime date) {
204201
this.beginDate = date;
205202
return this;
206203
}
207204

208-
public Builder setEndDate(LocalDateTime date) {
205+
public Builder setEndDate(ZonedDateTime date) {
209206
this.endDate = date;
210207
return this;
211208
}

hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/TestHddsSecureDatanodeInit.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import java.security.cert.CertificateExpiredException;
4343
import java.security.cert.X509Certificate;
4444
import java.time.Duration;
45-
import java.time.LocalDateTime;
45+
import java.time.ZonedDateTime;
4646
import java.util.ArrayList;
4747
import java.util.List;
4848
import java.util.concurrent.Callable;
@@ -318,7 +318,7 @@ public void testCertificateRotation() throws Exception {
318318

319319
Duration gracePeriod = securityConfig.getRenewalGracePeriod();
320320
X509Certificate newCert =
321-
generateX509Cert(null, LocalDateTime.now().plus(gracePeriod), Duration.ofSeconds(CERT_LIFETIME));
321+
generateX509Cert(null, ZonedDateTime.now().plus(gracePeriod), Duration.ofSeconds(CERT_LIFETIME));
322322
String pemCert = CertificateCodec.getPEMEncodedString(newCert);
323323
SCMSecurityProtocolProtos.SCMGetCertResponseProto responseProto =
324324
SCMSecurityProtocolProtos.SCMGetCertResponseProto
@@ -391,7 +391,7 @@ public void testCertificateRotationRecoverableFailure() throws Exception {
391391

392392
Duration gracePeriod = securityConfig.getRenewalGracePeriod();
393393
X509Certificate newCert =
394-
generateX509Cert(null, LocalDateTime.now().plus(gracePeriod), Duration.ofSeconds(CERT_LIFETIME));
394+
generateX509Cert(null, ZonedDateTime.now().plus(gracePeriod), Duration.ofSeconds(CERT_LIFETIME));
395395
String pemCert = CertificateCodec.getPEMEncodedString(newCert);
396396
// provide an invalid SCMGetCertResponseProto. Without
397397
// setX509CACertificate(pemCert), signAndStoreCert will throw exception.
@@ -441,12 +441,12 @@ public void testCertificateRotationRecoverableFailure() throws Exception {
441441
}
442442

443443
private static X509Certificate generateX509Cert(KeyPair keyPair,
444-
LocalDateTime startDate, Duration certLifetime) throws Exception {
444+
ZonedDateTime startDate, Duration certLifetime) throws Exception {
445445
if (keyPair == null) {
446446
keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
447447
}
448-
LocalDateTime start = startDate == null ? LocalDateTime.now() : startDate;
449-
LocalDateTime end = start.plus(certLifetime);
448+
ZonedDateTime start = startDate == null ? ZonedDateTime.now() : startDate;
449+
ZonedDateTime end = start.plus(certLifetime);
450450
return SelfSignedCertificate.newBuilder()
451451
.setBeginDate(start)
452452
.setEndDate(end)

hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
import java.security.cert.CertPath;
3535
import java.security.cert.CertificateException;
3636
import java.security.cert.X509Certificate;
37-
import java.time.LocalDateTime;
38-
import java.time.ZoneId;
37+
import java.time.Duration;
38+
import java.time.ZonedDateTime;
3939
import java.util.Date;
4040
import java.util.List;
4141
import java.util.concurrent.CompletableFuture;
@@ -205,8 +205,7 @@ public Future<CertPath> requestCertificate(
205205
PKCS10CertificationRequest csr,
206206
CertificateApprover.ApprovalType approverType, NodeType role,
207207
String certSerialId) {
208-
LocalDateTime beginDate = LocalDateTime.now();
209-
LocalDateTime endDate = expiryFor(beginDate, role);
208+
Duration certDuration = getDuration(role);
210209

211210
CompletableFuture<Void> csrInspection = approver.inspectCSR(csr);
212211
CompletableFuture<CertPath> certPathPromise = new CompletableFuture<>();
@@ -224,7 +223,7 @@ public Future<CertPath> requestCertificate(
224223
break;
225224
case KERBEROS_TRUSTED:
226225
case TESTING_AUTOMATIC:
227-
X509Certificate signedCertificate = signAndStoreCertificate(beginDate, endDate, csr, role, certSerialId);
226+
X509Certificate signedCertificate = signAndStoreCertificate(certDuration, csr, role, certSerialId);
228227
CertificateCodec codec = new CertificateCodec(config, componentName);
229228
CertPath certPath = codec.getCertPath();
230229
CertPath updatedCertPath = codec.prependCertToCertPath(signedCertificate, certPath);
@@ -240,27 +239,30 @@ public Future<CertPath> requestCertificate(
240239
return certPathPromise;
241240
}
242241

243-
private LocalDateTime expiryFor(LocalDateTime beginDate, NodeType role) {
242+
private Duration getDuration(NodeType role) {
244243
// When issuing certificates for sub-ca use the max certificate duration similar to self-signed root certificate.
245244
if (role == NodeType.SCM) {
246-
return beginDate.plus(config.getMaxCertificateDuration());
245+
return config.getMaxCertificateDuration();
247246
}
248-
return beginDate.plus(config.getDefaultCertDuration());
247+
return config.getDefaultCertDuration();
249248
}
250249

251250
private X509Certificate signAndStoreCertificate(
252-
LocalDateTime beginDate, LocalDateTime endDate, PKCS10CertificationRequest csr, NodeType role, String certSerialId
251+
Duration duration, PKCS10CertificationRequest csr, NodeType role, String certSerialId
253252
) throws IOException, OperatorCreationException, CertificateException {
254253

254+
ZonedDateTime beginDate = ZonedDateTime.now();
255+
ZonedDateTime endDate = beginDate.plus(duration);
256+
255257
lock.lock();
256258
X509Certificate xcert;
257259
try {
258260
Preconditions.checkState(!Strings.isNullOrEmpty(certSerialId));
259261
xcert = approver.sign(config,
260262
getPrivateKey(),
261263
getCACertificate(),
262-
Date.from(beginDate.atZone(ZoneId.systemDefault()).toInstant()),
263-
Date.from(endDate.atZone(ZoneId.systemDefault()).toInstant()),
264+
Date.from(beginDate.toInstant()),
265+
Date.from(endDate.toInstant()),
264266
csr, scmID, clusterID, certSerialId);
265267
if (store != null) {
266268
store.checkValidCertID(xcert.getSerialNumber());
@@ -486,9 +488,8 @@ private void generateRootCertificate(
486488
SecurityConfig securityConfig, KeyPair key)
487489
throws IOException, SCMSecurityException {
488490
Preconditions.checkNotNull(this.config);
489-
LocalDateTime beginDate = LocalDateTime.now();
490-
LocalDateTime endDate =
491-
beginDate.plus(securityConfig.getMaxCertificateDuration());
491+
ZonedDateTime beginDate = ZonedDateTime.now();
492+
ZonedDateTime endDate = beginDate.plus(securityConfig.getMaxCertificateDuration());
492493
SelfSignedCertificate.Builder builder = SelfSignedCertificate.newBuilder()
493494
.setSubject(this.subject)
494495
.setScmID(this.scmID)

hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultCAServer.java

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@
4444
import java.security.cert.CertificateFactory;
4545
import java.security.cert.X509Certificate;
4646
import java.time.LocalDate;
47-
import java.time.LocalDateTime;
4847
import java.time.ZoneId;
48+
import java.time.ZonedDateTime;
49+
import java.util.Date;
4950
import java.util.List;
51+
import java.util.TimeZone;
5052
import java.util.UUID;
5153
import java.util.concurrent.ExecutionException;
5254
import java.util.concurrent.Future;
@@ -450,9 +452,64 @@ clusterId, scmId, caStore, new DefaultProfile(),
450452
}
451453
}
452454

455+
@Test
456+
public void testDaylightSavingZone() throws Exception {
457+
TimeZone defaultTimeZone = TimeZone.getDefault();
458+
TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
459+
460+
String scmId = RandomStringUtils.secure().nextAlphabetic(4);
461+
String clusterId = RandomStringUtils.secure().nextAlphabetic(4);
462+
KeyPair keyPair =
463+
new HDDSKeyGenerator(securityConfig).generateKey();
464+
//TODO: generateCSR!
465+
PKCS10CertificationRequest csr = new CertificateSignRequest.Builder()
466+
.addDnsName("hadoop.apache.org")
467+
.addIpAddress("8.8.8.8")
468+
.addServiceName("OzoneMarketingCluster002")
469+
.setCA(false)
470+
.setClusterID(clusterId)
471+
.setScmID(scmId)
472+
.setSubject("Ozone Cluster")
473+
.setConfiguration(securityConfig)
474+
.setKey(keyPair)
475+
.build()
476+
.generateCSR();
477+
478+
CertificateServer testCA = new DefaultCAServer("testCA",
479+
clusterId, scmId, caStore,
480+
new DefaultProfile(),
481+
Paths.get(SCM_CA_CERT_STORAGE_DIR, SCM_CA_PATH).toString());
482+
testCA.init(securityConfig, CAType.ROOT);
483+
484+
Future<CertPath> holder = testCA.requestCertificate(
485+
csr, CertificateApprover.ApprovalType.TESTING_AUTOMATIC, SCM,
486+
String.valueOf(System.nanoTime()));
487+
// Right now our calls are synchronous. Eventually this will have to wait.
488+
assertTrue(holder.isDone());
489+
//Test that the cert path returned contains the CA certificate in proper
490+
// place
491+
List<? extends Certificate> certBundle = holder.get().getCertificates();
492+
493+
// verify new created SCM certificate
494+
X509Certificate certificate = (X509Certificate) certBundle.get(0);
495+
Date startDate = certificate.getNotBefore();
496+
Date endDate = certificate.getNotAfter();
497+
assertEquals(securityConfig.getMaxCertificateDuration().toMillis(),
498+
endDate.toInstant().toEpochMilli() - startDate.toInstant().toEpochMilli());
499+
500+
// verify root CA
501+
List<? extends Certificate> certificateList = testCA.getCaCertPath().getCertificates();
502+
certificate = (X509Certificate) certificateList.get(0);
503+
startDate = certificate.getNotBefore();
504+
endDate = certificate.getNotAfter();
505+
assertEquals(securityConfig.getMaxCertificateDuration().toMillis(),
506+
endDate.toInstant().toEpochMilli() - startDate.toInstant().toEpochMilli());
507+
TimeZone.setDefault(defaultTimeZone);
508+
}
509+
453510
private X509Certificate generateExternalCert(KeyPair keyPair) throws Exception {
454-
LocalDateTime notBefore = LocalDateTime.now();
455-
LocalDateTime notAfter = notBefore.plusYears(1);
511+
ZonedDateTime notBefore = ZonedDateTime.now();
512+
ZonedDateTime notAfter = notBefore.plusYears(1);
456513
String clusterID = UUID.randomUUID().toString();
457514
String scmID = UUID.randomUUID().toString();
458515
String subject = "testRootCert";

hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClientTestImpl.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.time.Duration;
4242
import java.time.LocalDateTime;
4343
import java.time.ZoneId;
44+
import java.time.ZonedDateTime;
4445
import java.util.ArrayList;
4546
import java.util.Date;
4647
import java.util.HashSet;
@@ -101,10 +102,10 @@ public CertificateClientTestImpl(OzoneConfiguration conf, boolean autoRenew)
101102
keyGen = new HDDSKeyGenerator(securityConfig);
102103
keyPair = keyGen.generateKey();
103104
rootKeyPair = keyGen.generateKey();
104-
LocalDateTime start = LocalDateTime.now();
105+
ZonedDateTime start = ZonedDateTime.now();
105106
String rootCACertDuration = conf.get(HDDS_X509_MAX_DURATION,
106107
HDDS_X509_MAX_DURATION_DEFAULT);
107-
LocalDateTime end = start.plus(Duration.parse(rootCACertDuration));
108+
ZonedDateTime end = start.plus(Duration.parse(rootCACertDuration));
108109

109110
// Generate RootCA certificate
110111
rootCert = SelfSignedCertificate.newBuilder()
@@ -134,15 +135,14 @@ public CertificateClientTestImpl(OzoneConfiguration conf, boolean autoRenew)
134135
.setDigitalSignature(true)
135136
.setDigitalEncryption(true);
136137

137-
start = LocalDateTime.now();
138+
start = ZonedDateTime.now();
138139
String certDuration = conf.get(HDDS_X509_DEFAULT_DURATION,
139140
HDDS_X509_DEFAULT_DURATION_DEFAULT);
140141
//TODO: generateCSR should not be called...
141142
x509Certificate = approver.sign(securityConfig, rootKeyPair.getPrivate(),
142143
rootCert,
143-
Date.from(start.atZone(ZoneId.systemDefault()).toInstant()),
144-
Date.from(start.plus(Duration.parse(certDuration))
145-
.atZone(ZoneId.systemDefault()).toInstant()),
144+
Date.from(start.toInstant()),
145+
Date.from(start.plus(Duration.parse(certDuration)).toInstant()),
146146
csrBuilder.build().generateCSR(), "scm1", "cluster1",
147147
String.valueOf(System.nanoTime()));
148148
certificateMap.put(x509Certificate.getSerialNumber().toString(),
@@ -259,9 +259,9 @@ public Set<X509Certificate> getAllCaCerts() {
259259
}
260260

261261
public void renewRootCA() throws Exception {
262-
LocalDateTime start = LocalDateTime.now();
262+
ZonedDateTime start = ZonedDateTime.now();
263263
Duration rootCACertDuration = securityConfig.getMaxCertificateDuration();
264-
LocalDateTime end = start.plus(rootCACertDuration);
264+
ZonedDateTime end = start.plus(rootCACertDuration);
265265
rootKeyPair = keyGen.generateKey();
266266
rootCert = SelfSignedCertificate.newBuilder()
267267
.setBeginDate(start)

hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/client/TestRootCaRotationPoller.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import java.security.KeyPair;
2828
import java.security.cert.X509Certificate;
2929
import java.time.Duration;
30-
import java.time.LocalDateTime;
30+
import java.time.ZonedDateTime;
3131
import java.util.ArrayList;
3232
import java.util.HashSet;
3333
import java.util.List;
@@ -72,7 +72,7 @@ public void setup() {
7272
public void testPollerDoesNotInvokeRootCaProcessor() throws Exception {
7373
//Given the root ca poller that knows a set of root ca certificates
7474
X509Certificate knownCert = generateX509Cert(
75-
LocalDateTime.now(), Duration.ofSeconds(50));
75+
ZonedDateTime.now(), Duration.ofSeconds(50));
7676
HashSet<X509Certificate> knownCerts = new HashSet<>();
7777
knownCerts.add(knownCert);
7878
List<String> certsFromScm = new ArrayList<>();
@@ -104,9 +104,9 @@ public void testPollerInvokesRootCaProcessors() throws Exception {
104104
//Given the root ca poller knowing a root ca certificate, and an unknown
105105
//root ca certificate
106106
X509Certificate knownCert = generateX509Cert(
107-
LocalDateTime.now(), Duration.ofSeconds(50));
107+
ZonedDateTime.now(), Duration.ofSeconds(50));
108108
X509Certificate newRootCa = generateX509Cert(
109-
LocalDateTime.now(), Duration.ofSeconds(50));
109+
ZonedDateTime.now(), Duration.ofSeconds(50));
110110
HashSet<X509Certificate> knownCerts = new HashSet<>();
111111
knownCerts.add(knownCert);
112112
List<String> certsFromScm = new ArrayList<>();
@@ -137,9 +137,9 @@ public void testPollerRetriesAfterFailure() throws Exception {
137137
//Given a the root ca poller knowing about a root ca certificate and the
138138
// SCM providing a new one
139139
X509Certificate knownCert = generateX509Cert(
140-
LocalDateTime.now(), Duration.ofSeconds(50));
140+
ZonedDateTime.now(), Duration.ofSeconds(50));
141141
X509Certificate newRootCa = generateX509Cert(
142-
LocalDateTime.now(), Duration.ofSeconds(50));
142+
ZonedDateTime.now(), Duration.ofSeconds(50));
143143
HashSet<X509Certificate> knownCerts = new HashSet<>();
144144
knownCerts.add(knownCert);
145145
List<String> certsFromScm = new ArrayList<>();
@@ -174,10 +174,10 @@ public void testPollerRetriesAfterFailure() throws Exception {
174174
}
175175

176176
private X509Certificate generateX509Cert(
177-
LocalDateTime startDate, Duration certLifetime) throws Exception {
177+
ZonedDateTime startDate, Duration certLifetime) throws Exception {
178178
KeyPair keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
179-
LocalDateTime start = startDate == null ? LocalDateTime.now() : startDate;
180-
LocalDateTime end = start.plus(certLifetime);
179+
ZonedDateTime start = startDate == null ? ZonedDateTime.now() : startDate;
180+
ZonedDateTime end = start.plus(certLifetime);
181181
return SelfSignedCertificate.newBuilder()
182182
.setBeginDate(start)
183183
.setEndDate(end)

hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/utils/TestCertificateCodec.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import java.security.cert.Certificate;
3030
import java.security.cert.CertificateFactory;
3131
import java.security.cert.X509Certificate;
32-
import java.time.LocalDateTime;
32+
import java.time.ZonedDateTime;
3333
import org.apache.commons.lang3.RandomStringUtils;
3434
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
3535
import org.apache.hadoop.hdds.security.SecurityConfig;
@@ -185,8 +185,8 @@ public void testMultipleCertReadWrite() throws Exception {
185185
private X509Certificate generateTestCert() throws Exception {
186186
HDDSKeyGenerator keyGenerator =
187187
new HDDSKeyGenerator(securityConfig);
188-
LocalDateTime startDate = LocalDateTime.now();
189-
LocalDateTime endDate = startDate.plusDays(1);
188+
ZonedDateTime startDate = ZonedDateTime.now();
189+
ZonedDateTime endDate = startDate.plusDays(1);
190190
return SelfSignedCertificate.newBuilder()
191191
.setSubject(RandomStringUtils.secure().nextAlphabetic(4))
192192
.setClusterID(RandomStringUtils.secure().nextAlphabetic(4))

0 commit comments

Comments
 (0)