Поделиться через


Клиентская библиотека ключей Azure Key Vault для JavaScript — версия 4.10.0

Azure Key Vault — это служба, которая позволяет шифровать ключи проверки подлинности, ключи учетной записи хранения, ключи шифрования данных, PFX-файлы и пароли с помощью защищенных ключей. Если вы хотите узнать больше о Azure Key Vault, вы можете просмотреть: Что такое Azure Key Vault?

Управляемый HSM в Azure Key Vault — это полностью управляемая, высокодоступная, однотенантная облачная служба, совместимая со стандартами, которая позволяет защитить криптографические ключи для облачных приложений с помощью fiPS 140-2 уровня 3, проверенных HSM. Если вы хотите узнать больше об управляемом HSM в Azure Key Vault, вы можете просмотреть следующие сведения: Что такое управляемый HSM в Azure Key Vault?

Клиент библиотеки ключей Azure Key Vault поддерживает ключи RSA, ключи с многоточием (EC), а также симметричные (окт) ключи при выполнении с управляемым HSM, каждый из которых поддерживает аппаратные модули безопасности (HSM). Она предлагает операции для создания, извлечения, обновления, удаления, очистки, резервного копирования, восстановления и перечисления ключей и его версий.

Используйте клиентную библиотеку для ключей Azure Key Vault в приложении Node.js, чтобы:

  • Создайте ключи с помощью многоточия или шифрования RSA, при необходимости поддерживаемых аппаратными модулями безопасности (HSM).
  • Импорт, удаление и обновление ключей.
  • Получите один или несколько ключей и удаленных ключей с их атрибутами.
  • Восстановите удаленный ключ и восстановите резервный ключ.
  • Получите версии ключа.

С помощью клиента шифрования, доступного в этой библиотеке, также имеется доступ к:

  • Шифрования
  • Расшифровки
  • Подписывание
  • Проверочный
  • Оболочка ключей
  • Распакуние ключей

Примечание. Этот пакет нельзя использовать в браузере из-за ограничений службы Azure Key Vault, см. этот документ для получения рекомендаций.

Ключевые ссылки:

Начало работы

Поддерживаемые в настоящее время среды

Предпосылки

Установите пакет

Установка клиентской библиотеки ключей Azure Key Vault с помощью npm

npm install @azure/keyvault-keys

Установка библиотеки удостоверений

Клиенты Azure Key Vault проходят проверку подлинности с помощью библиотеки удостоверений Azure. Установите его, а также с помощью npm

npm install @azure/identity

Настройка TypeScript

Пользователям TypeScript необходимо установить определения типов узлов:

npm install @types/node

Кроме того, необходимо включить compilerOptions.allowSyntheticDefaultImports в tsconfig.json. Обратите внимание, что если вы включили compilerOptions.esModuleInterop, allowSyntheticDefaultImports включен по умолчанию. Дополнительные сведения см. в руководстве по параметрам компилятора TypeScript .

Основные понятия

  • Клиент ключа — это основной интерфейс для взаимодействия с методами API, связанными с ключами в API Azure Key Vault из приложения JavaScript. После инициализации он предоставляет базовый набор методов, которые можно использовать для создания, чтения, обновления и удаления ключей.
  • версия ключа — это версия ключа в Key Vault. Каждый раз, когда пользователь назначает значение уникальному имени ключа, создается новая версия этого ключа. Получение ключа по имени всегда возвращает последнее назначенное значение, если в запросе не указана определенная версия.
  • обратимое удаление позволяет Key Vault поддерживать удаление и очистку как два отдельных шага, поэтому удаленные ключи не сразу теряются. Это происходит только в том случае, если в Key Vault включена обратимое удаление.
  • резервного копирования ключей можно создать из любого созданного ключа. Эти резервные копии приходят в виде двоичных данных и могут использоваться только для повторного создания ранее удаленного ключа.
  • Клиент шифрования — это отдельный интерфейс, взаимодействующий с методами API ключей в API Key Vault. Этот клиент фокусируется только на операциях шифрования, которые можно выполнить с помощью ключа, который уже создан в Key Vault. Дополнительные сведения об этом клиенте см. в разделе криптографии.

Проверка подлинности с помощью Azure Active Directory

Служба Key Vault использует Azure Active Directory для проверки подлинности запросов к своим API. Пакет @azure/identity предоставляет различные типы учетных данных, которые приложение может использовать для этого. README для @azure/identity предоставляет дополнительные сведения и примеры для начала работы.

