如何解决Go语言解析TPM2.0公钥的ASN.1结构错误?
解析TPM2.0公钥时ASN.1结构不匹配的问题解决
问题
使用Go语言解析TPM2.0公钥失败,收到ASN.1结构不匹配错误,具体错误日志如下:
{"time":"2024-09-06T18:11:36.5146545Z","level":"INFO","msg":"AK Public PEM is: ","!BADKEY":"-----BEGIN TPM2 PUBLIC KEY-----\nBIIBGAABAAsABSRyAAAAEAAUAAsIAAAAAAABAJqe9l3iktbQ5bPENbFbNvOeg3up\nNKvZZ+EcdUPltkibbs2N/DBfTLaOoGmkByHn16F0Sim8yV14cMQ75CBUvNCq/yFE\nVPwJCCrM3kRo7Z+yPvftgtctKHRCKi9VouDaRfEIwIOMlYFtksyoXaS4Box29WiU\n52Dm9O5AUChsgkeJB+e8DVZd2oZX4s7TGaGif1b4mYtKcTJqVzv55S05NW4TPoTc\nXJbhdTjDqiNbaL5BUklyevYqj8XF4GzbmdGohF9wIYcuoNJo03ZcntScteFynReL\n3BFVCZCNlvNoNN1QY6xKdKevDdwVBgfXWrOGGlSW4PpmJTH1tMeXx3xwlOM=\n-----END TPM2 PUBLIC KEY-----\n"} {"time":"2024-09-06T18:11:36.5153675Z","level":"INFO","msg":"Block type is: ","!BADKEY":"TPM2 PUBLIC KEY"} {"time":"2024-09-06T18:11:36.517942375Z","level":"INFO","msg":"asn1: structure error: tags don't match (16 vs {class:0 tag:4 length:280 isCompound:false}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} publicKeyInfo @4"}
复现代码如下:
package main import ( "crypto/x509" "encoding/pem" "fmt" "github.com/google/go-attestation/attest" "log/slog" "os" ) // Global variables var ( port string tpmPath string tpm *attest.TPM logger *slog.Logger knownEKs map[string][]byte currentChallenge []byte ) // TPM2PublicKey represents the structure of a TPM2 public key type TPM2PublicKey struct { Type uint16 NameAlg uint16 Attributes uint32 AuthPolicy []byte Parameters TPM2PublicKeyParameters Unique TPM2PublicKeyUnique } type TPM2PublicKeyParameters struct { SymmetricAlgorithm uint16 Scheme uint16 KeyBits uint16 Exponent uint32 } type TPM2PublicKeyUnique struct { RSA []byte } func main() { // Initialize structured logger logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) akPubPEM := "-----BEGIN TPM2 PUBLIC KEY-----\nBIIBGAABAAsABSRyAAAAEAAUAAsIAAAAAAABAJqe9l3iktbQ5bPENbFbNvOeg3up\nNKvZZ+EcdUPltkibbs2N/DBfTLaOoGmkByHn16F0Sim8yV14cMQ75CBUvNCq/yFE\nVPwJCCrM3kRo7Z+yPvftgtctKHRCKi9VouDaRfEIwIOMlYFtksyoXaS4Box29WiU\n52Dm9O5AUChsgkeJB+e8DVZd2oZX4s7TGaGif1b4mYtKcTJqVzv55S05NW4TPoTc\nXJbhdTjDqiNbaL5BUklyevYqj8XF4GzbmdGohF9wIYcuoNJo03ZcntScteFynReL\n3BFVCZCNlvNoNN1QY6xKdKevDdwVBgfXWrOGGlSW4PpmJTH1tMeXx3xwlOM=\n-----END TPM2 PUBLIC KEY-----\n" logger.Info("AK Public PEM is: ", akPubPEM) block, _ := pem.Decode([]byte(akPubPEM)) logger.Info("Block type is: ", block.Type) if block == nil || block.Type != "TPM2 PUBLIC KEY" { logger.Info("failed to decode PEM block containing public key") os.Exit(1) } pub, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { logger.Info(err.Error()) os.Exit(1) } fmt.Printf("Parsed public key: %v\n", pub) // Unmarshal ASN.1 structure // var tpm2Pub TPM2PublicKey // _, err := asn1.Unmarshal(block.Bytes, &tpm2Pub) // if err != nil { // logger.Info("ERROR: Failed to unmarshal: ", err) // os.Exit(1) // } // // Convert TPM2 public key to RSA public key // if tpm2.Algorithm(tpm2Pub.Type) != tpm2.AlgRSA { // os.Exit(1) // } os.Exit(0) }
解决方案
原因
x509.ParsePKIXPublicKey仅支持解析标准PKIX格式的公钥,而TPM2.0公钥采用的是TPM规范定义的专属ASN.1结构(参考TPM 2.0规范Part 2: Structures),和标准PKIX结构不兼容,因此直接调用该函数会触发结构不匹配错误。
修复步骤
- 利用已引入的
github.com/google/go-attestation/attest库解析TPM公钥,该库专为TPM相关操作设计,支持TPM2.0公钥的解析。 - 替换
x509.ParsePKIXPublicKey为attest.ParsePublicKey,并处理解析后的attest.PublicKey对象。 - 若需要将TPM公钥转换为标准RSA公钥,可从解析结果中提取模数和指数构造。
修改后的代码示例
package main import ( "crypto/rsa" "encoding/pem" "fmt" "github.com/google/go-attestation/attest" "log/slog" "math/big" "os" ) var logger *slog.Logger func main() { logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) akPubPEM := "-----BEGIN TPM2 PUBLIC KEY-----\nBIIBGAABAAsABSRyAAAAEAAUAAsIAAAAAAABAJqe9l3iktbQ5bPENbFbNvOeg3up\nNKvZZ+EcdUPltkibbs2N/DBfTLaOoGmkByHn16F0Sim8yV14cMQ75CBUvNCq/yFE\nVPwJCCrM3kRo7Z+yPvftgtctKHRCKi9VouDaRfEIwIOMlYFtksyoXaS4Box29WiU\n52Dm9O5AUChsgkeJB+e8DVZd2oZX4s7TGaGif1b4mYtKcTJqVzv55S05NW4TPoTc\nXJbhdTjDqiNbaL5BUklyevYqj8XF4GzbmdGohF9wIYcuoNJo03ZcntScteFynReL\n3BFVCZCNlvNoNN1QY6xKdKevDdwVBgfXWrOGGlSW4PpmJTH1tMeXx3xwlOM=\n-----END TPM2 PUBLIC KEY-----\n" logger.Info("AK Public PEM is: ", akPubPEM) block, _ := pem.Decode([]byte(akPubPEM)) logger.Info("Block type is: ", block.Type) if block == nil || block.Type != "TPM2 PUBLIC KEY" { logger.Info("failed to decode PEM block containing public key") os.Exit(1) } // 使用attest库解析TPM2公钥 tpmPub, err := attest.ParsePublicKey(block.Bytes) if err != nil { logger.Info("failed to parse TPM public key: ", err) os.Exit(1) } logger.Info("Parsed TPM public key: ", "algorithm", tpmPub.Algorithm, "key length", tpmPub.RSA().KeyBits) // 转换为标准RSA公钥 if tpmPub.Algorithm == attest.AlgorithmRSA { rsaParams := tpmPub.RSA() rsaPub := &rsa.PublicKey{ N: new(big.Int).SetBytes(rsaParams.Modulus), E: int(rsaParams.Exponent), } fmt.Printf("Standard RSA public key: %v\n", rsaPub) } else { logger.Info("not an RSA public key") os.Exit(1) } os.Exit(0) }
内容的提问来源于stack exchange,提问作者PhilBot
相关产品推荐
相关产品推荐

