You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Go语言将DSA PublicKey转换为PEM文件?遇序列化问题

DSA公钥对象重新生成PEM字符串的解决方法

问题现象

使用RSA PEM字符串时,代码可正常生成公钥对象并重新导出为PEM格式;但切换为DSA PEM字符串后,仅能解析得到DSA公钥对象,调用x509.MarshalPKIXPublicKey会报错:x509: unsupported public key type: *dsa.PublicKey,无法重新生成PEM字符串。

原测试代码:

package main

import (
    "C"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "os"
)

var rsaBytes = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyQubwvMR1ctRTTylCJ9u
imYlIMo8rkOXV8S7NaRB2pdEoeG1RiGVanTKwu1N6D7CNbXV+o+QVOpSB823iTb+
kxHaTpGcJwVvA1K3pJOgSSAx3bbr8Ekv1FXNZ3bGFOrzoD0OOWICDGnNQznQ8cJ8
+EFwjLBoqqqWM892BkGQuQTRLEmjrFOrLlTB99yXMeiD4c2iQFCtNZHpFviS9ahp
Irb/sEyhs2hFYqUUByZQQqo0ewv8MFiHGXG68t2LMnTkbff3KyUxgChEPiqAUNAb
LJ1JaFwypZdHr7KyJqatCIOk2m1Yz3umnW/KsM0Q+N9C4UZdn6GbZyStELGBJjsv
aQIDAQAB
-----END PUBLIC KEY-----`

var dsaBytes = `-----BEGIN PUBLIC KEY-----
MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9E
AMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f
6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv
8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtc
NrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwky
jMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/h
WuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAIb9o0KPsjAdzjK571e1Mx7ZhEyJ
GrcxHiN2sW8IztEbqrKKiMxpNlTwm234uBdtzVHE3uDWZpfHPMIRmwBjCYDFRowW
WVRdhdFXZlpCyp1gMWqJ11dh3FI3+O43DevRSyyuLRVCNQ1J3iVgwY5ndRpZU7n6
y8DPH4/4EBT7KvnV
-----END PUBLIC KEY-----`

func main() {
    // pemBlock, _ := pem.Decode([]byte(rsaBytes))
    pemBlock, _ := pem.Decode([]byte(dsaBytes))

    pubInterface, err := x509.ParsePKIXPublicKey(pemBlock.Bytes)
    pubkeyByte, err := x509.MarshalPKIXPublicKey(pubInterface)
    if err != nil {
        fmt.Println(err)
        os.Exit(0)
    }
    block := &pem.Block{
        Type:  "PUBLIC KEY",
        Bytes: pubkeyByte,
    }
    pemByte := pem.EncodeToMemory(block)
    fmt.Println(string(pemByte))
}

解决方案

Go标准库的x509.MarshalPKIXPublicKey不支持直接序列化*dsa.PublicKey,需要手动处理DSA公钥的ASN.1编码。修改后的代码如下:

package main

import (
    "C"
    "crypto/dsa"
    "crypto/x509"
    "encoding/asn1"
    "encoding/pem"
    "fmt"
    "os"
)

var rsaBytes = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyQubwvMR1ctRTTylCJ9u
imYlIMo8rkOXV8S7NaRB2pdEoeG1RiGVanTKwu1N6D7CNbXV+o+QVOpSB823iTb+
kxHaTpGcJwVvA1K3pJOgSSAx3bbr8Ekv1FXNZ3bGFOrzoD0OOWICDGnNQznQ8cJ8
+EFwjLBoqqqWM892BkGQuQTRLEmjrFOrLlTB99yXMeiD4c2iQFCtNZHpFviS9ahp
Irb/sEyhs2hFYqUUByZQQqo0ewv8MFiHGXG68t2LMnTkbff3KyUxgChEPiqAUNAb
LJ1JaFwypZdHr7KyJqatCIOk2m1Yz3umnW/KsM0Q+N9C4UZdn6GbZyStELGBJjsv
aQIDAQAB
-----END PUBLIC KEY-----`

var dsaBytes = `-----BEGIN PUBLIC KEY-----
MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9E
AMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f
6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv
8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtc
NrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwky
jMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/h
WuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAIb9o0KPsjAdzjK571e1Mx7ZhEyJ
GrcxHiN2sW8IztEbqrKKiMxpNlTwm234uBdtzVHE3uDWZpfHPMIRmwBjCYDFRowW
WVRdhdFXZlpCyp1gMWqJ11dh3FI3+O43DevRSyyuLRVCNQ1J3iVgwY5ndRpZU7n6
y8DPH4/4EBT7KvnV
-----END PUBLIC KEY-----`

// 定义DSA公钥的ASN.1结构
type dsaPublicKey struct {
    Parameters asn1.RawValue
    Y          asn1.RawValue
}

func main() {
    pemBlock, _ := pem.Decode([]byte(dsaBytes))
    if pemBlock == nil {
        fmt.Println("Failed to decode PEM block")
        os.Exit(1)
    }

    pubInterface, err := x509.ParsePKIXPublicKey(pemBlock.Bytes)
    if err != nil {
        fmt.Println("Failed to parse PKIX public key:", err)
        os.Exit(1)
    }

    var pubkeyByte []byte
    switch pub := pubInterface.(type) {
    case *dsa.PublicKey:
        // 手动序列化DSA公钥
        paramsBytes, err := asn1.Marshal(*pub.Parameters)
        if err != nil {
            fmt.Println("Failed to marshal DSA parameters:", err)
            os.Exit(1)
        }
        yBytes, err := asn1.Marshal(pub.Y)
        if err != nil {
            fmt.Println("Failed to marshal DSA Y value:", err)
            os.Exit(1)
        }

        dsaPub := dsaPublicKey{
            Parameters: asn1.RawValue{FullBytes: paramsBytes},
            Y:          asn1.RawValue{FullBytes: yBytes},
        }

        pubkeyByte, err = asn1.Marshal(dsaPub)
        if err != nil {
            fmt.Println("Failed to marshal DSA public key:", err)
            os.Exit(1)
        }
    default:
        // 其他类型(如RSA)使用标准方法序列化
        pubkeyByte, err = x509.MarshalPKIXPublicKey(pubInterface)
        if err != nil {
            fmt.Println("Failed to marshal public key:", err)
            os.Exit(1)
        }
    }

    block := &pem.Block{
        Type:  "PUBLIC KEY",
        Bytes: pubkeyByte,
    }
    pemByte := pem.EncodeToMemory(block)
    fmt.Println(string(pemByte))
}

关键说明

  • 新增dsaPublicKey结构体,匹配DSA公钥的PKIX ASN.1编码结构
  • 通过类型断言区分DSA公钥和其他类型公钥
  • 手动序列化DSA的参数(P、Q、G)和公钥值Y,再组合成完整的PKIX格式
  • 非DSA类型的公钥仍使用标准的x509.MarshalPKIXPublicKey方法

内容的提问来源于stack exchange,提问作者田伯成

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 09:39:25