Чтобы взаимодействовать со службой Azure Key Vault, необходимо создать экземпляр класса KeyClient, URL-адрес хранилища и объект учетных данных. В примерах, показанных в этом документе, используется объект учетных данных с именем DefaultAzureCredential, который подходит для большинства сценариев, включая локальные среды разработки и рабочей среды. Кроме того, мы рекомендуем использовать управляемое удостоверение для проверки подлинности в рабочих средах.

Дополнительные сведения о различных способах проверки подлинности и их соответствующих типах учетных данных см. в документации по идентификации Azure.

Вот краткий пример. Сначала импортируйте DefaultAzureCredential и KeyClient. После импорта этих данных можно подключиться к службе Key Vault:

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

// Build the URL to reach your key vault
const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`; // or `https://${vaultName}.managedhsm.azure.net` for managed HSM.

// Lastly, create our keys client and connect to the service
const client = new KeyClient(url, credential);

Указание версии API службы Azure Key Vault

По умолчанию этот пакет использует последнюю версию службы Azure Key Vault, которая 7.2. Версию службы можно изменить, задав параметр serviceVersion в конструкторе клиента, как показано ниже:

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

// Build the URL to reach your key vault
const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`; // or `https://${vaultName}.managedhsm.azure.net` for managed HSM.

// Change the Azure Key Vault service API version being used via the `serviceVersion` option
const client = new KeyClient(url, credential, {
  serviceVersion: "7.0", // Or 7.1
});

Примеры

В следующих разделах приведены фрагменты кода, охватывающие некоторые распространенные задачи с помощью ключей Azure Key Vault. Сценарии, описанные здесь, состоят из следующих:

Создание ключа

createKey создает ключ для хранения в Azure Key Vault. Если ключ с тем же именем уже существует, создается новая версия ключа.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";
const result = await client.createKey(keyName, "RSA");
console.log("result: ", result);

Второй параметр, отправленный в createKey, является типом ключа. Тип поддерживаемых ключей зависит от номера SKU и использования Azure Key Vault или управляемого HSM Azure. Список поддерживаемых типов ключей up-to-date см. в разделе О ключах

Получение ключа

Самый простой способ чтения ключей из хранилища — получить ключ по имени. Будет получена последняя версия ключа. При необходимости можно получить другую версию ключа, если указать ее как часть необязательных параметров.

getKey извлекает ключ предыдущих хранилищ в Key Vault.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

const latestKey = await client.getKey(keyName);
console.log(`Latest version of the key ${keyName}: `, latestKey);

const specificKey = await client.getKey(keyName, { version: latestKey.properties.version! });
console.log(`The key ${keyName} at the version ${latestKey.properties.version!}: `, specificKey);

Создание и обновление ключей с помощью атрибутов

Следующие атрибуты также можно назначить любому ключу в Key Vault:

  • tags: любой набор ключевых значений, которые можно использовать для поиска и фильтрации ключей.
  • keyOps: массив операций, которые этот ключ сможет выполнять (encrypt, decrypt, sign, verify, wrapKey, unwrapKey).
  • enabled: логическое значение, определяющее, можно ли считать или нет значение ключа.
  • notBefore: указанная дата, после которой можно получить значение ключа.
  • expires: указанная дата, после которой невозможно получить значение ключа.

Объект с этими атрибутами можно отправить в качестве третьего параметра createKeyсразу после имени и значения ключа, как показано ниже.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

const result = await client.createKey(keyName, "RSA", {
  enabled: false,
});
console.log("result: ", result);

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

Атрибуты также можно обновить до существующей версии ключа с updateKeyPropertiesследующим образом:

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

const result = await client.createKey(keyName, "RSA");
await client.updateKeyProperties(keyName, result.properties.version, {
  enabled: false,
});

Удаление ключа

Метод beginDeleteKey запускает удаление ключа. Этот процесс будет происходить в фоновом режиме, как только необходимые ресурсы будут доступны.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

const poller = await client.beginDeleteKey(keyName);
await poller.pollUntilDone();

Если обратимое удаление включено для Key Vault, эта операция будет помечена только как ключ удаленным ключом. Удаленный ключ не может быть обновлен. Они могут быть прочитаны, восстановлены или удалены.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

const poller = await client.beginDeleteKey(keyName);

// You can use the deleted key immediately:
const deletedKey = poller.getResult();

// The key is being deleted. Only wait for it if you want to restore it or purge it.
await poller.pollUntilDone();

// You can also get the deleted key this way:
await client.getDeletedKey(keyName);

// Deleted keys can also be recovered or purged:

// recoverDeletedKey also returns a poller, just like beginDeleteKey.
const recoverPoller = await client.beginRecoverDeletedKey(keyName);
await recoverPoller.pollUntilDone();

