OpenSSL: как получить соответствующий subject_hash из CMS SignerInfo?

Вопрос или проблема

Итак, OpenSSL может вычислить хеш-значение для X509 сертификатов, которое уникально идентифицирует этот сертификат:

https://www.openssl.org/docs/man1.0.2/man1/x509.html (аргумент -hash или -subject_hash)

Теперь, если у меня есть файл CMS (https://www.rfc-editor.org/rfc/rfc3852) в DER кодировке, который имеет поле SignedData с точно одним SignerInfo, но не содержит фактический сертификат подписи, как я могу вычислить хеш сертификата подписи, идентичный тому, что openssl x509 -hash -noout -in actual_cert.pem создал бы для данного сертификата?

Просмотр CMS с помощью openssl cms -inform DER -in infile.cms -cmsout -print дает

CMS_ContentInfo:
  contentType: pkcs7-signedData (1.2.840.113549.1.7.2)
  d.signedData:
    version: 3
    digestAlgorithms:
        algorithm: sha512 (2.16.840.1.101.3.4.2.3)
        parameter: NULL
    encapContentInfo:
      eContentType: id-smime-ct-TSTInfo (1.2.840.113549.1.9.16.1.4)
      eContent:
        0000 - 30 82 01 75 02 01 01 06-04 2a 03 04 01 30 31   0..u.....*...01
        000f - 30 0d 06 09 60 86 48 01-65 03 04 02 01 05 00   0...`.H.e......
        001e - 04 20 89 9b a3 d9 f7 77-e2 a7 4b dd 34 30 2b   . .....w..K.40+
        002d - c0 6c b3 f7 a4 6a c1 f5-65 ee 12 8f 79 fd 5d   .l...j..e...y.]
        003c - ab 99 d6 8b 02 03 2d 19-84 18 0f 32 30 32 31   ......-....2021
        004b - 30 32 30 34 31 34 33 30-35 36 5a 01 01 ff 02   0204143056Z....
        005a - 09 00 83 16 8e 99 d6 23-2e fc a0 82 01 11 a4   .......#.......
        0069 - 82 01 0d 30 82 01 09 31-11 30 0f 06 03 55 04   ...0...1.0...U.
        0078 - 0a 13 08 46 72 65 65 20-54 53 41 31 0c 30 0a   ...Free TSA1.0.
        0087 - 06 03 55 04 0b 13 03 54-53 41 31 76 30 74 06   ..U....TSA1v0t.
        0096 - 03 55 04 0d 13 6d 54 68-69 73 20 63 65 72 74   .U...mThis cert
        00a5 - 69 66 69 63 61 74 65 20-64 69 67 69 74 61 6c   ificate digital
        00b4 - 6c 79 20 73 69 67 6e 73-20 64 6f 63 75 6d 65   ly signs docume
        00c3 - 6e 74 73 20 61 6e 64 20-74 69 6d 65 20 73 74   nts and time st
        00d2 - 61 6d 70 20 72 65 71 75-65 73 74 73 20 6d 61   amp requests ma
        00e1 - 64 65 20 75 73 69 6e 67-20 74 68 65 20 66 72   de using the fr
        00f0 - 65 65 74 73 61 2e 6f 72-67 20 6f 6e 6c 69 6e   eetsa.org onlin
        00ff - 65 20 73 65 72 76 69 63-65 73 31 18 30 16 06   e services1.0..
        010e - 03 55 04 03 13 0f 77 77-77 2e 66 72 65 65 74   .U....www.freet
        011d - 73 61 2e 6f 72 67 31 22-30 20 06 09 2a 86 48   sa.org1"0 ..*.H
        012c - 86 f7 0d 01 09 01 16 13-62 75 73 69 6c 65 7a   ........busilez
        013b - 61 73 40 67 6d 61 69 6c-2e 63 6f 6d 31 12 30   [email protected]
        014a - 10 06 03 55 04 07 13 09-57 75 65 72 7a 62 75   ...U....Wuerzbu
        0159 - 72 67 31 0b 30 09 06 03-55 04 06 13 02 44 45   rg1.0...U....DE
        0168 - 31 0f 30 0d 06 03 55 04-08 13 06 42 61 79 65   1.0...U....Baye
        0177 - 72 6e                                          rn
    certificates:
      <ABSENT>
    crls:
      <ABSENT>
    signerInfos:
        version: 1
        d.issuerAndSerialNumber:
          issuer: O=Free TSA, OU=Root CA, CN=www.freetsa.org/[email protected], L=Wuerzburg, ST=Bayern, C=DE
          serialNumber: 13972846748170250626
        digestAlgorithm:
          algorithm: sha512 (2.16.840.1.101.3.4.2.3)
          parameter: NULL
        signedAttrs:
            object: contentType (1.2.840.113549.1.9.3)
            set:
              OBJECT:id-smime-ct-TSTInfo (1.2.840.113549.1.9.16.1.4)

            object: signingTime (1.2.840.113549.1.9.5)
            set:
              UTCTIME:Feb  4 14:30:56 2021 GMT

            object: id-smime-aa-signingCertificate (1.2.840.113549.1.9.16.2.12)
            set:
              SEQUENCE:
    0:d=0  hl=2 l=  26 cons: SEQUENCE
    2:d=1  hl=2 l=  24 cons:  SEQUENCE
    4:d=2  hl=2 l=  22 cons:   SEQUENCE
    6:d=3  hl=2 l=  20 prim:    OCTET STRING      [HEX DUMP]:916DA3D860ECCA82E34BC59D1793E7E968875F14

            object: messageDigest (1.2.840.113549.1.9.4)
            set:
              OCTET STRING:
                0000 - 4d b9 02 47 cb 66 6e 37-48 c7 56 04 1a   M..G.fn7H.V..
                000d - 77 45 23 95 72 1d 1d e8-62 3e 7b 68 9d   wE#.r...b>{h.
                001a - 58 43 88 64 a7 b3 1b be-bd 56 8e 58 8d   XC.d.....V.X.
                0027 - 8d 12 fe 11 dc 68 89 a5-56 aa bd 00 df   .....h..V....
                0034 - e4 8d f6 3b d8 8e 7d 78-c7 d2 42 a4      ...;..}x..B.
        signatureAlgorithm:
          algorithm: rsaEncryption (1.2.840.113549.1.1.1)
          parameter: NULL
        signature:
          0000 - 62 39 1e b9 0e e3 ab 74-fa 90 46 bd d6 78 bc   b9.....t..F..x.
          000f - 2e d6 a4 3a 7b f4 0e 45-11 ba 16 c0 48 46 5a   ...:{..E....HFZ
          001e - 52 87 c5 3c 9d ae c7 1d-83 dc c8 03 8f 2e 70   R..<..........p
          002d - 2c 4e 1f 6a 4e 5e 64 b7-5d 56 5e cb c9 6f af   ,N.jN^d.]V^..o.
          003c - 17 3d f4 2f c9 a5 b9 5c-d4 a1 03 1f 43 8f a3   .=./...\....C..
          004b - 46 13 62 df 4d f6 cc 48-ad 2c c3 43 85 5e 8c   F.b.M..H.,.C.^.
          005a - 5b da a8 97 8d 3a 06 28-72 56 f3 38 e3 06 ad   [....:.(rV.8...
          0069 - ca 80 28 28 73 3f 9a 6f-ed ba b9 ac ed f4 6f   ..((s?.o......o
          0078 - 69 9e 91 d4 d2 4d 6b 1f-98 53 16 66 d7 50 62   i....Mk..S.f.Pb
          0087 - 96 61 9f 0f f6 bd 94 19-d6 04 c5 7e f9 3c 89   .a.........~.<.
          0096 - 5a 8a d1 a1 05 72 4e 6f-9c 8a a5 ef 6b 36 8d   Z....rNo....k6.
          00a5 - e5 ee 8a e9 11 8b 1c 70-42 c7 32 6d 27 42 fb   .......pB.2m'B.
          00b4 - 99 71 25 ae 66 67 48 58-10 df 4a db 08 08 ea   .q%.fgHX..J....
          00c3 - b1 a0 d5 ca 22 4b 46 ad-12 fd a1 72 91 c4 8b   ...."KF....r...
          00d2 - 21 d2 ff d8 b3 13 7f f8-31 9c 42 f6 b4 ea b1   !.......1.B....
          00e1 - 15 21 8a ed e0 b9 6a 3c-0d 88 03 aa 4a ca f2   .!....j<....J..
          00f0 - 13 59 54 99 0b 19 70 4f-91 0a 7e f7 17 92 70   .YT...pO..~...p
          00ff - dd 0f 54 cc 1e e7 7b 42-d2 fa c2 53 3a 45 5a   ..T...{B...S:EZ
          010e - 45 09 c3 7b b5 34 6d 0b-40 82 72 45 4d eb 60   E..{[email protected].`
          011d - 00 57 c8 46 77 23 5b 1c-c0 ff 6b 01 5c 0e 2f   .W.Fw#[...k.\./
          012c - fb 87 b3 e6 42 e5 1b 1d-25 6c c5 43 c4 af b8   ....B...%l.C...
          013b - 9b 51 74 f2 c9 85 d2 54-52 ca b6 4e ac a1 83   .Qt....TR..N...
          014a - 28 80 99 11 d5 ed a0 82-ad cc df 7d 18 a4 2c   (..........}..,
          0159 - 05 79 c0 f9 be 7c 52 1e-33 84 0c a5 ae b4 4e   .y...|R.3.....N
          0168 - 6d 08 ee 68 13 44 35 15-5f e1 3d e5 72 36 72   m..h.D5._.=.r6r
          0177 - 05 8e 4c 4d 7f 0d ce 32-23 5c 16 bc 73 99 e6   ..LM...2#\..s..
          0186 - 68 ea c5 19 e7 4d d7 0f-22 d5 1c 61 ac a8 cf   h....M.."..a...
          0195 - b6 70 49 79 3c 22 1a 90-96 cd 3b fb 11 bb 56   .pIy<"....;...V
          01a4 - 4f 2a 41 a7 5d 61 f4 81-6a 1c ce 2d f9 0c bb   O*A.]a..j..-...
          01b3 - 91 80 7a 9d 9c 61 37 81-e1 77 20 d3 06 56 be   ..z..a7..w ..V.
          01c2 - f3 df 1c 74 47 ee ab 81-7a 03 80 96 95 a0 93   ...tG...z......
          01d1 - 4b f4 e6 b9 a2 f4 8b 2f-25 80 2f c9 b5 a3 99   K....../%./....
          01e0 - 34 e0 ab 8e 2b fb e3 ce-26 91 0a b3 6d af 18   4...+...&...m..
          01ef - 5a d7 a8 7c 3e c6 1c 17-0d e8 30 da df f2 5d   Z..|>.....0...]
          01fe - 51 3b                                          Q;
        unsignedAttrs:
          <ABSENT>

