Developer, I wrote some code in Go to parse the IDP extension information in a CRL, but I’m encountering an error while parsing the DistributionPoint. I’ve tried multiple times, but I can’t get it to work. Can you help me figure out what is wrong with this code?
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)
}
}
}
}
}
}
error:
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-----