Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto/x509: ParsePKCS1PrivateKey is not complaint with PKCS#1 standard #14512

Closed
wpc opened this issue Feb 25, 2016 · 5 comments
Closed

crypto/x509: ParsePKCS1PrivateKey is not complaint with PKCS#1 standard #14512

wpc opened this issue Feb 25, 2016 · 5 comments
Assignees
Milestone

Comments

@wpc
Copy link

@wpc wpc commented Feb 25, 2016

According to https://tools.ietf.org/html/rfc3447#section-3.2: a RSA private key may have either
of two representations: The first representation consists of a pair of n(modulus) and d(privateExponent); The second representation is made of primary factors (p, q) and CRT factors (dp, dq, qInv...). Both representation should be valid.

Go's current pkcs#1 implementation can not parse the first representation form of RSA private key. In https://github.com/golang/go/blob/release-branch.go1.6/src/crypto/x509/pkcs1.go#L39, line 54 checks n, d, p, q are all not zero.

if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
    return nil, errors.New("x509: private key contains zero or negative value")
}

This check makes crypto/x509.ParsePKCS1PrivateKey can only parse keys with mixture of first and second representation, such as keys generated by openssl.

I generate a key with Java bouncycastle's org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey, which use the first representation. It can not be parse it by go.

package main

import (
    "crypto/x509"
    "encoding/pem"
    "fmt"
)

var privateKeyStr = `
-----BEGIN RSA PRIVATE KEY-----
MIICHgIBAAKCAQEAkvro39f9TwktskxcxiDB4fzHE4AXDYe2FkWVVMGjlxGUVag3
vK9WPlIKgoGQJvoBnKXbgoM+544+F13JdYTivU9ym90SuBn4KpnZcntLitZWbCs1
zoMS6L1Xp3E6yjgg0fheYTQ1Ux2GZ92EL8eTQtxHijtZSDVRSEShzUlmD4hErYuz
O9qKmbRxBXR0BSec1PyFIrp64oj2HEPcKUijgmHy1LUtAkc3Jy7f8jmxoaJKzecb
RTv4WsV8iDQMD+OOCDa3ZZ6DRJknXsZWqHtfjtoJz0wo7O+E5xa/mkBjyEMeQQm8
VHzcuNu6DslCrUQTpZ2sRJL8c90m/Knp56rVbwIBAAKCAQAFzLTHsGQkbD5LXMwE
XWKJIkpQsKs4M3Sx6Ej+MS3dVio+PwcEAP8sMlyLzcT8ZL0Pi1PgEIG47Vnx3heG
9Zlk0u+4yr5kWVH7jFqSM99FOTPrzMCwx2dFm8MLrmIdfJ1+64f92euIvg+BpbID
mKJs7vg7sh/Rw5jTezHPQegvEKgWPGaJIfCeyeSZsOiWmfypzjfOPZEaHRo99h46
96T4YmG12Rm0TL6O3s3+EW0T20jrwoddoG8yrWKXSgjbliK4yknmR7ZZR+F5atJd
7fxm0tBl+XaqBBUIu2QQV3262ll3QQPFXPjJ5SpXIC+SIG/Mk0FH9ngKpuM0g+Mi
20OxAgEAAgEAAgEAAgEAAgEA
-----END RSA PRIVATE KEY-----
`

func main() {
    decodedPEM, _ := pem.Decode([]byte(privateKeyStr))

    if key, err := x509.ParsePKCS1PrivateKey(decodedPEM.Bytes); err == nil {
        fmt.Println(key)
    } else {
        panic(err)
    }
}
% go run go-pkcs1-issue.go
panic: x509: private key contains zero or negative value

But openssl can read it fine

% openssl rsa -noout -text -in rsa.pem
Private-Key: (2048 bit)
modulus:
    00:92:fa:e8:df:d7:fd:4f:09:2d:b2:4c:5c:c6:20:
    c1:e1:fc:c7:13:80:17:0d:87:b6:16:45:95:54:c1:
    a3:97:11:94:55:a8:37:bc:af:56:3e:52:0a:82:81:
    90:26:fa:01:9c:a5:db:82:83:3e:e7:8e:3e:17:5d:
    c9:75:84:e2:bd:4f:72:9b:dd:12:b8:19:f8:2a:99:
    d9:72:7b:4b:8a:d6:56:6c:2b:35:ce:83:12:e8:bd:
    57:a7:71:3a:ca:38:20:d1:f8:5e:61:34:35:53:1d:
    86:67:dd:84:2f:c7:93:42:dc:47:8a:3b:59:48:35:
    51:48:44:a1:cd:49:66:0f:88:44:ad:8b:b3:3b:da:
    8a:99:b4:71:05:74:74:05:27:9c:d4:fc:85:22:ba:
    7a:e2:88:f6:1c:43:dc:29:48:a3:82:61:f2:d4:b5:
    2d:02:47:37:27:2e:df:f2:39:b1:a1:a2:4a:cd:e7:
    1b:45:3b:f8:5a:c5:7c:88:34:0c:0f:e3:8e:08:36:
    b7:65:9e:83:44:99:27:5e:c6:56:a8:7b:5f:8e:da:
    09:cf:4c:28:ec:ef:84:e7:16:bf:9a:40:63:c8:43:
    1e:41:09:bc:54:7c:dc:b8:db:ba:0e:c9:42:ad:44:
    13:a5:9d:ac:44:92:fc:73:dd:26:fc:a9:e9:e7:aa:
    d5:6f
