使用 x509.MarshalPKCS1PublicKey 从 go 生成 RSA public 密钥
Generating RSA public keys from go using x509.MarshalPKCS1PublicKey
我正在尝试从 go 生成一个 public 密钥,如下所示:
reader := rand.Reader
bitSize := 2048
keypair, err := rsa.GenerateKey(reader, bitSize)
这似乎有效并生成了一些有意义的东西。接下来,我想将其中的 public 部分写成这样的 RSA 文件:
func PublicKeyToPemBytes(prvkey *rsa.PrivateKey) ([]byte, error) {
var pubkey *rsa.PublicKey
pubkey = &prvkey.PublicKey
pubkey_bytes := x509.MarshalPKCS1PublicKey(pubkey)
if pubkey_bytes == nil {
return nil, errors.New("Public key could not be serialized")
}
pubkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubkey_bytes,
},
)
return pubkey_pem, nil
}
这会生成一些看起来或多或少与您期望的一样的东西。这是我生成的虚拟密钥,只是为了显示:
-----BEGIN RSA PUBLIC KEY-----
MIIBCGKCAQEAMU6KIRUM2KACW7ISHRVRVPXG5YC7+D58Y26HV3TBHJCDNYE9Z8NE
S/XOJS58SCJL+6VLCH03RQWFLSSBZRDTAFGE4V0PTZXQ1ECUIVX6EIUWAVIKTQA9
7WEBNFU4MCHVLWFPULDAQOFP02M2WXUCI/DXCHH1R2QJCJWZKAUOERYDOP3+5YZI
CDHWX54T7GIAU6XV9M/5FH39EBLVDITK85/3RKRZIB/6SRBFSKQVWPNG69WJGIZU
YJYQNNKB8QXG5VCHRJ+OXITBWXYKFXBIKUIGE8AKUDL9OI2SR5I0HQ0AMLNCI9DA
SGHT6UQGZMVRKJC9/FVKLRQURLKMUL1AKWIDAQAB
-----END RSA PUBLIC KEY-----
但实际上并不正确:
$ grep -v -- ----- < remote.pub | base64 -d | dumpasn1 -
Warning: Input is non-seekable, some functionality has been disabled.
0 264: SEQUENCE {
4 257: [APPLICATION 2] {
: Error: Spurious EOC in definite-length item.
Error: Invalid data encountered at position 12: 4E 8A.
$ openssl asn1parse -in remote.pub
0:d=0 hl=4 l= 264 cons: SEQUENCE
4:d=1 hl=4 l= 257 cons: appl [ 2 ]
8:d=2 hl=2 l= 49 prim: EOC
59:d=2 hl=2 l= 8 prim: appl [ 11 ]
69:d=2 hl=2 l= 16 cons: appl [ 5 ]
71:d=3 hl=2 l= 0 prim: priv [ 19 ]
Error in encoding
140590716953024:error:0D07209B:asn1 encoding routines:ASN1_get_object:too long:../crypto/asn1/asn1_lib.c:91:
它应该看起来像这样:
0 266: SEQUENCE {
4 257: INTEGER
: 00 FB 11 99 FF 07 33 F6 E8 05 A4 FD 3B 36 CA 68
: E9 4D 7B 97 46 21 16 21 69 C7 15 38 A5 39 37 2E
...
: [ Another 129 bytes skipped ]
265 3: INTEGER 12345
: }
所以我认为我没有正确生成文件。 pkcs1.go 中的编组代码如下所示:
func MarshalPKCS1PublicKey(key *rsa.PublicKey) []byte {
derBytes, _ := asn1.Marshal(pkcs1PublicKey{
N: key.N,
E: key.E,
})
return derBytes
}
我不知道它是如何工作的,但我认为它应该只生成一个包含两个整数的序列,其中一个是 N,另一个是 E。我不确定为什么 dumpasn1 认为它编码[APPLICATION 2] 但 dumpasn1 和 openssl 都认为生成的内容甚至是有效的 ASN.1。
如果我对 documentation for Marshal 的理解正确,我认为 MarshalPKCS1PublicKey 函数应该做正确的事情,但由于某些原因它没有。
我稍微看了一下编码。对于由 openssl 生成的 rsa.pub 文件,它开始于:
0000000 30 82 01 0a 02 82 01 01 00 fb 11 99 ff 07 33 f6
使用我的方法,它生成了这个:
0000000 30 82 01 08 62 82 01 01 00 31 4e 8a 21 15 0c d8
我相信那里的关键信息是他们生成 0x02 (INTEGER) 而 go 方法生成 0x62。该 0x62 标记是一个 APPLICATION/CONSTRUCTED 整数,如 here 所述。我不是这方面的专家,但我认为这就是问题所在。我认为它应该生成标记类型设置为 UNIVERSAL 的标记 0x02(整数)。
不过,这真的是我自己所能做到的。有人可以告诉我我可能在哪里离开 rails 吗?
--克里斯
更新:pkcs1.go 中定义的 public 键对象如下所示:
type pkcs1PublicKey struct {
N *big.Int
E int
}
我想知道是否由于某种原因默认参数无法正常工作,所以我计划复制该结构并制作我自己的 marshal 方法。我的计划是在 N 和 E 上放置 asn1 字段标签,但我没做到那么远就不知何故 'fixed':
type myPublicKey struct {
N *big.Int
E int
}
func myMarshalPKCS1PublicKey(key *rsa.PublicKey) []byte {
derBytes, _ := asn1.Marshal(myPublicKey{
N: key.N,
E: key.E,
})
return derBytes
}
所以我确实这样做了,猜猜是什么......如果我调用 myMarshalPKCS1PublicKey() 而不是 x509 中的那个,我最终会得到一个解析正确的 ASN.1 文件。不仅如此,
openssl rsa -RSAPublicKey_in -in mykey.pub -text
有效 - 它正确地吐出模数和指数。
所以有些不同,尽管我认为我只是使用相同代码的副本。我通过将对我的编组器的调用替换为对 x509 模块中的编组器的调用来确认这一点,然后它停止工作。我被难住了。
我试图重现你的问题,这对我有用......
考虑
package main
import "fmt"
import "crypto/rand"
import "crypto/rsa"
import "crypto/x509"
import "encoding/hex"
import (
"encoding/pem"
"log"
"os"
)
func main() {
reader := rand.Reader
bitSize := 64
keypair, _:= rsa.GenerateKey(reader, bitSize)
fmt.Println("Public key ", &keypair.PublicKey)
pubkey_bytes := x509.MarshalPKCS1PublicKey(&keypair.PublicKey)
fmt.Println(hex.Dump(pubkey_bytes))
block := &pem.Block{
Type: "MESSAGE",
Bytes: pubkey_bytes ,
}
if err := pem.Encode(os.Stdout, block); err != nil {
log.Fatal(err)
}
}
当你运行它时,控制台会显示
Public key &{14927333011981288097 65537}
00000000 30 10 02 09 00 cf 28 8a 49 37 1b 42 a1 02 03 01 |0.....(.I7.B....|
00000010 00 01 |..|
-----BEGIN MESSAGE-----
MBACCQDPKIpJNxtCoQIDAQAB
-----END MESSAGE-----
我用https://asn1.io/asn1playground/
将其粘贴到架构中
World-Schema DEFINITIONS ::=
BEGIN
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
END
点击编译
将其粘贴到解码中
30 10 02 09 00 cf 28 8a 49 37 1b 42 a1 02 03 01 00 01
结果是
RSAPublicKey SEQUENCE: tag = [UNIVERSAL 16] constructed; length = 16
D0023E: Integer or enumerated value too long: 9; check field 'modulus' (type: INTEGER) of PDU #1 'RSAPublicKey'.
modulus INTEGER: tag = [UNIVERSAL 2] primitive; length = 9
2147483647
publicExponent INTEGER: tag = [UNIVERSAL 2] primitive; length = 3
65537
S0012E: Decoding of PDU #1 failed with the return code '10'.
我不确定他们为什么会发现错误,但 SEQUENCE 和 2 INTEGER 肯定在那里(你真的不需要工具来查看它)
编辑:error 与我们无关。
我用https://base64.guru/converter/decode/hex检查gopem.Encode生成的Base64是否正确
与您的代码唯一不同的是我使用的 bitSize(64 而不是 2048),因为它在 https://play.golang.org/p/VQ7h9hYtO3W
上有点长
我正在尝试从 go 生成一个 public 密钥,如下所示:
reader := rand.Reader
bitSize := 2048
keypair, err := rsa.GenerateKey(reader, bitSize)
这似乎有效并生成了一些有意义的东西。接下来,我想将其中的 public 部分写成这样的 RSA 文件:
func PublicKeyToPemBytes(prvkey *rsa.PrivateKey) ([]byte, error) {
var pubkey *rsa.PublicKey
pubkey = &prvkey.PublicKey
pubkey_bytes := x509.MarshalPKCS1PublicKey(pubkey)
if pubkey_bytes == nil {
return nil, errors.New("Public key could not be serialized")
}
pubkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubkey_bytes,
},
)
return pubkey_pem, nil
}
这会生成一些看起来或多或少与您期望的一样的东西。这是我生成的虚拟密钥,只是为了显示:
-----BEGIN RSA PUBLIC KEY-----
MIIBCGKCAQEAMU6KIRUM2KACW7ISHRVRVPXG5YC7+D58Y26HV3TBHJCDNYE9Z8NE
S/XOJS58SCJL+6VLCH03RQWFLSSBZRDTAFGE4V0PTZXQ1ECUIVX6EIUWAVIKTQA9
7WEBNFU4MCHVLWFPULDAQOFP02M2WXUCI/DXCHH1R2QJCJWZKAUOERYDOP3+5YZI
CDHWX54T7GIAU6XV9M/5FH39EBLVDITK85/3RKRZIB/6SRBFSKQVWPNG69WJGIZU
YJYQNNKB8QXG5VCHRJ+OXITBWXYKFXBIKUIGE8AKUDL9OI2SR5I0HQ0AMLNCI9DA
SGHT6UQGZMVRKJC9/FVKLRQURLKMUL1AKWIDAQAB
-----END RSA PUBLIC KEY-----
但实际上并不正确:
$ grep -v -- ----- < remote.pub | base64 -d | dumpasn1 -
Warning: Input is non-seekable, some functionality has been disabled.
0 264: SEQUENCE {
4 257: [APPLICATION 2] {
: Error: Spurious EOC in definite-length item.
Error: Invalid data encountered at position 12: 4E 8A.
$ openssl asn1parse -in remote.pub
0:d=0 hl=4 l= 264 cons: SEQUENCE
4:d=1 hl=4 l= 257 cons: appl [ 2 ]
8:d=2 hl=2 l= 49 prim: EOC
59:d=2 hl=2 l= 8 prim: appl [ 11 ]
69:d=2 hl=2 l= 16 cons: appl [ 5 ]
71:d=3 hl=2 l= 0 prim: priv [ 19 ]
Error in encoding
140590716953024:error:0D07209B:asn1 encoding routines:ASN1_get_object:too long:../crypto/asn1/asn1_lib.c:91:
它应该看起来像这样:
0 266: SEQUENCE {
4 257: INTEGER
: 00 FB 11 99 FF 07 33 F6 E8 05 A4 FD 3B 36 CA 68
: E9 4D 7B 97 46 21 16 21 69 C7 15 38 A5 39 37 2E
...
: [ Another 129 bytes skipped ]
265 3: INTEGER 12345
: }
所以我认为我没有正确生成文件。 pkcs1.go 中的编组代码如下所示:
func MarshalPKCS1PublicKey(key *rsa.PublicKey) []byte {
derBytes, _ := asn1.Marshal(pkcs1PublicKey{
N: key.N,
E: key.E,
})
return derBytes
}
我不知道它是如何工作的,但我认为它应该只生成一个包含两个整数的序列,其中一个是 N,另一个是 E。我不确定为什么 dumpasn1 认为它编码[APPLICATION 2] 但 dumpasn1 和 openssl 都认为生成的内容甚至是有效的 ASN.1。
如果我对 documentation for Marshal 的理解正确,我认为 MarshalPKCS1PublicKey 函数应该做正确的事情,但由于某些原因它没有。
我稍微看了一下编码。对于由 openssl 生成的 rsa.pub 文件,它开始于:
0000000 30 82 01 0a 02 82 01 01 00 fb 11 99 ff 07 33 f6
使用我的方法,它生成了这个:
0000000 30 82 01 08 62 82 01 01 00 31 4e 8a 21 15 0c d8
我相信那里的关键信息是他们生成 0x02 (INTEGER) 而 go 方法生成 0x62。该 0x62 标记是一个 APPLICATION/CONSTRUCTED 整数,如 here 所述。我不是这方面的专家,但我认为这就是问题所在。我认为它应该生成标记类型设置为 UNIVERSAL 的标记 0x02(整数)。
不过,这真的是我自己所能做到的。有人可以告诉我我可能在哪里离开 rails 吗?
--克里斯
更新:pkcs1.go 中定义的 public 键对象如下所示:
type pkcs1PublicKey struct {
N *big.Int
E int
}
我想知道是否由于某种原因默认参数无法正常工作,所以我计划复制该结构并制作我自己的 marshal 方法。我的计划是在 N 和 E 上放置 asn1 字段标签,但我没做到那么远就不知何故 'fixed':
type myPublicKey struct {
N *big.Int
E int
}
func myMarshalPKCS1PublicKey(key *rsa.PublicKey) []byte {
derBytes, _ := asn1.Marshal(myPublicKey{
N: key.N,
E: key.E,
})
return derBytes
}
所以我确实这样做了,猜猜是什么......如果我调用 myMarshalPKCS1PublicKey() 而不是 x509 中的那个,我最终会得到一个解析正确的 ASN.1 文件。不仅如此,
openssl rsa -RSAPublicKey_in -in mykey.pub -text
有效 - 它正确地吐出模数和指数。
所以有些不同,尽管我认为我只是使用相同代码的副本。我通过将对我的编组器的调用替换为对 x509 模块中的编组器的调用来确认这一点,然后它停止工作。我被难住了。
我试图重现你的问题,这对我有用......
考虑
package main
import "fmt"
import "crypto/rand"
import "crypto/rsa"
import "crypto/x509"
import "encoding/hex"
import (
"encoding/pem"
"log"
"os"
)
func main() {
reader := rand.Reader
bitSize := 64
keypair, _:= rsa.GenerateKey(reader, bitSize)
fmt.Println("Public key ", &keypair.PublicKey)
pubkey_bytes := x509.MarshalPKCS1PublicKey(&keypair.PublicKey)
fmt.Println(hex.Dump(pubkey_bytes))
block := &pem.Block{
Type: "MESSAGE",
Bytes: pubkey_bytes ,
}
if err := pem.Encode(os.Stdout, block); err != nil {
log.Fatal(err)
}
}
当你运行它时,控制台会显示
Public key &{14927333011981288097 65537}
00000000 30 10 02 09 00 cf 28 8a 49 37 1b 42 a1 02 03 01 |0.....(.I7.B....|
00000010 00 01 |..|
-----BEGIN MESSAGE-----
MBACCQDPKIpJNxtCoQIDAQAB
-----END MESSAGE-----
我用https://asn1.io/asn1playground/
将其粘贴到架构中
World-Schema DEFINITIONS ::=
BEGIN
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
END
点击编译
将其粘贴到解码中
30 10 02 09 00 cf 28 8a 49 37 1b 42 a1 02 03 01 00 01
结果是
RSAPublicKey SEQUENCE: tag = [UNIVERSAL 16] constructed; length = 16
D0023E: Integer or enumerated value too long: 9; check field 'modulus' (type: INTEGER) of PDU #1 'RSAPublicKey'.
modulus INTEGER: tag = [UNIVERSAL 2] primitive; length = 9
2147483647
publicExponent INTEGER: tag = [UNIVERSAL 2] primitive; length = 3
65537
S0012E: Decoding of PDU #1 failed with the return code '10'.
我不确定他们为什么会发现错误,但 SEQUENCE 和 2 INTEGER 肯定在那里(你真的不需要工具来查看它) 编辑:error 与我们无关。
我用https://base64.guru/converter/decode/hex检查gopem.Encode生成的Base64是否正确
与您的代码唯一不同的是我使用的 bitSize(64 而不是 2048),因为它在 https://play.golang.org/p/VQ7h9hYtO3W
上有点长