Golang中x509证书OID支持超过32位子标识符的实现方法

Golang中x509证书OID支持超过32位子标识符的实现方法 参考以下问题的修复方案:

github.com/golang/go

问题:encoding/asn1: ObjectIdentifier + crypto/x509.ParseCertificate 不支持大于28位的整数

由 enj 开启 于 2017-04-11
由 gopherbot 关闭 于 2017-04-13
你使用的Go版本是什么(go version)?
go version go1.7.5 linux/amd64(在 go 1.8+ 中应该相同)
操作系统是什么...
FrozenDueToAge NeedsDecision

这更适合在该问题中进行讨论,但由于时间久远已被锁定,所以我在此发帖。

目前Go对x509证书OID中的oid子标识符有一个限制,即必须小于31位。是否有计划改变这一限制?

我们有个客户希望使用其IT部门提供的证书。他们设置在证书中的内部CertificatePolicyID,其OID包含一个需要64位整数来表示的子标识符。该证书被我们产品中的其他组件(主要是基于Java的)接受,但被我们运行在Go中的一个小服务拒绝。


更多关于Golang中x509证书OID支持超过32位子标识符的实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中x509证书OID支持超过32位子标识符的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,encoding/asn1包确实对OID(对象标识符)的子标识符有32位整数的限制,这在处理某些需要更大整数范围的证书时会造成问题。虽然标准库目前没有直接支持超过32位的OID子标识符,但可以通过自定义ASN.1解析或使用第三方库来解决。以下是一个实现方法,基于手动解析DER编码的证书数据,并扩展OID处理逻辑。

首先,使用x509.ParseCertificate解析证书时,如果遇到超过32位的OID子标识符,标准库会返回错误。我们可以通过解析原始DER数据并修改OID处理部分来绕过这个限制。这里,我提供一个示例代码,使用asn1.Unmarshal手动解析证书的扩展字段,并自定义OID解析函数以支持更大的整数。

示例代码:

package main

import (
    "crypto/x509"
    "encoding/asn1"
    "fmt"
    "math/big"
)

// 自定义OID类型,使用big.Int支持大整数子标识符
type CustomOID []*big.Int

// 解析DER数据中的OID,支持超过32位的子标识符
func parseCustomOID(der []byte) (CustomOID, error) {
    var raw asn1.RawValue
    _, err := asn1.Unmarshal(der, &raw)
    if err != nil {
        return nil, err
    }
    if raw.Class != asn1.ClassUniversal || raw.Tag != asn1.TagOID {
        return nil, fmt.Errorf("not an OID")
    }

    oid := CustomOID{}
    data := raw.Bytes
    if len(data) == 0 {
        return oid, nil
    }

    // 解析第一个子标识符
    first := int(data[0] / 40)
    second := int(data[0] % 40)
    oid = append(oid, big.NewInt(int64(first)))
    oid = append(oid, big.NewInt(int64(second)))

    // 解析剩余子标识符,使用大整数处理
    val := big.NewInt(0)
    for i := 1; i < len(data); i++ {
        b := data[i]
        val.Lsh(val, 7)
        val.Or(val, big.NewInt(int64(b&0x7f)))
        if b&0x80 == 0 {
            oid = append(oid, new(big.Int).Set(val))
            val.SetInt64(0)
        }
    }
    return oid, nil
}

// 示例函数:解析证书扩展中的OID
func parseCertificateExtensions(der []byte) error {
    var cert struct {
        TBSCertificate struct {
            Version      int `asn1:"optional,explicit,default:0,tag:0"`
            SerialNumber *big.Int
            // 其他字段省略,专注于扩展
            Extensions []asn1.RawValue `asn1:"optional,explicit,tag:3"`
        }
    }
    _, err := asn1.Unmarshal(der, &cert)
    if err != nil {
        return err
    }

    for _, ext := range cert.TBSCertificate.Extensions {
        var extension struct {
            ID     asn1.ObjectIdentifier
            Critical bool `asn1:"optional"`
            Value  []byte
        }
        _, err := asn1.Unmarshal(ext.FullBytes, &extension)
        if err != nil {
            // 如果标准解析失败,尝试自定义OID解析
            oid, customErr := parseCustomOID(ext.FullBytes)
            if customErr != nil {
                return customErr
            }
            fmt.Printf("Custom OID: %v\n", oid)
        } else {
            fmt.Printf("Standard OID: %v\n", extension.ID)
        }
    }
    return nil
}

func main() {
    // 假设der是证书的DER编码数据,可以从文件或PEM解码获取
    // 例如:der, _ := ioutil.ReadFile("cert.der")
    der := []byte{} // 替换为实际DER数据
    err := parseCertificateExtensions(der)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    }
}

在这个示例中:

  • CustomOID 类型使用 []*big.Int 来存储OID子标识符,支持任意大的整数。
  • parseCustomOID 函数手动解析DER数据中的OID,使用 big.Int 处理每个子标识符,避免了32位限制。
  • parseCertificateExtensions 函数演示了如何解析证书的扩展字段,并在标准解析失败时回退到自定义解析。

这种方法允许处理包含超过32位子标识符的OID,例如在CertificatePolicyID中。注意,这需要直接操作DER数据,可能不适用于所有场景。如果证书的其他部分也受限制,可能需要更全面的自定义解析。

对于生产环境,建议监控Go标准库的更新,因为未来版本可能会原生支持更大的OID。目前,此方法是一个可行的变通方案。

回到顶部