Go加密库创建的PKCS1公钥和”openssl rsa …”之间的区别是什么?

huangapple go评论70阅读模式
英文:

Difference between a PKCS1 public key created by Go crypto libraries and "openssl rsa ..."

问题

我尝试使用Go语言的rsa和x509包生成一对PKCS8编码的私钥和相应的PKCS1编码的公钥。我希望与下面的openssl命令具有相同的行为:

$ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
$ openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub

我在下面添加了我的Go实现,只包含关键部分。要使用它,首先使用GeneratePrivateKey生成一个私钥(示例附在末尾,位大小为2048),然后使用以下两个方法对私钥和公钥进行编码。

然而,当我运行第二个openssl命令对由我的Go实现编码的私钥进行编码时,公钥不同(我在下面提供了一个示例以说明差异)。特别是,看起来openssl的输出在我的Go实现的输出之前添加了一个前缀。

如果能解释这个差异(最好是如何修复我的代码),我将非常感激。非常感谢!

我的Go代码如下:

package xyz

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
)

func GeneratePrivateKey(bitSize int) (*rsa.PrivateKey, error) {
	privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
	if err != nil {
		return nil, err
	}
	err = privateKey.Validate()
	if err != nil {
		return nil, err
	}
	return privateKey, nil
}

func EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey) (string, error) {
	privateDER, err := x509.MarshalPKCS8PrivateKey(privateKey)
	if err != nil {
		return "", err
	}
	privateBlock := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   privateDER,
	}
	privatePEM := pem.EncodeToMemory(&privateBlock)
	return string(privatePEM), nil
}

func EncodePublicKeyToPEM(privateKey *rsa.PrivateKey) string {
	publicRsaKey := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
	publicKeyBytes := pem.EncodeToMemory(
		&pem.Block{
			Type:  "PUBLIC KEY",
			Bytes: publicRsaKey,
		})
	return string(publicKeyBytes)
}

一个示例私钥:

-----BEGIN RSA PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDnT3OOrKW+S2m4
3fMyl3BjCxnZ2n39wlnYqunYSor5S8wlynhPc72tY8xmTVGhxBYul9LAXyO1/LLU
4cNARmHtSSmeUeYLCU3PMKP795oL4k/YPLqnracIyieN5s8JnmWGtqZo7hyysvhu
Yp0LDmJcQRpB3BoDyTs++/amRP5fnRkkoFABqIejbJEgsjmfXlRnjLR8H4cK8Dmp
5tm2QzSAWos/E3LV/yhhe4b0oTqxGf+lHC5i4FgPd4Co/oaq47QOQkqarxFd57DH
yKNQF3vbGJmVucFnHC+7l4QflcP2Kp/6WJL7IDXA5FC1CFLC5YCmT1pxog/0iPJb
bOiMC1KHAgMBAAECggEBANrJru7j5L3zDOP8o/1VestkA6apYQS2YU9sklWwZHVb
OZUBRv0359Bqljqppa4SED0wA/tLwjZv+CMeDBXuvM+5pTEIHESSH1P7mRBKah4V
iRpqjs7tlRHeTL2O9IQPm85pU0vq9aAdYpjek9aSW30RYymqa7zrCSwRHYK1vblm
zbGUpz3PQp5Mp/CPPBGW2arK5Syo25AJ9YphaWAKub+NXPRZTIVZFylvDtONQmwt
D8YknEPB9eQcgL41egW+ViOs7WOpB6qM8nvbNa+lvdGawog0cZefeNw0PyHxQmhB
Mt/VzvbFaqG2aeH73Na+oFX9/DuQxrvns126R42VK4kCgYEA9lkqUWvVSq8MBb+h
QLZKlSlJiJ1R/nmYqd98r7DCZYwNTRF4LqK0DUbHJCF0WJ3G0UxuKNc98+k3JVz+
5YiZMj6S660XYgQR62GaGKLiVUQmFrKCYUmLrpgDse/pvnA6BfM6MMwR/eIlitGn
B65QarKMwRphCIRrVXnViFs/QNsCgYEA8F91NvVxk14cQPBzdeHK5Z1E+ekHLQHU
nvdWpuqF4n/UWrQg4JI9ACjZIbEd5oJRkQa7M8KXonaQwFlqTe9Ov7MrMon4/CmC
l+XxBLMIb5Pif+biNle3Jhdk6N7H16S9w4PpyaT4nSCU7HNHVIbb+yjr2awKzMSH
yKi51VYKXsUCgYBMGW1GK65+3KY1HGZezDEr9M4fYqHSn5N2XnBxAYR7xBDTzuK2
XVKLi2K3GZKMUWj6fMbG3P7pWxDK/PhMxa61Y7kVDqpbf/BdKxBkLeUG+/9cIZa/
IO4CaDkz/W3Sg26ZKfK+4jtolzSLtooHiGSVIUTz3gc9j1Js3C8HaVCX4wKBgA2b
eck4r8tF4peShRbBR3kkJAl5tZxIpD6zhcZBNH8T+XOBCvNrk+R0a4ZKvoSUEAYD
uYk+lNLvn1YkOa5bw7t6axGbWer8dpYewDwh3nJijsnqyheFc6rGXEIKiomcm9l7
mDqACkrq00NnazxPvhhDwsY5xQq+fWlQk+SN0Fx5AoGAKIMHfXkYrObD8NxE4kvs
Knx3/aH2vAAsDbC+mA2wZOSZ+hsD3SO62ypI3UW3Fxn5+55sQMsMCqaOJBXrLZMh
2iyTMk7V5Bx+//+pa5Hv3yPmDeGtlAT+gbwaqrh5KM1eaW1TVZ1HdUNmamh9Iq0O
KrEPYf2r8+IjAhctV08Mx7I=
-----END RSA PRIVATE KEY-----

