我try 使用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实现,归结为要点.

然而,当运行第二个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-----

推荐答案

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

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

在使用Go代码生成公钥的情况下,情况相反:这里,页眉和页脚对应于PEM编码的X.509/SPKI密钥的页眉和页脚.但是,正文是PKCS#1格式的.这就是按键不同的原因.修复方法是在EncodePublicKeyToPEM()中使用X.509/SPKI格式的方法MarshalPKIXPublicKey(),而不是MarshalPKCS1PublicKey().

顺便说一句,判断 keys 的最佳方法是使用ASN.1个解析器,例如https://lapo.it/asn1js/.

Go相关问答推荐

如何用';贪婪原则';正确地

使用 OpenTelemetry 统一不同服务的范围

Golang 发送Post请求出现400错误

正确的 shell 程序进入 golang alpine docker 容器的入口点?

此 Golang 程序中的同步问题

如何处理 Go 的 firebase admin sdk 错误?

拆分文本并按空格获取字符串数组,如果文本长度超过 500,则获取字符串数组

Go:从 ssl 证书中获取 'subject/unstructeredName' 的值

处理程序后访问 HTTP 请求上下文

从 Makefile 运行时权限被拒绝

为什么 Go 被认为是部分抢占式的?

合并几千万文件最快的方法是什么

从golang中的url加载图像

如何优雅地映射到 Go 中返回可变长度数组的方法?

通用函数与外部包中的常见成员一起处理不同的 struct ?

是否可以使用按位运算在随机 unicode 字符串中找到重复字符?

防止在 Go 公用文件夹中列出目录

Golang 'defer' 导致发送(接收)API 响应延迟

带有 *s3.S3 对象的 Golang 单元测试

将函数的值作为输入参数返回给另一个