x509.ParsePKCS1PrivateKey fails to parse key generated with openssl

13,395 views
Skip to first unread message

Ain

unread,
Jan 30, 2016, 11:36:16 AM1/30/16
to golang-nuts
Hi

I created an self signed pk/cert using command:

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes

(btw openssl version reports "OpenSSL 1.0.1f 6 Jan 2014"). But trying to use this key with
x509.ParsePKCS1PrivateKey()
fails with following error:

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}  @5

I found an old (in the end of 2011) thread https://groups.google.com/forum/#!msg/golang-nuts/hHFbXwyePDA/8l6IcVwncnwJ where AGL suggest to use command

openssl rsa -in key.pem -out rsakey.pem

to convert the key. Indeed that works, but AGL also wrote that he made the nessesary changes so that the both key formats would be accepted. Now has something changed again or is it me doing something wrong? Should this be filed as bug?

go version go1.5.2 linux/amd64


TIA
ain

Shawn Milochik

unread,
Jan 30, 2016, 11:58:39 AM1/30/16
to golang-nuts

You might try this blog post. It has full examples of all of this. I was able to follow along and copy and run all the code (with minor modifications, because his snippets were not all taken from the same application, so some names changed).

It runs fine for me on Go 1.5.2 and 1.6rc1.


Ain

unread,
Jan 30, 2016, 1:09:22 PM1/30/16
to golang-nuts
Thanks, that's very useful article. However, my main concern is that go's x509 package seems not to be able to parse all the formats commonly used... or was it just me doing something in the wrong way?


ain

Shawn Milochik

unread,
Jan 30, 2016, 1:29:23 PM1/30/16
to golang-nuts
On Sat, Jan 30, 2016 at 1:09 PM, Ain <ain.v...@gmail.com> wrote:
Thanks, that's very useful article. However, my main concern is that go's x509 package seems not to be able to parse all the formats commonly used... or was it just me doing something in the wrong way?


I'm not very familiar with this stuff myself -- I am going to be doing something with self-signed certs in the near future, but haven't yet.

Have you been able to use openssl-generated keypairs with Go before? I was thinking you could find the relevant subset of the sample code in that blog post and test it with keypairs generated in Go and openssl and confirm/deny that it works with one and not the other, or maybe even use the blog post to figure out that the keys you're using need to be manipulated in some additional way described in the post that you're not yet doing.

Sorry I'm not experienced enough with certs in Go to spot the issue, but maybe someone else on the list can get you closer to the right answer.


Ain

unread,
Jan 30, 2016, 1:58:20 PM1/30/16
to golang-nuts
I just started with go and crypto stuff myself, so no, I don't have any prior experience with go/openssl combo.
As I wrote in my original message, converting the key with

openssl rsa -in key.pem -out rsakey.pem
command makes it work wit go (the rsakey.pem is successfully parsed by go), so it seems to be question about formats supported by go.


ain

Piers

unread,
Jan 30, 2016, 2:29:03 PM1/30/16
to golang-nuts

Do you have a short working code example? I think you missed something out that's making it harder to diagnose - x509.ParsePKCS1PrivateKey() doesn't work with PEM format data at all in RSA or generic style - it expects the decoded DER (binary) bytes. But you say it works after running the workaround which also emits PEM. Perhaps you're calling a different function...

Piers

unread,
Jan 30, 2016, 2:31:00 PM1/30/16
to golang-nuts
And when I say "working" I guess I mean compiles and gives a good result with the output of `openssl rsa -in key.pem -out rsakey.pem` but not the direct output of `openssl req`.

Ain

unread,
Jan 30, 2016, 3:45:38 PM1/30/16
to golang-nuts

On Saturday, January 30, 2016 at 9:31:00 PM UTC+2, Piers wrote:
And when I say "working" I guess I mean compiles and gives a good result with the output of `openssl rsa -in key.pem -out rsakey.pem` but not the direct output of `openssl req`.

I used following snipet:

    f, err := os.Open(file)
    if err != nil {
        return nil, err
    }
    buf, err := ioutil.ReadAll(f)
    if err != nil {
        return nil, err
    }
    p, _ := pem.Decode(buf)
    if p == nil {
        return nil, errors.New("no pem block found")
    }
    return x509.ParsePKCS1PrivateKey(p.Bytes)

