Skip to content

TLS Handshake failed: tls: failed to parse certificate from server: x509: negative serial number #755

Open

Description

@jam-re

Latest release to date shows the following error on certain certificates: TLS Handshake failed: tls: failed to parse certificate from server: x509: negative serial number

A new release using the latest Go version should be done to fix

Activity

  1. dlevy-msft-sql commented on May 19, 2026

    @dlevy-msft-sql
    Contributor

    Thanks for the report! Short version: this is almost certainly your SQL Server's self-signed certificate, not a sqlcmd bug, and a Go upgrade won't fix it.

    What's happening

    The error comes from Go's standard library (crypto/x509), not from sqlcmd or go-mssqldb directly. Since Go 1.23, the parser rejects X.509 certificates with negative serial numbers, because RFC 5280 §4.1.2.2 requires the serial to be a positive integer. In Go 1.23 there was an opt-out via GODEBUG=x509negativeserial=1; that knob was deprecated in 1.24 and removed in 1.25. sqlcmd is already built with Go 1.25, so rebuilding against a newer toolchain would make this stricter, not looser.

    Why this is almost certainly SQL Server 2017

    SQL Server's self-signed TLS certificate (the one it generates at startup when no admin-provided cert is configured) historically used a random 16-byte serial without clearing the high bit. About half the time the result DER-encodes as a negative integer. SQL Server 2017's generator was never fixed, and the certificate persists across restarts, so a server that drew an unlucky serial will fail every connection from any modern client.

    We can confirm this is 2017-specific from the go-mssqldb CI itself (pr-validation.yml): the matrix runs 2017/2019/2022/2025, and only the 2017-latest branch sets GODEBUG=x509negativeserial=1 with the comment:

    SQL 2017's self-signed certificate has a negative serial number that Go 1.23+ rejects by default. This GODEBUG override is only for CI testing against SQL Server 2017 and MUST NOT be used in production.

    2019/2022/2025 don't need it.

    Could you confirm SELECT @@VERSION and whether the cert is the auto-generated self-signed one (subject == issuer, matches the machine name) or one you provisioned? That tells us which fix applies.

    Fixes, in order of correctness

    1. Replace the SQL Server certificate with an RFC-compliant one. Any modern CA (current AD CS, OpenSSL with a positive -set_serial, public CAs) will do. This is the right long-term fix and also unblocks recent .NET, JDK 21+, and Python-on-current-OpenSSL clients that are tightening the same rule.
    2. Disable encryption for this connection: sqlcmd -No ... (or -N false). This bypasses the TLS handshake entirely so the cert is never parsed. Only appropriate on a trusted network. Note: -C (trust server certificate) alone will not work, because parsing happens before validation, and the cert has to parse before we get a chance to skip verification.
    3. Not recommended: building sqlcmd against Go 1.22 with GODEBUG=x509negativeserial=1. Maintenance dead end, and the GODEBUG no longer exists in Go 1.25.

    What's coming on the driver side

    go-mssqldb#356 is open to wrap this exact handshake error with actionable guidance (the GODEBUG context, the -No workaround, and a pointer to reissuing the cert), so the next sqlcmd release built against an updated driver will print something more useful than the bare x509: negative serial number. The underlying behavior won't change, but the diagnostic will.

    We're going to close this as not actionable inside sqlcmd. Please reopen if your server cert is out of your control and -No isn't viable, so we can discuss it further.

  2. jam-re commented on May 21, 2026

    @jam-re
    Author

    It happens with mssql/server:2019-CU30-ubuntu-20.04 as well (sometimes) and GODEBUG=x509negativeserial=1 has to be set

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