Stir/SHaken: Cert not trusted: unable to get certificate CRL

Hello,
I’m setting an Asterisk 20.8.1 up to support Stir/Shaken.
When I’m setting either crl_file or crl_path in stir_shaken.conf, an incoming call produces:
Cert ‘https://XXX/yyy.cer’ not trusted: unable to get certificate CRL

When I remove both crl_file or crl_path settings from stir_shaken.conf, nothing specific is logged when a call comes in.

  1. Is it correct to think that when both crl_file and crl_path settings are absent from stir_shaken.conf, Asterisk skips CRL checking (ie current cert revocation is not checked at all) ?

  2. If positive, how can I solve this “Cert ‘https://XXX/yyy.cer’ not trusted …” message ?

Cheers

Is your cert genereatd via a cert provider ?

That message comes directly from the call to OpenSSL’s X509_verify_cert() API. Are you absolutely sure that both the verification and profile crl_file and crl_path are empty?

The outputs from stir_shaken show verification and stir_shaken show eprofile <profile_name> will help.

You can verify the cert manually by retrieving the cert with curl and openssl verify.

curl -o yyy.cer https://XXX/yyy.cer
openssl verify -CAfile <ca_file> yyy.cer
or
openssl verify -CApath <ca_path> yyy.crt

Use the same ca_file or ca_path from stir_shaken.conf.

You should also post the results of openssl x509 -in yyy.cer -text -noout. In particular, does it have a CRL section?

The un-trusted ‘https://XXX/yyy.cer’ is issued by local cert authority.
I’m quite confident the cert itself is OK as if I remove Asterisk’s CRL settings, the cert is rated as OK by Asterisk.
I haven’t analyzed this cert content yet but my intuition is the cert is OK and my CRL settings are not.

@gjoseph

I’ve tried two alternatives:

  1. with crl_file set and crl_path unset
  2. with crl_file unset and crl_path set

In both alternatives, both stir_shaken show verification and stir_shaken show eprofile <profilename> printed consistent output (one value set, the other being unset).

One important observation, in alternative 1, both stir_shaken show verification and stir_shaken show eprofile <profilename> commands consistently restarted Asterisk !

Can you confirm leaving both crl_file and crl_path unset, implies CRL is not checked at all ?

Locally, CRL is only published in DER format.
So I download it in DER format before converting it to PEM format, as in my testing, Asterisk 20.8.1 didn’t like DER (see other thread).
Once this is done, this is what I’ve got:

openssl verify -CAfile /etc/asterisk/stir/<cafile>.cer /etc/asterisk/crl/crl.pem
unable to load certificate
139833714574656:error:0909006C:PEM routines:get_name:no start line:../crypto/pem/pem_lib.c:745:Expecting: TRUSTED CERTIFICATE

I’m not able to replicate this. Can you open an issue at Issues · asterisk/asterisk · GitHub and post a backtrace?

I think you’re mixing your CRL and CA Cert files. You say you’re downloading the CRL in DER format then converting it to PEM but you’re using the -CAfile option to openssl verify. For your purposes, you need to specify the CA certificate, the CRL and your own certificate so something like…

openssl verify -CAfile <ca_file> -CRLfile <crl_file> <certificate>

The help for openssl verify does state that the CRLfile must be in PEM format. This may explain why it needs to be in PEM format for Asterisk as well.

@gjoseph
As an failed run of openssl verify -CAfile <ca_file> yyy.cer indicates an issue, does it make to run it preventively for each profile, during rs_stir_shaken.so module reload to warn sysadmin against inconsistent data ?

It’s possible but as I said earlier, we pass those parameters directly to OpenSSL on startup/reload so if it doesn’t complain then we have no other way to check. I’ll be trying to load DER format CRLs later today.

As requested, I will open an issue in Github and provide a backtrace as requested for the case where Asterisk restart when typing stir_shaken show verification.

