An issuance run stops with entry 22: invalid expiry date, and the request sitting in front of you is perfectly fine. The complaint comes from the CA database parser, not from the CSR and not from the VPN client that will eventually present the certificate. That distinction decides everything after it: a record is malformed, so the repair belongs in the CA state rather than in the request or the peer configuration. Treat the numbered entry as a line in index.txt, and the problem shrinks to a bounded data question with a checkable answer.

Read the error as a database complaint

Every openssl ca invocation walks its text database before it signs anything. Each line holds a status letter, an expiry timestamp, an optional revocation timestamp, a serial in hex, a filename and the subject. When one field will not parse, the command reports the line number and stops instead of guessing at the rest of the record.

That stop is protective rather than pedantic. A record the tool cannot read leaves it unable to decide whether the requested subject already exists, whether a serial has been handed out, or what the next CRL should contain. Continuing past a bad line would produce exactly the quiet inconsistency that surfaces months later, usually during a revocation nobody can explain. Read the message as a data complaint and the investigation stays inside the CA directory.

Copy the CA state before touching anything

Snapshot the whole CA directory, not just the database file. The index, its attribute file, the serial and CRL number files, the private key and the issued certificates all take part in later operations, and a repair that restores one while missing the others creates a second incident. Use the CA's own backup routine where one exists, and pause scheduled issuance for the duration of the work.

Record the OpenSSL build and the configuration file the failing command actually loaded. Installations often keep more than one CA directory, and the error only means something against the database that the selected configuration names.

Find the entry and decode its date field

The reported line number counts from the first record, so sed -n '22p' index.txt lands on the offending entry once you have confirmed there are no blank or comment lines above it. Print that line with visible delimiters, because tabs and trailing spaces are common and invisible in an ordinary editor window.

The expiry field is a timestamp whose width depends on the year. Values through 2049 are written in UTCTime as YYMMDDHHMMSSZ, while later values switch to GeneralizedTime with a four-digit year. A record that mixes the two forms, or that carries a two-digit year where the parser now expects four, fails at exactly this point. So does a truncated field left behind by an interrupted write.

Compare the record against the certificate it refers to rather than against another CA's file. The signed certificate is the authority on what the validity should say, and it already exists as a parseable object.

Repair the record against the signed certificate

Read the real dates with openssl x509 -in issued.pem -noout -dates -serial -subject. Convert notAfter into the timestamp width your build writes for that year, and confirm the serial matches the entry you are about to edit before you change a character.

Edit only the broken field, keep the status letter and the serial, and leave the rest of the line alone. If the record cannot be reconciled with any signed certificate, treat it as a stray entry and retire it through the CA's own revocation path instead of deleting the line by hand.

Do not repair by trimming a date to fit a guess about the year. A two-character cut is not portable between builds, and it silently changes the validity the database reports to future duplicate-subject and CRL checks. When the original value is unrecoverable, reissue under a new serial and write down why the old one was abandoned. The audit trail is part of the repair, not paperwork around it.

Verify issuance and CRL generation before you walk away

Run one issuance with the same configuration and confirm it completes without a line-number complaint. Then generate a CRL with openssl ca -gencrl -config openssl.cnf and check that it parses, because a damaged record often surfaces there rather than during signing.

Inspect the new certificate with openssl x509 -noout -dates -serial and compare it to the line the CA just wrote. Confirm the status letter and expiry agree with the certificate, and confirm openssl verify -CAfile ca.pem issued.pem returns without error. Then restore scheduled issuance and watch the first run, since a second malformed line will announce itself immediately.

Ingingo zihuye