Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]Discussion: x.509 Certificates - deterministic handling and fingerprinting. #360

Open
2 tasks
tsellers-r7 opened this issue May 12, 2021 · 0 comments
Open
2 tasks

Comments

@tsellers-r7
Copy link
Contributor

tsellers-r7 commented May 12, 2021

[WIP] Issue

Summary

This issue is intended to serve as a point of discussion related to the effort to standardize our handling of the distinguished name values found in x.509 certificate subject and issuer fields. The intent is to arrive at a field ordering that is

  • portable across programming languages (at least Java, Python, and Go)
  • deterministic even when processing unknown fields

Commentary from HD about handling in Go:

This fingerprint set matches the Subject field of x509 certificates. These x509
certificates may be sourced from any SSL or TLS service. If a particular system
has identical subject and issuer fields, the subject field should be preferred.
The format of the Subject field is built from the x509 distinguished names using
a specific order. This order matches the Go implementation at the URL:
https://golang.org/src/crypto/x509/pkix/pkix.go#203
The ToRDNSequence() function builds the string in reverse order:
func (n Name) ToRDNSequence() (ret RDNSequence) {
ret = n.appendRDNs(ret, n.Country, oidCountry)
ret = n.appendRDNs(ret, n.Province, oidProvince)
ret = n.appendRDNs(ret, n.Locality, oidLocality)
ret = n.appendRDNs(ret, n.StreetAddress, oidStreetAddress)
ret = n.appendRDNs(ret, n.PostalCode, oidPostalCode)
ret = n.appendRDNs(ret, n.Organization, oidOrganization)
ret = n.appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
if len(n.CommonName) > 0 {
ret = n.appendRDNs(ret, []string{n.CommonName}, oidCommonName)
}
if len(n.SerialNumber) > 0 {
ret = n.appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber)
}
for _, atv := range n.ExtraNames {
ret = append(ret, []AttributeTypeAndValue{atv})
}
return ret
}
All names are separated by commas and any commas inside a name are escaped with a
single backslash character. See RFC 2253 for additional details on formatting.
Practically, most Subjects start with the Common Name (CN=) and then step through
Organization Unit (OU), Organization (O), and then some level of location, but
typically Locality (L) and Country (C). Names are guaranteed to be listed in
the order described above, but may start at any point in the list. For example,
Subjects may start with a Serial Number (SERIALNUMBER=) or even Extra Names, but
these are somewhat rare. Keep this name order in mind when working on these
fingerprints.

Commentary and example from HD on handling of extra fields in Go:

The relevant Go code for extra name handling:

			oidString := tv.Type.String()
			typeName, ok := attributeTypeNames[oidString]
			if !ok {
				derBytes, err := asn1.Marshal(tv.Value)
				if err == nil {
					s += oidString + "=#" + hex.EncodeToString(derBytes)
					continue // No value escaping necessary.
				}

				typeName = oidString
			}

In practice this looks like:

CN=device.corp.com,OU=VMware Engineering,O=VMware,L=Palo Alto,ST=California,C=US,1.2.840.113549.1.9.1=#0c0f766d636140766d776172652e636f6d

1.2.840.113549.1.9.1 is the OID of the EMAILADDRESS attribute. The value includes the DER bytes, including the Type and Length before the value ([email protected]).

TODO before requesting feedback from others:

  • Build simple Python, Ruby, Go, and Rust tools that connect to an HTTP endpoint and emit the certificate with the least amount of processing
  • Use the tools above to provide reference examples to show the differences

Examples

Some example subject values that haven't been accounted for in our prior discussions.

unstructuredName

{
  "C": "US",
  "CN": "foo.bar",
  "L": "Palo Alto",
  "O": "VMware, Inc",
  "OU": "VMware ESX Server Default Certificate",
  "ST": "California",
  "emailAddress": "[email protected]",
  "unstructuredName": "1617207215,564d7761726520496e632e"
}

businessCategory, jurisdictionC , jurisdictionL, jurisdictionST

{
  "C": "DE",
  "CN": "foo.bar",
  "L": "Hannover",
  "O": "TUI AG",
  "ST": "Niedersachsen",
  "businessCategory": "Private Organization",
  "jurisdictionC": "DE",
  "jurisdictionL": "Charlottenburg",
  "jurisdictionST": "Berlin",
  "serialNumber": "HRB 000"
}

name

{
  "name": "AR2220-Self-Signed-Certificate-2102352934DMGA000458"
}

description

{
  "CN": "00:09:52:05:b9:73",
  "O": "Auerswald",
  "description": "Vendor=Auerswald;SN=0000000000;MAC=00:00:00:00:00:00;DevClass=PBX;DevTyp=COMpact5200"
}`

From Microsoft Active Directory, DC

{
  "CN": "foo.bar.local",
  "DC": "va",
  "OU": "devices"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant