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

如何解决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结构不兼容,因此直接调用该函数会触发结构不匹配错误。

修复步骤

  1. 利用已引入的github.com/google/go-attestation/attest库解析TPM公钥,该库专为TPM相关操作设计,支持TPM2.0公钥的解析。
  2. 替换x509.ParsePKIXPublicKey为attest.ParsePublicKey,并处理解析后的attest.PublicKey对象。
  3. 若需要将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 14:38:09