使用Golang解析CRL中的IDP扩展信息
使用Golang解析CRL中的IDP扩展信息 开发者,我用Go编写了一些代码来解析CRL中的IDP扩展信息,但在解析DistributionPoint时遇到了错误。我已经尝试了多次,但无法让它正常工作。你能帮我找出这段代码的问题所在吗?
package main
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"flag"
"fmt"
"os"
"strings"
)
type IssuingDistributionPoint struct {
DistributionPoint asn1.RawValue `asn1:"optional,tag:0,explicit"`
OnlyContainsUserCerts bool `asn1:"optional,tag:1"`
OnlyContainsCACerts bool `asn1:"optional,tag:2"`
OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"`
IndirectCRL bool `asn1:"optional,tag:4"`
OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"`
}
var oidMap = map[string]string{
"2.5.4.3": "CN",
"2.5.4.6": "C",
"2.5.4.10": "O",
"2.5.4.11": "OU",
"1.2.840.113549.1.9.1": "E",
"0.9.2342.19200300.100.1.25": "DC",
"0.9.2342.19200300.100.1.1": "UID",
}
func parseRDN(data []byte) (string, error) {
var builder strings.Builder
var raw asn1.RawValue
if _, err := asn1.Unmarshal(data, &raw); err != nil {
return "", fmt.Errorf("initial parsing failed: %v", err)
}
if raw.Tag == asn1.TagSequence {
type rdnSequence []pkix.AttributeTypeAndValue
var rdns rdnSequence
if _, err := asn1.Unmarshal(raw.FullBytes, &rdns); err != nil {
return "", fmt.Errorf("failed to parse rdnSequence: %v", err)
}
for i, rdn := range rdns {
builder.WriteString(fmt.Sprintf("RDN[%d]:\n", i+1))
oid := rdn.Type.String()
name := oidMap[oid]
if name == "" {
name = oid
}
value, err := decodeAttributeValue(rdn.Value)
if err != nil {
return "", fmt.Errorf("failed to decode attribute value: %v", err)
}
if name == "DC" {
if decoded, err := decodeDomainComponent(value); err == nil {
value = decoded
}
}
builder.WriteString(fmt.Sprintf(" Type:%-8s Value:%s\n", name, value))
}
return builder.String(), nil
}
return "", fmt.Errorf("unexpected tag type: %d", raw.Tag)
}
func decodeAttributeValue(raw interface{}) (string, error) {
switch v := raw.(type) {
case string:
return v, nil
case []byte:
if isPrintable(string(v)) {
return string(v), nil
}
return fmt.Sprintf("#%X", v), nil
default:
return fmt.Sprintf("%v", v), nil
}
}
func decodeDomainComponent(value string) (string, error) {
if strings.HasPrefix(value, "#") {
decoded, err := hex.DecodeString(value[1:])
if err != nil {
return "", fmt.Errorf("HEX decoding failed: %v", err)
}
return string(decoded), nil
}
return value, nil
}
func isPrintable(s string) bool {
for _, r := range s {
if r < 32 || r > 126 {
return false
}
}
return true
}
func parseGeneralName(gn asn1.RawValue) (interface{}, error) {
if gn.Class == asn1.ClassContextSpecific && gn.Tag == 1 {
return parseRDN(gn.FullBytes)
}
if gn.Tag == asn1.TagSequence {
var names []asn1.RawValue
if _, err := asn1.Unmarshal(gn.Bytes, &names); err != nil {
return nil, fmt.Errorf("failed to decode GeneralNames: %v", err)
}
var results []string
for _, name := range names {
res, err := parseGeneralName(name)
if err != nil {
return nil, err
}
results = append(results, fmt.Sprintf("%v", res))
}
return strings.Join(results, ", "), nil
}
if gn.Class == asn1.ClassContextSpecific {
switch gn.Tag {
case 0, 2, 6:
return string(gn.Bytes), nil
}
}
return nil, fmt.Errorf("unsupported GeneralName type: tag=%d class=%d", gn.Tag, gn.Class)
}
func main() {
crlFilePath := flag.String("crl", "", "Path to the CRL file")
flag.Parse()
if *crlFilePath == "" {
fmt.Println("CRL file path must be provided")
os.Exit(1)
}
derBytes, err := os.ReadFile(*crlFilePath)
if err != nil {
fmt.Printf("Failed to read file: %v\n", err)
os.Exit(1)
}
crl, err := x509.ParseRevocationList(derBytes)
if err != nil {
fmt.Printf("Failed to parse CRL: %v\n", err)
os.Exit(1)
}
oidIssuingDistributionPoint := asn1.ObjectIdentifier{2, 5, 29, 28}
for _, ext := range crl.Extensions {
if ext.Id.Equal(oidIssuingDistributionPoint) {
var idp IssuingDistributionPoint
if _, err := asn1.Unmarshal(ext.Value, &idp); err != nil {
fmt.Printf("Failed to decode IDP extension: %v\n", err)
continue
}
fmt.Printf("IDP Extension Flags:\n")
fmt.Printf(" Only Contains User Certs: %t\n", idp.OnlyContainsUserCerts)
fmt.Printf(" Only Contains CA Certs: %t\n", idp.OnlyContainsCACerts)
fmt.Printf(" Indirect CRL: %t\n", idp.IndirectCRL)
if len(idp.DistributionPoint.Bytes) > 0 {
var dpName asn1.RawValue
if _, err := asn1.Unmarshal(idp.DistributionPoint.Bytes, &dpName); err != nil {
fmt.Printf("Failed to unpack DistributionPointName: %v\n", err)
continue
}
if dpName.Class == asn1.ClassContextSpecific {
switch dpName.Tag {
case 0: // fullName
fmt.Println("Distribution Point Type: fullName")
var generalNames []asn1.RawValue
if _, err := asn1.Unmarshal(dpName.Bytes, &generalNames); err != nil {
fmt.Printf("Failed to parse GeneralNames: %v\n", err)
continue
}
for i, gn := range generalNames {
result, err := parseGeneralName(gn)
if err != nil {
fmt.Printf("[Entry %d] Parsing error: %v\n", i+1, err)
continue
}
fmt.Printf("[Entry %d] Distribution Point: %s\n", i+1, result)
}
case 1: // nameRelativeToCRLIssuer
fmt.Println("Distribution Point Type: nameRelativeToCRLIssuer")
result, err := parseRDN(dpName.Bytes)
if err != nil {
fmt.Printf("Failed to parse RDN: %v\n", err)
continue
}
fmt.Println(result)
default:
fmt.Printf("Unknown distribution point tag: %d\n", dpName.Tag)
}
}
}
}
}
}
错误:
IDP Extension Flags:
Only Contains User Certs: false
Only Contains CA Certs: false
Indirect CRL: false
Distribution Point Type: nameRelativeToCRLIssuer
Failed to parse RDN: failed to parse rdnSequence: asn1: structure error: sequence tag mismatch
Pem:
-----BEGIN X509 CRL-----
MIICnTCCAYUCAQEwDQYJKoZIhvcNAQELBQAwTjELMAkGA1UEBhMCVVMxCzAJBgNV
BAgMAlVTMQswCQYDVQQHDAJVUzELMAkGA1UECgwCVVMxCzAJBgNVBAMMAlVTMQsw
CQYDVQQLDAJVUxcNMjUwMTAxMDAwMDAwWhcNMjUxMjAxMDAwMDAwWjA1MDMCFByA
Ai74HyQF7pamEty2H+CscB5eFw0yNTAzMjcwMjUxMTBaMAwwCgYDVR0VBAMKAQag
gcswgcgwgasGA1UdHAEB/wSBoDCBnaCBmqGBlzAJBgNVBAYTAkNOMAkGA1UEChMC
Q0EwCgYDVQQDEwNDUkwwEQYKCZImiZPyLGQBGQwDY29tMBQGA1UECxMNSVQgRGVw
YXJ0bWVudDAUBgoJkiaJk/IsZAEBDAZib2IxMjMwFQYKCZImiZPyLGQBGQwHZXhh
bXBsZTAdBgkqhkiG9w0BCQEWEHVzZXJAZXhhbXBsZS5jb20wGAYDVR0UBBECDxnP
/97adO3y9qRGDM7hQDANBgkqhkiG9w0BAQsFAAOCAQEApPcq43Py08J9wMWTXIQT
Q3E30ACBzEW2E+3HZ5818Z/FK7+YYV4umPZ5JQVINqYoTbpRoBrdh8VJyJ2U/B1u
9NDgtMRv7gVHad+uy3ciRG+nvOa9JP4a0a3GMN5nhWIykghH9LBYOL48WCP6r3pO
t8inbw7bSB25HSDIuHHeuChchDOgv926MFmYTphaFY7h6sFRDjHVSSFJEicSRx/t
0OJ8mtfwBqWLw9725u4A5b08FGAfSV0UBE25QqqpE/W5Vt4tDmDfR8idEsQyRbCf
qETKAFd0a7J5MiI4MNj3CaUWUbsq1kZsFfSRFqPJyoiXlUm8jz6n/8eLrV3rDLiS
bg==
-----END X509 CRL-----
更多关于使用Golang解析CRL中的IDP扩展信息的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
你好! 当然可以!请分享你的Go代码,以及你遇到的特定错误信息。如果可能的话,也请附上你正在使用的任何相关CRL示例数据,以及你如何处理IDP扩展的详细信息。这样,我就能分析问题并有效地帮助排查。
你的代码在解析nameRelativeToCRLIssuer类型的DistributionPoint时遇到了ASN.1序列标签不匹配的问题。问题在于parseRDN函数期望接收一个完整的RDN序列,但实际传入的是经过ASN.1编码的RelativeDistinguishedName结构。
以下是修复后的parseRDN函数和相关的解析逻辑:
func parseRDN(data []byte) (string, error) {
var builder strings.Builder
// 直接解析为pkix.AttributeTypeAndValue序列
var atvs []pkix.AttributeTypeAndValue
if _, err := asn1.Unmarshal(data, &atvs); err != nil {
return "", fmt.Errorf("failed to parse RDN sequence: %v", err)
}
for i, atv := range atvs {
builder.WriteString(fmt.Sprintf("RDN[%d]:\n", i+1))
oid := atv.Type.String()
name := oidMap[oid]
if name == "" {
name = oid
}
value, err := decodeAttributeValue(atv.Value)
if err != nil {
return "", fmt.Errorf("failed to decode attribute value: %v", err)
}
if name == "DC" {
if decoded, err := decodeDomainComponent(value); err == nil {
value = decoded
}
}
builder.WriteString(fmt.Sprintf(" Type:%-8s Value:%s\n", name, value))
}
return builder.String(), nil
}
// 修改main函数中解析nameRelativeToCRLIssuer的部分
if dpName.Tag == 1 { // nameRelativeToCRLIssuer
fmt.Println("Distribution Point Type: nameRelativeToCRLIssuer")
// nameRelativeToCRLIssuer是RelativeDistinguishedName类型
// 需要直接解析为RDN序列
result, err := parseRDN(dpName.Bytes)
if err != nil {
fmt.Printf("Failed to parse RDN: %v\n", err)
continue
}
fmt.Println(result)
}
另外,你的parseGeneralName函数在处理directoryName(tag 4)时也有问题。需要添加对directoryName的支持:
func parseGeneralName(gn asn1.RawValue) (interface{}, error) {
if gn.Class == asn1.ClassContextSpecific {
switch gn.Tag {
case 0: // otherName
return fmt.Sprintf("otherName: %X", gn.Bytes), nil
case 1: // rfc822Name
return fmt.Sprintf("rfc822Name: %s", string(gn.Bytes)), nil
case 2: // dNSName
return fmt.Sprintf("dNSName: %s", string(gn.Bytes)), nil
case 4: // directoryName
// directoryName是Name类型,需要特殊处理
return parseRDN(gn.Bytes)
case 6: // uniformResourceIdentifier
return fmt.Sprintf("URI: %s", string(gn.Bytes)), nil
case 7: // iPAddress
return fmt.Sprintf("IP: %X", gn.Bytes), nil
case 8: // registeredID
var oid asn1.ObjectIdentifier
if _, err := asn1.Unmarshal(gn.Bytes, &oid); err != nil {
return nil, fmt.Errorf("failed to parse registeredID: %v", err)
}
return fmt.Sprintf("registeredID: %s", oid.String()), nil
}
}
return nil, fmt.Errorf("unsupported GeneralName type: tag=%d class=%d", gn.Tag, gn.Class)
}
对于你提供的CRL数据,IDP扩展可能不包含DistributionPoint字段。你可以添加一个检查来避免空指针错误:
if idp.DistributionPoint.FullBytes != nil {
// 现有的解析逻辑
}
完整的修复示例:
package main
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"flag"
"fmt"
"os"
"strings"
)
type IssuingDistributionPoint struct {
DistributionPoint asn1.RawValue `asn1:"optional,tag:0"`
OnlyContainsUserCerts bool `asn1:"optional,tag:1"`
OnlyContainsCACerts bool `asn1:"optional,tag:2"`
OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"`
IndirectCRL bool `asn1:"optional,tag:4"`
OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"`
}
var oidMap = map[string]string{
"2.5.4.3": "CN",
"2.5.4.6": "C",
"2.5.4.10": "O",
"2.5.4.11": "OU",
"1.2.840.113549.1.9.1": "E",
"0.9.2342.19200300.100.1.25": "DC",
"0.9.2342.19200300.100.1.1": "UID",
}
func parseRDN(data []byte) (string, error) {
var builder strings.Builder
var atvs []pkix.AttributeTypeAndValue
if _, err := asn1.Unmarshal(data, &atvs); err != nil {
return "", fmt.Errorf("failed to parse RDN sequence: %v", err)
}
for i, atv := range atvs {
builder.WriteString(fmt.Sprintf("RDN[%d]:\n", i+1))
oid := atv.Type.String()
name := oidMap[oid]
if name == "" {
name = oid
}
value, err := decodeAttributeValue(atv.Value)
if err != nil {
return "", fmt.Errorf("failed to decode attribute value: %v", err)
}
if name == "DC" {
if decoded, err := decodeDomainComponent(value); err == nil {
value = decoded
}
}
builder.WriteString(fmt.Sprintf(" Type:%-8s Value:%s\n", name, value))
}
return builder.String(), nil
}
func decodeAttributeValue(raw interface{}) (string, error) {
switch v := raw.(type) {
case string:
return v, nil
case []byte:
if isPrintable(string(v)) {
return string(v), nil
}
return fmt.Sprintf("#%X", v), nil
default:
return fmt.Sprintf("%v", v), nil
}
}
func decodeDomainComponent(value string) (string, error) {
if strings.HasPrefix(value, "#") {
decoded, err := hex.DecodeString(value[1:])
if err != nil {
return "", fmt.Errorf("HEX decoding failed: %v", err)
}
return string(decoded), nil
}
return value, nil
}
func isPrintable(s string) bool {
for _, r := range s {
if r < 32 || r > 126 {
return false
}
}
return true
}
func parseGeneralName(gn asn1.RawValue) (interface{}, error) {
if gn.Class == asn1.ClassContextSpecific {
switch gn.Tag {
case 0:
return fmt.Sprintf("otherName: %X", gn.Bytes), nil
case 1:
return fmt.Sprintf("rfc822Name: %s", string(gn.Bytes)), nil
case 2:
return fmt.Sprintf("dNSName: %s", string(gn.Bytes)), nil
case 4:
return parseRDN(gn.Bytes)
case 6:
return fmt.Sprintf("URI: %s", string(gn.Bytes)), nil
case 7:
return fmt.Sprintf("IP: %X", gn.Bytes), nil
case 8:
var oid asn1.ObjectIdentifier
if _, err := asn1.Unmarshal(gn.Bytes, &oid); err != nil {
return nil, fmt.Errorf("failed to parse registeredID: %v", err)
}
return fmt.Sprintf("registeredID: %s", oid.String()), nil
}
}
return nil, fmt.Errorf("unsupported GeneralName type: tag=%d class=%d", gn.Tag, gn.Class)
}
func main() {
crlFilePath := flag.String("crl", "", "Path to the CRL file")
flag.Parse()
if *crlFilePath == "" {
fmt.Println("CRL file path must be provided")
os.Exit(1)
}
derBytes, err := os.ReadFile(*crlFilePath)
if err != nil {
fmt.Printf("Failed to read file: %v\n", err)
os.Exit(1)
}
crl, err := x509.ParseRevocationList(derBytes)
if err != nil {
fmt.Printf("Failed to parse CRL: %v\n", err)
os.Exit(1)
}
oidIssuingDistributionPoint := asn1.ObjectIdentifier{2, 5, 29, 28}
for _, ext := range crl.Extensions {
if ext.Id.Equal(oidIssuingDistributionPoint) {
var idp IssuingDistributionPoint
if _, err := asn1.Unmarshal(ext.Value, &idp); err != nil {
fmt.Printf("Failed to decode IDP extension: %v\n", err)
continue
}
fmt.Printf("IDP Extension Flags:\n")
fmt.Printf(" Only Contains User Certs: %t\n", idp.OnlyContainsUserCerts)
fmt.Printf(" Only Contains CA Certs: %t\n", idp.OnlyContainsCACerts)
fmt.Printf(" Indirect CRL: %t\n", idp.IndirectCRL)
if len(idp.DistributionPoint.Bytes) > 0 {
var dpName asn1.RawValue
if _, err := asn1.Unmarshal(idp.DistributionPoint.Bytes, &dpName); err != nil {
fmt.Printf("Failed to unpack DistributionPointName: %v\n", err)
continue
}
if dpName.Class == asn1.ClassContextSpecific {
switch dpName.Tag {
case 0:
fmt.Println("Distribution Point Type: fullName")
var generalNames []asn1.RawValue
if _, err := asn1.Unmarshal(dpName.Bytes, &generalNames); err != nil {
fmt.Printf("Failed to parse GeneralNames: %v\n", err)
continue
}
for i, gn := range generalNames {
result, err := parseGeneralName(gn)
if err != nil {
fmt.Printf("[Entry %d] Parsing error: %v\n", i+1, err)
continue
}
fmt.Printf("[Entry %d] Distribution Point: %s\n", i+1, result)
}
case 1:
fmt.Println("Distribution Point Type: nameRelativeToCRLIssuer")
result, err := parseRDN(dpName.Bytes)
if err != nil {
fmt.Printf("Failed to parse RDN: %v\n", err)
continue
}
fmt.Println(result)
default:
fmt.Printf("Unknown distribution point tag: %d\n", dpName.Tag)
}
}
}
}
}
}
主要修改:
- 简化了
parseRDN函数,直接解析为pkix.AttributeTypeAndValue序列 - 修复了
parseGeneralName函数对directoryName(tag 4)的支持 - 移除了IssuingDistributionPoint结构体标签中的
explicit标记,因为IDP扩展使用隐式标签


