H2H

Как сформировать подпись

Подписание алгоритмом RSA

Для подписания алгоритмом RSA необходимо выполнить следующие шаги:

1

Подготовьте данные для подписи в формате JSON

Подписываться должен весь блок данных тела запроса, исключая digestSignatures.

2

Выполните каноникализацию JSON-данных, используя правила RFC8785

Пример каноникализированного JSON-сообщения:

1{"company":{"accountNumber":"40702810201400022055"},"externalId":"eb19f3ef-ca6a-4f84-a28f-c6e7b469b486","requisite":[{"payoutId":"b766f52d-f6e6-4a47-bd71-ab332bd0d314","payoutRequisite":{"byPan":{"comment":"Курьерские услуги за период с 1.12.2020-20.12.2020","pan":"JSv++SDzmKNPIBrLc0G2k3W8lx6DjsHwSFptBCGvUAkq7hVe7KWeACIjjTOAeFKujHQguSIKcqmX93rakldGsTa6c5iZTEAsyhBp5dcPLti8F9x90iXNvCUgvPEXGclqBNvTCuVGaNbmAWrGbogHTn4LKmxxrmzeMIL+e51/YFdTpTnJ384xhiYzAzX4XxFY2CsgNOlyN5xCesOjgeYz+UBlW0MCcY9c8AJBh7+DwU09+zlS0GNtpti7GoETeQrjUv6aPqqu2IMGfO116/xVheUdwLNhcDCT2uvbRpmk9B7pMuLuekGhaA1odvoTghjvY1d1RWnjyO4ZrHpBMpVesg=="},"payoutTotalAmount":"24.00","payoutType":"CARD"},"selfemployed":{"name":"Диогенович","patronymic":"Камень","surname":"Безукоризненный"}}]}
3

Сформируйте контейнер электронной подписи в формате PKCS#7 (CMS) в виде структуры ContentInfo в соответствии со стандартом RFC 5652 (Cryptographic Message Syntax)

