如何用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,提问作者田伯成
相关产品推荐
相关产品推荐

