From f9fa982fd33f3491cd9c9f8b757edf4034f1897a Mon Sep 17 00:00:00 2001 From: aiekseu Date: Thu, 7 Mar 2024 15:29:10 +0300 Subject: [PATCH] =?UTF-8?q?feat(cryptopro-cades):=20=D0=94=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=20=D0=B8=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=B0=20=D1=81=D0=B5=D1=80=D1=82=D0=B8=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=D1=82=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(cryptopro-cades): Добавлено разворачивание полей доступных считывателей * feat(cryptopro-cades): Добавлена функция генерации контейнера и запроса на сертификат * feat(cryptopro-cades): Вынес и описал тип для генерации контейнера и CSR * doc(cryptopro-cades): Задокументировал функцию создания запроса на сертификат * refactor(cryptopro-cades): Переименовал и экспортировал функцию запроса на сертификат * doc(cryptopro-cades): Убрал неверный комментарий к полю * test(cryptopro-cades): Написал тест на внешнюю функцию * doc(cryptopro-cades): Задокументировал некоторые константы --- packages/cryptopro-cades/README.md | 1 + packages/cryptopro-cades/src/api/createCSR.ts | 196 ++++++++++++++++++ .../cryptopro-cades/src/api/getReaders.ts | 17 +- packages/cryptopro-cades/src/api/index.ts | 2 + .../convertStringToUTF8ByteArray.test.ts | 16 ++ .../convertStringToUTF8ByteArray.ts | 40 ++++ .../convertStringToUTF8ByteArray/index.ts | 1 + .../cryptopro-cades/src/constants/cades.ts | 128 ++++++++++++ .../cryptopro-cades/src/constants/crypto.ts | 17 ++ .../cryptopro-cades/src/constants/index.ts | 2 + .../src/constants/oids-dictionary.ts | 5 + .../cryptopro-cades/src/types/IReaderMode.ts | 34 +++ .../src/types/cadesplugin/index.ts | 2 - packages/cryptopro-cades/src/types/index.ts | 2 + .../src/types/\320\241reateCSRInputDTO.ts" | 53 +++++ 15 files changed, 508 insertions(+), 8 deletions(-) create mode 100644 packages/cryptopro-cades/src/api/createCSR.ts create mode 100644 packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/convertStringToUTF8ByteArray.test.ts create mode 100644 packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/convertStringToUTF8ByteArray.ts create mode 100644 packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/index.ts create mode 100644 packages/cryptopro-cades/src/constants/crypto.ts create mode 100644 packages/cryptopro-cades/src/types/IReaderMode.ts create mode 100644 "packages/cryptopro-cades/src/types/\320\241reateCSRInputDTO.ts" diff --git a/packages/cryptopro-cades/README.md b/packages/cryptopro-cades/README.md index b11a777..af5f994 100644 --- a/packages/cryptopro-cades/README.md +++ b/packages/cryptopro-cades/README.md @@ -58,3 +58,4 @@ npm install @astral/cryptopro-cades - getSystemInfo - получение информации о системе пользователя - версия КриптоПро ЭЦП Browser plug-in, версия CSP (VipNet или CryptoPro) - pluginConfig - возможность включить вывод отладочной информации, подписываться на все создаваемые исключения, отключать проверку корректности системы, ограничивать тип криптопровайдера которым можно пользоваться, его версии. - getReaders - получение списка доступных считывателей (в т.ч. вставленных токенов) с помощью CryptoPro CSP. +- createCSR - формирование контейнера и запроса на сертификат за один криптосеанс. Работает с VipNet и CryptoPro diff --git a/packages/cryptopro-cades/src/api/createCSR.ts b/packages/cryptopro-cades/src/api/createCSR.ts new file mode 100644 index 0000000..4970bf5 --- /dev/null +++ b/packages/cryptopro-cades/src/api/createCSR.ts @@ -0,0 +1,196 @@ +import { + ALLOW_EXPORT_FLAG, + AT_KEYEXCHANGE, + CERT_POLICY_QUALIFIER_TYPE, + CRYPTO_OBJECTS, + CSP_NAME_MAX_LENGTH, + SUBJECT_SIGN_TOOL_OID, + XCN_CRYPT_STRING_BASE64, + XCN_CRYPT_STRING_BASE64REQUESTHEADER, +} from '../constants'; +import { CreateCSRInputDTO } from '../types/СreateCSRInputDTO'; +import { outputDebug } from '../utils'; + +import { createObject } from './createObject'; +import { afterPluginLoaded } from './internal/afterPluginLoaded'; +import { convertStringToUTF8ByteArray } from './internal/convertStringToUTF8ByteArray'; +import { setCryptoProperty } from './internal/setCryptoProperty'; + +/** + * Функция, формирующая контейнер и запрос на сертификат за один криптосеанс + * @param {CreateCSRInputDTO} data данные для формирования контейнера и запроса на сертификат + * @returns {Promise} Строка, содержащая CSR в DER формате + */ +export const createCSR = (data: CreateCSRInputDTO): Promise => { + return afterPluginLoaded(async () => { + const logData = []; + + logData.push({ data }); + + try { + // Формируем приватный ключ + const pKey = await createObject(CRYPTO_OBJECTS.privateKey); + + await setCryptoProperty(pKey, 'ProviderName', data.providerName); + await setCryptoProperty(pKey, 'ProviderType', data.providerCode); + await setCryptoProperty(pKey, 'ContainerName', data.containerName); + + await setCryptoProperty( + pKey, + 'ExportPolicy', + data.isExportable ? ALLOW_EXPORT_FLAG : 0, + ); + + // помечаем, что ключ и для шифрования, и для подписывания + // @see https://learn.microsoft.com/ru-ru/windows/win32/api/certenroll/ne-certenroll-x509keyspec + await setCryptoProperty(pKey, 'KeySpec', AT_KEYEXCHANGE); + + // инициализируем запрос на сертификат + const certRequestPkcs10 = await createObject( + CRYPTO_OBJECTS.certificateRequest, + ); + + await certRequestPkcs10.InitializeFromPrivateKey(0x1, pKey, ''); + + // описываем аттрибуты Subject сертификата + const distinguishedName = await createObject( + CRYPTO_OBJECTS.distinguishedName, + ); + const dnValue = data.attributes + .map((attr) => `${attr.oid}="${attr.value}"`) + .join(', '); + + await distinguishedName.Encode(dnValue); + await setCryptoProperty(certRequestPkcs10, 'Subject', distinguishedName); + + // объект с расширениями, который будем наполнять + const extensions = await certRequestPkcs10.X509Extensions; + + // key usages сертификата + const keyUsageExt = await createObject(CRYPTO_OBJECTS.extensionKeyUsage); + + await keyUsageExt.InitializeEncode(data.keyUsage); + await extensions.Add(keyUsageExt); + + // описываем enhanced key usages сертификата + const enhKeyUsageCollection = await createObject( + CRYPTO_OBJECTS.objectIds, + ); + + for (const enhKeyOid of data.enhKeyUsage) { + const enhKeyUsageOid = await createObject(CRYPTO_OBJECTS.objectId); + + await enhKeyUsageOid.InitializeFromValue(enhKeyOid); + await enhKeyUsageCollection.Add(enhKeyUsageOid); + } + + const enhancedKeyUsageExt = await createObject( + CRYPTO_OBJECTS.extensionEnhancedKeyUsage, + ); + + await enhancedKeyUsageExt.InitializeEncode(enhKeyUsageCollection); + // добавляем enhanced key usages в расширения сертификата + await extensions.Add(enhancedKeyUsageExt); + + // описываем политики сертификата + const certPolicyCollection = await createObject( + CRYPTO_OBJECTS.certificatePolicies, + ); + + for (const policy of data.certPolicies) { + const policyOid = await createObject(CRYPTO_OBJECTS.objectId); + + await policyOid.InitializeFromValue(policy.oid); + + const certPolicy = await createObject(CRYPTO_OBJECTS.certificatePolicy); + + await certPolicy.Initialize(policyOid); + + // если oid не полностью определяет политику, то необходимо определить и добавить квалификатор + if (Boolean(policy.value.length)) { + const policyQualifier = await createObject( + CRYPTO_OBJECTS.policyQualifier, + ); + + await policyQualifier.InitializeEncode( + policy.value, + CERT_POLICY_QUALIFIER_TYPE.UNKNOWN, + ); + + const policyQualifierCollection = await certPolicy.PolicyQualifiers; + + await policyQualifierCollection.Add(policyQualifier); + } + + await certPolicyCollection.Add(certPolicy); + } + + const certPoliciesExt = await createObject( + CRYPTO_OBJECTS.extensionCertificatePolicies, + ); + + await certPoliciesExt.InitializeEncode(certPolicyCollection); + // добавляем политики в расширения сертификата + await extensions.Add(certPoliciesExt); + + // subject sign tool (custom) + // Необходимо добавлять руками, т.к. плагин может вписать только CryptoPRO CSP 5.0 + // @see https://aleksandr.ru/blog/dobavlenie_subjectsigntool_v_kriptopro_ecp_browser_plug_in + // TODO: 09.2024 это поле станет необязательным, можно удалить код после доработок на УЦ + const subjectSignToolOid = await createObject(CRYPTO_OBJECTS.objectId); + + await subjectSignToolOid.InitializeFromValue(SUBJECT_SIGN_TOOL_OID); + + // необходимо обрезать название (на деле оно всегда < 128 символов) + const shortName = data.signTool.slice(0, CSP_NAME_MAX_LENGTH); + const utf8arr = convertStringToUTF8ByteArray(shortName); + + // @see https://learn.microsoft.com/ru-ru/windows/win32/seccertenroll/about-utf8string + const utf8stringTag = 0x0c; + + utf8arr.unshift(utf8stringTag, utf8arr.length); + + const base64String = btoa( + // @ts-ignore TODO: обновить версию TS, 4.8.3 -> 4.9.5 + String.fromCharCode.apply(null, new Uint8Array(utf8arr)), + ); + + const subjectSignToolExt = await createObject(CRYPTO_OBJECTS.extension); + + await subjectSignToolExt.Initialize( + subjectSignToolOid, + XCN_CRYPT_STRING_BASE64, + base64String, + ); + + // добавляем способ подписания в расширения сертификата + await extensions.Add(subjectSignToolExt); + + // identification kind + const identificationKindExt = await createObject( + CRYPTO_OBJECTS.extensionIdentificationKind, + ); + + await identificationKindExt.InitializeEncode(data.identificationKind); + await extensions.Add(identificationKindExt); + + // запрос + const enroll = await createObject(CRYPTO_OBJECTS.enrollment); + + await enroll.InitializeFromRequest(certRequestPkcs10); + + const csr = await enroll.CreateRequest( + XCN_CRYPT_STRING_BASE64REQUESTHEADER, + ); + + logData.push({ csr }); + + return csr; + } catch (error) { + logData.push({ error }); + throw error.message; + } finally { + outputDebug('createCSR >>', logData); + } + })(); +}; diff --git a/packages/cryptopro-cades/src/api/getReaders.ts b/packages/cryptopro-cades/src/api/getReaders.ts index 0b63f36..2d11652 100644 --- a/packages/cryptopro-cades/src/api/getReaders.ts +++ b/packages/cryptopro-cades/src/api/getReaders.ts @@ -2,8 +2,8 @@ import { CRYPTO_OBJECTS, DEFAULT_CRYPTO_PROVIDER } from '../constants'; import { CryptoError } from '../errors'; import { type CCspInformation, - type CReaderMode, type CReaderModes, + type IReaderMode, } from '../types'; import { outputDebug } from '../utils'; @@ -15,16 +15,16 @@ import { unwrap } from './internal/unwrap'; /** * Кэш из доступных считывателей. */ -let readersCache: CReaderMode[] | null; +let readersCache: IReaderMode[] | null; /** * Получить список доступных считывателей (в т.ч. вставленных токенов) с помощью CryptoPro CSP. * @throws {CryptoError} в случае ошибки. - * @returns {Promise} Информация о доступных считывателях. + * @returns {Promise} Информация о доступных считывателях. */ export function getReaders( resetCache: boolean = false, -): Promise { +): Promise { if (readersCache && !resetCache) { return Promise.resolve(readersCache); } @@ -46,7 +46,7 @@ export function getReaders( } const logData = []; - const readers: CReaderMode[] = []; + const readers: IReaderMode[] = []; try { const cspInformation: CCspInformation = await createObject( @@ -65,7 +65,12 @@ export function getReaders( for (let i = 0; i < readersCount; i++) { const reader = await unwrap(readerModes.ItemByIndex(i)); - readers.push(reader); + readers.push({ + Name: await unwrap(reader.Name), + NickName: await unwrap(reader.NickName), + CarrierFlags: await unwrap(reader.CarrierFlags), + Media: await unwrap(reader.Media), + }); } return (readersCache = readers); diff --git a/packages/cryptopro-cades/src/api/index.ts b/packages/cryptopro-cades/src/api/index.ts index af90c3b..1d5e6de 100644 --- a/packages/cryptopro-cades/src/api/index.ts +++ b/packages/cryptopro-cades/src/api/index.ts @@ -31,3 +31,5 @@ export { findCertificateBySkid } from './findCertificateBySkid'; export { checkPlugin } from './checkPlugin'; export { getReaders } from './getReaders'; + +export { createCSR } from './createCSR'; diff --git a/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/convertStringToUTF8ByteArray.test.ts b/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/convertStringToUTF8ByteArray.test.ts new file mode 100644 index 0000000..9166785 --- /dev/null +++ b/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/convertStringToUTF8ByteArray.test.ts @@ -0,0 +1,16 @@ +import { convertStringToUTF8ByteArray } from './convertStringToUTF8ByteArray'; + +describe('convertStringToUTF8ByteArray', () => { + it('Конвертирует название СКЗИ Крипто Про как Тулбокс', () => { + const stringToConvert = '"КриптоПро CSP" (версия 4.0)'; + const resultFromToolbox = [ + 34, 208, 154, 209, 128, 208, 184, 208, 191, 209, 130, 208, 190, 208, 159, + 209, 128, 208, 190, 32, 67, 83, 80, 34, 32, 40, 208, 178, 208, 181, 209, + 128, 209, 129, 208, 184, 209, 143, 32, 52, 46, 48, 41, + ]; + + const result = convertStringToUTF8ByteArray(stringToConvert); + + expect(result).toEqual(resultFromToolbox); + }); +}); diff --git a/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/convertStringToUTF8ByteArray.ts b/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/convertStringToUTF8ByteArray.ts new file mode 100644 index 0000000..a44af50 --- /dev/null +++ b/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/convertStringToUTF8ByteArray.ts @@ -0,0 +1,40 @@ +/** + * Forked from github.com/google/closure-library + * Converts a JS string to a UTF-8 "byte" array. + * @param {string} str 16-bit unicode string. + * @return {!Array} UTF-8 byte array. + * @see https://github.com/google/closure-library/blob/e877b1eac410c0d842bcda118689759512e0e26f/closure/goog/crypt/crypt.js + */ +export function convertStringToUTF8ByteArray(str: string): number[] { + // TODO(user): Use native implementations if/when available + var out = [], + p = 0; + + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + + if (c < 128) { + out[p++] = c; + } else if (c < 2048) { + out[p++] = (c >> 6) | 192; + out[p++] = (c & 63) | 128; + } else if ( + (c & 0xfc00) == 0xd800 && + i + 1 < str.length && + (str.charCodeAt(i + 1) & 0xfc00) == 0xdc00 + ) { + // Surrogate Pair + c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff); + out[p++] = (c >> 18) | 240; + out[p++] = ((c >> 12) & 63) | 128; + out[p++] = ((c >> 6) & 63) | 128; + out[p++] = (c & 63) | 128; + } else { + out[p++] = (c >> 12) | 224; + out[p++] = ((c >> 6) & 63) | 128; + out[p++] = (c & 63) | 128; + } + } + + return out; +} diff --git a/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/index.ts b/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/index.ts new file mode 100644 index 0000000..ab9d107 --- /dev/null +++ b/packages/cryptopro-cades/src/api/internal/convertStringToUTF8ByteArray/index.ts @@ -0,0 +1 @@ +export * from './convertStringToUTF8ByteArray'; diff --git a/packages/cryptopro-cades/src/constants/cades.ts b/packages/cryptopro-cades/src/constants/cades.ts index 2a2e64c..a2d1d49 100644 --- a/packages/cryptopro-cades/src/constants/cades.ts +++ b/packages/cryptopro-cades/src/constants/cades.ts @@ -822,8 +822,24 @@ export const enum CADESCOM_MEDIA_TYPE { MEDIA_TYPE_SCARD = 0x00000008, } +/** + * Тип кодировки, при котором строка имеет кодировку base64 с начальным и конечным заголовками сертификатов. + * @see https://learn.microsoft.com/ru-ru/windows/win32/api/certenroll/ne-certenroll-encodingtype + */ export const XCN_CRYPT_STRING_BASE64HEADER = 0; +/** + * Тип кодировки, при котором строка закодирована в кодировке Base64 без начальных и конечных заголовков сертификата. + * @see https://learn.microsoft.com/ru-ru/windows/win32/api/certenroll/ne-certenroll-encodingtype + */ +export const XCN_CRYPT_STRING_BASE64 = 1; + +/** + * Тип кодировки, при котором строка в кодировке Base64 содержит начальные и конечные заголовки запроса на сертификат. + * @see https://learn.microsoft.com/ru-ru/windows/win32/api/certenroll/ne-certenroll-encodingtype + */ +export const XCN_CRYPT_STRING_BASE64REQUESTHEADER = 3; + export const AT_KEYEXCHANGE = 1; export const AT_SIGNATURE = 2; @@ -842,6 +858,34 @@ export const CARRIER_FLAG_ABLE_VISUALISE_SIGNATURE = 64; export const CARRIER_FLAG_VIRTUAL = 128; +/** + * Флаг, разрешающий экспорт закрытого ключа. Описывает свойство ExportPolicy объекта PrivateKey + */ +export const ALLOW_EXPORT_FLAG = 0x1; + +/** + * Тип квалификатора, применяемого к политике сертификата. + * @see https://learn.microsoft.com/ru-ru/windows/win32/api/certenroll/ne-certenroll-policyqualifiertype + */ +export const enum CERT_POLICY_QUALIFIER_TYPE { + /** + * Тип квалификатора не указан. + */ + UNKNOWN = 0, + /** + * Квалификатор — это URL-адрес, указывающий на заявление о сертификации (CPS) + */ + URL = 1, + /** + * Квалификатор — это текстовая инструкция, отображаемая приложением для любого пользователя, который использует сертификат. + */ + USER_NOTICE = 2, + /** + * ? + */ + FLAGS = 3, +} + /** * OID (атрибут) сертификата. * The OID object represents an object identifier (OID) that is used by several CAPICOM properties. @@ -966,4 +1010,88 @@ export const enum CRYPTO_OBJECTS { * @see https://docs.cryptopro.ru/cades/plugin/certenroll/ccspinformation */ cspInformation = 'X509Enrollment.CCspInformation', + + /** + * Объект описывает закрытый ключ. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx509privatekey + */ + privateKey = 'X509Enrollment.CX509PrivateKey', + + /** + * Объект описывает запрос на сертификат формата PKCS#10. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx509certificaterequestpkcs10 + */ + certificateRequest = 'X509Enrollment.CX509CertificateRequestPkcs10', + + /** + * Объект описывает поле Subject в соответствии с X500 DistinguishedName. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx500distinguishedname + */ + distinguishedName = 'X509Enrollment.CX500DistinguishedName', + + /** + * Объект описывает расширение KeyUsage (варианты использования ключа) в запросе на сертификат. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx509extensionkeyusage + */ + extensionKeyUsage = 'X509Enrollment.CX509ExtensionKeyUsage', + + /** + * Объект описывает расширение EnhancedKeyUsage (расширенные варианты использования ключа) в запросе на сертификат. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx509extensionenhancedkeyusage + */ + extensionEnhancedKeyUsage = 'X509Enrollment.CX509ExtensionEnhancedKeyUsage', + + /** + * Объект описывает коллекцию объектных идентификаторов (oid'ов). + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cobjectids + */ + objectIds = 'X509Enrollment.CObjectIds', + + /** + * Объект описывает объектный идентификатор (oid). + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cobjectid + */ + objectId = 'X509Enrollment.CObjectId', + + /** + * Объект описывает коллекцию политик сертификата. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/ccertificatepolicies + */ + certificatePolicies = 'X509Enrollment.CCertificatePolicies', + + /** + * Объект указывает политику сертификата, которая определяет цели использования сертификата. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/ccertificatepolicy + */ + certificatePolicy = 'X509Enrollment.CCertificatePolicy', + + /** + * Объект используется для уточнения политики сертификата, если объектный идентификатор не позволяет полностью определить ее. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cpolicyqualifier + */ + policyQualifier = 'X509Enrollment.CPolicyQualifier', + + /** + * Объект описывает коллекцию политик сертификата. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx509extensioncertificatepolicies + */ + extensionCertificatePolicies = 'X509Enrollment.CX509ExtensionCertificatePolicies', + + /** + * Объект описывает расширение в запросе на сертификат. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx509extensioncertificatepolicies + */ + extension = 'X509Enrollment.CX509Extension', + + /** + * Объект описывает расширение 1.2.643.100.114 с информацией об идентификации заявителя при выдаче сертификата ключа проверки ЭП. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx509extensionidentificationkind?id=cx509extensionidentificationkind + */ + extensionIdentificationKind = 'X509Enrollment.CX509ExtensionIdentificationKind', + + /** + * Объект предназначен для создания запросов на сертификат и установки полученных сертификатов. + * @see https://docs.cryptopro.ru/cades/plugin/certenroll/cx509enrollment?id=cx509enrollment + */ + enrollment = 'X509Enrollment.CX509Enrollment', } diff --git a/packages/cryptopro-cades/src/constants/crypto.ts b/packages/cryptopro-cades/src/constants/crypto.ts new file mode 100644 index 0000000..cb32289 --- /dev/null +++ b/packages/cryptopro-cades/src/constants/crypto.ts @@ -0,0 +1,17 @@ +/** + * Ограничитель длины названия СКЗИ в поле Subject Sign Tool запроса на сертификат. + * На практике все названия короче 127 символов, но ограничение необходимо, + * чтобы в крайнем кейсе не сломать структуру запроса. + * + * @description DER формат имеет формат TLV (Type, Length, Value). + * Длина рассчитывается по следующему принципу: + * - Если значение первого байта между 0 и 127 включительно, + * то это и есть длина содержимого, начинающаяся со следующего байта. (наш случай) + * + * - Если первый бит установлен в единицу (то есть значение > 127), + * то мы исключаем эту единицу, а полученное значение - это сколько следующих байт содержат + * целочисленное значение длины, вычитываем значение из них. (случай, которого избегаем с помощью ограничения длины) + * + * @see https://habr.com/ru/articles/722732/ + */ +export const CSP_NAME_MAX_LENGTH = 127; diff --git a/packages/cryptopro-cades/src/constants/index.ts b/packages/cryptopro-cades/src/constants/index.ts index 3d4195e..456fc99 100644 --- a/packages/cryptopro-cades/src/constants/index.ts +++ b/packages/cryptopro-cades/src/constants/index.ts @@ -5,3 +5,5 @@ export * from './storeType'; export * from './cryptoProviders'; export * from './oids-dictionary'; + +export * from './crypto'; diff --git a/packages/cryptopro-cades/src/constants/oids-dictionary.ts b/packages/cryptopro-cades/src/constants/oids-dictionary.ts index 197df0e..994a7ee 100644 --- a/packages/cryptopro-cades/src/constants/oids-dictionary.ts +++ b/packages/cryptopro-cades/src/constants/oids-dictionary.ts @@ -47,3 +47,8 @@ export const attributeOids = { innLe: '1.2.643.100.4', email: '1.2.840.113549.1.9.1', }; + +/** + * Oid способа типа подписания (названия СКЗИ) в запросе на сертификат + */ +export const SUBJECT_SIGN_TOOL_OID = '1.2.643.100.111'; diff --git a/packages/cryptopro-cades/src/types/IReaderMode.ts b/packages/cryptopro-cades/src/types/IReaderMode.ts new file mode 100644 index 0000000..18fa80d --- /dev/null +++ b/packages/cryptopro-cades/src/types/IReaderMode.ts @@ -0,0 +1,34 @@ +/** + * Объект, содержащий информацию о режимах работы доступного считывателя. + * @see https://docs.cryptopro.ru/cades/reference/cadescom/cadescom_class/creadermode + */ +export type IReaderMode = { + /** + * Возвращает имя для данного режима работы считывателя, с порядковым номером + * @example HDIMAGE + * @example Aktiv Rutoken lite + * @example Aktiv Rutoken lite 01 – если вставлен еще один токен + */ + Name: string; + + /** + * Возвращает никнейм для данного режима работы считывателя, не уникальное значение + * @example HDD key storage + * @example Rutoken lite + * @example Rutoken ECP + */ + NickName: string; + + /** + * Возвращает медиа для данного режима работы считывателя, уникальное значение для токенов + * @example NO_UNIQUE, NO_MEDIA – для диска или облака + * @example pkcs11_rutoken_ecp_435461fa + * @example rutoken_lt_40a6544f + */ + Media: string; + + /** + * Возвращает результат побитового сложения флагов для данного режима работы считывателя + */ + CarrierFlags: number; +}; diff --git a/packages/cryptopro-cades/src/types/cadesplugin/index.ts b/packages/cryptopro-cades/src/types/cadesplugin/index.ts index 6dd888e..6c67267 100644 --- a/packages/cryptopro-cades/src/types/cadesplugin/index.ts +++ b/packages/cryptopro-cades/src/types/cadesplugin/index.ts @@ -24,6 +24,4 @@ export type { IVersion } from './IVersion'; export type { CCspInformation } from './CCspInformation'; -export type { CReaderMode } from './CReaderMode'; - export type { CReaderModes } from './CReaderModes'; diff --git a/packages/cryptopro-cades/src/types/index.ts b/packages/cryptopro-cades/src/types/index.ts index 1ffd06c..28d4882 100644 --- a/packages/cryptopro-cades/src/types/index.ts +++ b/packages/cryptopro-cades/src/types/index.ts @@ -7,3 +7,5 @@ export * from './ICryptoProvider'; export * from './ISystemInfo'; export * from './WithOptionalPromise'; + +export * from './IReaderMode'; diff --git "a/packages/cryptopro-cades/src/types/\320\241reateCSRInputDTO.ts" "b/packages/cryptopro-cades/src/types/\320\241reateCSRInputDTO.ts" new file mode 100644 index 0000000..a6983f3 --- /dev/null +++ "b/packages/cryptopro-cades/src/types/\320\241reateCSRInputDTO.ts" @@ -0,0 +1,53 @@ +export type CreateCSRInputDTO = { + /** + * Наименование СКЗИ + */ + providerName: string; + + /** + * Код СКЗИ + */ + providerCode: number; + + /** + * Имя контейнера, который необходимо создать. + * Если в начало строки вставить Media нужного считывателя, СКЗИ не покажет диалог с выбором хранилища + */ + containerName: string; + + /** + * Флаг возможности экспорта закрытого ключа + */ + isExportable: boolean; + + /** + * Массив аттрибутов поля Subject + */ + attributes: { oid: string; value: string }[]; + + /** + * Сумма флагов назначения ключа + * @see https://learn.microsoft.com/ru-ru/windows/win32/api/certenroll/ne-certenroll-x509keyusageflags + */ + keyUsage: number; + + /** + * Массив oid'ов расширенных вариантов использования ключа + */ + enhKeyUsage: string[]; + + /** + * Массив политик сертификата. Если передан value, то к политике добавляется квалификатор + */ + certPolicies: { oid: string; value: string }[]; + + /** + * Subject Sign Tool – человеческое название используемого СКЗИ. С 09.2024 будет необязательным + */ + signTool: string; + + /** + * Тип идентификации в соответствии с 795 приказом ФСБ. Принимает значения {0, 1, 2, 3} + */ + identificationKind: number; +};