// And here is how to purge a deleted key
await client.purgeDeletedKey(keyName);

Так как ключи занимают некоторое время, чтобы получить полное удаление, beginDeleteKey возвращает объект Poller, который отслеживает базовую операцию длительного выполнения в соответствии с нашими рекомендациями: https://azure.github.io/azure-sdk/typescript_design.html#ts-lro

Полученный опрос позволит получить удаленный ключ, вызвав poller.getResult(). Вы также можете дождаться завершения удаления, выполнив отдельные вызовы службы, пока ключ не будет удален или дождитесь завершения процесса:

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

const poller = await client.beginDeleteKey(keyName);

// You can use the deleted key immediately:
let deletedKey = poller.getResult();

// Or you can wait until the key finishes being deleted:
deletedKey = await poller.pollUntilDone();
console.log(deletedKey);

Еще один способ ждать, пока ключ не будет полностью удален, — выполнить отдельные вызовы, как показано ниже.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const poller = await client.beginDeleteKey(keyName);

while (!poller.isDone()) {
  await poller.poll();
  await delay(5000);
}

console.log(`The key ${keyName} is fully deleted`);

Настройка автоматической смены ключей

С помощью KeyClient можно настроить автоматическую смену ключей для ключа, указав политику поворота. Кроме того, KeyClient предоставляет метод для смены ключа по запросу путем создания новой версии заданного ключа.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

// Set the key's automated rotation policy to rotate the key 30 days before expiry.
const policy = await client.updateKeyRotationPolicy(keyName, {
  lifetimeActions: [
    {
      action: "Rotate",
      timeBeforeExpiry: "P30D",
    },
  ],
  // You may also specify the duration after which any newly rotated key will expire.
  // In this case, any new key versions will expire after 90 days.
  expiresIn: "P90D",
});

// You can get the current key rotation policy of a given key by calling the getKeyRotationPolicy method.
const currentPolicy = await client.getKeyRotationPolicy(keyName);

// Finally, you can rotate a key on-demand by creating a new version of the given key.
const rotatedKey = await client.rotateKey(keyName);

Итерации списков ключей

Используя KeyClient, вы можете получить и выполнить итерацию по всем ключам в Azure Key Vault, а также через все удаленные ключи и версии определенного ключа. Доступны следующие методы API:

  • listPropertiesOfKeys перечисляет все не удаленные ключи по их именам только в последних версиях.
  • listDeletedKeys перечислит все удаленные ключи по их именам только в последних версиях.
  • listPropertiesOfKeyVersions перечисляет все версии ключа на основе имени ключа.

Что можно использовать следующим образом:

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

for await (const keyProperties of client.listPropertiesOfKeys()) {
  console.log("Key properties: ", keyProperties);
}

for await (const deletedKey of client.listDeletedKeys()) {
  console.log("Deleted: ", deletedKey);
}

for await (const versionProperties of client.listPropertiesOfKeyVersions(keyName)) {
  console.log("Version properties: ", versionProperties);
}

Все эти методы возвращают все доступные результаты одновременно. Чтобы получить их по страницам, добавьте .byPage() сразу после вызова метода API, который вы хотите использовать, как показано ниже.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const keyName = "MyKeyName";

for await (const page of client.listPropertiesOfKeys().byPage()) {
  for (const keyProperties of page) {
    console.log("Key properties: ", keyProperties);
  }
}

for await (const page of client.listDeletedKeys().byPage()) {
  for (const deletedKey of page) {
    console.log("Deleted key: ", deletedKey);
  }
}

for await (const page of client.listPropertiesOfKeyVersions(keyName).byPage()) {
  for (const versionProperties of page) {
    console.log("Version: ", versionProperties);
  }
}

Криптография

Эта библиотека также предлагает набор программ шифрования, доступных через CryptographyClient. Как и в KeyClient, CryptographyClient будет подключаться к Azure Key Vault с предоставленным набором учетных данных. После подключения CryptographyClient может шифровать, расшифровывать, подписывать, проверять, упаковывать ключи и распаковывать ключи.

Теперь можно подключиться к службе хранилища ключей так же, как и в KeyClient. Нам потребуется скопировать некоторые параметры из хранилища ключей, к которому мы подключаемся к переменным среды. После того как они находятся в нашей среде, мы можем получить к ним доступ со следующим кодом:

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

// Create or retrieve a key from the keyvault
const myKey = await client.createKey("MyKey", "RSA");

// Lastly, create our cryptography client and connect to the service
const cryptographyClient = new CryptographyClient(myKey, credential);

Шифрование

