---
title: "Определение секретного хранилища Web3"
description: "Формальное определение секретного хранилища Web3"
lang: ru
sidebarDepth: 2
---

Чтобы ваше приложение работало в Эфириуме, вы можете использовать объект web3, предоставляемый библиотекой Web3.js. Внутри он взаимодействует с локальным узлом через RPC-вызовы. [web3](https://github.com/ethereum/web3.js/) работает с любым узлом Эфириума, который предоставляет слой RPC.

`web3` содержит объект `eth` — web3.eth.

```js
var fs = require("fs")
var recognizer = require("ethereum-keyfile-recognizer")

fs.readFile("keyfile.json", (err, data) => {
  var json = JSON.parse(data)
  var result = recognizer(json)
})

/** результат
 *               [ 'web3', 3 ]   файл ключей Web3 (v3)
 *  [ 'ethersale', undefined ]   файл ключей Ethersale
 *                        null     недействительный файл ключей
 */
```

В этом документе описывается **версия 3** определения секретного хранилища Web3.

## Определение {#definition}

Фактическое кодирование и декодирование файла остается в значительной степени неизменным по сравнению с версией 1, за исключением того, что криптографический алгоритм больше не привязан жестко к AES-128-CBC (теперь минимальным требованием является AES-128-CTR). Большинство значений и алгоритмов аналогичны версии 1, за исключением `mac`, который задается как SHA3 (Keccak-256) от конкатенации вторых слева 16 байтов производного ключа вместе с полным `ciphertext`.

Файлы секретных ключей хранятся непосредственно в `~/.web3/keystore` (для Unix-подобных систем) и `~/AppData/Web3/keystore` (для Windows). Они могут называться как угодно, но хорошим соглашением является `<uuid>.json`, где `<uuid>` — это 128-битный UUID, присвоенный секретному ключу (прокси-идентификатор, сохраняющий приватность адреса секретного ключа).

Все такие файлы имеют связанный с ними пароль. Чтобы получить секретный ключ для данного файла `.json`, сначала нужно получить ключ шифрования файла; это делается путем взятия пароля файла и его пропускания через функцию формирования ключа (KDF), как описано в ключе `kdf`. Зависящие от KDF статические и динамические параметры для функции KDF описаны в ключе `kdfparams`.

PBKDF2 должен поддерживаться всеми минимально совместимыми реализациями, что обозначается как:

- `kdf`: `pbkdf2`

Для PBKDF2 параметры kdfparams включают:

- `prf`: должно быть `hmac-sha256` (может быть расширено в будущем);
- `c`: количество итераций;
- `salt`: соль, передаваемая в PBKDF;
- `dklen`: длина производного ключа. Должна быть >= 32.

После того как ключ файла был получен, его следует проверить путем вычисления MAC. MAC должен вычисляться как хеш SHA3 (Keccak-256) от массива байтов, образованного конкатенацией вторых слева 16 байтов производного ключа с содержимым ключа `ciphertext`, то есть:

```js
KECCAK(DK[16..31] ++ <ciphertext>)
```

(где `++` — оператор конкатенации)

Это значение следует сравнить с содержимым ключа `mac`; если они различаются, следует запросить другой пароль (или отменить операцию).

После проверки ключа файла зашифрованный текст (ключ `ciphertext` в файле) может быть расшифрован с использованием алгоритма симметричного шифрования, указанного в ключе `cipher` и параметризованного через ключ `cipherparams`. Если размер производного ключа и размер ключа алгоритма не совпадают, в качестве ключа для алгоритма следует использовать дополненные нулями крайние правые байты производного ключа.

Все минимально совместимые реализации должны поддерживать алгоритм AES-128-CTR, что обозначается как:

- `cipher: aes-128-ctr`

Этот шифр принимает следующие параметры, заданные в качестве ключей для ключа cipherparams:

- `iv`: 128-битный вектор инициализации для шифра.

Ключом для шифра являются крайние левые 16 байтов производного ключа, то есть `DK[0..15]`

Создание/шифрование секретного ключа должно быть, по сути, обратным этим инструкциям. Убедитесь, что `uuid`, `salt` и `iv` действительно случайны.

В дополнение к полю `version`, которое должно действовать как «жесткий» идентификатор версии, реализации также могут использовать `minorversion` для отслеживания небольших, не нарушающих совместимость изменений в формате.

## Тестовые векторы {#test-vectors}

Детали:

- `Address`: `008aeeda4d805471df9b2a5b0f38a0c3bcba786b`
- `ICAP`: `XE542A5PZHH8PYIZUBEJEO0MFWRAPPIL67`
- `UUID`: `3198bc9c-6672-5ab3-d9954942343ae5b6`
- `Password`: `testpassword`
- `Secret`: `7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d`

### PBKDF2-SHA-256 {#pbkdf2-sha-256}

Тестовый вектор с использованием `AES-128-CTR` и `PBKDF2-SHA-256`:

Содержимое файла `~/.web3/keystore/3198bc9c-6672-5ab3-d9954942343ae5b6.json`:

```json
{
  "crypto": {
    "cipher": "aes-128-ctr",
    "cipherparams": {
      "iv": "6087dab2f9fdbbfaddc31a909735c1e6"
    },
    "ciphertext": "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
    "kdf": "pbkdf2",
    "kdfparams": {
      "c": 262144,
      "dklen": 32,
      "prf": "hmac-sha256",
      "salt": "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
    },
    "mac": "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"
  },
  "id": "3198bc9c-6672-5ab3-d995-4942343ae5b6",
  "version": 3
}
```

**Промежуточные значения**:

`Derived key`: `f06d69cdc7da0faffb1008270bca38f5e31891a3a773950e6d0fea48a7188551`
`MAC Body`: `e31891a3a773950e6d0fea48a71885515318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46`
`MAC`: `517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2`
`Cipher key`: `f06d69cdc7da0faffb1008270bca38f5`

### Scrypt {#scrypt}

Тестовый вектор с использованием AES-128-CTR и Scrypt:

```json
{
  "crypto": {
    "cipher": "aes-128-ctr",
    "cipherparams": {
      "iv": "740770fce12ce862af21264dab25f1da"
    },
    "ciphertext": "dd8a1132cf57db67c038c6763afe2cbe6ea1949a86abc5843f8ca656ebbb1ea2",
    "kdf": "scrypt",
    "kdfparams": {
      "dklen": 32,
      "n": 262144,
      "p": 1,
      "r": 8,
      "salt": "25710c2ccd7c610b24d068af83b959b7a0e5f40641f0c82daeb1345766191034"
    },
    "mac": "337aeb86505d2d0bb620effe57f18381377d67d76dac1090626aa5cd20886a7c"
  },
  "id": "3198bc9c-6672-5ab3-d995-4942343ae5b6",
  "version": 3
}
```

**Промежуточные значения**:

`Derived key`: `7446f59ecc301d2d79bc3302650d8a5cedc185ccbb4bf3ca1ebd2c163eaa6c2d`
`MAC Body`: `edc185ccbb4bf3ca1ebd2c163eaa6c2ddd8a1132cf57db67c038c6763afe2cbe6ea1949a86abc5843f8ca656ebbb1ea2`
`MAC`: `337aeb86505d2d0bb620effe57f18381377d67d76dac1090626aa5cd20886a7c`
`Cipher key`: `7446f59ecc301d2d79bc3302650d8a5c`

## Изменения по сравнению с версией 1 {#alterations-from-v2}

В этой версии исправлено несколько несоответствий с версией 1, опубликованной [здесь](https://github.com/ethereum/homestead-guide/blob/master/old-docs-for-reference/go-ethereum-wiki.rst/Passphrase-protected-key-store-spec.rst). Вкратце это:

- Использование заглавных букв необоснованно и непоследовательно (scrypt в нижнем регистре, Kdf в смешанном, MAC в верхнем).
- Адрес не нужен и ставит под угрозу приватность.
- `Salt` по своей сути является параметром функции формирования ключа и должен быть связан с ней, а не с криптографией в целом.
- _SaltLen_ не нужен (просто выводится из Salt).
- Функция формирования ключа задана, однако криптографический алгоритм жестко определен.
- `Version` по своей сути является числовым значением, но представлен в виде строки (структурированное управление версиями было бы возможно со строкой, но это можно считать выходящим за рамки редко меняющегося формата конфигурационного файла).
- `KDF` и `cipher` условно являются родственными концепциями, но организованы по-разному.
- `MAC` вычисляется через фрагмент данных, не зависящий от пробелов(!)

В формат были внесены изменения, чтобы получить следующий файл, функционально эквивалентный примеру, приведенному на странице по ссылке выше:

```json
{
  "crypto": {
    "cipher": "aes-128-cbc",
    "ciphertext": "07533e172414bfa50e99dba4a0ce603f654ebfa1ff46277c3e0c577fdc87f6bb4e4fe16c5a94ce6ce14cfa069821ef9b",
    "cipherparams": {
      "iv": "16d67ba0ce5a339ff2f07951253e6ba8"
    },
    "kdf": "scrypt",
    "kdfparams": {
      "dklen": 32,
      "n": 262144,
      "p": 1,
      "r": 8,
      "salt": "06870e5e6a24e183a5c807bd1c43afd86d573f7db303ff4853d135cd0fd3fe91"
    },
    "mac": "8ccded24da2e99a11d48cda146f9cc8213eb423e2ea0d8427f41c3be414424dd",
    "version": 1
  },
  "id": "0498f19a-59db-4d54-ac95-33901b4f1870",
  "version": 2
}
```

## Изменения по сравнению с версией 2 {#alterations-from-v2-2}

Версия 2 была ранней реализацией на C++ с рядом ошибок. Все основные элементы остались в ней без изменений.