正如问题所述,谁能告诉我如何将base64编码的公钥转换为crypto.PublicKeyecdsa.PublicKey?我在网上找不到这样的例子.任何有关这方面的文件或片段都将不胜感激.

推荐答案

由于特别提到了ECDSA,ParsePKIXPublicKey应该是要使用的功能,如对问题的 comments 中所指出的.ParsePKCS1PublicKey将用于较旧格式的RSA公钥(可由报头-----BEGIN RSA PUBLIC KEY-----识别).顺便说一句,ParsePKIXPublicKey还可以使用编码的密钥类型对象标识符来解析较新格式的RSA公钥(可由头部-----BEGIN PUBLIC KEY-----识别,请注意缺失的RSA).除了支持ECDSA和RSA外,还支持DSA和ED25519.

下面是一个完整的、自包含的示例,展示了如何使用OpenSSL创建ECDSARSA格式的样例密钥,并使用go读取公钥.

Generation of an RSA public key

openssl genrsa -out rsaPrivateKey.pem 1024  
openssl rsa -in rsaPrivateKey.pem -pubout > rsaPublicKey.pem

Generation of an ECDSA public key

openssl ecparam -name prime256v1 -genkey -out ecdsaPrivateKey.pem 
openssl ec -in ecdsaPrivateKey.pem -pubout -out ecdsaPublicKey.pem

Test

测试可能如下所示:

package main

import (
    "crypto/dsa"
    "crypto/ecdsa"
    "crypto/ed25519"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "errors"
    "fmt"
    "log"
    "os"
)

func main() {
    rsaPublicKey, err := readPublicKey("rsaPublicKey.pem")
    if err != nil {
        log.Fatal(err)
    }
    err = printPublicKey(rsaPublicKey)
    if err != nil {
        log.Fatal(err)
    }

    ecdsaPublicKey, err := readPublicKey("ecdsaPublicKey.pem")
    if err != nil {
        log.Fatal(err)
    }
    err = printPublicKey(ecdsaPublicKey)
    if err != nil {
        log.Fatal(err)
    }
}

这里使用两个函数readPublicKeyprintPublicKey,它们分别用公钥读取和打印PEM文件.

如文档所示,读取RSA或ECDSA密钥的代码应该是相同的:

func readPublicKey(filename string) (pub any, err error) {
    publicKey, err := os.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    block, _ := pem.Decode(publicKey)
    if block == nil {
        return nil, errors.New("decoding public key PEM failed")
    }

    pub, err = x509.ParsePKIXPublicKey(block.Bytes)
    if err != nil {
        return nil, err
    }
    return pub, nil
}

最后,可以通过类型切换轻松地访问数据,如注释中引用的documentation所示.

func printPublicKey(pub any) error {
    switch pub := pub.(type) {
    case *rsa.PublicKey:
        fmt.Println("pub is of type RSA:", pub)
    case *dsa.PublicKey:
        fmt.Println("pub is of type DSA:", pub)
    case *ecdsa.PublicKey:
        fmt.Println("pub is of type ECDSA:", pub)
    case ed25519.PublicKey:
        fmt.Println("pub is of type Ed25519:", pub)
    default:
        return errors.New("unknown type of public key")
    }
    return nil
}

Differentiation of the key types

使用很酷的在线ASN.1脚本解码器https://lapo.it/asn1js/,用户可以输入生成的ECDSA pem.

这将在第一对象识别符ecPublicKey (ANSI X9.62 public key type)中显示.

而对于RSA人来说,这将是rsaEncryption (PKCS #1)人.

secp256k1

并不是所有的曲线类型都受GO的加密包支持,特别是secp256k1.例如,您可以通过上述在线工具中的第二个对象标识符来标识曲线类型.

try 将上面的代码与其一起使用将产生以下错误消息:x509: unsupported elliptic curve.

在这种情况下,您可以例如使用包github.com/decred/dcrd/secp256k1.

为了获得secp256k1.ParsePubKey的适当输入形式,例如可以使用未压缩的密钥格式(0x04+x+y),该格式又可以通过Unmarshalasn1包中提取(也参见ParsePKIXPublicKey的内部 struct ).

Self-contained example

Generation of an ECDSA public key with secp256k1 curve type

openssl ecparam -name secp256k1 -genkey -out ecdsaSecp256k1PrivateKey.pem
openssl ec -in ecdsaSecp256k1PrivateKey.pem -pubout -out ecdsaSecp256k1PublicKey.pem

用于测试的自含式示例可能如下所示:

package main

import (
    "crypto/x509/pkix"
    "encoding/asn1"
    "encoding/pem"
    "errors"
    "fmt"
    "github.com/decred/dcrd/dcrec/secp256k1"
    "log"
    "os"
)

type publicKeyInfo struct {
    Raw       asn1.RawContent
    算法rithm pkix.算法rithmIdentifier
    PublicKey asn1.BitString
}

func readSecp256k1PublicKey(filename string) (*secp256k1.PublicKey, error) {
    publicKey, err := os.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    block, _ := pem.Decode(publicKey)
    if block == nil {
        return nil, errors.New("decoding public key PEM failed")
    }
    var pki publicKeyInfo
    if _, err := asn1.Unmarshal(block.Bytes, &pki); err != nil {
        return nil, err
    }
    pub, err := secp256k1.ParsePubKey(pki.PublicKey.Bytes)
    if err != nil {
        return nil, err
    }
    return pub, nil
}

func main() {
    pub, err := readSecp256k1PublicKey("ecdsaSecp256k1PublicKey.pem")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("public key %T", pub)
}

调试控制台上的输出将为:

public key *secp256k1.PublicKey

Go相关问答推荐

语法-for循环中的initit陈述是否允许分配?

Go汇编器命名为Constants

按键值排序字符串- Golang

允许在 struct 中使用复合作为函数参数

这是实现超时的常见方法,为什么 time.After 不起作用

从给定顶点查找图形中所有闭合路径的算法

Exchange Web 服务 - 使用 soap xml 请求查找所有未读邮件

泛型:实现嵌套接口

在反向 GORM 中创建查询有一个关系

如何确定作为函数参数传递的指针是否正在被修改或副本是否正在被修改?

如何将 npm 安装进度条通过管道传输到终端?

通过环境变量配置 OTLP 导出器

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

Go 泛型:自引用接口约束

未定义 protoc protoc-gen-go 时间戳

有没有办法判断值是否满足接口中定义的类型约束?

Golang SSH客户端错误无法验证,try 的方法[无公钥],没有支持的方法

Go模板中的浮点除法

在 go (1.18) 的泛型上实现多态的最佳方法是什么?

Go 泛型是否与 LINQ to Objects 等效?