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.
Go version
go version go1.23.5 linux/amd64
Output of
go envin your module/workspace: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
openssland Google Chrome).I ultimately noticed that this comes from the fact that only
elliptic.Unmarshalis called inparsePublicKey, instead ofelliptic.UnmarshallCompressedin the case the pubkey is compressed.go/src/crypto/x509/parser.go
Line 294 in 116b823
Reproduction
Generating a Compressed Pubkey Certificate
Parsing the Certificate from Go
What did you see happen?
The output of the above program is the following when using a certificate with a compressed subject pubkey:
What did you expect to see?
I expect to see the same thing as for certificates with an uncompressed subject pubkey, EG:
Note: This can be replicated by removing
-conv_form compressedfrom the shell script above and regenerating the certificate with an uncompressed subject pubkey.