What version of Go are you using (go version)?
The docker image golang:1.10-stretch from Docker Hub, although I can also reproduce this bug locally with go1.10.5 on Debian stable from the stretch-backports repository.
$ go version
go version go1.10.5 linux/amd64
Does this issue reproduce with the latest release?
Yes, specifically with go version go1.11.2 linux/amd64 from the docker image golang:latest (at time of writing, December 7th, 2018, 17:41).
What operating system and processor architecture are you using (go env)?
docker on Debian stable from the official repositories:
Details
% docker version
Client:
Version: 18.09.0
API version: 1.39
Go version: go1.10.4
Git commit: 4d60db4
Built: Wed Nov 7 00:48:46 2018
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 18.09.0
API version: 1.39 (minimum version 1.12)
Go version: go1.10.4
Git commit: 4d60db4
Built: Wed Nov 7 00:16:44 2018
OS/Arch: linux/amd64
Experimental: false
Running go env in the docker image yields:
go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build381253034=/tmp/go-build -gno-record-gcc-switches"
What did you do?
Try me on play.golang.org.
Compiling the following program yields the following error:
package main
import (
"crypto/x509"
"encoding/pem"
"log"
)
func main() {
pemEncodedPublicKey :=
[]byte(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArBPlIPFnrLAOPY7Ltkxh
+Y1Jn7G0hjY+OVSb1I4Qi2zaypXMAeDKtcVoyCaEfGk1U6oMDVLj09iehUGtNBt8
jtV5IVoTu4DBlc9gKLdBt61mUb3l9ClZS6JWCiac3puInA+6VyBqtjcrwNITrHvw
O/qWRTmovG/Mw8kh49rYPDZ1jh3JMCAfbck3dvg/ULQVYMtjvTDDz9rKoPywz6Mn
68EgXy86jXfB0ekhhwuTnEALlO1bDQ0hrdzg8tum08OJJQH89VmbV2jB8lCZleS3
u11NvpdCkkjWK5+lyvPaVrg76pIYZ6gmpD9l6MbIK9XNmMHemsmRMi18HLuh+bTE
XwIDAQAB
-----END PUBLIC KEY-----`)
pkcs1RSAPublicKey, _ := pem.Decode(pemEncodedPublicKey)
_, err := x509.ParsePKCS1PublicKey(pkcs1RSAPublicKey.Bytes)
if err != nil {
log.Fatalf("Could not parse PKCS1 RSA public key: %s", err)
}
}
Output:
2018/12/07 17:44:27 Could not parse PKCS1 RSA public key: asn1: structure error: tags don't match (2 vs {class:0 tag:16 length:13 isCompound:true}) {optional:false explicit:false application:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} @2
What did you expect to see?
No error, as the provided public key is a valid PKCS#1 public key, generated by openssl.
What did you see instead?
An error indicating invalid ASN.1 encoding.
In-depth explanation
The problem stems from crypto/x509.ParsePKCS1PublicKey expecting a public key encoded like this:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
However, a valid PCKS#1 public key can also look like this:
PublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
PublicKey BIT STRING
}
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}
If we put the public key provided in the program in the file pkcs1.pem, we can see the ASN structure of our cert by using openssl:
% openssl asn1parse -in pkcs1.pem -inform pem -dump
0:d=0 hl=4 l= 290 cons: SEQUENCE
4:d=1 hl=2 l= 13 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING
0000 - 00 30 82 01 0a 02 82 01-01 00 ac 13 e5 20 f1 67 .0........... .g
0010 - ac b0 0e 3d 8e cb b6 4c-61 f9 8d 49 9f b1 b4 86 ...=...La..I....
0020 - 36 3e 39 54 9b d4 8e 10-8b 6c da ca 95 cc 01 e0 6>9T.....l......
0030 - ca b5 c5 68 c8 26 84 7c-69 35 53 aa 0c 0d 52 e3 ...h.&.|i5S...R.
0040 - d3 d8 9e 85 41 ad 34 1b-7c 8e d5 79 21 5a 13 bb ....A.4.|..y!Z..
0050 - 80 c1 95 cf 60 28 b7 41-b7 ad 66 51 bd e5 f4 29 ....`(.A..fQ...)
0060 - 59 4b a2 56 0a 26 9c de-9b 88 9c 0f ba 57 20 6a YK.V.&.......W j
0070 - b6 37 2b c0 d2 13 ac 7b-f0 3b fa 96 45 39 a8 bc .7+....{.;..E9..
0080 - 6f cc c3 c9 21 e3 da d8-3c 36 75 8e 1d c9 30 20 o...!...<6u...0
0090 - 1f 6d c9 37 76 f8 3f 50-b4 15 60 cb 63 bd 30 c3 .m.7v.?P..`.c.0.
00a0 - cf da ca a0 fc b0 cf a3-27 eb c1 20 5f 2f 3a 8d ........'.. _/:.
00b0 - 77 c1 d1 e9 21 87 0b 93-9c 40 0b 94 ed 5b 0d 0d w...!....@...[..
00c0 - 21 ad dc e0 f2 db a6 d3-c3 89 25 01 fc f5 59 9b !.........%...Y.
00d0 - 57 68 c1 f2 50 99 95 e4-b7 bb 5d 4d be 97 42 92 Wh..P.....]M..B.
00e0 - 48 d6 2b 9f a5 ca f3 da-56 b8 3b ea 92 18 67 a8 H.+.....V.;...g.
00f0 - 26 a4 3f 65 e8 c6 c8 2b-d5 cd 98 c1 de 9a c9 91 &.?e...+........
0100 - 32 2d 7c 1c bb a1 f9 b4-c4 5f 02 03 01 00 01 2-|......_.....
Dropping the first 24 bytes yields the encoded BIT STRING, and is what golang currently accepts as PKCS#1 key:
% openssl asn1parse -in pkcs1.pem -inform pem -dump -strparse 24
0:d=0 hl=4 l= 266 cons: SEQUENCE
4:d=1 hl=4 l= 257 prim: INTEGER :AC13E520F167ACB00E3D8ECBB64C61F98D499FB1B486363E39549BD48E108B6CDACA95CC01E0CAB5C568C826847C693553AA0C0D52E3D3D89E8541AD341B7C8ED579215A13BB80C195CF6028B741B7AD6651BDE5F429594BA2560A269CDE9B889C0FBA57206AB6372BC0D213AC7BF03BFA964539A8BC6FCCC3C921E3DAD83C36758E1DC930201F6DC93776F83F50B41560CB63BD30C3CFDACAA0FCB0CFA327EBC1205F2F3A8D77C1D1E921870B939C400B94ED5B0D0D21ADDCE0F2DBA6D3C3892501FCF5599B5768C1F2509995E4B7BB5D4DBE97429248D62B9FA5CAF3DA56B83BEA921867A826A43F65E8C6C82BD5CD98C1DE9AC991322D7C1CBBA1F9B4C45F
265:d=1 hl=2 l= 3 prim: INTEGER :010001
Coincidentally, this corresponds to dropping the first 32 characters of our PEM-encoded certificate, yielding the "wrong" public key format, which golang accepts happily:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEArBPlIPFnrLAOPY7Ltkxh+Y1Jn7G0hjY+OVSb1I4Qi2zaypXMAeDK
tcVoyCaEfGk1U6oMDVLj09iehUGtNBt8jtV5IVoTu4DBlc9gKLdBt61mUb3l9ClZ
S6JWCiac3puInA+6VyBqtjcrwNITrHvwO/qWRTmovG/Mw8kh49rYPDZ1jh3JMCAf
bck3dvg/ULQVYMtjvTDDz9rKoPywz6Mn68EgXy86jXfB0ekhhwuTnEALlO1bDQ0h
rdzg8tum08OJJQH89VmbV2jB8lCZleS3u11NvpdCkkjWK5+lyvPaVrg76pIYZ6gm
pD9l6MbIK9XNmMHemsmRMi18HLuh+bTEXwIDAQAB
-----END RSA PUBLIC KEY-----
(Sidenote: The standard mandates that header and footer must say RSA PUBLIC KEY, but it works with PUBLIC KEY as well.)
Putting this key in the above program yields no errors.
How to fix
There are a few solutions to this problem:
- Rename / introduce a new function (e.g.
ParseRSAPublicKey) for parsing RSA public keys and fix ParsePKCS1PublicKey to only parse PKCS#1 public keys.
- Cover both formats with
ParsePKCS1PublicKey
I'd opt for the first one as it is the cleanest solution. If this can wait until the new year, I could also take a shot at implementing the solution, whatever it turns out to be.
I haven't checked the other functions in crypto/x509, but I can imagine some of them have the same issue.
What version of Go are you using (
go version)?The docker image
golang:1.10-stretchfrom Docker Hub, although I can also reproduce this bug locally withgo1.10.5on Debian stable from the stretch-backports repository.Does this issue reproduce with the latest release?
Yes, specifically with
go version go1.11.2 linux/amd64from the docker imagegolang:latest(at time of writing, December 7th, 2018, 17:41).What operating system and processor architecture are you using (
go env)?dockeron Debian stable from the official repositories:Details
Running
go envin the docker image yields:go envOutputWhat did you do?
Try me on play.golang.org.
Compiling the following program yields the following error:
Output:
What did you expect to see?
No error, as the provided public key is a valid PKCS#1 public key, generated by
openssl.What did you see instead?
An error indicating invalid ASN.1 encoding.
In-depth explanation
The problem stems from
crypto/x509.ParsePKCS1PublicKeyexpecting a public key encoded like this:However, a valid PCKS#1 public key can also look like this:
If we put the public key provided in the program in the file
pkcs1.pem, we can see the ASN structure of our cert by usingopenssl:% openssl asn1parse -in pkcs1.pem -inform pem -dump 0:d=0 hl=4 l= 290 cons: SEQUENCE 4:d=1 hl=2 l= 13 cons: SEQUENCE 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 17:d=2 hl=2 l= 0 prim: NULL 19:d=1 hl=4 l= 271 prim: BIT STRING 0000 - 00 30 82 01 0a 02 82 01-01 00 ac 13 e5 20 f1 67 .0........... .g 0010 - ac b0 0e 3d 8e cb b6 4c-61 f9 8d 49 9f b1 b4 86 ...=...La..I.... 0020 - 36 3e 39 54 9b d4 8e 10-8b 6c da ca 95 cc 01 e0 6>9T.....l...... 0030 - ca b5 c5 68 c8 26 84 7c-69 35 53 aa 0c 0d 52 e3 ...h.&.|i5S...R. 0040 - d3 d8 9e 85 41 ad 34 1b-7c 8e d5 79 21 5a 13 bb ....A.4.|..y!Z.. 0050 - 80 c1 95 cf 60 28 b7 41-b7 ad 66 51 bd e5 f4 29 ....`(.A..fQ...) 0060 - 59 4b a2 56 0a 26 9c de-9b 88 9c 0f ba 57 20 6a YK.V.&.......W j 0070 - b6 37 2b c0 d2 13 ac 7b-f0 3b fa 96 45 39 a8 bc .7+....{.;..E9.. 0080 - 6f cc c3 c9 21 e3 da d8-3c 36 75 8e 1d c9 30 20 o...!...<6u...0 0090 - 1f 6d c9 37 76 f8 3f 50-b4 15 60 cb 63 bd 30 c3 .m.7v.?P..`.c.0. 00a0 - cf da ca a0 fc b0 cf a3-27 eb c1 20 5f 2f 3a 8d ........'.. _/:. 00b0 - 77 c1 d1 e9 21 87 0b 93-9c 40 0b 94 ed 5b 0d 0d w...!....@...[.. 00c0 - 21 ad dc e0 f2 db a6 d3-c3 89 25 01 fc f5 59 9b !.........%...Y. 00d0 - 57 68 c1 f2 50 99 95 e4-b7 bb 5d 4d be 97 42 92 Wh..P.....]M..B. 00e0 - 48 d6 2b 9f a5 ca f3 da-56 b8 3b ea 92 18 67 a8 H.+.....V.;...g. 00f0 - 26 a4 3f 65 e8 c6 c8 2b-d5 cd 98 c1 de 9a c9 91 &.?e...+........ 0100 - 32 2d 7c 1c bb a1 f9 b4-c4 5f 02 03 01 00 01 2-|......_.....Dropping the first 24 bytes yields the encoded
BIT STRING, and is what golang currently accepts as PKCS#1 key:% openssl asn1parse -in pkcs1.pem -inform pem -dump -strparse 24 0:d=0 hl=4 l= 266 cons: SEQUENCE 4:d=1 hl=4 l= 257 prim: INTEGER :AC13E520F167ACB00E3D8ECBB64C61F98D499FB1B486363E39549BD48E108B6CDACA95CC01E0CAB5C568C826847C693553AA0C0D52E3D3D89E8541AD341B7C8ED579215A13BB80C195CF6028B741B7AD6651BDE5F429594BA2560A269CDE9B889C0FBA57206AB6372BC0D213AC7BF03BFA964539A8BC6FCCC3C921E3DAD83C36758E1DC930201F6DC93776F83F50B41560CB63BD30C3CFDACAA0FCB0CFA327EBC1205F2F3A8D77C1D1E921870B939C400B94ED5B0D0D21ADDCE0F2DBA6D3C3892501FCF5599B5768C1F2509995E4B7BB5D4DBE97429248D62B9FA5CAF3DA56B83BEA921867A826A43F65E8C6C82BD5CD98C1DE9AC991322D7C1CBBA1F9B4C45F 265:d=1 hl=2 l= 3 prim: INTEGER :010001Coincidentally, this corresponds to dropping the first 32 characters of our PEM-encoded certificate, yielding the "wrong" public key format, which golang accepts happily:
(Sidenote: The standard mandates that header and footer must say
RSA PUBLIC KEY, but it works withPUBLIC KEYas well.)Putting this key in the above program yields no errors.
How to fix
There are a few solutions to this problem:
ParseRSAPublicKey) for parsing RSA public keys and fixParsePKCS1PublicKeyto only parse PKCS#1 public keys.ParsePKCS1PublicKeyI'd opt for the first one as it is the cleanest solution. If this can wait until the new year, I could also take a shot at implementing the solution, whatever it turns out to be.
I haven't checked the other functions in
crypto/x509, but I can imagine some of them have the same issue.