For the other issue (ERROR shown on console when calls come in), here is the openssl command that works OK:
openssl verify -CAfile <ca_file> -untrusted <intermediate_cert> -CRLfile <crl_file> `

Where:
is the downloadable cert representing the originating ITSP
<intermediate_cert> is the cert mentioned in CA Issuers field (this file is present in ca_path directory)
<ca_file> is the file present in ca_file in stir_shaken.conf (and mentioned in <intermediate_cert> CA Issuers field)
<crl_file> is the file present in crl_file in stir_shaken.conf

If I omit the -untrusted setting, openssl prints an error out.

Does it help ?

I filed issue #809 on Github for Asterisk restarting.

I’m a bit confused. So now there’s an intermediate cert?

Place the CA certificate file and the intermediate certificate file in the ca_path directory then ensure that each file has only 1 certificate in it. If a file has more than 1 certificate, you’ll need to split them out into separate files. They must be PEM files.
When that’s done, run openssl rehash <ca_path>.

Now run openssl verify -CApath <ca_path> -CRLfile <crl_file> <certificate>
If that doesn’t verify then asterisk won’t either.
If openssl does verify and asterisk doesn’t, you’ll need to send me the certificate crl files so I can test. I’ll PM you instructions if this is the case.

The command bellow worked OK
openssl verify -CApath <ca_path> -CRLfile <crl_file> <certificate>

After changing stir_shaken.conf’s content to adapt to this (ie setting ca_path, setting or un-setting ca_file) and restarting Asterisk, I still have the same output bellow.
Cert 'https://xxx.cer' not trusted: unable to get certificate CRL.

Setting debug level to 5, doesn’t add anything helping to understand why Asterisk is unable to get certificate CRL or why passing this CRL to openssl from within Asterisk produces a different result than passing it to Shell’s openssl verifiy.

I’m ready to privately send needed files.

Hoping this could be helpful, here is an extract of the Python3 I used to convert a CRL publicly published in DER format, to a file in PEM format.

with open(f'{TMPDIR}/crl.pem', 'wb') as fd:
    crl = x509.load_der_x509_crl(response.content)
    count = fd.write(crl.public_bytes(encoding=Encoding.PEM))

Stored file then has a single (long) block of data surrounded by

-----BEGIN X509 CRL-----
-----END X509 CRL-----

Correct. If neither is set no CRL checking is done.

I’ve also confirmed that all files must be in PEM format, not DER.
You don’t need python to convert from DER to PEM. Just use…
openssl crl -inform DER -in crl.der -outform PEM -out crl.pem

I did find another bug in addition to the crash you reported… I was setting CRL_CHECK_ALL on the certificate store in addition to CRL_CHECK and this was causing issues. When I remove that flag, I can successfully use CRL checking on MY generated CA, intermediate certificates and CRLs. Yours however are still an issue.

One thing that’s missing from the openssl verify command line I gave you was the -crl_check so no crl checking was being done. If you add that parameter verification fails with the same error that asterisk shows.

openssl verify -show_chain -verbose -CApath ./capath -CRLfile crl.pem -crl_check 7509624163b221b0.cer
CN=SHAKEN FRTE00, O=Orange, C=FR
error 3 at 0 depth lookup: unable to get certificate CRL
error 7509624163b221b0.cer: verification failed

If you look at the 15 and 7509624163b221b0 certificates you’ll see that they point to two different CRLs and the two CRLs were issued by two different CAs.

15…

X509v3 CRL Distribution Points: 
  Full Name:
     URI:https://api.man-bpco.fr/ca/crl                CRL Issuer:
     DirName:C = FR, O = Base des Certificats Op\C3\A9rateurs, CN = BPCO R1 - SHAKEN Root
             C = FR, O = Base des Certificats Opérateurs, OU=Certificate Authority CN=BPCO R1 - SHAKEN Root

7509624163b221b0…

X509v3 CRL Distribution Points: 
    Full Name:
      URI:https://api.man-bpco.fr/crl                CRL Issuer:
      DirName:CN = BPCO PA1, O = Base des Certificats Op\C3\A9rateurs, OU = Policy Authority, C = FR
    CRL Issuer:
      DirName:CN = BPCO PA2, O = Base des Certificats Op\C3\A9rateurs, OU = Policy Authority, C = FR

If you pull down both CRLs, and dump them, you’ll see that the CRL from https://api.man-bpco.fr/crl is an “Indirect CRL” was actually issued by https://api.man-bpco.fr/ca/certs/13.cer

I have no idea how an “Indirect CRL” is supposed to verify but I’ll do more research.

If you pull down the CRL referenced in the intermediate certificate, the intermediate certificate itself does verify.

openssl verify -show_chain -verbose -CApath ./capath -CRLfile crl_from_int.pem -crl_check 15.cer 
15.cer: OK
Chain:
depth=0: CN=BPCO CA1 - SHAKEN Intermediate, O=Base des Certificats Opérateurs, OU=Certificate Authority, C=FR (untrusted)
depth=1: CN=BPCO R1 - SHAKEN Root, O=Base des Certificats Opérateurs, OU=Certificate Authority, C=FR

OK, I think I’ve got it figured out…

You already had…

File Purpose
008f53fd8d779901c7.cer Root CA Certificate
15.cer Intermediate CA Certificate
7509624163b221b0.cer End Entity Certificate
The cert in the X5U field of the Identity header
crl.pem CRL retrieved from the End Entity CRL extension
https://api.man-bpco.fr/crl

Now here’s the issue. If you examine the CRL…

Certificate Revocation List (CRL):
  Version 2 (0x1)
  Signature Algorithm: ecdsa-with-SHA256
  Issuer: CN=BPCO PA1, O=Base des Certificats Opérateurs, OU=Policy Authority, C=FR
  Last Update: Jul 16 08:54:21 2024 GMT
  Next Update: Jul 23 08:54:21 2024 GMT
  CRL extensions:
    X509v3 Issuing Distribution Point: critical
      Indirect CRL
        Authority Information Access: 
          CA Issuers - URI:https://api.man-bpco.fr/ca/certs/13.cer

It’s not signed by either the root or Intermediate CAs. It’s signed by the “BPCO Policy Authority” so to get the EndEntity certificate to verify against the CRL, you also need need the PA’s certificate which you’ll see is at https://api.man-bpco.fr/ca/certs/13.cer.

So now…

cat 008f53fd8d779901c7.cer 15.cer > fullchain.pem
openssl verify -show_chain -verbose -CAfile fullchain.pem -CRLfile crl.pem -crl_check -extended_crl -untrusted 13.cer 7509624163b221b0.cer
7509624163b221b0.cer: OK
Chain:
depth=0: CN=SHAKEN FRTE00, O=Orange, C=FR (untrusted)
depth=1: CN=BPCO CA1 - SHAKEN Intermediate, O=Base des Certificats Opérateurs, OU=Certificate Authority, C=FR
depth=2: CN=BPCO R1 - SHAKEN Root, O=Base des Certificats Opérateurs, OU=Certificate Authority, C=FR

Note the -extended_crl -untrusted 13.cer parameters. They’re required to get the verification to succeed against an indirect CRL.

So, I’ve got a bit of work to do to add the ability to specify untrusted certs in stir_shaken.conf and to make sure the EXTENDED_CRL_SUPPORT flag is set before we call X509_verify_cert(). I can probably do that this weekend but no promises.

There’s a PR up on GitHub that should resolve these issues and more…

1 Like

Using this PR #815, I could positively receive incoming call and (implicitly) checked the x5u cert against CRL: no error of any kind !

Thanks you very very much for this quick and efficient PR !
I can’t wait for it to be included in a future Asterisk version (hopefully next 20.10.0 version).

I ended splitting cert files in 3 different /etc/asterisk sub-directories:
one for CAfile, one for CRL and one for “untrusted” files.

I think I’ll move these files somewhere else (in /var hierarchy ?).

Thanks again !

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.