由我的Go实现编码的公钥:

-----BEGIN PUBLIC KEY-----
MIIBCgKCAQEA509zjqylvktpuN3zMpdwYwsZ2dp9/cJZ2Krp2EqK+UvMJcp4T3O9
rWPMZk1RocQWLpfSwF8jtfyy1OHDQEZh7UkpnlHmCwlNzzCj+/eaC+JP2Dy6p62n
CMonjebPCZ5lhramaO4csrL4bmKdCw5iXEEaQdwaA8k7Pvv2pkT+X50ZJKBQAaiH
o2yRILI5n15UZ4y0fB+HCvA5qebZtkM0gFqLPxNy1f8oYXuG9KE6sRn/pRwuYuBY
D3eAqP6GquO0DkJKmq8RXeewx8ijUBd72xiZlbnBZxwvu5eEH5XD9iqf+liS+yA1
wORQtQhSwuWApk9acaIP9IjyW2zojAtShwIDAQAB
-----END PUBLIC KEY-----

由第二个openssl命令生成的公钥(注意,它与前一个公钥只有前缀MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A不同):

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA509zjqylvktpuN3zMpdw
YwsZ2dp9/cJZ2Krp2EqK+UvMJcp4T3O9rWPMZk1RocQWLpfSwF8jtfyy1OHDQEZh
7UkpnlHmCwlNzzCj+/eaC+JP2Dy6p62nCMonjebPCZ5lhramaO4csrL4bmKdCw5i
XEEaQdwaA8k7Pvv2pkT+X50ZJKBQAaiHo2yRILI5n15UZ4y0fB+HCvA5qebZtkM0
gFqLPxNy1f8oYXuG9KE6sRn/pRwuYuBYD3eAqP6GquO0DkJKmq8RXeewx8ijUBd7
2xiZlbnBZxwvu5eEH5XD9iqf+liS+yA1wORQtQhSwuWApk9acaIP9IjyW2zojAtS
hwIDAQAB
-----END PUBLIC KEY-----
英文:

I try to generate a pair of a PKCS8 encoded private key and the corresponding PKCS1 encoded public key using the rsa and x509 packages of Go. I want to have the same behavior as the openssl commands below:

$ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
$ openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub

I have added my Go implementation below, boiled down to the essentials.
To use it, I first generate a private key with GeneratePrivateKey (with bit size 2048 for the example attached on the end), and then encode the private key and the public key with the following two methods.

However, when running the second openssl command to encode the public key on the private key encoded by my Go implementation, the public keys differ (I included an example for the difference below). In particular, it looks as if the openssl output extends the output of my Go implementation by a prefix.

I would be grateful for any explaination of the difference (and in the best case, how I can fix my code). Thank you very much!

My Go code looks as follows:

package xyz

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
)