Итак, я вижу, что есть один объект SignerInfo.

Согласно 5.3. Тип SignerInfo в https://www.rfc-editor.org/rfc/rfc3852 :

Поля типа SignerInfo имеют следующие значения:

      version - это номер версии синтаксиса. Если SignerIdentifier является
      CHOICE issuerAndSerialNumber, тогда версия ДОЛЖНА быть 1. Если
      SignerIdentifier - это subjectKeyIdentifier, тогда версия
      ДОЛЖНА быть 3.

      sid указывает на сертификат подписывающего (и таким образом на публичный
      ключ подписывающего). Публичный ключ подписывающего необходим получателю
      для проверки подписи. SignerIdentifier предоставляет два
      альтернативных варианта для указания публичного ключа подписывающего. 
      Альтернатива issuerAndSerialNumber идентифицирует сертификат подписывающего
      по расширенному имени его издателя и серийному номеру сертификата;
      subjectKeyIdentifier идентифицирует сертификат подписывающего
      по идентификатору ключа.

Таким образом, в предоставленном примере версия SignerInfo равна 1, поэтому сертификат идентифицируется по issuerAndSerialNumber, так как мне вычислить соответствующий X509 хеш? Просто извлечь issuerAndSerialNumber.issuer и вычислить SHA-1? Если так, то как мне это извлечь, используя Openssl?

