First, congrats to Let's Encrypt for deploying a new feature (ARI) into production. Let's give it a shot!
I'm adding ARI support to ACMEz, which is used by CertMagic and Caddy. The draft spec says:
The full request URL is computed by concatenating the renewalInfo URL
from the server's directory with a forward slash and the base64url-
encoded [RFC4648] bytes of a DER-encoded CertID ASN.1 sequence
[RFC6960].
My current API design takes in a decoded *x509.Certificate. I assume the SerialNumber field (big.Int) is the "CertID" I'm looking for (the post I link to below also refers to code in Boulder that calls it "Serial")?
However, we're apparently supposed to submit the ASN.1 bytes -- in that case should I use the Raw field? How do I extract only the relevant bytes?
I found this post today:
which was helpful in showing me what decoded structure I'm looking for, but I'm hoping someone with experience in Go can shine a light on things to make sure I get it right.
I probably should actually learn the ins and outs of ASN.1 at some point (I know LE has a great explainer on their blog).
Hang on a sec, I just noticed the reference to RFC 6960 in the quoted spec. When I search in the doc for "CertID" I find a grammar:
CertID ::= SEQUENCE {
hashAlgorithm AlgorithmIdentifier,
issuerNameHash OCTET STRING, -- Hash of issuer's DN
issuerKeyHash OCTET STRING, -- Hash of issuer's public key
serialNumber CertificateSerialNumber }
where this is used in an OCSP request. Huh... CertMagic already staples OCSP so we have code that makes a request. What does it look like, I wonder?
So I go to our code that does it, but we call a third-party package function, ocsp.CreateRequest(), which takes in an *x509.Certificate and returns []byte.
Huh. So what does that function do?
It returns a structure that looks very familiar now:
You can think of ASN.1 as like JSON in a world where people cared a ton about formal specifications and not having different serializations that are canonically equivalent to each other. It is perhaps a world in which people actively disagreed with Jon Postel about "be liberal what you accept", and wanted interoperability problems and type confusions to be noticed early and cause up-front failures. In this world, ever trying to write your own JSON by hand (or view JSON in a text editor) is considered crazy because your human subjectivity might cause you to misinterpret a data type somewhere. The specifiers have also provided further insurance against reading or writing serialized data by hand by making the serialization format genuinely hard for humans to deal with.
Therefore, you would have a habit of always encoding and decoding using a serialization library, and basically never trying to view or understand serialized data any other way¹. However, because of the formal specification thing, the serialization library's data types may or may not match directly with your programming language's data types, so it may be required in some cases to provide some kind of explicit type indications so that people using a different programming language will be sure of what was meant.
¹ although if you do look at a lot of it, you may start to notice patterns anyway, like that AQAB in PEM-encoded RSA public keys...
Issue opened upstream with Go to export some things:
PS. The spec says:
The ACME Server MAY restrict the hash algorithms which it accepts
(for example, only allowing SHA256 to limit the number of potential
cache keys); if it receives a request whose embedded hashAlgorithm
field contains an unacceptable OID, it SHOULD respond with HTTP
status code 400 (Bad Request).
I see that Let's Encrypt supports SHA-256. Is there a way to know which hashes are supported?
I like "trial and error" [but I'm sure there's a faster way(s)].
Thou, it can sometimes be more complete/correct than what's been advertised/expected/defined.
Just for giggles: What's a manual?
Back in my day... everything was done manually.
We had to use chisels on stone tablets to code - LOL
I just haven't found it documented. I guessed SHA-256 was supported based on the spec. I just am wondering how to tell users of my code which hash to use.
Cool, thanks. I ended up just extracting the relevant code from the ocsp package... but I'll keep this as reference since this approach may be useful too!