Для создания структуры контейнера электронной подписи PKCS#7, вычисления хэша данных и электронной подписи необходимо использовать криптографические провайдеры, работающие с RSA, например, Bouncy Castle, OpenSSL.

  1. 1.
    Контейнер может быть присоединённым или отсоединённым. Рекомендуется использовать присоединённую подпись, чтобы данные и подпись передавались вместе.
  2. 2.
    Формат подписи - RFC 5652 (PKCS#7/CMS).
  3. 3.
    Алгоритм хэш-функции - SHA-256 (OID 2.16.840.1.101.3.4.2.1).
  4. 4.
    Алгоритм подписи - sha256WithRSAEncryption.

Контейнер электронной подписи CMS ContentInfo в общем виде должен содержать contentType (OID 1.2.840.113549.1.7.2) и content, содержащий структуру signedData.

Конструкция SignedData содержит последовательность всех атрибутов электронной подписи:

1SignedData ::= SEQUENCE {
2version CMSVersion,
3digestAlgorithms DigestAlgorithmIdentifiers,
4encapContent Info EncapsulatedContentInfo,
5certificates [0] IMPLICIT CertificateSet OPTIONAL, 
6crls [1] IMPLICIT Revocation InfoChoices OPTIONAL, 
7signerInfos SignerInfos }

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

1SignerInfo ::= SEQUENCE { 
2version CMSVersion,
3sid SignerIdentifier,
4digestAlgorithm DigestAlgorithmIdentifier,
5signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL, 
6signatureAlgorithm SignatureAlgorithmIdentifier,
7signature SignatureValue,
8unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }

Для формирования подписи необходимо использовать подписываемые атрибуты (signedAttrs). В набор signedAttrs обязательно должны быть включены следующие четыре атрибута:

  • •
    ContentType = OID 1.2.840.113549.1.7.1
  • •
    SigningTime = OID 1.2.840.113549.1.9.5
  • •
    CMSAlgorithmProtection = OID 1.2.840.113549.1.9.52 (RFC 6211)
  • •
    messageDigest = OID 1.2.840.113549.1.9.4

Атрибут unsignedAttrs в SignerInfo не используется.

4

Закодируйте контейнер и передайте подпись в запросе

Полученный PKCS#7-контейнер необходимо закодировать в base64 и передать в параметре тела запроса base64Encoded массива digestSignatures.

Пример формирования подписи на Java
Пример формирования подписи на Java
1@Override
2public RsaSignResponse sign(RsaSignRequest request) throws Exception {
3  Security.addProvider(new BouncyCastleProvider());
4  BouncyCastleProvider provider = (BouncyCastleProvider) Security.getProvider("BC");
5  PrivateKey key = (PrivateKey) keyStore.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray());
6  X509Certificate cert = (X509Certificate) keyStore.getCertificate(KEYSTORE_ALIAS);
7  CMSTypedData msg = new CMSProcessableByteArray(request.getDataToSign().getBytes());
8  CMSSignedDataGenerator signedDataGen = new CMSSignedDataGenerator();
9  X509CertificateHolder signCert = new X509CertificateHolder(cert.getEncoded());
10  ContentSigner signer = new JcaContentSignerBuilder("sha256WithRSAEncryption")
11        .setProvider(provider)
12        .build(key);
13  signedDataGen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
14        new JcaDigestCalculatorProviderBuilder().setProvider(provider).build()
15  ).setDirectSignature(true).build(signer, signCert));
16  // добавляем сертификат в подпись
17  JcaCertStore certs = new JcaCertStore(Collections.singletonList(cert));
18  signedDataGen.addCertificates(certs);
19  CMSSignedData signedData = signedDataGen.generate(msg, true);
20  byte[] signatureBytes = signedData.getEncoded("DER");
21  MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
22  byte[] hashedString = messageDigest.digest(request.getDataToSign().getBytes());
23  return new RsaSignResponse()
24        .base64Encoded(new String(Base64.getEncoder().encode(signatureBytes)));
25
26}
Пример формирования подписи на PHP
Пример формирования подписи на PHP
1public function sign(string $data, string $password = null): string
2{
3  $key = file_get_contents($this->private_key);
4  $cert = file_get_contents($this->certificate);
5  $privateKey = openssl_pkey_get_private($key, $password ?: $this->private_key_password);
6  $certid = openssl_x509_read($cert);
7  $inputTmpFile = tmpfile();
8  fwrite($inputTmpFile, $data);
9  fseek($inputTmpFile, 0);
10  $outputTmpFile = tmpfile();
11  openssl_pkcs7_sign(
12      stream_get_meta_data($inputTmpFile)['uri'],
13      stream_get_meta_data($outputTmpFile)['uri'],
14      $certid,
15      $privateKey,
16      [],
17      PKCS7_ATTACHED
18  );
19  fseek($outputTmpFile, 0);
20  $signature = fread($outputTmpFile, filesize(stream_get_meta_data($outputTmpFile)['uri']));
21  $signature = trim(explode("\n\n", $signature, 2)[1]);
22  fclose($inputTmpFile);
23  fclose($outputTmpFile);
24  return str_replace("\n", '', $signature);
25}

Подписание алгоритмом ГОСТ с использованием сертификата УКЭП

Для подписания может использоваться сертификат УКЭП, выпущенный любым аккредитованным УЦ. Если сертификат принадлежит лицу, обладающим правом подписи по уставу компании, то он самодостаточен. Если сертификат выпущен для уполномоченного лица, обладающего правом подписи по доверенности, то, помимо сертификата, потребуется выпустить машиночитаемую доверенность (МЧД).

  1. 1.
    Сформируйте электронную подпись с использованием криптопровайдера КриптоПро CSP.
  2. 2.
    Закодируйте полученное значение отсоединённой электронной подписи в формат base64.
  3. 3.
    Передайте закодированное значение подписи в параметре base64Encoded массива digestSignatures.