You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
crypto: X509Certificate returns "Bad time value" instead of throwing for an unreadable validity time #67285
Linux ARCH 7.2.14-arch1-1 #1 SMP PREEMPT_DYNAMIC Fri, 30 Oct 2026 14:32:40 +0000 x86_64 GNU/Linux
Subsystem
crypto
What steps will reproduce the bug?
The certificate below is signed by a test CA; its notAfter is a well-formed UTCTime with an invalid hour (261205994748Z).
const{ X509Certificate }=require('node:crypto');constpem=`-----BEGIN CERTIFICATE-----MIIC5TCCAc2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0IENBMB4XDTI2MTEwNTIyNDc0OFoXDTI2MTIwNTk5NDc0OFowFjEUMBIGA1UEAwwLZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNayUEWSzK683fVdu/NUbXxjf/q6NEspMkAQapmQeWGERAZvs67X+WvnGrNyHVfRnZ1T2mkj+mSoRtUFkUsGS1lrQMxurmh2CFszK+U27gZwLCamBZ823kEV5rL9exmSzH7ySVLe+IX5IauVEONSaKhvng1iMZyp0wdnHaNiPbfznrz6kjxcA3QeymfN45E3pcxKcNL2HvGRRsMmZgK0X3kwOVrr9EBm+tfZtEwcc4NCdA5P2iFKiwGsiCP4UWQiXVpV5lrC9WuoY0nBWL+SrpBi/TUPpKezMJt4WhTAUKTd5mJkz4oxfcKs9kgq5AC3cyCFbqpRXUGFDYn1HGJY0pAgMBAAGjQjBAMB0GA1UdDgQWBBRTRZOBbsiOaLkPQvNbIuca5QtLmDAfBgNVHSMEGDAWgBQioYz7xbMnzJE0zuCU33ABjrroEDANBgkqhkiG9w0BAQsFAAOCAQEAfCcZz4yPRjXukSTl3dXBEYTWAum8KEUiXpXpFGjMVJVZUp1cPUVzDen67I34xJ4EPiu9VAOOtgmPrBX+531h4fCsHiw1frWSwvDKzDEFs413TMSIUbI0EWEJeMkrwquPjhFAvzmzuCDr1pdDvLkAc2Bl+IpwaFKiCtTSJ9GRIvn4JDZoCXxBuNAQu8DSrkuGjKowpDGutOOGjA5dtp308dU13Ae4WFY4ABoTIjpqu8V6fx85Eucdz97SfIMwqcaWPkPWr0bMQwwaTrzYkNYCa+Zy5qk8hx7V1lcBlHBAMRK8NA2t6PtjIh0JzM/oAu6BuPAsmq1FbAkXEiCrvq0V6A==-----END CERTIFICATE-----`;constx=newX509Certificate(pem);console.log(x.validTo);// 'Bad time value'console.log(x.validToDate);// Invalid Dateconsole.log(x.toLegacyObject().valid_to);// 'Bad time value'console.log(Date.now()>newDate(x.validTo));// false
How often does it reproduce? Is there a required condition?
Always, for a notBefore or notAfter that OpenSSL cannot convert to a date.
What is the expected behavior? Why is that the expected behavior?
validTo/validToDate (and validFrom/validFromDate) throw, as subjectAltName does for a malformed extension since fd3c87e.
What do you see instead?
"Bad time value" and Invalid Date, without an error. Comparisons with Invalid Date are always false, so a check such as Date.now() > new Date(x.validTo) treats the certificate as not expired.
Additional information
X509View::getValidFrom/getValidTo in deps/ncrypto/ncrypto.cc write the string for unprintable times; before v26.12.1 it was the partial output of a failed ASN1_TIME_print().
Related: #63265, which also covered the unpropagated ASN1_TIME_print() failure.
Comparisons with Invalid Date return false for all four relational operators. A positive validity-window check (now >= notBefore && now <= notAfter) therefore rejects an invalid bound, while an out-of-range check (now < notBefore || now > notAfter) can miss it. In this example only notAfter is malformed, so the notBefore check can still pass.
I have changes lined up to make the validity getters and legacy conversion throw when a validity time cannot be converted. I'll propose those once my current X.509 series has landed.
These will be semver-major changes, so the earliest stable release would be Node.js 27.x, scheduled for April 2027.
In the meantime, a documentation PR clarifying the current behavior would be useful: the string getters can return "Bad time value", and the Date getters can return an invalid Date. Callers should explicitly reject invalid dates before comparing validity bounds.
Activity
panva commentedon Nov 6, 2026
Comparisons with
Invalid Datereturnfalsefor all four relational operators. A positive validity-window check (now >= notBefore && now <= notAfter) therefore rejects an invalid bound, while an out-of-range check (now < notBefore || now > notAfter) can miss it. In this example onlynotAfteris malformed, so thenotBeforecheck can still pass.I have changes lined up to make the validity getters and legacy conversion throw when a validity time cannot be converted. I'll propose those once my current X.509 series has landed.
These will be semver-major changes, so the earliest stable release would be Node.js 27.x, scheduled for April 2027.
In the meantime, a documentation PR clarifying the current behavior would be useful: the string getters can return
"Bad time value", and the Date getters can return an invalidDate. Callers should explicitly reject invalid dates before comparing validity bounds.panva commentedon Nov 6, 2026
cc @pimterry