encrypt зашифрует сообщение.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const myKey = await client.createKey("MyKey", "RSA");
const cryptographyClient = new CryptographyClient(myKey.id, credential);

const encryptResult = await cryptographyClient.encrypt({
  algorithm: "RSA1_5",
  plaintext: Buffer.from("My Message"),
});
console.log("encrypt result: ", encryptResult.result);

расшифровать

decrypt расшифровывает зашифрованное сообщение.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const myKey = await client.createKey("MyKey", "RSA");
const cryptographyClient = new CryptographyClient(myKey.id, credential);

const encryptResult = await cryptographyClient.encrypt({
  algorithm: "RSA1_5",
  plaintext: Buffer.from("My Message"),
});
console.log("encrypt result: ", encryptResult.result);

const decryptResult = await cryptographyClient.decrypt({
  algorithm: "RSA1_5",
  ciphertext: encryptResult.result,
});
console.log("decrypt result: ", decryptResult.result.toString());

Подписание

sign криптографически подписывает дайджест (хэш) сообщения с подписью.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";
import { createHash } from "node:crypto";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

let myKey = await client.createKey("MyKey", "RSA");
const cryptographyClient = new CryptographyClient(myKey, credential);

const signatureValue = "MySignature";
const hash = createHash("sha256");

const digest = hash.update(signatureValue).digest();
console.log("digest: ", digest);

const signResult = await cryptographyClient.sign("RS256", digest);
console.log("sign result: ", signResult.result);

Подписывая данные

signData криптографически подписывает сообщение с подписью.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const myKey = await client.createKey("MyKey", "RSA");
const cryptographyClient = new CryptographyClient(myKey, credential);

const signResult = await cryptographyClient.signData("RS256", Buffer.from("My Message"));
console.log("sign result: ", signResult.result);

Проверьте

verify будет криптографически проверять, подписан ли подписанный дайджест с заданной подписью.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";
import { createHash } from "node:crypto";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const myKey = await client.createKey("MyKey", "RSA");
const cryptographyClient = new CryptographyClient(myKey, credential);

const hash = createHash("sha256");
hash.update("My Message");
const digest = hash.digest();

const signResult = await cryptographyClient.sign("RS256", digest);
console.log("sign result: ", signResult.result);

const verifyResult = await cryptographyClient.verify("RS256", digest, signResult.result);
console.log("verify result: ", verifyResult.result);

Проверка данных

verifyData будет криптографически проверять, подписан ли подписанное сообщение с заданной подписью.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const myKey = await client.createKey("MyKey", "RSA");
const cryptographyClient = new CryptographyClient(myKey, credential);

const buffer = Buffer.from("My Message");

const signResult = await cryptographyClient.signData("RS256", buffer);
console.log("sign result: ", signResult.result);

const verifyResult = await cryptographyClient.verifyData("RS256", buffer, signResult.result);
console.log("verify result: ", verifyResult.result);

Упаковка ключа

wrapKey упаковывает ключ с помощью слоя шифрования.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const myKey = await client.createKey("MyKey", "RSA");
const cryptographyClient = new CryptographyClient(myKey, credential);

const wrapResult = await cryptographyClient.wrapKey("RSA-OAEP", Buffer.from("My Key"));
console.log("wrap result:", wrapResult.result);

Распаковка ключа

unwrapKey развернет завернутый ключ.

import { DefaultAzureCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";

const credential = new DefaultAzureCredential();

const vaultName = "<YOUR KEYVAULT NAME>";
const url = `https://${vaultName}.vault.azure.net`;

const client = new KeyClient(url, credential);

const myKey = await client.createKey("MyKey", "RSA");
const cryptographyClient = new CryptographyClient(myKey, credential);

const wrapResult = await cryptographyClient.wrapKey("RSA-OAEP", Buffer.from("My Key"));
console.log("wrap result:", wrapResult.result);

const unwrapResult = await cryptographyClient.unwrapKey("RSA-OAEP", wrapResult.result);
console.log("unwrap result: ", unwrapResult.result);

Устранение неполадок

Дополнительные сведения о диагностике различных сценариев сбоя см. в руководстве по устранению неполадок .

Включение ведения журнала может помочь выявить полезные сведения о сбоях. Чтобы просмотреть журнал HTTP-запросов и ответов, задайте для переменной среды AZURE_LOG_LEVEL значение info. В альтернативном порядке, логирование можно включить во время выполнения, вызвав setLogLevel в @azure/logger:

import { setLogLevel } from "@azure/logger";

setLogLevel("info");

Дальнейшие шаги

Дополнительные примеры кода можно найти по следующим ссылкам:

Вклад

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