1

I want to compute the SHA256 fingerprint for the Public Key of a X509Certificate2 object in c#. The following code is used as suggested in this post (however I need it for the public key):

            using (var hasher = SHA256.Create())
            {
                var hash = hasher.ComputeHash(cert.PublicKey.EncodedKeyValue.RawData);
                string fingerprint = BitConverter.ToString(hash);
            }

The output for a specific certificate is

26a7d549af907d9a740813bef4a98757ad6a4da100e9696ef7b22459eb32207a

But GNUTLS computes in a different way.

#gnutls-cli --print-cert <CERTIFICATE>
- Certificate type: X.509
- Got a certificate list of 1 certificates.
- Certificate[0] info:
 - subject `O=XXXX,CN=XXXX', issuer `O=XXXX,CN=XXX', serial 0x56XXXX1, RSA key 2432 bits, signed using RSA-SHA256, activated `2016-02-06 19:06:57 UTC', expires `XXXX-XX-XX 23:59:59 UTC', pin-sha256="G2Z1XXXXXXXXXXX50/Qxk="
        Public Key ID:
                sha1:7c1d4151620093f11xxxx220ba3d705d75c06dfb
                sha256:1b6675c7e0567xxxxxxx03e279684897fb0606dc55b5c940036562c79d3f4319
        Public Key PIN:
                pin-sha256:G2Z1x+BWxxxxxxxxxxhIl/sGBtxVtclAA2Vix50/Qxk=
        Public key's random art:
                +--[ RSA 2432]----+
                |        ++.+B=.  |
                |        .o .oo+  |
                |          o..o . |
                |       .  .= ..  |
                |      ..S.o o  . |
                |    .....o .    E|
                |    .o  . o      |
                |     o.  o       |
                |    . ..         |
                +-----------------+


-----BEGIN CERTIFICATE-----
MIIDyDCCAoCgAwIBAgIEVrZEUTANBgkqhkiG9w0BAQsFADAkMQ8wDQYDVQQDEwZW
...
...
c0TLqiTJQJe55Rf/k/luygGEmoGbIonC8zC/iB8XoieWzH3F0k1yI4YEU8nLhGXq
Kz0SKPX/SPWywRLY
-----END CERTIFICATE-----
From My Code: 26a7d549af907d9a740813bef4a98757ad6a4da100e9696ef7b22459eb32207a
From  gnutls: 1b6675c7e0567xxxxxxx03e279684897fb0606dc55b5c940036562c79d3f4319

I have checked other ways to elaborate what happens. Using this post I used this to compute what is the public key data:

# openssl s_client -connect <HTTPS_URL_WITH_THE_CERT> | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | xxd

30820152300d06092a864886f70d01010105000382013f003082013a0282013100ca4602d7440461f2d3efe5c3f1f182a269319644b7ff989bd4cd44964f0002c6d0217da845db2c3801fc4be72d7e85510a2dd8e4cbd210cce4dd8c20c6925f0bc88b87b2a9653ffa3b700131da0a46aa20598e7fa452ee0e28648ee23da25ad68cf7f47c866990d05e17e110a230801d0d3c03b6de750e27d14a50c99455fdc339c7988d5b957dee17db47044aaa38327bc42a8bd22d550b484a33c0efbe2c07ba7f8c18c19cab81c4fa4325267a96501586c017da2c50fa65cf073502230659a0fbf7fa3af9335a3b94ff285fc5341e211f04b7bce0688eb157840e7ea04f79c88b491241996b8f166e1aaaf9920e901b583c8449bb92e2b3d4db55b8b541e9f531f1bac6528a03eaf756366c045a6e8b8637a3942bd2d8bf8a0d522061cfe416e7aba684cdcac7cf637048c4b865ff0203010001

But when I run BitConverter.ToString(cert.PublicKey.EncodedKeyValue.RawData) I get this:

3082013A0282013100CA4602D7440461F2D3EFE5C3F1F182A269319644B7FF989BD4CD44964F0002C6D0217DA845DB2C3801FC4BE72D7E85510A2DD8E4CBD210CCE4DD8C20C6925F0BC88B87B2A9653FFA3B700131DA0A46AA20598E7FA452EE0E28648EE23DA25AD68CF7F47C866990D05E17E110A230801D0D3C03B6DE750E27D14A50C99455FDC339C7988D5B957DEE17DB47044AAA38327BC42A8BD22D550B484A33C0EFBE2C07BA7F8C18C19CAB81C4FA4325267A96501586C017DA2C50FA65CF073502230659A0FBF7FA3AF9335A3B94FF285FC5341E211F04B7BCE0688EB157840E7EA04F79C88B491241996B8F166E1AAAF9920E901B583C8449BB92E2B3D4DB55B8B541E9F531F1BAC6528A03EAF756366C045A6E8B8637A3942BD2D8BF8A0D522061CFE416E7ABA684CDCAC7CF637048C4B865FF0203010001

Which has less 24 bytes compared to previous command.

30820152300d06092a864886f70d01010105000382013f00
=> missing data in public key in C# code

Update 1

Thanks to tapaco, I checked the certificate with the tool, and I realized that the missing part seems to be the encapsulation part of the key. enter image description here

enter image description here

Is there anything missing here?

Update 2

I used the following code and it matches the gnutls signature.

 Asn1InputStream bIn = new Asn1InputStream(cert.RawData);
 DerSequence seq = bIn.ReadObject() as DerSequence;
 string signature = BitConverter.ToString(SHA256.Create().ComputeHash(((DerSequence)seq[0].ToAsn1Object())[6].GetEncoded()));

// 1b6675c7e0567xxxxxxx03e279684897fb0606dc55b5c940036562c79d3f4319

But I don't know if it works in all cases.

raitech
  • 41
  • 1
  • 6
  • 1
    The keys simply have different formats, the longer one the X.509/SPKI format, the shorter one the PKCS#1 format. Check this in an ASN.1 parser, e.g. https://lapo.it/asn1js/. – Topaco Oct 09 '22 at 19:08
  • Nice tool! Thank you. I updated the question with the findings from this tool – raitech Oct 09 '22 at 20:05
  • What exactly is your intention? If you want to use the C# code to generate the fingerprint created with GNUTLS, you need to convert the PKCS#1 key (i.e. `cert.PublicKey.EncodedKeyValue.RawData`) to an X.509/SPKI key. How to do this depends on the .NET version (which you should add if you want to convert). – Topaco Oct 09 '22 at 20:40
  • Eventually I want to use the computed signature in openconnect cli. As in new versions of openconnect, I have to specify the signature of self-signed cert to allow the connection. openconnect --servercert sha256:xxxxxxxxxxxxxx host – raitech Oct 09 '22 at 20:51
  • I have used the following code and it matched the gnutls computation BitConverter.ToString(SHA256.Create().ComputeHash(((DerSequence)seq[0].ToAsn1Object())[6].GetEncoded())) – raitech Oct 09 '22 at 21:22
  • Why don't you just convert the key? You may not even need BC for that. Depends on your .NET version though. Post your .NET version! – Topaco Oct 09 '22 at 22:01
  • It is .Net 4.6 . I don't mind converting I need SHA256 to pass it to openconnect to let it connect to unsecure invalid cert server and openconnect uses GNUTLS signature – raitech Oct 09 '22 at 22:09
  • Just for completeness, key conversion is most convenient on .NET Framework using BC. – Topaco Oct 09 '22 at 23:49

1 Answers1

0

I ended up using the following code and it matches the gnutls computation

 Asn1InputStream bIn = new Asn1InputStream(cert.RawData);
 DerSequence seq = bIn.ReadObject() as DerSequence;
 string signature = BitConverter.ToString(SHA256.Create().ComputeHash(((DerSequence)seq[0].ToAsn1Object())[6].GetEncoded()));

// 1b6675c7e0567xxxxxxx03e279684897fb0606dc55b5c940036562c79d3f4319
raitech
  • 41
  • 1
  • 6