Is this a bad certificate? (Having nine-bit long key usage extension)

Kenneth Lundin kenneth@REDACTED
Tue Sep 21 11:14:45 CEST 2021


In this case it is the encoding of KeyUsage which is the problem, it is
questionable if it is correctly encoded by GnuTLS.

It is declared like this:
KeyUsage ::= BIT STRING {
     digitalSignature        (0),
     nonRepudiation          (1),
     keyEncipherment         (2),
     dataEncipherment        (3),
     keyAgreement            (4),
     keyCertSign             (5),
     cRLSign                 (6),
     encipherOnly            (7),
     decipherOnly            (8) }

And encoded like this by GnuTLS (as I understand it from Jesses initial
description)
<<3,3,7,16#A0,0>> with the interpretation of bytes and bits like this:
Byte 0: Value 3:  The UNIVERSAL tag for BIT STRING
Byte 1: Value 3: Length = 3
Byte 2: Value 7: Number of unused bits in last octet = 7
Byte 3: Value 16#A0: (2#10100000) The first 8 bits of the bitstring with
bit (0) digitalSignature and bit (2) keyEncipherment set
Byte 4: Value 0: The first bit is zero and the 7 remaining bits are unused

If we read the standard docs below I interpret this as 1) we have a BIT
STRING with a NamedBitList (22.7 in X.680 applies) and then we apply 11.2.1
and especially 11.2.2 from X.690 which says that trailing zero bits should
be removed before encoding which I don't think has been done in the above
case.

So I think GnuTLS might be wrong here in that it does not encode according
to DER.

The encoding that Erlang/OTP is using which I claim is according to DER is
like this:
<<3,2,5,16#A0>> with the interpretation of bytes and bits like this:
Byte 0: Value 3:  The UNIVERSAL tag for BIT STRING
Byte 1: Value 2: Length = 2
Byte 2: Value 5: Number of unused bits in last octet = 5
Byte 3: Value 16#A0: (2#10100000) The first 3 bits of the bitstring with
bit (0) digitalSignature and bit (2) keyEncipherment set
                        , the remaining 5 bits are unused.
This encoding is removing all trailing zero bits which make the encoding
use as few bytes as possible

>From the standard in X.690 and X.680 we have :

X.690
11.2 Unused bits
11.2.1 Each unused bit in the final octet of the encoding of a bit string
value shall be set to zero.
11.2.2 Where Rec. ITU-T X.680 | ISO/IEC 8824-1, 22.7, applies, the bitstring
shall have all trailing 0 bits removed before
it is encoded.
NOTE 1 – In the case where a size constraint has been applied, the abstract
value delivered by a decoder to the application will be on e
of those satisfying the size constraint and differing from the transmitted
value only in the number of trailing 0 bits.
NOTE 2 – If a bitstring value has no 1 bits, then an encoder shall encode
the value with a length of 1 and an initial octet set to 0

X.680
22.7 When a "NamedBitList" is used in defining a bitstring type ASN.1
encoding rules are free to add (or remove)
arbitrarily any trailing 0 bits to (or from) values that are being encoded
or decoded. Application designers should ISO/IEC 8824-1:2021 (E)
Rec. ITU-T X.680 (02/2021) 43
therefore ensure that different semantics are not associated with such
values which differ only in the number of trailing
0 bits.

This is just one case where the intention with DER does not hold since many
certificates created also with OpenSSL does not follow the standard (The
ASN.1 specs) for example when it comes to the encoding of CountryName
(there are a few other data fields more which are problematic). The
CountryName is sometimes encoded as Printable STRING and sometimes as UTF8
STRING and the length constraint of 2
is also violated in some occasions. As the UNIVERSAL Tag for these types
differ the encoded bytes will be different.
The Erlang/OTP SSL implementation have been forced to accept those "non
confirming to standard" encoding because otherwise the interoperability
with other servers and clients would have been to limited.

The solution is to never trust that a certificate which is decoded up to
its internal Erlang representation and then encoded back again will result
in exactly the same sequence of bytes as in the original.

/Kenneth

On Tue, Sep 21, 2021 at 10:26 AM Bram Verburg <bram.verburg@REDACTED>
wrote:

