23

I'm trying to implement an X.509 certificate generator from scratch (I know about the existing ones, but I need yet another one). What I cannot understand is how to calculate the SHA-1 (or any other) fingerprint of the certificate.

The RFC5280 says that the input to the signature function is the DER-encoded tbsCertificate field. Unfortunately, the hash that I calculate differs from the one produced by OpenSSL. Here's a step-by-step example.

  1. Generate a certificate using OpenSSL's x509 tool (in a binary DER form, not the ASCII PEM)
  2. Calculate its SHA-1 hash using openssl x509 -fingerprint
  3. Extract the TBS field using dd (or anything else) and store it in a separate file; calculate its hash using the sha1sum utility

Now, the hashes I get at steps 2 and 3 are different. Can someone please give me a hint what I may be doing wrong?

JoSSte
  • 2,953
  • 6
  • 34
  • 54
Roman Dmitrienko
  • 3,375
  • 3
  • 37
  • 48
  • When you calculate the SHA-1 hash of the tbsCertificate, are you using [PKCS#1](http://tools.ietf.org/html/rfc2313) padding, as specified in [rfc 3279](http://tools.ietf.org/html/rfc3279)? – sarnold Jan 26 '11 at 11:28
  • Uhm... RFC2313 (PKCS#1) only specifies RSA encryption. As far as I understand, padding is not needed at the SHA-1 computing phase? – Roman Dmitrienko Jan 26 '11 at 12:13
  • Oh, by the way, at the Step 3 I calculate the hash using sha1sum utility – Roman Dmitrienko Jan 26 '11 at 12:22
  • Arrrgh, my bad! Actually, `openssl x509 -fingerprint` simply outputs SHA-1 hash of the WHOLE DER-encoded certificate (not only the tbsCertificate part) – Roman Dmitrienko Jan 26 '11 at 12:49
  • 1
    @Roman D: Since it's not at all obvious, you should write that as an answer and accept it. – caf Jan 28 '11 at 01:52
  • @caf: thank you for reminding me to do it! Done :) – Roman Dmitrienko Jan 28 '11 at 07:18
  • But how is it possible to get the hash over the whole certificate, isn't that a chicken and egg problem. I mean how can we calculate hash over the whole certificate ( tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING ) without having the signature value. or you mean just its just over TBSCertificate along with the header on top of TBSCertificate –  Jul 31 '12 at 01:29
  • Nope. If we are talking about the fingerprint as reported by the OpenSSL (`openssl x509 -fingerprint`), it is actually a mere SHA1 hash of the whole certificate. It is not stored in the certificate itself. Compare the output of the `openssl x509 -fingerprint -inform der -in YOUR_CERT.DER` and the `sha1sum YOUR_CERT.DER`. Be careful to use the DER-encoded certificate for this experiment, NOT the PEM one! – Roman Dmitrienko Jul 31 '12 at 19:51
  • Hi, I have a doubt here. If the hash is calculated over the whole certificate, then how come all the certificates in the Certification Path have different "Thumbprint" ? One more doubt, is the sha-1 fingerprint and thumbprint of a certificate the same thing? – Abhineet Aug 29 '12 at 07:06
  • 1
    @Abhineet, how do you get the thumbprint of the certificate? I'm not sure what it is. You can calculate SHA-1 of the certificate and see if it the same as thumbprint or not. – Roman Dmitrienko Aug 29 '12 at 09:01
  • OK..let me make myself more clear. Right click any signed PE and view all the certificates. They all have a unique thumbprint with them. So, what i am asking is, Do you know any way to calculate that Thumbprint? I have tried calculating the certificate sha-1 but no use and then again I tried calculating the sha-1 of public key with and without exponent but again no progress. Can you guide me how to do that or how to make the certification path as they show in the properties of a signed PE? – Abhineet Aug 30 '12 at 05:30
  • 1
    @Abhineet, I don't have a Windows PC available right now to take a look. Maybe the thumbprint that is shown there is the hash of the public key, as described in [wikipedia](http://en.wikipedia.org/wiki/Public_key_certificate)? – Roman Dmitrienko Aug 30 '12 at 05:36
  • Yeah even I went through that and tried but to no positive conclusion. Anyways thanks for the help @RomanD – Abhineet Aug 30 '12 at 05:38

2 Answers2

28

Ok, so it turned out that the fingerprint calculated by OpenSSL is simply a hash over the whole certificate (in its DER binary encoding, not the ASCII PEM one!), not only the TBS part, as I thought.

For anyone who cares about calculating certificate's digest, it is done in a different way: the hash is calculated over the DER-encoded (again, not the PEM string) TBS part only, including its ASN.1 header (the ID 0x30 == ASN1_SEQUENCE | ASN1_CONSTRUCTED and the length field). Please note that the certificate's ASN.1 header is not taken into account.

Roman Dmitrienko
  • 3,375
  • 3
  • 37
  • 48
  • 1
    you could add to your answer (or question) how you extracted the TBS part with dd exactly for future reference :) – Filipe Pina Nov 17 '11 at 17:39
  • Oh... That was quite a while ago, so I don't remember the details. I think I manually extracted the TBS offset and length from the hexdump of the certificate, and then used these values as dd's arguments, nothing complicated there – Roman Dmitrienko Nov 24 '11 at 17:46
  • @Roman D your answer says 'hash over the whole certificate' in the first paragraph and 'TBS part only' in the second paragraph. Is this a mistake? And what is the TBS part of a certificate? I couldn't find anything useful from my google search. – chitti Oct 23 '12 at 17:55
  • @chitti No, it is not a mistake, I even put it in italics to stress the idea. :) First paragraph is about the fingerprint, second one is about the digest. You can read about the TBSCertificate field in the RFC5280 (see 4.1.1, 4.1.1.1, 4.1.2). Basically, it is one of the required fields of a certificate (other two are signatureAlgorithm and signatureValue). – Roman Dmitrienko Oct 24 '12 at 03:26
  • @FilipePina You can use the OpenSSL function i2d_X509_CINF to get the TBS portion of the X509. – Sivachandran Jul 23 '14 at 08:57
3

The finger print is similar to term "Thumbprint" in .net. Below code snippet should help you to compute finger print :

    public String generateFingerPrint(X509Certificate cert) throws CertificateEncodingException,NoSuchAlgorithmException {

MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] hash = digest.digest(cert.getEncoded[]);

final char delimiter = ':';
// Calculate the number of characters in our fingerprint
      // ('# of bytes' * 2) chars + ('# of bytes' - 1) chars for delimiters
      final int len = hash.length * 2 + hash.length - 1;
      // Typically SHA-1 algorithm produces 20 bytes, i.e. len should be 59
      StringBuilder fingerprint = new StringBuilder(len);

      for (int i = 0; i < hash.length; i++) {
         // Step 1: unsigned byte
         hash[i] &= 0xff;

         // Steps 2 & 3: byte to hex in two chars
         // Lower cased 'x' at '%02x' enforces lower cased char for hex value!
         fingerprint.append(String.format("%02x", hash[i]));

         // Step 4: put delimiter
         if (i < hash.length - 1) {
            fingerprint.append(delimiter);
         }
      }

      return fingerprint.toString();


    }