Skip to content

Commit e7983d7

Browse files
committed
crypto: change validFrom and validTo fields to Date types
Changed the type of `validFrom` and `validTo` from string to Date, parsing them according to OpenSSL's implementation of formatting `ASN1_TIME_print`.
1 parent c6aeddf commit e7983d7

File tree

3 files changed

+131
-6
lines changed

3 files changed

+131
-6
lines changed

doc/api/crypto.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2861,7 +2861,7 @@ Returns the PEM-encoded certificate.
28612861
added: v15.6.0
28622862
-->
28632863

2864-
* Type: {string}
2864+
* Type: {Date}
28652865

28662866
The date/time from which this certificate is valid.
28672867

@@ -2871,7 +2871,7 @@ The date/time from which this certificate is valid.
28712871
added: v15.6.0
28722872
-->
28732873

2874-
* Type: {string}
2874+
* Type: {Date}
28752875

28762876
The date/time until which this certificate is valid.
28772877

lib/internal/crypto/x509.js

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
'use strict';
22

33
const {
4+
ArrayPrototypeIncludes,
5+
Date,
6+
NumberParseInt,
47
ObjectSetPrototypeOf,
58
SafeMap,
9+
StringPrototypeSplit,
610
Symbol,
711
} = primordials;
812

@@ -61,6 +65,51 @@ let lazyTranslatePeerCertificate;
6165

6266
const kInternalState = Symbol('kInternalState');
6367

68+
const ASN1_months = [
69+
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
70+
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
71+
];
72+
73+
// Parse the given certificate validity time according to the OpenSSL implementation of `ASN1_TIME_print`.
74+
// MMM dd hh:mm:ss.ms yyyy GMT
75+
// It is fine to assume that the timezone is in GMT(Zulu), according to RFC 5280.
76+
function parseASN1Time(value = '') {
77+
if (value.length === 0) {
78+
return undefined;
79+
}
80+
81+
const date = new Date();
82+
83+
// Replace all multiple contiguous spaces with a single space
84+
value = value.replace(/ +/g, ' ');
85+
const arr = StringPrototypeSplit(value, ' ');
86+
87+
if (!ArrayPrototypeIncludes(ASN1_months, arr[0])) {
88+
return undefined;
89+
}
90+
91+
const mon = ASN1_months.indexOf(arr[0]);
92+
const day = NumberParseInt(arr[1]);
93+
const year = NumberParseInt(arr[3]);
94+
date.setUTCFullYear(year, mon, day);
95+
96+
const time = StringPrototypeSplit(arr[2], ':');
97+
const hours = NumberParseInt(time[0]);
98+
const min = NumberParseInt(time[1]);
99+
const timeSeconds = StringPrototypeSplit(time[2], '.');
100+
const sec = NumberParseInt(timeSeconds[0]);
101+
date.setUTCHours(hours, min, sec);
102+
103+
if (timeSeconds.length > 1) {
104+
const ms = NumberParseInt(timeSeconds[1]);
105+
date.setUTCMilliseconds(ms);
106+
} else {
107+
date.setUTCMilliseconds(0);
108+
}
109+
110+
return date;
111+
}
112+
64113
function isX509Certificate(value) {
65114
return value[kInternalState] !== undefined;
66115
}
@@ -133,6 +182,7 @@ class X509Certificate {
133182
subjectAltName: this.subjectAltName,
134183
issuer: this.issuer,
135184
infoAccess: this.infoAccess,
185+
isValid: this.isValid,
136186
validFrom: this.validFrom,
137187
validTo: this.validTo,
138188
fingerprint: this.fingerprint,
@@ -208,7 +258,7 @@ class X509Certificate {
208258
value = this[kHandle].validFrom();
209259
this[kInternalState].set('validFrom', value);
210260
}
211-
return value;
261+
return parseASN1Time(value);
212262
}
213263

214264
get validTo() {
@@ -217,7 +267,7 @@ class X509Certificate {
217267
value = this[kHandle].validTo();
218268
this[kInternalState].set('validTo', value);
219269
}
220-
return value;
270+
return parseASN1Time(value);
221271
}
222272

