-
Notifications
You must be signed in to change notification settings - Fork 118
Rework Certificate issuance API, make DER/PEM serialization stable #205
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes the API much better, nice!
796b5d7
to
f55ac1c
Compare
The `Certificate` type as it exists at this commit has no signature, and it isn't self-signed. It _may_ be turned into a self signed certificate by calling `serialize_der()` or `serialize_pem()`, or it may be turned into a certificate signed by an issuer! Simplify the comment before more substantial refactoring.
This commit associates deriving a key identifier with the `KeyIdMethod` enum. This will make it easier to calculate a key identifier with only the public key in hand as opposed to the full parameters. Along the way the rustdocs associated with `KeyIdMethod` are tuned up for clarity/specificity.
Rather than have the `write_x509_authority_key_identifier` fn take a `Certificate` as input in order to calculate the AKI value, have the caller find the AKI and pass it to the write fn for writing. This simplifies generating a certificate with the issuer's `KeyPair`, and not a `Certificate`.
This commit updates the various `serialize_*` fns to take more targeted inputs as opposed to a `Certificate`. This will make it easier to perform subsequent refactors.
This will make it easier to have more than one callsite that performs on-demand key generation.
This commit removes `Certificate::from_params`, replacing it with `Certificate::generate_self_signed`. The `serialize_x` fns are replaced with `pem()` and `der()` accessors. To issue a certificate signed by an issuer, a new `Certificate::generate` constructor can be used. Unit tests, and the rustls-cert-gen utility, are updated accordingly.
In preparation for removing the `KeyPair` from `Certificate` we need to hold on to the subject public key info for use when generating key identifiers.
Rather than store `KeyPair` in `Certificate`, take `KeyPair` as a parameter when required for signing and return `KeyPair` from the generate methods as a new `CertifiedKey` type. This will allow unifying the API around the idea that a `Certificate` is public material. Tests and the cert gen utility are updated accordingly.
This commit updates the rustdoc comments on `Certificate::serialize_request_der` and `Certificate::serialize_request_pem`, emphasizing that they generate and sign CSRs for each invocation. The purpose of the `subject_key` argument is emphasized. Along the way, fix an inaccurate comment in `CertificateParams::write_request` - we're writing the _subject_ distinguished name, not the issuer. The `Name` field in the syntax in RFC 2986 is `subject`.
This unifies the creation of `Certificate`s to the `Certificate` constructors instead of being spread across both `Certificate` and `CertificateSigningRequest`.
This rename better emphasizes the role the CSR type plays, and aligns it with other `*Params` types that already exist in the API surface.
A quick pass through the rustdoc strings, adding more detail and clarifying text where appropriate.
f55ac1c
to
f3daf4b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks great!
I also really like the I'm also thinking about a |
As an alternative to #212, this goes further in the direction of #205, removing `alg` and `key_pair` from `CertificateParams` and requiring the caller to pass in a reference to them when signing. This seems to have a number of nice properties: * `alg` is derived from the passed in `KeyPair`, so key algorithm mismatch errors can no longer occur * No need for passing in a signing algorithm when parsing from pre-existing parameters or key pairs * Should make it easy to support long-lived (remote) key pairs The main downside as far as I can see is that the top-level API gets a bit more complicated, because generating a `KeyPair` must now be done by the caller, and for now we force them to pick a signing algorithm. I think we might mitigate this by adding a no-argument constructor like `generate_default()` (or use `generate()` for the no-argument variant and `generate_for()` for the variant that requires an argument). Generally, this feels like a clear improvement in the API's design to me.
Previously a
Certificate
was a container forCertificateParams
and aKeyPair
, most commonly created from aCertificateParams
instance. Serializing theCertificate
(either as self signed withserialize_pem
orserialize_der
, or signed by an issuer withserialize_pem_with_signer
orserialize_der_with_signer
) would issue a certificate and produce the serialized form in one operation. The net result is that if a user wanted both DER and PEM serializations they would likely callserialize_der(_with_signer)
and thenserialize_pem(_with_signer)
and mistakenly end up with the encoding of two distinct certificates, not the PEM and DER encoding of the same cert. Since theKeyPair
contains a private key this API design also meant that theCertificate
type had to be handled with care, andZeroized
.This branch reworks the issuance API and
Certificate
type to better match user expectation:Certificate
is only public material and represents an issued certificate that can be serialized in a stable manner in DER or PEM encoding.I recommend reviewing this commit-by-commit, but here is a summary of the most notable API changes:
Certificate::from_params
andCertificate::serialize_der
andCertificate::serialize_pem
for issuing a self-signed certificate are replaced withCertificate::generate_self_signed()
and callingder
orpem
on the result.Certificate::from_params
andCertificate::serialize_der_with_signer
andCertificate::serialize_pem_with_signer
for issuing a certificate signed by another certificate are replaced withCertificate::generate()
and callingder
orpem
on the result.CertificateSigningRequest::serialize_der_with_signer
andCertificateSigningRequest::serialize_pem_with_signer
for issuing a certificate from a CSR are replaced withCertificate::from_request
and callingder
orpem
on the result. TheCertificateSigningRequest
type is renamed toCertificateSigningRequestParams
to better emphasize its role and match the other*Params
types that already exist.Certificate
construction time, thepem
andder
fns are now infallible.Certificate
no longer holdsKeyPair
, thegenerate
fns now expect a&KeyPair
argument for the signer when issuing a certificate signed by another certificate.CertifiedKey
that contains both aCertificate
and aKeyPair
. For params that specify a compatibleKeyPair
it is passed through in theCertifiedKey
as-is. For params without aKeyPair
a newly generatedKeyPair
is used.In the future we should look at harmonizing the creation of
CertificateSigningRequest
andCertificateRevocationList
to better match this updated API. Unfortunately I don't have time to handle that at the moment. Since this API surface is relatively niche compared to theCertificate
issuance flow it felt valuable to resolve #62 without blocking on this future work.Resolves #62