> Hi Ingela,
>
> I was looking into this too, and I noticed that this particular leaf
> certificate, once decoded as an OTPCertificate record, does not encode back
> to the same DER value (which is likely due to the unusual representation of
> the KeyUsage extension in the original file):
>
> 1> {ok, PEM} =
> file:read_file("cert.pem").
>
> {ok,<<"-----BEGIN
> CERTIFICATE-----\nMIIEfDCCAuSgAwIBAgIUdcWg+bxobl7OWSOjbNxyaXTnqx8wDQYJKoZIhvcNAQEL\nBQAwMTEXMBUGA1U"...>>}
> 2> DER = element(2, hd(public_key:pem_decode(PEM))).
> <<48,130,4,124,48,130,2,228,160,3,2,1,2,2,20,117,197,160,
>   249,188,104,110,94,206,89,35,163,108,220,...>>
> 3> DER = public_key:pkix_encode('OTPCertificate',
> public_key:pkix_decode_cert(DER, otp), otp).
> ** exception error: no match of right hand side value
> <<48,130,4,123,48,130,2,227,160,3,2,1,2,2,20,117,197,160,
>
> 249,188,104,110,94,206,89,35,163,108,220,...>>
>
> (That would presumably mean the original DER representation is not really
> proper DER, as I believe ASN.1's DER encoding is supposed to always produce
> the same (canonical) byte sequence, in order to allow for reliable
> signature verification...)
>
> I haven't quite pinned it down, but this commit
> https://github.com/erlang/otp/pull/4941/commits/80f9b71323c9c5a3bc111fcbc5c6c5ee497e27a6 that
> was included in 24.0.4 seems to sometimes reconstruct a DER representation
> from an OTPCertificate record, and if that DER value is passed to
> public_key:pkix_verify/2 instead of the original DER representation, the
> signature won't match and ssl will throw the reported error.
>
> Hope that might help,
>
> Bram
>
> ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
> On Tuesday, September 21st, 2021 at 10:48 AM, Ingela Andin <
> ingela.andin@REDACTED> wrote:
>
> Hello again!
>
> I thought of a possible cause  of your strange problem. If the ROOT-cert
> is sent as part of the chain (optional in the protocol).  We must check
> that it is a certificate that we trust, part of our "trust store". This was
> done by  a match of the DER-version of the cert and we should match the
> decode  structure instead.
>
> In such cases the following patch ought to solve your problem.  Can you
> please test it? It is based on the latest maint!
>
>
> diff --git a/lib/ssl/src/ssl_certificate.erl
> b/lib/ssl/src/ssl_certificate.erl
> index cbeb4e4521..1a277964b6 100644
> --- a/lib/ssl/src/ssl_certificate.erl
> +++ b/lib/ssl/src/ssl_certificate.erl
> @@ -596,13 +596,13 @@ path_candidate(Cert, ChainCandidateCAs,
> CertDbHandle) ->
>              [Root | lists:reverse(Chain)]
>      end.
>
> -handle_partial_chain([#cert{der=DERIssuerCert, otp=OtpIssuerCert}=Cert|
> Rest] = Path, PartialChainHandler,
> +handle_partial_chain([#cert{otp=OtpIssuerCert}=Cert| Rest] = Path,
> PartialChainHandler,
>                       CertDbHandle, CertDbRef) ->
>      case public_key:pkix_is_self_signed(OtpIssuerCert) of
>          true -> %% IssuerCert = ROOT (That is ROOT was included in chain)
>              {ok, {SerialNr, IssuerId}} =
> public_key:pkix_issuer_id(OtpIssuerCert, self),
>              case ssl_manager:lookup_trusted_cert(CertDbHandle, CertDbRef,
> SerialNr, IssuerId) of
> -                {ok, #cert{der=DERIssuerCert}} -> %% Match sent ROOT to
> trusted ROOT
> +                {ok, #cert{otp = OtpIssuerCert}} -> %% Match sent ROOT to
> trusted ROOT
>                      maybe_shorten_path(Path, PartialChainHandler, {Cert,
> Rest});
>                  {ok, _} -> %% Did not match trusted ROOT
>                      maybe_shorten_path(Path, PartialChainHandler,
> {invalid_issuer, Path});
>
>
> Regards Ingela Erlang/OTP team- Ericsson AB
>
>
> Den fre 17 sep. 2021 kl 18:24 skrev Jesse Bickel - NOAA Affiliate <
> jesse.bickel@REDACTED>:
>
>> Good morning,
>>
>> This is part question and part potential bug report. I don't have a github
>> account associated with my workplace at the moment which is why I ask
>> here.
>>
>> When we tested an upgrade to rabbitmq, the tls v1.2 server rejected client
>> certificates with the following message: "In state certify at
>> ssl_handshake.erl:1975 generated SERVER ALERT: Fatal - Bad Certificate."
>> The
>> issue happens with otp versions 24.0.4 and later. The earlier version
>> 24.0.3
>> interoperates with the certificates successfully. After we took a closer
>> look,
>> the issue has something to do with the key usage extension. If we
>> generate a
>> similar certificate in all other respects but remove the key usage
>> extension
>> otp does not reject the certificate. The issue also occurs without mutual
>> authentication as a CLIENT ALERT when an erlang/otp client validates the
>> same
>> kind of server certificate.
>>
>> The certificates that otp 24.0.4+ rejects were generated by gnutls
>> certtool,
>> but more specifically the difference in the certificate itself is the
>> length of
>> the bit string in the key usage extension. Both openssl and gnutls
>> certtool in
>> their high-level text forms of the working and not working certificates
>> show
>> the key usage as having the same two values of digital signature and key
>> encipherment. The difference underneath is that the one generated by
>> gnutls
>> certtool version 3.6.9 (rejected by otp) is nine bits, value 101000000,
>> whereas
>> the one generated by openssl 1.1.1f (accepted by otp) is three bits,
>> value 101.
>> Both versions appear to be valid asn.1, several tools appear to see both
>> versions as valid certificates, and I cannot easily tell from the
>> relevant ietf
>> and itu documents that either is invalid.
>>
>> Below are sample certificate authority and leaf certificates that can
>> reproduce
>> the issue in two local erlang/otp sessions in a typical mode of
>> server-only
>> authentication with tls v1.2 during the handshake.
>>
>> Is this a bug introduced in otp 24.0.4 or is this an invalid certificate?
>>
>> Jesse Bickel
>>
>> --
>> Contractor, ERT, Inc.
>> Federal Affiliation: NWC/OWP/NOAA/DOC
>>
>> Sample certificate authority x.509 certificate:
>>
>> -----BEGIN CERTIFICATE-----
>> MIIEMzCCApugAwIBAgIUcgjKevzc6MaGOyaSC0VdidlvgO4wDQYJKoZIhvcNAQEL
>> BQAwMTEXMBUGA1UEAxMORmFrZWRvbWFpbjIgQ0ExFjAUBgNVBAoTDUZha2UgRG9t
>> YWluIDIwHhcNMjEwOTE0MTgzNDI5WhcNMjIwOTE0MTgzNDMxWjAxMRcwFQYDVQQD
>> Ew5GYWtlZG9tYWluMiBDQTEWMBQGA1UEChMNRmFrZSBEb21haW4gMjCCAaIwDQYJ
>> KoZIhvcNAQEBBQADggGPADCCAYoCggGBALLOuMsXBwU57q2cauVHr1zUneJ3KKt7
>> 6H8RzZJB/yoltld/9RjGeQGZ3EfeLb1dm9Gag2AwTBRzrnYIcQz215ABLfcdMYyo
>> B3UE4rU+E3N9XMmGAEWbQh3lAprYeUnKbt7yjKOuUrQvfNtHhvNOpDQjg2VRbYnx
>> TQS6Go/ycRSywoBKSlr3G/sZ0pKt+8BumfMfIZZtPQw9j46+VibBW9UyQZJ7iBUH
>> q1AXT8XiFtAzVq33dwb+B5gd1QVrIu1vIzyGQt5dliEHHfJcdksmZBLru1GR6uzF
>> bndg3Y9MyYj5YQ+6M9wtcLKgJwHHfLgb30FPmY5a945tSHL9cv38sk11o7zgI6Vn
>> d+KhHxFRs8g0dBq8/ZKXF4UA7RxihXAB/SwVW4XVgpMGCMh/hrDzqKS/oexuOt8n
>> NDumTd2NYsUx06+uV/lc405DGmnF0cvCsV+3wulBs09lUsQlwFvFhU3z2pDSWWPs
>> 8qCwcPn1WKBx1bZvBMZzys8qPc6NP4sjIQIDAQABo0MwQTAPBgNVHRMBAf8EBTAD
>> AQH/MA8GA1UdDwEB/wQFAwMHhAAwHQYDVR0OBBYEFFJsVCuIulfHcznrk/ALXKSn
>> Q+9IMA0GCSqGSIb3DQEBCwUAA4IBgQA6/WBJ5IYfcvxQnMeCQI2ewRiMxEQQxqFX
>> E9aXJDq5ohhY+lquWcMD8+yNAQbsE2Sw3KQppelEvN1aLgDwW8e7LR5Dw7SLtwZB
>> tFtWx2eJoqjuTLoxhjnWaR3A50YUgjNOiV5UIV5E8A1f1wsJcdM2VobQgdNyfvnm
>> vDdHKaGP9+TfmrdGimVwVLmQCtXzp62pyIkJbc8lOOGifCNzDmRgl1ehsEYn4T/M
>> NWxan3mRGLBjGn90ojxAMBXASB6BrQNa4Kvrs0bzyUToBPHMeBWsYA6SlyfLMN9L
>> svajh99xEYN63+AVNpZMDIBe4Dhmw8dTU8z1beDJqdODG8+Krmp58KwTAZQYGp6o
>> iyA5W1PmEms3Our9pBoJZ6guE8qMMxo6th+CwXsOQQLcmUIienLfFv6x1hZidN6G
>> bTmD2GOK25J/2zXlEXQuT4HNZfDuIh2IBxlo607ct72Dq9OGENLL6ujGhzU2XL6J
>> /TM/05Gfa+buDfysohjCSbtAIqvolr8=
>> -----END CERTIFICATE-----
>>
>> Sample leaf certificate signed by above certificate authority:
>>
>> -----BEGIN CERTIFICATE-----
>> MIIEfDCCAuSgAwIBAgIUdcWg+bxobl7OWSOjbNxyaXTnqx8wDQYJKoZIhvcNAQEL
>> BQAwMTEXMBUGA1UEAxMORmFrZWRvbWFpbjIgQ0ExFjAUBgNVBAoTDUZha2UgRG9t
>> YWluIDIwHhcNMjEwOTE0MTgzOTA0WhcNMjIwOTEzMTgzOTA2WjAsMRIwEAYDVQQD
>> Ewlsb2NhbGhvc3QxFjAUBgNVBAoTDUZha2UgRG9tYWluIDIwggGiMA0GCSqGSIb3
>> DQEBAQUAA4IBjwAwggGKAoIBgQDcNlMZrrMMWS5mKBCZKegx78hQutjQN4BEWsnK
>> WNPEpgtBV6H7Ihk43Jn19EArCujglZfI5sdV9il/8uJFB9DplICnPvet6HvspxLa
>> 4hLFm+dI1Av6MglB6vaJInEXK6zVM39NMJVAy7LcryJYAhVMIuC4wTUOHRqAgx8o
>> w6dtb7fKSV3OTN3UmGJRPnzGULR3wc8yjGCXnPgu0D497w2FoZ4IWU2H/T+djQDx
>> i8giZ7KV+11XHfm3ZEsukutJIwsehP3XmCrvttZz8oQuNzJvA05BYRmgOkc7rJeW
>> ioXL8SC3rMeN4tZHPOd1O6QMVA3IqRGqoobwvt6HKKgE5LWw8GMG5HBDUFuYFDWY
>> cNkNObTM+x06WVoOKqjDUMb0x+clZwHSclTbEACzxT+iv3hh7+zQ9/0Z7BgYKxfN
>> QQgtDxIvguFI5O/BKzWAeBY24InHyBphprXmAL+q79hqjpt6Diaz8GEY7qHJcvss
>> npx7Q9HWqPd+GsPMBjGHRmy6Wk0CAwEAAaOBkDCBjTAUBgNVHREEDTALgglsb2Nh
>> bGhvc3QwDAYDVR0TAQH/BAIwADAPBgNVHQ8BAf8EBQMDB6AAMBYGA1UdJQEB/wQM
>> MAoGCCsGAQUFBwMBMB0GA1UdDgQWBBSblx0hTlJdxGO9+sAQnevrQUtX+jAfBgNV
>> HSMEGDAWgBRSbFQriLpXx3M565PwC1ykp0PvSDANBgkqhkiG9w0BAQsFAAOCAYEA
>> fHfAPrLNL2oChRjhS5h7twylgoxlAL06CITEwQtRSbdoP/g+XUdv/CFp7fqE9rKz
>> XNrtgM6KSgK7N7riYojCheEo/76Jk+PHPljzCrv9BoX6K44liU1Nf2IXIAG2+llY
>> aDmMeZPFI3pZ92oFCmKmzMvCaf/idOqNFihS/hBNXfbczuROnAuw/zip1QItnCJt
>> YTk1Og7BMjF3LNQYZRwz3KuG/fyDJx7DMrKL+EOsOmebFnIqiM0Em0TGIzdY2aTG
>> 3hDbMUQz82R/8kO5d9keIGiL4pXp1gGDf8TdK7cEDblcwt1cyNX6jm6F1+qGWgCw
>> EbnNn2MjpdBD0BDNluQaysQb6KDomv2HHhwDtB6RDM26GN3uUyZfPDzvzH8TmNbH
>> UIymPlYs9XHlvEYJeeu9shd9hLegAidWHBs31eUKZaURJrsHd110iGhVpGEjvdvS
>> +YxkNsVwgmiPjJpjxsHOABdwpFBO54F8ayEiYDiLhboXEHJj0ju57TMstzcOmQBL
>> -----END CERTIFICATE-----
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20210921/bd4bd119/attachment-0001.htm>


More information about the erlang-questions mailing list