From c2cda7b9ed96a36069af1c515404d628d9bbbf0e Mon Sep 17 00:00:00 2001 From: "peter.reisinger" Date: Mon, 16 Dec 2024 20:47:58 +0000 Subject: [PATCH] add more extensions --- pkg/cert/asn.go | 100 ++++++++++++++++++++++++++++++++++++----- pkg/cert/cert.go | 5 ++- pkg/cert/extensions.go | 95 +++++++++++++++++++++++++-------------- 3 files changed, 154 insertions(+), 46 deletions(-) diff --git a/pkg/cert/asn.go b/pkg/cert/asn.go index 6bc9f45..757c092 100644 --- a/pkg/cert/asn.go +++ b/pkg/cert/asn.go @@ -235,6 +235,49 @@ func ToKeyUsage(in []byte) ([]string, error) { return toKeyUsage(out), nil } +// AuthorityInfoAccessSyntax ::= +// SEQUENCE SIZE (1..MAX) OF AccessDescription +// +// AccessDescription ::= SEQUENCE { +// accessMethod OBJECT IDENTIFIER, +// accessLocation GeneralName } + +type AccessDescription struct { + AccessMethod string + AccessLocation string +} + +func ToAuthorityInformationAccess(in []byte) ([]AccessDescription, error) { + sequence := asn1.RawValue{Tag: asn1.TagSequence} + if _, err := asn1.Unmarshal(in, &sequence); err != nil { + return nil, err + } + in = sequence.Bytes + + var accesses []AccessDescription + for { + var out struct { + AccessMethod asn1.ObjectIdentifier + AccessLocation asn1.RawValue // TODO parse to general name + } + rest, err := asn1.Unmarshal(in, &out) + if err != nil { + return nil, err + } + name := toGeneralName(out.AccessLocation) + oid := out.AccessMethod.String() + accesses = append(accesses, AccessDescription{ + AccessMethod: fmt.Sprintf("%s (%s)", accessDescriptorsOIDs[oid], oid), + AccessLocation: fmt.Sprintf("%s: %s", name.Type, name.Value), + }) + if len(rest) == 0 { + break + } + in = rest + } + return accesses, nil +} + func ToExtendedKeyUsage(in []byte) ([]string, error) { sequence := asn1.RawValue{Tag: asn1.TagSequence} if _, err := asn1.Unmarshal(in, &sequence); err != nil { @@ -252,7 +295,7 @@ func ToExtendedKeyUsage(in []byte) ([]string, error) { extKeyUsage := out.String() if v, ok := idKpOIDs[extKeyUsage]; ok { - extKeyUsage = fmt.Sprintf("%s - %s", extKeyUsage, v) + extKeyUsage = fmt.Sprintf("%s (%s)", v, extKeyUsage) } extKeyUsages = append(extKeyUsages, extKeyUsage) @@ -312,7 +355,7 @@ func ToCertificatePolicies(in []byte) ([]string, error) { policy := out.PolicyIdentifier.String() if v, ok := certificatePoliciesOIDs[policy]; ok { // if we find correct oid, use that - policy = fmt.Sprintf("%s - %s", policy, v) + policy = fmt.Sprintf("%s (%s)", v, policy) } // TODO - policy qualifiers when I find appropriate cert to test @@ -326,6 +369,14 @@ func ToCertificatePolicies(in []byte) ([]string, error) { return policies, nil } +func ToSignedCertificateTimestampList(in []byte) ([]byte, error) { + var out asn1.RawValue // OCTET STRING + if _, err := asn1.Unmarshal(in, &out); err != nil { + return nil, err + } + return out.Bytes, nil +} + // --- bit strings and conversions --- // order is important, it matches either asn tag or bit string @@ -420,26 +471,34 @@ func toGeneralName(in asn1.RawValue) GeneralName { // --- OIDs --- var certificatePoliciesOIDs = map[string]string{ - "2.23.140.1.1": "ev-guidelines", + "2.5.29.32.0": "any policy", + "2.5.29.32.2": "ldap", + + "2.23.140.1.1": "ev guidelines", // baseline requirements - "2.23.140.1.2.1": "domain-validated", - "2.23.140.1.2.2": "organization-validated", - "2.23.140.1.2.3": "individual-validated", + "2.23.140.1.2.1": "domain validated", + "2.23.140.1.2.2": "organization validated", + "2.23.140.1.2.3": "individual validated", - "2.23.140.1.3": "extended-validation-codesigning", + "2.23.140.1.3": "extended-validation codesigning", // code-signing-requirements - "2.23.140.1.4.1": "code-signing", + "2.23.140.1.4.1": "code signing", "2.23.140.1.4.2": "timestamping", // smime - "2.23.140.1.5.1": "mailbox-validated", - "2.23.140.1.5.2": "organization-validated", - "2.23.140.1.5.3": "sponsor-validated", - "2.23.140.1.5.4": "individual-validated", + "2.23.140.1.5.1": "mailbox validated", + "2.23.140.1.5.2": "organization validated", + "2.23.140.1.5.3": "sponsor validated", + "2.23.140.1.5.4": "individual validated", "2.23.140.31": "onion-ev", + + // google trust services, certificate policy + "1.3.6.1.4.1.11129.2.5.3.1": "signed http exchanges", + "1.3.6.1.4.1.11129.2.5.3.2": "client authentication", + "1.3.6.1.4.1.11129.2.5.3.3": "document signing", } var idKpOIDs = map[string]string{ @@ -454,3 +513,20 @@ var idKpOIDs = map[string]string{ "1.3.6.1.5.5.7.3.9": "OCSP signing", // TODO add the rest } + +var accessDescriptorsOIDs = map[string]string{ + "1.3.6.1.5.5.7.48.1": "ocsp", + "1.3.6.1.5.5.7.48.2": "ca issuers", + "1.3.6.1.5.5.7.48.3": "time stamping", + "1.3.6.1.5.5.7.48.4": "dvcs", + "1.3.6.1.5.5.7.48.5": "ca repository", + "1.3.6.1.5.5.7.48.6": "http certs", + "1.3.6.1.5.5.7.48.7": "http crls", + "1.3.6.1.5.5.7.48.8": "xkms", + "1.3.6.1.5.5.7.48.9": "signed object repository", + "1.3.6.1.5.5.7.48.10": "rpki manifest", + "1.3.6.1.5.5.7.48.11": "signed object", + "1.3.6.1.5.5.7.48.12": "cmc", + "1.3.6.1.5.5.7.48.13": "rpki notify", + "1.3.6.1.5.5.7.48.14": "stir tn list", +} diff --git a/pkg/cert/cert.go b/pkg/cert/cert.go index 909947d..d364802 100644 --- a/pkg/cert/cert.go +++ b/pkg/cert/cert.go @@ -209,7 +209,10 @@ func (c Certificate) Extensions() string { if v.Critical { name = fmt.Sprintf("%s [critical]", name) } - lines = append(lines, fmt.Sprintf("%s\n %s", name, v.Value)) + lines = append(lines, name) + for _, line := range v.Values { + lines = append(lines, fmt.Sprintf(" %s", line)) + } } return strings.Join(lines, "\n") } diff --git a/pkg/cert/extensions.go b/pkg/cert/extensions.go index 8633b5e..1198c58 100644 --- a/pkg/cert/extensions.go +++ b/pkg/cert/extensions.go @@ -11,7 +11,7 @@ type Extension struct { Name string Oid string Critical bool - Value string + Values []string } func ToExtensions(in []pkix.Extension) []Extension { @@ -22,20 +22,20 @@ func ToExtensions(in []pkix.Extension) []Extension { Name: name, Oid: v.Id.String(), Critical: v.Critical, - Value: value, + Values: value, }) } return out } -func parseExtension(in pkix.Extension) (string, string) { +func parseExtension(in pkix.Extension) (string, []string) { if fn, ok := extensionsByOid[in.Id.String()]; ok { return fn(in.Value) } - return "-", in.Id.String() + return "-N/A-", []string{in.Id.String()} } -var extensionsByOid = map[string]func(in []byte) (string, string){ +var extensionsByOid = map[string]func(in []byte) (string, []string){ "2.5.29.35": parseAuthorityKeyIdentifier, "2.5.29.14": parseSubjectKeyIdentifier, "2.5.29.15": parseKeyUsage, @@ -52,11 +52,9 @@ var extensionsByOid = map[string]func(in []byte) (string, string){ //"2.5.29.54": parseInhibitAnyPolicy, //"2.5.29.46": parseFreshestCRL, // private internet extensions - //"1.3.6.1.5.5.7.1": parseAuthorityInformationAccess, + "1.3.6.1.5.5.7.1.1": parseAuthorityInformationAccess, //"1.3.6.1.5.5.7.11": parseSubjectInformationAccess, - // TODO - //"1.3.6.1.5.5.7.1.1": parseAuthorityInfoAccessSyntax - // "1.3.6.1.4.1.11129.2.4.2" parseOID ??? + "1.3.6.1.4.1.11129.2.4.2": parseSignedCertificateTimestampList, } // AuthorityKeyIdentifier ::= SEQUENCE { @@ -65,11 +63,11 @@ var extensionsByOid = map[string]func(in []byte) (string, string){ // authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } // -- authorityCertIssuer and authorityCertSerialNumber MUST both // -- be present or both be absent -func parseAuthorityKeyIdentifier(in []byte) (string, string) { +func parseAuthorityKeyIdentifier(in []byte) (string, []string) { name := "Authority Key Identifier" out, err := ToAuthorityKeyIdentifier(in) if err != nil { - return name, err.Error() + return name, []string{err.Error()} } fields := []string{formatHexArray(out.KeyIdentifier)} @@ -80,17 +78,17 @@ func parseAuthorityKeyIdentifier(in []byte) (string, string) { if out.AuthorityCertSerialNumber != 0 { fields = append(fields, fmt.Sprintf("Authority Cert SN: %d", out.AuthorityCertSerialNumber)) } - return name, strings.Join(fields, ", ") + return name, fields } // SubjectKeyIdentifier ::= KeyIdentifier -func parseSubjectKeyIdentifier(in []byte) (string, string) { +func parseSubjectKeyIdentifier(in []byte) (string, []string) { name := "Subject Key Identifier" out := asn1.RawValue{Tag: asn1.TagOctetString} if _, err := asn1.Unmarshal(in, &out); err != nil { - return name, err.Error() + return name, []string{err.Error()} } - return name, formatHexArray(out.Bytes) + return name, []string{formatHexArray(out.Bytes)} } // KeyUsage ::= BIT STRING { @@ -104,40 +102,40 @@ func parseSubjectKeyIdentifier(in []byte) (string, string) { // cRLSign (6), // encipherOnly (7), // decipherOnly (8) } -func parseKeyUsage(in []byte) (string, string) { +func parseKeyUsage(in []byte) (string, []string) { name := "Key Usage" out, err := ToKeyUsage(in) if err != nil { - return name, err.Error() + return name, []string{err.Error()} } - return name, strings.Join(out, ", ") + return name, out } -func parseCertificatePolicies(in []byte) (string, string) { +func parseCertificatePolicies(in []byte) (string, []string) { name := "Certificate Policies" out, err := ToCertificatePolicies(in) if err != nil { - return name, err.Error() + return name, []string{err.Error()} } - return name, strings.Join(out, ", ") + return name, out } -func parseSubjectAltName(in []byte) (string, string) { +func parseSubjectAltName(in []byte) (string, []string) { name := "Subject Alt. Name" out, err := ToGeneralNames(in) if err != nil { - return name, err.Error() + return name, []string{err.Error()} } - return name, strings.Join(out, ", ") + return name, out } -func parseExtendedKeyUsage(in []byte) (string, string) { +func parseExtendedKeyUsage(in []byte) (string, []string) { name := "Extended Key Usage" out, err := ToExtendedKeyUsage(in) if err != nil { - return name, err.Error() + return name, []string{err.Error()} } - return name, strings.Join(out, ", ") + return name, out } // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint @@ -150,11 +148,11 @@ func parseExtendedKeyUsage(in []byte) (string, string) { // DistributionPointName ::= CHOICE { // fullName [0] GeneralNames, // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } -func parseCRLDistributionPoints(in []byte) (string, string) { +func parseCRLDistributionPoints(in []byte) (string, []string) { name := "CRL Distribution Points" out, err := ToCRLDistributionPoints(in) if err != nil { - return name, err.Error() + return name, []string{err.Error()} } var points []string for _, v := range out { @@ -172,22 +170,53 @@ func parseCRLDistributionPoints(in []byte) (string, string) { points = append(points, strings.Join(point, " ")) } } - return name, strings.Join(points, "; ") + return name, points +} + +// AuthorityInfoAccessSyntax ::= +// SEQUENCE SIZE (1..MAX) OF AccessDescription +// +// AccessDescription ::= SEQUENCE { +// accessMethod OBJECT IDENTIFIER, +// accessLocation GeneralName } + +func parseAuthorityInformationAccess(in []byte) (string, []string) { + name := "Authority Information Access" + out, err := ToAuthorityInformationAccess(in) + if err != nil { + return name, []string{err.Error()} + } + var fields []string + for _, v := range out { + fields = append(fields, fmt.Sprintf("%s - %s", v.AccessMethod, v.AccessLocation)) + } + return name, fields +} + +func parseSignedCertificateTimestampList(in []byte) (string, []string) { + name := "CT Precertificate SCTs" + return name, []string{"..."} + // TODO parse "Certificate Transparency", validate against openssl x509 output + //out, err := ToSignedCertificateTimestampList(in) + //if err != nil { + // return name, []string{err.Error()} + //} + //return name, []string{formatHexArray(out)} } // BasicConstraints ::= SEQUENCE { // cA BOOLEAN DEFAULT FALSE, // pathLenConstraint INTEGER (0..MAX) OPTIONAL } -func parseBasicConstraints(in []byte) (string, string) { +func parseBasicConstraints(in []byte) (string, []string) { name := "Basic Constraints" out, err := ToBasicConstraints(in) if err != nil { - return name, err.Error() + return name, []string{err.Error()} } fields := []string{fmt.Sprintf("CA: %t", out.CA)} if out.PathLenConstraint != 0 { fields = append(fields, fmt.Sprintf("PathLenConstraint: %d", out.PathLenConstraint)) } - return name, strings.Join(fields, ", ") + return name, fields }