[Pki-devel] ACME certificate IDs

Fraser Tweedale ftweedal at redhat.com
Mon Mar 23 01:54:35 UTC 2020


On Fri, Mar 20, 2020 at 03:41:05PM -0400, Endi Sukma Dewata wrote:
> ----- Original Message -----
> > > Let me backtrack a little bit. Is there a plan to modify Dogtag to
> > > eventually support different serial number domains? If not, this is
> > > not an issue for Dogtag.
> >
> > There is no plan to do so.  It is not an issue for Dogtag.  But
> > still, I feel basing certificate ID on only serial number is not a
> > robust approach in general.
> > 
> > > If there is such plan, will the issuer DN
> > > be unique across LWCAs? If issuer DN will be unique, it's something
> > > to consider. If not, (issuer DN, serial number) will not be unique
> > > either, so we need to use something else such as authority ID.
> > > 
> > > Or is there another backend with multiple issuers that we want to
> > > support in the future? The cert ID will have to be something like
> > > (issuer ID, serial number) where the issuer ID is unique for the
> > > backend. If the issuer DN is unique, it can be used as the issuer
> > > ID. Otherwise, it needs to be a backend-specific unique ID similar
> > > to authority ID in Dogtag.
> >
> > All certs must have unique (issuer,serial).  This is implied by the
> > requirement that all certs from a given issuer must have different
> > serial numbers.
> > 
> > > We need to consider these possibilities before changing the cert ID.
> > > On the other hand, I'm still not sure it's actually necessary to
> > > include these information into cert ID.
> > > 
> > > Let's look at the code. For cert enrollment (ACMEFinalizeOrderService)
> > > we convert the serial number that we get from ACMEBackend into cert ID:
> > > 
> > >   BigInteger serialNumber = backend.issueCertificate(csr);
> > >   String certID =
> > >   Base64.encodeBase64URLSafeString(serialNumber.toByteArray());
> > > 
> > > We can change it so ACMEBackend can generate the cert ID like this:
> > > 
> > >   String certID = backend.issueCertificate(csr);
> > > 
> > 
> > I strongly agree with pushing the certID generation into the
> > ACMEBackend.  Stepping away from the whole (issuer,serial)
> > discussion, say (for example) the only "handle" a backend has for
> > accessing a cert is a UUID.  Then storing the serial number is no
> > good - you cannot derive the UUID handle from the serial number.
> > 
> > So the backend must generate the (String) certID that is appropriate
> > for that backend.
> > 
> > > If the cert ID is (issuer DN, serial number), we can generate the
> > > cert ID from the new cert. But does the backend return the new cert
> > > or just the serial number?
> > >
> > Yeah, good question; of course you must be able to retrieve the
> > cert (and therefore you can learn the Issuer DN) but this could mean
> > another round-trip to Dogtag.  Which is the next thing you said :)
> > 
> > > If the serial number is not unique, the
> > > backend might need to be changed to return the cert itself so we
> > > can get the issuer DN.
> > > 
> > > If the cert ID is (authority ID, serial number), how do we get the
> > > authority ID since it's not included in the cert? The backend might
> > > need to be changed to return the authority ID along with the new
> > > cert, or to provide a way to look up the authority ID using a cert.
> > >
> > I am not suggesting to use the authority ID.  But FWIW Dogtag does
> > enforce that Issuer DN <-> Authority ID is a bijection.
> > 
> > > 
> > > For cert retrieval (ACMECertificateService) we're passing the cert ID
> > > to ACMEBackend:
> > > 
> > >   String certChain = backend.getCertificateChain(certID);
> > > 
> > > The ACMEBackend can extract the issuer DN or authority ID from the
> > > cert ID so it can retrieve the cert from the backend again.
> > > 
> > > Since we get the cert during enrollment anyway, we can actually store
> > > it into ACME database like this:
> > > 
> > >   String certChain = backend.issueCertificate(csr);
> > >   String certID = database.addCert(certChain, orderID, accountID,
> > >   expirationTime);
> > > 
> > > Later we can simply retrieve it from the database instead of calling
> > > the backend again:
> > > 
> > >   String certChain = database.getCertificateChain(certID);
> > > 
> > As I said in previous email, I am opposed to storing the cert
> > (chain) in the ACME database.  If some backend requires it e.g.
> > because the backend itself does not store the cert, then it can be
> > optional.  But we do not need that now.
> > 
> > > Here the cert ID can simply be a unique ID generated by the database.
> > > Unlike earlier, the backend doesn't need to know about cert ID at all.
> > > 
> > > For cert revocation (ACMERevokeCertificateService) the client will
> > > only provide the cert binaries. It doesn't provide the cert ID.
> > > 
> > And the ACMEBackend implementation receives the cert, and must work
> > out what to do with it.  How it tells the backend system to revoke
> > the certificate, and whether that process even involves a string
> > CertID handle, or just a serial number, or the (issuer,serial) pair,
> > or whatever, depends on the backend system.  But I think that the
> > current interface:
> > 
> >     public void revokeCert(ACMERevocation revocation) ...
> > 
> > ... is suitable.
> > 
> > > Currently the ACMEEngine.validateRevocation() will generate the
> > > cert ID from the serial number so it can find the order that
> > > generated the cert (so we can authorize the account):
> > > 
> > >   String certID =
> > >   Base64.encodeBase64URLSafeString(serialNumber.toByteArray());
> > >   ACMEOrder order = database.getOrderByCertificate(certID);
> > > 
> > > We can changed it so ACMEBackend can generate the cert ID like this:
> > > 
> > >   String certID = backend.getCertID(certBytes);
> > >   ACMEOrder order = database.getOrderByCertificate(certID);
> > > 
> > > If the cert ID is (issuer DN, serial number), we can generate the
> > > cert ID from the provided cert binaries. But if the cert ID is
> > > (authority ID, serial number), how do we get the authority ID? Do
> > > we call the lookup operation above again to get the authority ID?
> > > 
> > > Instead of that we can do this:
> > > 
> > >   String certID = database.getCertID(certBytes);
> > >   ACMEOrder order = database.getOrderByCertificate(certID);
> > > 
> > > which doesn't involve the backend at all.
> > > 
> > > So backend-issued cert ID might work if we use a backend that
> > > already provides the required functionality above. Otherwise we
> > > may need to modify the backend, which is not always an option.
> > > 
> > > The database-issued cert ID is a solution that doesn't require
> > > modifications to the backend, so I think it should be the default
> > > option. The certs stored in ACME database should be considered
> > > a cache. The server can purge it so it doesn't grow too large if
> > > that's a concern.
> > > 
> > > Note that regardless of cert ID, the above revocation mechanism
> > > relies on order, authorization, or cert records in ACME database,
> > > which may not be available depending on the server's purging
> > > policy. If someone needs to have a reliable revocation mechanism
> > > they need to revoke using the private key.
> > > 
> > Let us put aside the discussion about whether for the PKIBackend we
> > use only (serial) as certID, or (issuer,serial) pair.  I think we
> > *should* switch to something derived from (issuer,serial), but we do
> > not *need* to.  So we can leave that discussion for now.
> > 
> > The main change we need is for ACMEBackend.issuerCertificate to
> > return String certID, i.e.:
> > 
> >     public String issuerCertificate(String csr) ...
> > 
> > because BigInteger (i.e. serial) may not be an appropriate "handle"
> > for all backends.  Hence we should require each ACMEBackend
> > implementation to produce the appropriate certIDs.
> > 
> > Do you agree?
> > 
> > Cheers,
> > Fraser
> > 
> 
> Hi Fraser,
> 
> Please take a look at this PR:
> https://github.com/dogtagpki/pki/pull/350
> 
> So the PKIBackend will continue to work like before, but
> these changes should allow us to support:
> - both single-authority and multi-authority backends
> - both one-step and two-step enrollments
> 
> One thing though, I think we discussed before about using UUID to
> generate the serial number which has a very low chance of collision.
> If a backend uses UUID for all of its certificate authorities, can
> the serial number by itself be considered unique?
> 
Practically speaking, I'd say yes.

As I already discussed at length, I'd prefer we rely on what X.509
*requires* to be unique (i.e. (issuer,serial)) where possible.  But
if we are confident a backend with multiple issuers has a single
serial number domain (whether they are UUIDs or just a single
sequential source like Dogtag), *and* that this property is unlikely
to change, then I can live with it.

I'll try to review PR 350 today.

Cheers,
Fraser




More information about the Pki-devel mailing list