Skip Menu |

Subject: PKINIT should handle CMS SignedData without certificates
Download (untitled) / with headers
text/plain 2.9KiB
A KDC reply to a Diffie-Hellman PKINIT request contains an RFC 3852
SignedData object with a payload (the DH reply), an optional set of
certificates, and a SignerInfo object. The SignerInfo object contains
the identifier of the signing certificate and the signature itself.

RFC 4556 explicitly allows the SignedData certificates set to be omitted
if the request's kdcPkId matches the signer certificate; otherwise the
certificates "SHOULD be sufficient to construct at least one
certification path from the KDC certificate to one trust anchor
acceptable by the client". If the signer certificate is itself a trust
anchor as presented by the client, then it could also be considered
reasonable for the KDC to omit the certificates field of the SignedData,
as no certificates are needed to construct a path.

At present, we never (at least with OpenSSL) specify a kdcPkId value in
the request; there is code to construct it, but it relies on
pkinit_get_kdc_cert() doing something non-trivial, which it currently
doesn't. So a KDC will never omit the certificate field on account of a
kdcPkId value we send, as things stand.

But it is easy enough to tell the client that the KDC certificate is a
trust anchor, in which case a Heimdal KDC will reply with no
certificates because it removes trust anchors from the set of
certificates in the reply. So we ought to handle at least the case
where the SignerInfo sid is a trust anchor, rather than a certificate in
the SignedData message. We arguably also be able to handle the case
where the SignerInfo sid is an intermediate certificate (though we don't
tell the KDC about those, I don't think) or is the ID we put into
kdcPkId (although we currently never put an ID there).

At current there are three code paths for verifying the CMS SignedData
value: NSS, OpenSSL 1.0.x with CMS functions, and OpenSSL 0.9.x with the
older PKCS7 functions. The NSS code looks like it might already work
for these cases, but I'm not sure. The OpenSSL code wants to start by
extracting the signer cert, verify that cert, and then verify the
signature against the certificate with CMS_verify or PKCS7_verify.

With OpenSSL 0.9.x, we use PKCS7_cert_from_signer_info(), which only
works if the SignedData supplies the certificate. I'm not sure of an
easy way to fix the bug for this case, and time will eventually carry us
past having to worry about OpenSSL 0.9.x anyway.

With OpenSSL 1.0.x, we call CMS_set1_signers_certs(cms, NULL, 0) to
match the signer ID against the certificates in the cms message, and
then call CMS_SignerInfo_get0_algs to get the certificate which has (we
hope) been set in the signer info. We can use the second argument of
CMS_set1_signers_certs to match the signer ID against other certificates
sets such as the configured trusted anchors and intermediate certs. If
we ever build out pkinit_get_kdc_cert(), we'll need to match against
that as well.

Thanks to Henry Hotz and Nalin Dahyabhai for their help in investigating
this issue.
Subject: SVN Commit

Handle PKINIT DH replies with no certs

If a PKINIT Diffie-Hellman reply contains no certificates in the
SignedData object, that may be because the signer certificate was a
trust anchor as transmitted to the KDC. Heimdal's KDC, for instance,
filters client trust anchors out of the returned set of certificates.
Match against idctx->trustedCAs and idctx->intermediateCAs to handle
this case. This fix only works with OpenSSL 1.0 or later; when built
against OpenSSL 0.9.x, the client will still require a cert in the

Code changes suggested by
Author: Greg Hudson <>
Commit: db83abc7dcfe369bd4467c78eebb7028ba0c0e0d
Branch: master
src/plugins/preauth/pkinit/pkinit_crypto_openssl.c | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)