this returned the error with "direct output of openssl req" but works after converting the key.


ain

 

Piers

unread,
Jan 31, 2016, 6:33:49 AM1/31/16
to golang-nuts
Thanks, that makes sense. It looks to me like the update you mentioned in the first post was made to the cryto/tls package, to support (Load)X509KeyPair() methods.

There is a private function in the crypto/tls package which would probably do what you need (or you could call ParsePKCS8PrivateKey() directly if you know it'll always be that type):   (from https://golang.org/src/crypto/tls/tls.go?#L254)

// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey
(der []byte) (crypto.PrivateKey, error) {
       
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
               
return key, nil
       
}
       
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
               
switch key := key.(type) {
               
case *rsa.PrivateKey, *ecdsa.PrivateKey:
                       
return key, nil
               
default:
                       
return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
               
}
       
}
       
if key, err := x509.ParseECPrivateKey(der); err == nil {
               
return key, nil
       
}


       
return nil, errors.New("crypto/tls: failed to parse private key")
}

Note the return types of this function is different from the different return types of ParsePKCS1PrivateKey and ParsePKCS8PrivateKey!

Pooja Lakkundi

unread,
Apr 20, 2020, 1:55:49 PM4/20/20
to golang-nuts
Hello Ain, 
Did u find any solution to this. Even Iam facing same issue.

Kevin Chadwick

unread,
Apr 20, 2020, 3:02:29 PM4/20/20
to golang-nuts
On 2020-04-20 13:03, Pooja Lakkundi wrote:
> (btw openssl version reports "OpenSSL 1.0.1f 6 Jan 2014"). But trying to use
> this key with
> x509.ParsePKCS1PrivateKey()
> fails with following error:
>
> 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}  @5

Not sure this will help but I found LibreSSL is much faster than stdlib
rsa.GenerateKey.

When I use openssl genrsa

I find x509.ParsePKCS1PrivateKey works fine.

Did you run it through pem.Decode first and hand the output.bytes to
x509.ParsePKCS1PrivateKey?

James Mackerel

unread,
Apr 21, 2020, 12:08:22 PM4/21/20
to golang-nuts
Hi,


If this is your code to parse your private key:

    f, err := os.Open(file)
    if err != nil {
        return nil, err
    }
    buf, err := ioutil.ReadAll(f)
    if err != nil {
        return nil, err
    }
    p, _ := pem.Decode(buf)
    if p == nil {
        return nil, errors.New("no pem block found")
    }
    return x509.ParsePKCS1PrivateKey(p.
Bytes)

I tried your commands. key.pem seems like a pkcs8 encoded key, and rsakey.pem seems like a pkcs1 key. That
may be the reason why you got an error when you try to parse a pkcs8 private key with ParsePKCS1PrivateKey.

James

Trevor Gevers

unread,
Apr 21, 2020, 12:47:08 PM4/21/20
to golang-nuts
I had to do the same thing in order to create a JWT for salesforce integration and the caveat was that the pem file
was encoded, this worked for me, notice as stated above pem.Decode() is needed.

package auth

import (
        "crypto/x509"
        "encoding/pem"
        "fmt"
        jwt "github.com/dgrijalva/jwt-go"
        "io/ioutil"
        "time"
)

func createToken() (token string, err error) {
        claims := jwt.StandardClaims{
                Issuer:    "client_id",
                Subject:   "em...@gmail.com",
                Audience:  "https://login.salesforce.com",
                ExpiresAt: time.Now().Add(time.Minute * 3).Unix(),
        }

        at := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
        crt, err := ioutil.ReadFile("test-crt/private_key.pem")
        if err != nil {
                panic(err)
        }
        block, _ := pem.Decode(crt)
        if block == nil {
                fmt.Println("No PEM blob found")
        }
        signKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
        if err != nil {
                panic(err)
        }
        token, err = at.SignedString(signKey)

        if err != nil {
                return
        }
        return
}
Reply all
Reply to author
Forward
0 new messages