Is this a bad certificate? (Having nine-bit long key usage extension)
Jesse Bickel - NOAA Affiliate
jesse.bickel@REDACTED
Wed Sep 22 00:21:41 CEST 2021
I apologize for the delayed response. Thank you all for working on this issue even though it may be more of a gnutls issue.
Kenneth Lundin wrote:
> 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.
Yes, this is consistent with what I see. Gnutls keeps the trailing zero bits while openssl strips the trailing zero bits. But both tolerate each others' differing representations (for better or worse).
> 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.
I am not as familiar with interpreting the x.680/x.690 documents but I think you make a strong case. It would make sense to have exactly one correct encoding of a certificate such that one always gets exactly the same signature, for example.
> 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
>
> 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
>
>
> 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!
I tried this to no avail but we see there is a later patch at https://github.com/erlang/otp/pull/5222 which I assume is related. When I tried this patch a few minutes ago, I still get this:
** exception error: no match of right hand side value {error,{tls_alert,{bad_certificate,"TLS client: In state certify at ssl_handshake.erl:1989 generated CLIENT ALERT: Fatal - Bad Certificate\n"}}}
Ingela, I directly emailed you the dummy/throwaway/test key corresponding to the leaf certificate that I posted earlier and also the commands my teammate found to help reproduce the issue. I hope this helps, thank you for your help too.
Best,
Jesse Bickel
--
Contractor, ERT, Inc.
Federal Affiliation: NWC/OWP/NOAA/DOC
More information about the erlang-questions
mailing list