func GeneratePrivateKey(bitSize int) (*rsa.PrivateKey, error) {
	privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
	if err != nil {
		return nil, err
	}
	err = privateKey.Validate()
	if err != nil {
		return nil, err
	}
	return privateKey, nil
}

func EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey) (string, error) {
	privateDER, err := x509.MarshalPKCS8PrivateKey(privateKey)
	if err != nil {
		return "", err
	}
	privateBlock := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   privateDER,
	}
	privatePEM := pem.EncodeToMemory(&privateBlock)
	return string(privatePEM), nil
}

func EncodePublicKeyToPEM(privateKey *rsa.PrivateKey) string {
	publicRsaKey := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
	publicKeyBytes := pem.EncodeToMemory(
		&pem.Block{
			Type:  "PUBLIC KEY",
			Bytes: publicRsaKey,
		})
	return string(publicKeyBytes)
}

An exemplary private key:

-----BEGIN RSA PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDnT3OOrKW+S2m4
3fMyl3BjCxnZ2n39wlnYqunYSor5S8wlynhPc72tY8xmTVGhxBYul9LAXyO1/LLU
4cNARmHtSSmeUeYLCU3PMKP795oL4k/YPLqnracIyieN5s8JnmWGtqZo7hyysvhu
Yp0LDmJcQRpB3BoDyTs++/amRP5fnRkkoFABqIejbJEgsjmfXlRnjLR8H4cK8Dmp
5tm2QzSAWos/E3LV/yhhe4b0oTqxGf+lHC5i4FgPd4Co/oaq47QOQkqarxFd57DH
yKNQF3vbGJmVucFnHC+7l4QflcP2Kp/6WJL7IDXA5FC1CFLC5YCmT1pxog/0iPJb
bOiMC1KHAgMBAAECggEBANrJru7j5L3zDOP8o/1VestkA6apYQS2YU9sklWwZHVb
OZUBRv0359Bqljqppa4SED0wA/tLwjZv+CMeDBXuvM+5pTEIHESSH1P7mRBKah4V
iRpqjs7tlRHeTL2O9IQPm85pU0vq9aAdYpjek9aSW30RYymqa7zrCSwRHYK1vblm
zbGUpz3PQp5Mp/CPPBGW2arK5Syo25AJ9YphaWAKub+NXPRZTIVZFylvDtONQmwt
D8YknEPB9eQcgL41egW+ViOs7WOpB6qM8nvbNa+lvdGawog0cZefeNw0PyHxQmhB
Mt/VzvbFaqG2aeH73Na+oFX9/DuQxrvns126R42VK4kCgYEA9lkqUWvVSq8MBb+h
QLZKlSlJiJ1R/nmYqd98r7DCZYwNTRF4LqK0DUbHJCF0WJ3G0UxuKNc98+k3JVz+
5YiZMj6S660XYgQR62GaGKLiVUQmFrKCYUmLrpgDse/pvnA6BfM6MMwR/eIlitGn
B65QarKMwRphCIRrVXnViFs/QNsCgYEA8F91NvVxk14cQPBzdeHK5Z1E+ekHLQHU
nvdWpuqF4n/UWrQg4JI9ACjZIbEd5oJRkQa7M8KXonaQwFlqTe9Ov7MrMon4/CmC
l+XxBLMIb5Pif+biNle3Jhdk6N7H16S9w4PpyaT4nSCU7HNHVIbb+yjr2awKzMSH
yKi51VYKXsUCgYBMGW1GK65+3KY1HGZezDEr9M4fYqHSn5N2XnBxAYR7xBDTzuK2
XVKLi2K3GZKMUWj6fMbG3P7pWxDK/PhMxa61Y7kVDqpbf/BdKxBkLeUG+/9cIZa/
IO4CaDkz/W3Sg26ZKfK+4jtolzSLtooHiGSVIUTz3gc9j1Js3C8HaVCX4wKBgA2b
eck4r8tF4peShRbBR3kkJAl5tZxIpD6zhcZBNH8T+XOBCvNrk+R0a4ZKvoSUEAYD
uYk+lNLvn1YkOa5bw7t6axGbWer8dpYewDwh3nJijsnqyheFc6rGXEIKiomcm9l7
mDqACkrq00NnazxPvhhDwsY5xQq+fWlQk+SN0Fx5AoGAKIMHfXkYrObD8NxE4kvs
Knx3/aH2vAAsDbC+mA2wZOSZ+hsD3SO62ypI3UW3Fxn5+55sQMsMCqaOJBXrLZMh
2iyTMk7V5Bx+//+pa5Hv3yPmDeGtlAT+gbwaqrh5KM1eaW1TVZ1HdUNmamh9Iq0O
KrEPYf2r8+IjAhctV08Mx7I=                                        
-----END RSA PRIVATE KEY-----