Если сейчас версия SignerInfo была бы 3 вместо 1, если я правильно понимаю спецификацию, то поля issuerAndSerialNumber не было бы, а вместо этого было бы поле SubjectKeyIdentifier. Будет ли это уже хеш, который openssl x509 -hash -noout -in actual_cert.pem выдал бы? Если нет, как мне вычислить соответствующий хеш в этом случае?

Как обсуждалось в комментариях, вы не можете сделать это для CMS SignedData в общем, но вы можете сделать это для временной метки RFC3161, которая содержит элемент tsa [0] GeneralName OPTIONAL, который все, что я смотрел, делает. Поскольку у меня нет ваших токенов, я использовал один, который есть в моих тестовых данных от freetsa:

$ cat se244280.c
/* se244280.c 2021-02-06 */
/* МИНИМАЛЬНАЯ ОБРАБОТКА ОШИБОК ТОЛЬКО ДЛЯ ТЕСТА */
#include <stdio.h>
#include <openssl/err.h>
#include <openssl/pkcs7.h>
#include <openssl/ts.h>

void err (const char *msg, int info){ fprintf (stderr, "%s: %d\n", msg, info); ERR_print_errors_fp(stderr); exit(1); }

int main (int argc, char**argv)
{
  BIO *bin = BIO_new_file(argv[1],"rb");
  PKCS7 *token;
  if( !(token = d2i_PKCS7_bio(bin,NULL)) ) err("d2i_PKCS7_bio",0);
  if( !PKCS7_type_is_signed(token)
    || OBJ_obj2nid(token->d.sign->contents->type) != NID_id_smime_ct_TSTInfo
    || token->d.sign->contents->d.other->type != V_ASN1_OCTET_STRING )
    printf ("not a token\n"), exit(1);

  ASN1_OCTET_STRING econt = * token->d.sign->contents->d.other->value.octet_string;
  const unsigned char *ptr = econt.data;
  TS_TST_INFO *info;
  if( !(info = d2i_TS_TST_INFO(NULL, &ptr, econt.length)) ) err("d2i_TS_TST_INFO",0);
  if( !info->tsa || info->tsa->type != GEN_DIRNAME )
    printf ("no/bad TSA\n"), exit(1);
  printf ("%08lx\n", X509_NAME_hash( info->tsa->d.dirn ) );
  return 0;
}
$ gcc se244280.c -o se244280 -lcrypto
$ ./se244280 token0
cc952886
$ openssl x509 -in tokencert0 -inform der -noout -subject_hash
cc952886
$ # tokencert0 - это сертификат подписывающего для этого TSA, полученный из другого токена 
$ # где я установил опцию в запросе, чтобы сертификаты были включены -- 
$ # что является более простым и официальным решением для получения сертификатов подписывающего
$

