Skip to content

crypto: X509Certificate returns "Bad time value" instead of throwing for an unreadable validity time #67285

Open

Description

@ndossche

Version

v26.13.0 (also v26.12.0 and main)

Platform

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');

const pem = `-----BEGIN CERTIFICATE-----
MIIC5TCCAc2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0
IENBMB4XDTI2MTEwNTIyNDc0OFoXDTI2MTIwNTk5NDc0OFowFjEUMBIGA1UEAwwL
ZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNayUE
WSzK683fVdu/NUbXxjf/q6NEspMkAQapmQeWGERAZvs67X+WvnGrNyHVfRnZ1T2m
kj+mSoRtUFkUsGS1lrQMxurmh2CFszK+U27gZwLCamBZ823kEV5rL9exmSzH7ySV
Le+IX5IauVEONSaKhvng1iMZyp0wdnHaNiPbfznrz6kjxcA3QeymfN45E3pcxKcN
L2HvGRRsMmZgK0X3kwOVrr9EBm+tfZtEwcc4NCdA5P2iFKiwGsiCP4UWQiXVpV5l
rC9WuoY0nBWL+SrpBi/TUPpKezMJt4WhTAUKTd5mJkz4oxfcKs9kgq5AC3cyCFbq
pRXUGFDYn1HGJY0pAgMBAAGjQjBAMB0GA1UdDgQWBBRTRZOBbsiOaLkPQvNbIuca
5QtLmDAfBgNVHSMEGDAWgBQioYz7xbMnzJE0zuCU33ABjrroEDANBgkqhkiG9w0B
AQsFAAOCAQEAfCcZz4yPRjXukSTl3dXBEYTWAum8KEUiXpXpFGjMVJVZUp1cPUVz
Den67I34xJ4EPiu9VAOOtgmPrBX+531h4fCsHiw1frWSwvDKzDEFs413TMSIUbI0
EWEJeMkrwquPjhFAvzmzuCDr1pdDvLkAc2Bl+IpwaFKiCtTSJ9GRIvn4JDZoCXxB
uNAQu8DSrkuGjKowpDGutOOGjA5dtp308dU13Ae4WFY4ABoTIjpqu8V6fx85Eucd
z97SfIMwqcaWPkPWr0bMQwwaTrzYkNYCa+Zy5qk8hx7V1lcBlHBAMRK8NA2t6Ptj
Ih0JzM/oAu6BuPAsmq1FbAkXEiCrvq0V6A==
-----END CERTIFICATE-----`;

const x = new X509Certificate(pem);
console.log(x.validTo);                            // 'Bad time value'
console.log(x.validToDate);                        // Invalid Date
console.log(x.toLegacyObject().valid_to);          // 'Bad time value'
console.log(Date.now() > new Date(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.

Activity

  1. panva commented on Nov 6, 2026

    @panva
    Member

    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.

  2. panva commented on Nov 6, 2026

    @panva
    Member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions