Генерация RSA publi c ключей из go с использованием x509.MarshalPKCS1PublicKey - PullRequest
2 голосов
/ 28 января 2020

Я пытаюсь сгенерировать ключ c publi из go следующим образом:

reader := rand.Reader
bitSize := 2048

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

Это, кажется, работает и генерирует что-то, что имеет смысл. Затем я хочу записать опубликованную часть этого c в виде файла 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
}

Я не знаю, как это работает, но я думаю, что он должен генерировать только одну последовательность c с двумя целыми числами, одним из них является N, другим E. Я не уверен, почему dumpasn1 считает, что он закодировал [ПРИЛОЖЕНИЕ 2], но ни dumpasn1, ни openssl не считают, что сгенерированное было даже допустимым ASN.1.

Если я понимаю документация для Marshal правильно, я думаю, что функция MarshalPKCS1PublicKey должна работать правильно, но по некоторым причинам это не так.

Я немного взглянул на кодировку. Для файла rsa.pub, сгенерированного openssl, он начинается:

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, как описано здесь . Я не эксперт в этом, но я думаю, что это проблема. Я думаю, что это должно быть генерирование тега 0x02 (целое число) с типом тега, установленным в UNIVERSAL.

Это действительно, насколько я могу сделать самостоятельно, хотя. Может кто-нибудь сказать мне, где я мог сойти с рельсов?

- Крис

ОБНОВЛЕНИЕ: ключевой объект publi c, определенный в pkcs1. go, выглядит следующим образом:

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

Мне было интересно, если по каким-то причинам параметры по умолчанию не работают должным образом, поэтому я планировал скопировать эту структуру и создать свой собственный метод маршала. Мой план состоял в том, чтобы поместить теги полей asn1 на N и E, но я не продвинулся слишком далеко, прежде чем это было как-то «исправлено»:

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, и он перестает работать. Я в тупике.

1 Ответ

2 голосов
/ 28 января 2020

Я пытался воспроизвести ваш вопрос, и это работает для меня ...

Рассмотрим

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 определенно есть (вам не нужен инструмент, чтобы увидеть это) РЕДАКТИРОВАТЬ: ошибка для нас не важна.

Я использовал https://base64.guru/converter/decode/hex, чтобы проверить, что Base64, сгенерированный go pem. Код правильный.

Единственная разница с вашим кодом заключается в том, что я использовал bitSize (64 вместо 2048), потому что он был немного длиннее https://play.golang.org/p/VQ7h9hYtO3W

...