Как рассчитать сертификат X.509 SHA-1?

Я пытаюсь реализовать генератор сертификатов X.509 с нуля (я знаю о существующих, но мне нужен еще один). Я не могу понять, как вычислить SHA-1 (или любой другой) отпечаток сертификата.

RFC5280 говорит, что вход для функции подписи - это поле tbsCertificate, закодированное DER. К сожалению, хэш, который я вычисляю, отличается от того, который был получен OpenSSL. Здесь приводится пошаговый пример.

  • сгенерировать сертификат с использованием инструмента OpenSSL x509 (в двоичной форме DER, а не ASCII PEM)
  • вычислить свой SHA-1 хэш с помощью openssl x509 -fingerprint
  • извлеките поле TBS с помощью dd (или что-нибудь еще) и сохраните его в отдельном файле; вычислить свой хэш с помощью утилиты sha1sum

Теперь хеши, которые я получаю на шагах 2 и 3, различны. Может кто-нибудь, пожалуйста, дайте мне подсказку, что я могу делать неправильно?

Ответы

Ответ 1

Итак, оказалось, что отпечаток, рассчитанный OpenSSL, является просто хешем над сертификатом всего (в его двоичной кодировке DER, не ASCII PEM one!), а не только часть TBS, как я думал.

Для всех, кто интересуется расчетом дайджестов сертификатов, это делается по-другому: хеш рассчитывается по DER-кодировке (опять же, а не по строке PEM) только части TBS, включая заголовок ASN.1 (идентификатор 0x30 == ASN1_SEQUENCE | ASN1_CONSTRUCTED и поле длины). Обратите внимание, что заголовок сертификата ASN.1 не учитывается.

Ответ 2

Отпечаток пальца аналогичен термину "Thumbprint" в .net. Ниже фрагмент кода должен помочь вам распечатать отпечаток пальца:

    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();


    }