223273
get fingerprint() {

test/parallel/test-crypto-x509.js

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ const der = Buffer.from(
9494
assert.strictEqual(x509.subjectAltName, undefined);
9595
assert.strictEqual(x509.issuer, issuerCheck);
9696
assert.strictEqual(x509.infoAccess, infoAccessCheck);
97-
assert.strictEqual(x509.validFrom, 'Sep 3 21:40:37 2022 GMT');
98-
assert.strictEqual(x509.validTo, 'Jun 17 21:40:37 2296 GMT');
97+
assert.deepStrictEqual(x509.validFrom, new Date('September 3, 2022 21:40:37'));
98+
assert.deepStrictEqual(x509.validTo, new Date('June 17, 2296 21:40:37'));
9999
assert.strictEqual(
100100
x509.fingerprint,
101101
'8B:89:16:C4:99:87:D2:13:1A:64:94:36:38:A5:32:01:F0:95:3B:53');
@@ -359,3 +359,78 @@ UcXd/5qu2GhokrKU2cPttU+XAN2Om6a0
359359

360360
assert.strictEqual(cert.checkIssued(cert), false);
361361
}
362+
363+
{
364+
// Test date parsing of `parseASN1Time`, according to RFC 5280.
365+
366+
// Validity dates up until the year 2049 are encoded as UTCTime.
367+
// The fomatting of UTCTime changes from the year ~1949 to 1950~.
368+
const certPemUTCTime = `-----BEGIN CERTIFICATE-----
369+
MIIE/TCCAuWgAwIBAgIUHbXPaFnjeBehMvdHkXZ+E3a78QswDQYJKoZIhvcNAQEL
370+
BQAwDTELMAkGA1UEBhMCS1IwIBgPMTk0OTEyMjUyMzU5NThaFw01MDAxMDEyMzU5
371+
NThaMA0xCzAJBgNVBAYTAktSMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
372+
AgEAtFfV2DB2dZFFaR1PPZMmyo0mSDAxGReoixxlhQTFZZymU71emWV/6gR8MxAE
373+
L5+uzpgBvOZWgEbELWeV/gzZGU/x1Cki0dSJ0B8Qwr5HvKX6oOZrJ8t+wn4SRceq
374+
r6MRPskDpTjnvelt+VURGmawtKKHll5fSqfjRWkQC8WQHdogXylRjd3oIh9p1D5P
375+
hphK/jKddxsRkLhJKQWqTjAy2v8hsJAxvpCPnlqMCXxjbQV41UTY8+kY3RPG3d6c
376+
yHBGM7dzM7XWVc79V9z/rjdRcxE2eBqrJT/yR3Cok8wWVVfQEgBfpolHUZxA8K4N
377+
tubTez9zsJy7xUG7udf91wXWVHMBHXg6m/u5nIW0fAXGMtnG/H6FMyyBDbJoUlqm
378+
VRTG71DzvBXpd/qx2P5LkU1JjWY3U8HSn6Q1DJzMIrbOmWpdlFYXxzLlXU2vG8Q3
379+
PmdAHDDYW3M2YBVCdKqOtsuL2dMDuqRWdi3iCCPSR2UCm4HzAVYSe2FP8SPcY3xs
380+
1NX+oDSpTxXruJYHGUp10/pXoqMrGT1IBgv2Dhsm3jcfRLSXkaBDJIKLO6dXmLBt
381+
rlxM0DphiKnP6lDjpv7EDMdwsakz0zib3JrTmSLSbwZXR4abITmtbYbTpY3XAq7c
382+
adO8YCMTCtb50ZbYEpGDAjOcWFHUlQQMsgZM2zc8ZHPY4EkCAwEAAaNTMFEwHQYD
383+
VR0OBBYEFExDmZyzdo8ccjX7iFIwU7JYMV+qMB8GA1UdIwQYMBaAFExDmZyzdo8c
384+
cjX7iFIwU7JYMV+qMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
385+
ADEF/JIH+Ku9NqrO47Q/CEn9qpIgmqX10d1joDjchPY3OHIIyt8Xpo845mPBTM7L
386+
dnMJSlkzJEk0ep9qAGGdKpBnLq8B/1mgCWQ81jwrwdYSsY+4xark+7+y0fij6qAt
387+
L4T6aA37nbV5q5/DMOwZucFwRTf9ZI1IjC+MaQmnV01vGCogqqfLQ9v26bVBRE1K
388+
UIixH0r3f/LWtuo0KaebZbb+oq6Zb8ljKJaUlt5OB8Zy5NrcP69r29QJUR57ukT6
389+
rt7fk5mOj2NBLMCErLHa7E6+GAUG94QEgdKzZ4yr2aduhMAfnOnK/HfuXO8TVa8/
390+
+oYENr47M8x139+yu92C8Be1MRk0VHteBaScUL+IaY3HgGbYR1lT0azvIyBN/DCN
391+
bYczI7JQGYVitLuaUYFw/RtK7Qg1957/ZmGeGa+86aTLXbqsGjI951D81EIzdqod
392+
1QW/Jn3yMNeVIzF9eYVEy2DIJjGgM2A8NWbqfWGUAUMRgyTxH1j42tnWG3eRnMsX
393+
UnQfpY8i3v6gYoNNgEZktrqgpmukTWgl08TlDtBCjXTBkcBt4dxDApeoy7XWKq+/
394+
qBY/+uIsG30BRgJhAwApjdnCs7l5xpwtqluXFwOxyTWNV5IfChO7QFqWPlSVIHML
395+
UidvpWWipVLZgK+oDks+bKTobcoXGW9oXobiIYqslXPy
396+
-----END CERTIFICATE-----`.trim();
397+
const c1 = new X509Certificate(certPemUTCTime);
398+
399+
assert.deepStrictEqual(c1.validFrom, new Date('December 25, 1949 23:59:58'));
400+
assert.deepStrictEqual(c1.validTo, new Date('January 1, 1950 23:59:58'));
401+
402+
// The GeneralizedTime format is used for dates in 2050 or later.
403+
const certPemGeneralizedTime = `-----BEGIN CERTIFICATE-----
404+
MIIE/TCCAuWgAwIBAgIUYHPUNd6S5xlNMjrWSaekgCBrbDQwDQYJKoZIhvcNAQEL
405+
BQAwDTELMAkGA1UEBhMCS1IwIBcNNDkxMjI2MDAwMDAxWhgPMjA1MDAxMDIwMDAw
406+
MDFaMA0xCzAJBgNVBAYTAktSMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
407+
AgEAlBPjQXHTzQWflvq6Lc01E0gVSSUQd5XnfK9K8TEN8ic/6iJVBWK8OwTmwh6u
408+
KdSO+DrTpoTA3Wo4T7oSL89xsyJN5JHiIT2VdZvgcXkv+ZL+rZ2INzYSSXbPQ8V+
409+
Md5A7tNWGJOvneD1Pb+AKrVXn6N1+xiKuv08U+d6ZCcv8P2cGUJCQr5BSg6eXPm2
410+
ZIoFhNLDaqleci0P/Bs7uMwKjVr2IP99bCMwTS2STxexEmYf4J3wgNXBOHxspLcS
411+
p7Yt3JgezvzRn5kijQi7ceS24q/fsGCCwB706mOKdYLCfEL1DhhEr27+XICw7zOF
412+
Q8tSe33IfSdxejEVV+lf/jGW5zFH5m+lDTJC0VAUCBG5E7q57yFaoQ44CQWtbMHZ
413+
+dtodKx4B0lzWXJs8xkGo0rl9/1CuY2iPX3lB6xxlX50ruj8stccMwarRzUvfkjw
414+
AhnbUs9X1ooFyVXmVYXWzR0gP1/q05Zob03khX1NipGbMf0RBI4WlItkiRsrEl9x
415+
08YPbrUyd7JnFkgG0O5TcmTzHr9cTJHg5BzclQA9/V0HuslSVOkKMMlKHky2zcqY
416+
dDBmWtfTrvowaB7hTGD6YK4R9JCDUy7oeeK4ZUxRNCnJY698HodE9lQu+F0cJpbY
417+
uZExFapE/AWA8ftlw2/fXoK0L3DhYsOVQkHd2YbrvzZEHVMCAwEAAaNTMFEwHQYD
418+
VR0OBBYEFNptaIzozylFlD0+JKULue+5gvfZMB8GA1UdIwQYMBaAFNptaIzozylF
419+
lD0+JKULue+5gvfZMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
420+
AFXP4SCP6VFMINaKE/rLJOaFiVVuS4qDfpGRjuBryNxey4ErBFsXcPi/D0LIsNkb
421+
c3qsex1cJadZdg3sqdkyliyYgzjJn1fSsiPT8GMiMnckw9awIwqtucGf+6SPdL6o
422+
9jp0xg6KsRHWjN0GetCz89hy9HwSiSwiLpTxVYOMLjQ+ey8KXPk0LNaXve/++hrr
423+
gN+cvcPKkspAE5SMTSKqHwVUD4MRJgdQqYDqB6demCq9Yl+kyQg9gVnuzkpKeNBT
424+
qNVeeA6gczCpYV4rUMqT0UVVPbPOcygwZP2o7tUyNk6fmYzyLpi5R+FYD/PoowFp
425+
LOrIaG426QaXhLr4U0i+HD/LhHZ4AWWt0OYAvbkk/xrhmagUcyeOxUrcYl6tA3NQ
426+
sjPV2FNGitX+zOyxfMxcjf0RpaBbyMsO6DSfQidDchFvPR9VFX4THs/0mP02IK27
427+
MpsZj8AG2/jjPz6ytnWBJGuLeIt2sWnluZyldX+V9QEEhEmrEweUolacKF5ESODG
428+
SHyZZVSUCK0bJfDfk5rXCQokWCIe+jHbW3CSWWmBRz6blZDeO/wI8nN4TWHDMCu6
429+
lawls1QdAwfP4CWIq4T7gsn/YqxMs74zDCXIF0tfuPmw5FMeCYVgnXQ7et8HBfeE
430+
CWwQO8JZjJqFtqtuzy2n+gLCvqePgG/gmSqHOPm2ZbLW
431+
-----END CERTIFICATE-----`.trim();
432+
const c2 = new X509Certificate(certPemGeneralizedTime);
433+
434+
assert.deepStrictEqual(c2.validFrom, new Date('December 26, 2049 00:00:01'));
435+
assert.deepStrictEqual(c2.validTo, new Date('January 2, 2050 00:00:01'));
436+
}

0 commit comments

Comments
 (0)