The public key as encoded by my Go implementation:

-----BEGIN PUBLIC KEY-----                                      
MIIBCgKCAQEA509zjqylvktpuN3zMpdwYwsZ2dp9/cJZ2Krp2EqK+UvMJcp4T3O9
rWPMZk1RocQWLpfSwF8jtfyy1OHDQEZh7UkpnlHmCwlNzzCj+/eaC+JP2Dy6p62n
CMonjebPCZ5lhramaO4csrL4bmKdCw5iXEEaQdwaA8k7Pvv2pkT+X50ZJKBQAaiH
o2yRILI5n15UZ4y0fB+HCvA5qebZtkM0gFqLPxNy1f8oYXuG9KE6sRn/pRwuYuBY
D3eAqP6GquO0DkJKmq8RXeewx8ijUBd72xiZlbnBZxwvu5eEH5XD9iqf+liS+yA1
wORQtQhSwuWApk9acaIP9IjyW2zojAtShwIDAQAB                        
-----END PUBLIC KEY-----

The public key as generated by the second openssl command (note that it only differs from the former by the prefix MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A)

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA509zjqylvktpuN3zMpdw
YwsZ2dp9/cJZ2Krp2EqK+UvMJcp4T3O9rWPMZk1RocQWLpfSwF8jtfyy1OHDQEZh
7UkpnlHmCwlNzzCj+/eaC+JP2Dy6p62nCMonjebPCZ5lhramaO4csrL4bmKdCw5i
XEEaQdwaA8k7Pvv2pkT+X50ZJKBQAaiHo2yRILI5n15UZ4y0fB+HCvA5qebZtkM0
gFqLPxNy1f8oYXuG9KE6sRn/pRwuYuBYD3eAqP6GquO0DkJKmq8RXeewx8ijUBd7
2xiZlbnBZxwvu5eEH5XD9iqf+liS+yA1wORQtQhSwuWApk9acaIP9IjyW2zojAtS
hwIDAQAB
-----END PUBLIC KEY-----

答案1

得分: 4

以下是翻译好的内容:

OpenSSL语句生成了一个以PKCS#8格式的私钥和以X.509/SPKI格式的公钥,两者都是PEM编码的。

使用Go代码生成的私钥具有PKCS#8格式,但是PEM编码使用了错误的头部和尾部(正确的应该是*-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----*)。修复方法是在EncodePrivateKeyToPEM()中的pem.Block()调用中相应地调整类型(Type: "PRIVATE KEY")。

对于使用Go代码生成的公钥,情况相反:这里的头部和尾部对应于PEM编码的X.509/SPKI密钥。然而,主体是PKCS#1格式。这就是为什么这两个密钥不同的原因。修复方法是在EncodePublicKeyToPEM()中使用MarshalPKIXPublicKey()方法来处理X.509/SPKI格式,而不是MarshalPKCS1PublicKey()

顺便说一下,检查密钥的最佳方法是使用ASN.1解析器,例如https://lapo.it/asn1js/。

英文:

The OpenSSL statements generate a private key in PKCS#8 format and a public key in X.509/SPKI format, both PEM encoded.

The private key generated with the Go Code has the PKCS#8 format, but the PEM encoding uses the wrong header and footer (correct would be -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----). The fix is to adjust in EncodePrivateKeyToPEM() in the pem.Block() call the type accordingly (Type: "PRIVATE KEY").

In the case of the public key generated with the Go Code, the situation is reversed: Here, the header and footer correspond to those of a PEM encoded X.509/SPKI key. However, the body is PKCS#1 formatted. This is the reason why the keys differ. The fix is to use in EncodePublicKeyToPEM() the method MarshalPKIXPublicKey() for the X.509/SPKI format instead of MarshalPKCS1PublicKey().

By the way, the best way to inspect the keys is to use an ASN.1 parser, e.g. https://lapo.it/asn1js/.

huangapple
  • 本文由 发表于 2022年7月7日 02:00:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/72888041.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定