【问题标题】:Generating RSA public keys from go using x509.MarshalPKCS1PublicKey使用 x509.MarshalPKCS1PublicKey 从 go 生成 RSA 公钥
【发布时间】:2020-01-27 21:22:30
【问题描述】:

我正在尝试从 go 生成一个公钥,如下所示:

reader := rand.Reader
bitSize := 2048

keypair, err := rsa.GenerateKey(reader, bitSize)

这似乎有效并产生了一些有意义的东西。接下来,我想把它的公共部分写成这样的 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 认为它编码了 [应用程序 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

使用我的 go 方法,它会生成:

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(整数)。

不过,这真的是我自己能做到的。谁能告诉我我可能在哪里出轨了?

--克里斯

更新:pkcs1.go 中定义的公钥对象如下所示:

type pkcs1PublicKey struct {
    N *big.Int
    E int
}

我想知道是否由于某种原因默认参数不能正常工作,所以我计划复制该结构并制作我自己的 marshal 方法。我的计划是在 N 和 E 上放置 asn1 字段标签,但在它以某种方式“修复”之前我并没有做到这一点:

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 模块中的调用替换对我的编组器的调用来确认这一点,并且它停止工作。我被难住了。

【问题讨论】:

  • 您是否仅根据openssl 和dumpasn1 工具质疑RSA 密钥的有效性?或者是否存在密钥不起作用的客户端/服务器用例?有趣的是,我看到 PKI CA 以 .pfx 格式生成的证书/密钥,openssl 无法解析 - 但在 Windows 客户端上运行良好。
  • 嗯,是的......但添加对 ASN.1 的理解。这种格式的键是两个整数的序列。而且我知道出于某种原因,go x509 正在使用错误的 ASN.1 类型标签构建它——或者至少一个与这些常用工具所理解的不同的标签。我实际上是在谈论使用 Spring 和 gRPC 的东西,但为此我不想要证书,我只想要一个密钥对。这就是为什么我尽可能选择最简单的信封。不过,既然我有自己的 pkcs1 编组工作,我只是想找人解释一下为什么 go 的 x509 编组器的行为方式。
  • @colminator:如图所示生成的密钥不仅仅是无效的 RSA 密钥格式,它甚至不是有效的 asn1,至少根据 lapo.it 解码器。当我有更多时间时,我将不得不更仔细地检查为什么会这样。

标签: go rsa asn.1


【解决方案1】:

我试图重现您的问题,这对我有用...

考虑

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检查go pem.Encode生成的Base64是否正确。

与您的代码的唯一区别是我使用的 bitSize(64 而不是 2048),因为它在 https://play.golang.org/p/VQ7h9hYtO3W 上有点长

【讨论】:

    猜你喜欢
    • 2012-07-17
    • 2011-07-11
    • 2015-10-19
    • 1970-01-01
    • 2022-08-10
    • 2019-08-15
    • 2017-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多