publicExponent: 0
privateExponent:
    05:cc:b4:c7:b0:64:24:6c:3e:4b:5c:cc:04:5d:62:
    89:22:4a:50:b0:ab:38:33:74:b1:e8:48:fe:31:2d:
    dd:56:2a:3e:3f:07:04:00:ff:2c:32:5c:8b:cd:c4:
    fc:64:bd:0f:8b:53:e0:10:81:b8:ed:59:f1:de:17:
    86:f5:99:64:d2:ef:b8:ca:be:64:59:51:fb:8c:5a:
    92:33:df:45:39:33:eb:cc:c0:b0:c7:67:45:9b:c3:
    0b:ae:62:1d:7c:9d:7e:eb:87:fd:d9:eb:88:be:0f:
    81:a5:b2:03:98:a2:6c:ee:f8:3b:b2:1f:d1:c3:98:
    d3:7b:31:cf:41:e8:2f:10:a8:16:3c:66:89:21:f0:
    9e:c9:e4:99:b0:e8:96:99:fc:a9:ce:37:ce:3d:91:
    1a:1d:1a:3d:f6:1e:3a:f7:a4:f8:62:61:b5:d9:19:
    b4:4c:be:8e:de:cd:fe:11:6d:13:db:48:eb:c2:87:
    5d:a0:6f:32:ad:62:97:4a:08:db:96:22:b8:ca:49:
    e6:47:b6:59:47:e1:79:6a:d2:5d:ed:fc:66:d2:d0:
    65:f9:76:aa:04:15:08:bb:64:10:57:7d:ba:da:59:
    77:41:03:c5:5c:f8:c9:e5:2a:57:20:2f:92:20:6f:
    cc:93:41:47:f6:78:0a:a6:e3:34:83:e3:22:db:43:
    b1
prime1: 0
prime2: 0
exponent1: 0
exponent2: 0
coefficient: 0

Go version: go1.6 darwin/amd64
OS: Mac OSX 10.11.1
Openssl version: 0.9.8zg 14 July 2015

@agl
Copy link
Contributor

@agl agl commented Mar 10, 2016

The first form is very obscure and supporting it cleanly would require code to recover the primes from the private exponent. That's possible, but it's complex and thus it's unclear whether it would be worthwhile.

@agl agl self-assigned this Mar 10, 2016
@bradfitz bradfitz changed the title crypto/x509.ParsePKCS1PrivateKey is not complaint with PKCS#1 standard crypto/x509: ParsePKCS1PrivateKey is not complaint with PKCS#1 standard Apr 10, 2016
@bradfitz bradfitz added this to the Unplanned milestone Apr 10, 2016
@agl
Copy link
Contributor

@agl agl commented Apr 12, 2016

(I'm going to close this unless a significant motivation arises.)

@agl agl closed this Apr 12, 2016
@fetzerms
Copy link

@fetzerms fetzerms commented Jun 5, 2016

I am currently facing the same problem. Is there some kind of workaround (maybe using openssl?) to convert the .pem file to a appropriate format?

@eahydra
Copy link

@eahydra eahydra commented Jun 6, 2016

@fetzerms To solve the problem, I used to manual build private key, copy the crypto/rsa/pkcs1v15.go SignPKCS1v15, rewrite it to avoid check, so can be work. like this: https://play.golang.org/p/fsenJlFhQY

becasue My project just need to signature verify, I don't depend OpenSSL that is so big.
Hope to help you.

@fetzerms
Copy link

@fetzerms fetzerms commented Jun 6, 2016

Some little updates on how i solved worked around this issue:

First i dumped the private key using openssl
openssl rsa -noout -text -in rsa.pem

I then used a java function from Stackoverflow to convert the openssl output to a Java BigInteger (Url: http://stackoverflow.com/a/9303339). Afterwards i used another java method to calculate p and q from Stackoverflow aswell (Url: http://stackoverflow.com/a/28299742).

After converting the numbers from hex to decimal, I finally used the calculated p and q values to generate a new .pem file using rsatool from github (https://github.com/ius/rsatool):
python rsatool.py -f PEM -o key.pem -p <number> -q <number>

The generated key.pem can be used with the x509.ParsePKCS1PrivateKey func. Maybe these steps are helpful for other people aswell.

@golang golang locked and limited conversation to collaborators Jun 6, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants
You can’t perform that action at this time.