Skip to content

crypto/x509: ParseCertificate fails to parse certificates with a compressed EC public key #73142

Description

@austin-jadlowiec

Go version

go version go1.23.5 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/austin/.cache/go-build'
GOENV='/home/austin/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/austin/.go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/austin/.go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.23.5'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/austin/.config/go/telemetry'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefi
x-map=/tmp/go-build2413653899=/tmp/go-build -gno-record-gcc-switches'

What did you do?

Background

We have an existing EC (prime256v1) root CA certificate that we need to use with some Go infrastructure, however it has a compressed key as its subject public key. Resigning the certificate is a difficult operation due to the private key being placed in a dedicated hardware security module (HSM).

This certificate is able to be successfully parsed and validated by other crypto stacks (namely openssl and Google Chrome).

I ultimately noticed that this comes from the fact that only elliptic.Unmarshal is called in parsePublicKey, instead of elliptic.UnmarshallCompressed in the case the pubkey is compressed.

x, y := elliptic.Unmarshal(namedCurve, der)

Reproduction

Generating a Compressed Pubkey Certificate

#!/bin/sh
# generate a prime256v1 private key
openssl ecparam -genkey -name prime256v1 -out key.pem
# generate a compressed public key from the private key
openssl ec -in key.pem -pubout -out key.pub -conv_form compressed
# generate the certificate using the compressed public key
openssl x509 -new -sha256 -key key.pem -force_pubkey key.pub -out ca.crt \
    -subj '/C=US/ST=Illinois/L=Chicago/CN=test.com'

# print out the certificate, notice leading 02 or 03 in public key signifying compressed form
openssl x509 -in ca.crt -noout -text

Parsing the Certificate from Go

package main

import (
        "crypto/x509"
        "encoding/pem"
        "fmt"
        "io/ioutil"
        "log"
)

func main() {
        certFile, err := ioutil.ReadFile("ca.crt")
        if err != nil {
                log.Fatalf("Error reading certificate file: %v", err)
        }

        // Decode the PEM encoded certificate
        block, _ := pem.Decode(certFile)
        if block == nil || block.Type != "CERTIFICATE" {
                log.Fatalf("Failed to decode PEM certificate")
        }

        // Parse the certificate
        cert, err := x509.ParseCertificate(block.Bytes)
        if err != nil {
                log.Fatalf("Error parsing certificate: %v", err)
        }

        fmt.Printf("Certificate imported successfully:")
        fmt.Printf("  Subject: %v\n", cert.Subject)
}

What did you see happen?

The output of the above program is the following when using a certificate with a compressed subject pubkey:

2025/04/02 17:18:00 Error parsing certificate: x509: failed to unmarshal elliptic curve point
exit status 1

What did you expect to see?

I expect to see the same thing as for certificates with an uncompressed subject pubkey, EG:

Certificate imported successfully:  Subject: CN=test.com,L=Chicago,ST=Illinois,C=US

Note: This can be replicated by removing -conv_form compressed from the shell script above and regenerating the certificate with an uncompressed subject pubkey.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions