Как работает подпись CSR?

#openssl #x509

#openssl #x509

Вопрос:

Я пытаюсь подписать файл CSR в формате PEM, используя OpenSSL API.

Я не могу использовать X509_sign() метод, потому что мне нужно подписать запрос с помощью стороннего API. Этот API требует, чтобы мое приложение отправляло данные, требующие подписи, и оно вернет мне подпись.

Мне нужно отправить ему char* для подписи сторонний API, но я не понимаю, как работает процесс подписи:

  • Нужно ли мне создавать это char* только с данными из CSR, например, субъектом, показателем степени и модулем открытого ключа:

    Char* = "DN=test,O=something...123456(this is the exponent)a2:43:65(this is the modulus)...

  • Или мне нужно создать свой буфер со всеми элементами, которые появляются, когда я использую -text команду в OpenSSL:

    Certificate:
    Data:
    Subject: dn=test, O=something
    Public key info:
    Exponent: 123456
    Modulus:
    A2:43:65
    ....

Подводя итог: какие элементы запроса на самом деле использует центр сертификации при выполнении подписи сертификата X509 и в каком формате должны быть представлены эти данные?

Комментарии:

1. Вам нужно взглянуть на документацию стороннего API и посмотреть, какой формат ввода он ожидает. Возможно, он ожидает какой-то стандартный формат, возможно, нет. С помощью представленной вами информации мы можем сказать вам, какая информация должна присутствовать в этой строке, но не как ее нужно форматировать.

Ответ №1:

Ваш заголовок и тело не совпадают; openssl X509_sign() подписывает сертификат, а не CSR. CSR — это не сертификат, cert — это не CSR, и выдача сертификата — это НЕ «подписание CSR», как говорят многие неосведомленные или ленивые люди. (Обычно) CSR на самом деле подписываются инициатором запроса, а не центром сертификации. openssl использует type X509_REQ для CSR и X509_REQ_sign подписывает CSR.

Но ответ в основном тот же. сертификат X.509 или CSR PKCS # 10 — это последовательность из трех элементов: тело, содержащее подписываемые данные, AlgorithmIdentifier и подпись (в виде битовой СТРОКИ). Они определяются с помощью http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One и кодируется с использованием отличительных правил кодирования или DER. Текстовая форма, которую вы вводите или видите отображаемой openssl req -text или openssl x509 -text или другими программами, представляет те же данные, но, как правило, недостаточна для воспроизведения точных битов, и у вас должны быть точные биты для работы цифровой подписи. (Это то, что означает «Различаемый» в DER; это изменяет базовые правила кодирования или BER, указывая точные октеты и биты для использования в определенных, иначе неоднозначных случаях.) Для CSR смотрите мой ответ в https://security.stackexchange.com/questions/58717/what-part-of-the-csr-is-hashed-in-order-to-create-its-signature/58735 ; для сертификата «оболочка» та же, но тело существенно отличается и содержит совсем немного больше, чем DN и publickey.

TL; DR Для X509* cert вы подписываете DER-кодировку cert->cert_info , которая имеет тип X509_CINF и может быть закодирована с помощью i2d_X509_CINF() . Для X509_REQ* csr вы подписываете DER-кодировку csr->req_info с типом X509_REQ_INFO с помощью i2d_X509_REQ_INFO() .

Альтернатива: в openssl реализована концепция движка, представляющего собой внешнюю библиотеку и / или аппаратный блок, который обрабатывает некоторые криптографические операции. Если ваш сторонний API является или может быть «обернут» как движок openssl, вы можете использовать (почти все) обычные процедуры openssl, настроив и указав этот движок. Если этот движок еще не существует, выполнение его исключительно для вашего проекта, вероятно, потребует слишком много работы, но если вы предвидите другие (будущие) варианты использования, это может стоить того.

Комментарии:

1. Спасибо, у меня это сработало, но я не знаю, как установить эту подпись в мой сертификат X509… Вы знаете как?

2. Если вы хотите использовать OpenSSL, измените ->sig_alg на amp;X509_ALGOR, содержащий копию значений в *cert_info-> signature, которые должны были быть правильными до того, как вы подписали, и -> signature на amp;ASN1_BIT_STRING, содержащую значение подписи «wire» (просто число в байтах bigendian для RSA, DER-кодирование ПОСЛЕДОВАТЕЛЬНОСТИ из двух целых чисел для DSA или ECDSA). Следуйте дисциплине OpenSSL, чтобы любой указатель, который вы устанавливаете в структуре этого типа, указывал на уникальный объект, созданный с помощью xxx_new или уникальной памяти из OPENSSL_malloc, и любой указатель, который вы уничтожаете, был xxx_free или OPENSSL_free, в зависимости от обстоятельств. …

3. … Затем вы можете закодировать и / или записать полученный X509 * с помощью пользовательского i2d_X509[_fp}_bio] или PEM_write[_bio]_X509, отобразить witch X509_print и т.д. В качестве альтернативы, возьмите кодировку DER тела, которая у вас уже есть (для подписи), кодировку DER algid ‘signature’ из тела и кодировку DER / BER битовой СТРОКИ, содержащей каноническую подпись, и объедините затем в ПОСЛЕДОВАТЕЛЬНОСТЬ DER / BER. (Не забудьте октет неиспользуемых бит в битовой СТРОКЕ.) Если вам нужен PEM, то преобразуйте DER в PEM стандартным способом, с помощью base64 разрывов начальных / конечных строк.

4. Большое тебе спасибо, Дэйв, это очень помогло. В моем первом подходе я забыл добавить копию X509_ALGO в часть cert_info, и моя подпись была неправильной. Последний вопрос, я не совсем понимаю, что вы имеете в виду, говоря: и -> подпись должна быть amp;ASN1_BIT_STRING, содержащей значение подписи «wire» (просто число в байтах bigendian для RSA, DER кодирование ПОСЛЕДОВАТЕЛЬНОСТИ из двух целых чисел для DSA или ECDSA). Потому что то, что я делаю, чтобы поместить подпись в мой объект X509, это:

5. сертификат-> подпись-> данные = новый неподписанный символ [длина подписи]; memcpy (сертификат-> подпись-> данные, подпись, длина подписи); сертификат-> подпись-> длина = длина подписи;