Ответ или решение

Как получить соответствующее значение subject_hash из CMS SignerInfo с использованием OpenSSL

OpenSSL — это мощный инструмент для работы с криптографией, который позволяет управлять сертификатами и различными стандартами шифрования, включая CMS (Cryptographic Message Syntax). В этом ответе мы подробно разберем, как извлечь соответствующее значение subject_hash из объекта SignerInfo в CMS.

Что такое subject_hash?

subject_hash — это хэш-значение (SHA-1), которое уникально идентифицирует X.509 сертификат в соответствии с его полем Subject. Это значение, в частности, используется для сравнения сертификатов, чтобы убедиться, что они соответствуют друг другу.

Контекст задачи

Вы имеете CMS файл в DER-формате, который содержит единственный объект SignerInfo, но не включает сертификат. Внутри SignerInfo без вопросов присутствуют issuer и serialNumber, которые идентифицируют сертификат.

Для того чтобы получить хэш сертификата безопасной подписи, вам необходимо выполнить несколько шагов.

Шаги для извлечения subject_hash

  1. Извлечение данных из CMS:
    Сначала извлеките данные из вашего файла CMS. Используйте следующую команду:

    openssl cms -inform DER -in infile.cms -cmsout -print

    Это предоставит вам детализированную информацию о содержимом CMS, включая SignerInfo.

  2. Извлечение полей issuer и serialNumber:
    Из выведенной информации обратите внимание на поля:

    • issuer: это Distinguished Name (DN) сертифицированного центра.
    • serialNumber: это уникальный идентификатор сертификата.
  3. Вычисление хэша:
    Убедитесь, что у вас есть правильный формат DN, который требуется для вычисления хэша. Далее используйте команду OpenSSL для вычисления subject_hash.

    openssl x509 -in actual_cert.pem -hash -noout

    Если у вас нет физической копии сертификата, вы можете сохранить issuer в файл и использовать openssl x509 для его обработки.

  4. Хэш для SubjectKeyIdentifier (версия 3):
    Если версия SignerInfo равна 3, идентификатор будет представлен в формате SubjectKeyIdentifier. В этом случае:

    • Вам необходимо извлечь значение из поля subjectKeyIdentifier, которое должно быть в самих данных SignerInfo. Это значение не является хэшем сертификата, и для его получения вам будет нужно сначала получить сам сертификат.

Пример кода на C

Для специалистов, использующих возможность программирования, предварительно установите OpenSSL и используйте следующий пример кода:

#include <stdio.h>
#include <openssl/err.h>
#include <openssl/pkcs7.h>
#include <openssl/x509.h>

// Функция для обработки ошибок
void err(const char *msg) {
    fprintf(stderr, "%s\n", msg);
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}

// Главная функция для получения хэша
int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <CMS file>\n", argv[0]);
        return 1;
    }

    // Здесь код для открытия файла и извлечения SignerInfo...

    // Предполагается получение issuer и serialNumber из SignerInfo
    // Теперь вы должны вычислить хэш-значение
    // На следующем шаге вы можете использовать X509_NAME_hash() чтобы получить нужный хэш.

    return 0;
}

Заключение

Извлечение subject_hash из объекта CMS SignerInfo в OpenSSL может быть выполнено с помощью несколько прямолинейных шагов. Понимание структуры CMS и правильного извлечения информации о сертификате является ключом к успешному выполнению этой задачи. Надеюсь, это руководство было полезным и поможет вам успешно решить вашу задачу.

Оцените материал
Добавить комментарий

Капча загружается...