英文:
how to convert a base64 encoded public key to crypto.PublicKey or a ecdsa.PublicKey
问题
问题中提到,有人能否向我展示如何将一个base64
编码的公钥转换为crypto.PublicKey
或ecdsa.PublicKey
?我在网上找不到相关的示例。对于这个问题,我会很感激任何文档或代码片段。
英文:
As the question states can anyone show me how I can convert a base64
encoded public key into a crypto.PublicKey
or a ecdsa.PublicKey
? I am unable to find an example of this online. Any documentation or snippets on this regard would be appreciated
答案1
得分: 0
由于特别提到了ECDSA
,所以应该使用ParsePKIXPublicKey
函数,正如问题的评论中所指出的。ParsePKCS1PublicKey
用于旧格式的RSA公钥(可通过标题-----BEGIN RSA PUBLIC KEY-----
识别)。顺便说一句,ParsePKIXPublicKey
还可以解析新格式的RSA公钥(可通过标题-----BEGIN PUBLIC KEY-----
识别,注意缺少RSA
)- 使用编码的密钥类型对象标识符。除了ECDSA和RSA之外,还支持DSA和ED25519。
下面是一个完整的、自包含的示例,展示了如何使用openssl在ECDSA
和RSA
格式中创建示例密钥,并使用Go读取公钥。
生成RSA公钥
openssl genrsa -out rsaPrivateKey.pem 1024
openssl rsa -in rsaPrivateKey.pem -pubout > rsaPublicKey.pem
生成ECDSA公钥
openssl ecparam -name prime256v1 -genkey -out ecdsaPrivateKey.pem
openssl ec -in ecdsaPrivateKey.pem -pubout -out ecdsaPublicKey.pem
测试
测试代码可能如下所示:
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)
}
}
这里使用了两个函数readPublicKey
和printPublicKey
,分别用于读取和打印带有公钥的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
}
最后,可以使用类型切换轻松访问数据,就像在评论中引用的文档中演示的那样。
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
}
区分密钥类型
使用在线的ASN.1 JavaScript解码器https://lapo.it/asn1js/,可以将生成的ECDSA pem输入。
第一个对象标识符将显示为ecPublicKey (ANSI X9.62 public key type)
。
而对于RSA
,这将是rsaEncryption (PKCS #1)
。
secp256k1
并非所有的曲线类型都受到Go的crypto包支持,特别是secp256k1
。可以通过上述提到的在线工具中的第二个对象标识符来识别曲线类型。
尝试使用上述代码将会产生以下错误消息:x509: unsupported elliptic curve
。
在这种情况下,可以使用github.com/decred/dcrd/secp256k1
包。为了获得secp256k1.ParsePubKey
的适当输入形式,例如可以使用未压缩的密钥格式(0x04 + x + y),而这可以通过asn1
包中的Unmarshal
从中提取(还可以参考ParsePKIXPublicKey
的内部实现)。
自包含示例
使用secp256k1曲线类型生成ECDSA公钥
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
Algorithm pkix.AlgorithmIdentifier
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
英文:
Since ECDSA
was specifically mentioned, ParsePKIXPublicKey
should be the function to use, as noted in the comments to the questions. ParsePKCS1PublicKey
would be for an RSA public key in an older format (recognizable by the header -----BEGIN RSA PUBLIC KEY-----
). By the way, ParsePKIXPublicKey
can also parse RSA public keys in the newer format (recognizable by the header -----BEGIN PUBLIC KEY-----
, note the missing RSA
) - using the encoded key type object identifier. Besides ECDSA and RSA also DSA and ED25519 are supported.
Here is a complete, self-contained example showing the creation of sample keys with openssl in ECDSA
and RSA
formats and reading the public key with 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
A test might look like this:
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)
}
}
Here two functions readPublicKey
and printPublicKey
are used, which respectively read and print the PEM file with the public key.
As shown in the documentation, the code to read an RSA or ECDSA key would be the same:
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
}
Finally, the data can be easily accessed with a type switch, as nicely illustrated in the documentation referenced in the comment.
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
Using the cool online ASN.1 JavaScript decoder https://lapo.it/asn1js/ one can feed in the generated ECDSA pem.
The will reveal in the first object identifer ecPublicKey (ANSI X9.62 public key type)
.
Whereas for RSA
this would be rsaEncryption (PKCS #1)
.
secp256k1
Not all curve types are supported by Go's crypto package, notably secp256k1
. You can identify the curve type, for example, by the second object identifier in the online tool mentioned above.
Attempting to use the above code with it would produce the following error message: x509: unsupported elliptic curve
.
In such case, you could for example use the package github.com/decred/dcrd/secp256k1
.
To get the appropriate input form for secp256k1.ParsePubKey
, e.g. the uncompressed key format (0x04 + x + y) can be used, which in turn can be extracted via Unmarshal
from the asn1
package (see also to the internals of ParsePKIXPublicKey
).
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
A self-contained example for testing might look something like this:
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
Algorithm pkix.AlgorithmIdentifier
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)
}
The output on the debug console would be:
public key *secp256k1.PublicKey
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论