Привет, Хабр!
В комментариях к нашему первому посту народ требовал
Управление ключами
Одна из проблем реализации end-to-end шифрования заключается в безопасной передаче ключей шифрования получателю.
Наиболее очевидный способ решения проблемы — криптография с открытым ключом. Каждый пользователь системы имеет ключ, состоящий из двух частей: закрытая часть (секретный ключ), который хранится в секрете и никогда не передается по сети; открытая часть (публичный ключ), который должен быть доступен для всех участников системы. Секретный ключ принято использовать для подписания и расшифровки, в то время как с помощью открытого ключа производятся операции шифрования и проверки подписи.
Исходя из определения секретного и публичного ключа следует, что абсолютно любой участник системы способен, используя публичный ключ, зашифровать сообщение, но только обладатель секретного ключа способен это сообщение расшифровать.
Первое что следует принять во внимание при реализации схемы с открытым ключом — это возможность подмены публичного ключа третьей стороной. Злоумышленник, располагающий возможностью модифицировать трафик, способен перехватить публичный ключ пользователя Алиса и подменить его на свой собственный публичный ключ. Это приводит к тому, что пользователь Боб, при попытке отправить зашифраванное сообщение Алисе, на самом деле зашифрует его ключом атакующего, который сможет расшифровать и прочитать секретные данные.
Один из возможных вариантов предотвращения такой ситуации — это возможность однозначно идентифицировать обладателя ключа. В таком случае подмена ключа станет очевидна для Боба. В качестве идентификатора, можно указать e-mail адрес, номер телефона и другую уникальную, но легко распознаваемую информацию. При этом следует учесть, что идентификатор указываемый при создании нового ключа, должен быть проверен и подтвержден. В противном случае мы рискуем получить систему, в которой каждый пользователь сможет выдать себя за кого угодно.
Для проверки имени потребуется сервис проверки личности (identity validation service). Как правило, такие сервисы высылают случайно сгенерированный код на указанный в качестве имени e-mail адрес или номер телефона и ждут подтверждения от пользователя.
Однако использование сервиса проверки личности лишь перекладывает проблему доверия с канала связи на сам сервис. На самом деле ничто не мешает владельцу сервиса создать скомпрометированный ключ, якобы принадлежащий третьей стороне.
Для того, чтобы полностью избавиться от возможности появления скомпрометированных ключей, необходима аутентификация ключей.
Аутентификация ключей – способ убедить Боба, что ключ который он считает принадлежащим Алисе, на самом деле принадлежит Алисе. Существует несколько способов решения проблемы аутентификации ключей. Наиболее очевидный среди них – это обмен ключами при личной встрече или с использованием более надежного канала связи. Несмотря на высокую надежность, этот способ не всегда реализуем в реальном мире. Поэтому используется один из следующих методов аутентификации ключей.
Первый способ – использование доверенных центров сертификации. Когда определенный пользователь (доверенный центр) системы располагает неограниченным доверием и способен выступать в качестве заверителя ключей. Ни один ключ в такой системе не считается надежным без подписи доверенного центра.
Второй способ – использование сети доверия. При этом, каждый пользователь системы в праве подписать ключ любого другого пользователя, подтверждая тем самым, что ключ на самом деле принадлежит указанному владельцу. Чем больше заверяющих подписей под ключом, тем большего доверия он заслуживает.
Таким образом, процедура создания, хранения и передачи ключей имеет ряд подводных камней, которые легко обойти с помощью Virgil Security.
Virgil Security
Virgil Security предоставляет набор бесплатных криптографических сервисов, использование которых помогает решить множество проблем, связанных с безопасностью. В частности, проблему управления ключами.
На сегодняшний день Virgil Security это:
- Virgil crypto library – это оболочка над mbedTLS с открытым исходным кодом, которая позволяет использовать все стандартные алгоритмы шифрования и подписи. А также добавляет гибридные алгоритмы шифрования (ECIES). Virgil crypto library позволяет менять алгоритмы в будущем без першифровки существующих данных — принцип crypto agility.
- Virgil identity service – сервис проверки личности с открытым API, позволяющий проверить валидность e-mail адреса. Сервис высылает на указанный e-mail секретный код, который должен быть введен пользователем для подтверждения факта владения адресом.
- Virgil keys service – сервис публичных ключей, позволяющий работать с надежным и доступным хранилищем публичных ключей. Каждый ключ в хранилище привязан к специальной структуре данных, называемой VirgilCard. VirgilCard обязательно включает в себя: уникальный идентификатор карты — id, идентификатор владельца ключа — e-mail адрес, номер телефона и т.д., а также дату создания ключа. С помощью Virgil keys service можно быстро находить публичные ключи пользователей системы, а также удалять карты со скомпрометированными ключами.
- Virgil private keys service — сервис секретных ключей, отвечающий за загрузку, скачивание и удаление секретных ключей. Для доступа к секретному ключу, необходимо подтвердить владение Virgil картой, к которой он привязан.
Все вышеперечисленные сервисы имеют широкий выбор готовых SDK и могут использоваться для решения самого широкого спектра задач, связанных с информационной безопасностью.
Virgil services и end-to-end шифрование
Ну вот теперь пришло время немного покодить. Покажем как с помощью Virgil Security осуществить надежное end-to-end шифрование. Для этого реализуем следующую схему:
Обратите внимание, что при шифровании используется ECIES схема. Это означает, что сообщение шифруется симметричным алгоритмом AES с помощью эфемерного симметричного ключа, восстановить который возможно только обладая приватным ключом получателя. Шифрование же производится с использованием публичного ключа получателя.
Для работы с сервисами Virgil Security нам нужно получить секретный токен и пара публичный/закрытый ключ. Для этого регистрируемся на сайте Virgil Security. После этого приступаем к реализации e2ee.
- Скачиваем и устанавливаем VirgilSDK используя NPM:
npm install virgil-sdk
- Инициализируем работу с сервисами Virgil Security:
var virgil = new VirgilSDK("%ACCESS_TOKEN%");
- Генерируем пару ключей:
var keyPair = virgil.crypto.generateKeyPair(); console.log(keyPair.publicKey); console.log(keyPair.privateKey);
И получаем вот такой результат:-----BEGIN PUBLIC KEY-----
MFswFQYHKoZIzj0CAQYKKwYBBAGXVQEFAQNCAAQO8ohmBRyclmcfQ38Lwmvv4Cau
jyX6vWn8kJrR0RRfFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-----END PUBLIC KEY-----
-----BEGIN EC PRIVATE KEY-----
MHkCAQEEIFB+lOUvbb4WX+e3zLkAcYpvZR3qpQI8Ru/tcnciCMkIoAwGCisGAQQB
l1UBBQGhRANCAAQO8ohmBRyclmcfQ38Lwmvv4CaujyX6vWn8kJrR0RRfFQAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-----END EC PRIVATE KEY-----
- Публикуем открытый ключ. Для этого необходим validation token, подтверждающий личность владельца ключа. Получить validation token можно двумя способами: первый воспользоваться сервисом Virgil Identity, который проверит действительно ли владелец ключа имеет доступ к узаканному email адресу и сгенерирует validation token. Либо можно воспользоваться приватным ключом приложения и с его помощью сгенерировать validation token самостоятельно. В таком случае разработчик избегает необходимость доверять стороннему сервису идентификации. Воспользуемся полученным validation token и сохраним публичный ключ на сервере:
var options = { public_key: keyPair.publicKey, private_key: keyPair.privateKey, identity: { type: 'member', value: 'Darth Vader', validation_token: '%VALIDATION_TOKEN%' } }; virgil.cards.create(options).then(function (card){ myCard = card; console.log(card); });
В результате на сервере публичных ключей будет создана карта следующего вида:{ "id":"3e5a5d8b-e0b9-4be6-aa6b-66e3374c05b3", "authorized_by":"com.virgilsecurity.twilio-ip-messaging-demo", "hash":"QiWtZjZyIQhqZK7+3nZmIEWFBU+qI64EzSuqBcY+E7ZtKPwd4ZyU6gdfU/VzbTn6dHtfahCzHasN...", "data":null, "created_at":"2016-05-03T14:34:08+0000", "public_key":{ "id":"359abe31-3344-453a-a292-fd98a83e500a", "public_key":"-----BEGIN PUBLIC KEY-----\nMFswFQYHKoZIzj0CAQYKKwYBBAGXVQEFAQNCAAQ...", "created_at":"2016-05-03T14:34:08+0000" }, "identity":{ "id":"965ea277-ab78-442c-93fe-6bf1d70aeb4b", "type":"member", "value":"Darth Vader", "created_at":"2016-05-03T14:34:08+0000" } }
- Создаем канал, по которому будут передаваться зашифрованные сообщения. Сделать это будет не так сложно, как кажется. С недавних пор мы стали партнерами с компанией Twilio, которая в частности предоставляет API для реализации IP мессенджера. Пример готового IP мессенджера со встроенной функцией end-to-end шифрования можно найти тут.
// Create a Channel twilioClient.createChannel({ friendlyName: 'general' }).then(function(channel) { generalChannel = channel; });
- Чтобы отправить секретное сообщение найдем публичные ключи собеседников и воспользуемся им для шифрования
// Receive the list of Channel's recipients Promise.all(generalChannel.getMembers().map(function(member) { // Search for the member’s cards on Virgil Keys service return virgil.cards.search({ value: member.identity, type: 'member' }) .then(function(cards){ return { recipientId: cards[0].id, publicKey: cards[0].public_key.public_key }; }); }).then(function(recipients) { var message = $('#chat-input').val(); var encryptedMessage = virgil.crypto.encryptStringToBase64(message, recipients); generalChannel.sendMessage(encryptedMessage); console.log(encryptedMessage); });
Зашифрованное сообщение выглядит следующим образом:MIIDBQIBADCCAv4GCSqGSIb3DQEHA6CCAu8wggLrAgECMYICvDCCAVoCAQKgJgQkMDg3YjgwYmMtMzNjYi00MTI1LWI4YTgtYTE
3OTEwM2Y3ZjRkMBUGByqGSM49AgEGCisGAQQBl1UBBQEEggEUMIIBEAIBADBbMBUGByqGSM49AgEGCisGAQQBl1UBBQEDQgAEcd
8fhKqYlZxvcmmodg7Z3PNhE1LXLJqobouEcRfZaRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAYBgcogYxxAgUCM
A0GCWCGSAFlAwQCAgUAMEEwDQYJYIZIAWUDBAICBQAEMEaJMAvX7S+52BpI5hYyFOc0noIc+qdFFrQanNAtNGBAX/Pxeg5yJ2iA
JijyZ8ut9zBRMB0GCWCGSAFlAwQBKgQQ81bklcNOyU/QTatCigSzoAQwHnAcbXk0daExIIS+sr6aIvVuF/o6j+1Rs5bvq2WVN41
k/Oir5x7KZTSR7v3nx+fTMIIBWgIBAqAmBCRmNzM4YTUwNi1hMDYwLTQ1MDgtYTJkYS04NjY1NjZlYzg0ODMwFQYHKoZIzj0CAQ
YKKwYBBAGXVQEFAQSCARQwggEQAgEAMFswFQYHKoZIzj0CAQYKKwYBBAGXVQEFAQNCAARJ5C3hsYuI2Sf14k60Dz5Mv5yD/AsVA
zPfsmlreGTC2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBgGByiBjHECBQIwDQYJYIZIAWUDBAICBQAwQTANBglg
hkgBZQMEAgIFAAQwhu7WM1rff9RYsQ+dmfX9Os3Irwm4cm5bIvUlcGXlCfmEsrjTyTg5MGjYLtxbYtL9MFEwHQYJYIZIAWUDBAE
qBBCfKdP/gZnkVwJvv4Hdf2eWBDC3czBjV/yPGeGTqBIilHSsrqwK7lVMTBuKR+mR3eNdh+yBIAcOk4rveSUbDuWagDIwJgYJKo
ZIhvcNAQcBMBkGCWCGSAFlAwQBLgQMfjkCvK3UgXdorcYUmtCHHuSm4yfBacMsniMADAeos7qN7OmNsFU1
- Получатель расшифровывает сообщение, используя секретный ключ:
// Listen for new Messages sent to a Channel generalChannel.on('messageAdded', function(message) { // Decrypt the Message using card id and private key values. var decryptedMessage = virgil.crypto.decryptStringFromBase64( message.body, myCard.id, keyPair.privateKey ); console.log(message.author + ': ' + decryptedMessage); });
И увидит следующее:Darth Vader: Luke. I am your father!
Достаточно просто. Вы пишите всего несколько строк кода и получаете полноценное end-to-end шифрование с возможностью аутентификации ключей. При этом сервисы Virgil Security не имеют доступа к секретным ключам пользователей, что исключает возможность чтения секретных данных.
Комментарии (9)
sseroshtan
31.05.2016 19:01+1А NIST SP 800-90A у вас с дыркой?
Данный алгоритм реализован как часть библиотеки MbedTLS — https://tls.mbed.org/ctr-drbg-source-code
На дынный момент уязвимостей в данном модуле не найдено — https://tls.mbed.org/securityIvan_83
31.05.2016 23:22-1Те используется там Dual_EC_DRBG с параметрами от NIST или нет вы лично не в курсе?
Уязвимость и бэкдор это разные вещи, и почему я должен верить написанному на каком то сайте?
Либу посмотрел.
Dual_EC_DRBG там нет, используют CTR_DRBG и HMAC_DRBG.
Либа выглядит весьма уныло на фоне libressl: реализовано мало алгоритмов, реализации далеки от оптимальных в плане оптимизации по скорости. Уверен что там пачка варнингов при сборке вылезает, если врубить -Wall.dmitrydain
01.06.2016 01:46+1Проверил с -Wall под OSX — warnings нет, но тут надо смотреть gcc version.
LibreSSL ето fork OpenSSL кстати — там размеры в разы больше как раз из-за реализации устаревших алгоритмов. К примеру: des(3), idea, rc2, rc4(3) + все кривости ASN.1 OpenSSL есть, хотя их чинят.
Скорость сравнить тяжело — в LibreSSL нет сделанного ECIES. Сравнивать AES — он у всех на аппаратном уровне. mbed TLS очень удобен, значительно более чем OpenSSL и LibreSSL как библиотека реализующая «crypto primitives».Ivan_83
01.06.2016 02:08Я как минимум видел не используемую метку в одной из функций, и это при беглом осмотре-листании.
Сравнивайте хэши, программный AES.
Элиптика очень сильно просаженная по скорости в этой либе, тут даже тестов не надо.
Из того в чём я понимаю:
— md5, sha1, sha2 — около референсные реализации. Забыл посмотреть работу с буфером в update от sha, в референсе она по одному байту копировала во временный буфер.
— ECDSA: работа с большими цифрами так себе, многие вещи можно было сделать быстрее в разы, хотя бы подсчёт битов, да и сдвиги. Умножение точки на скаляр вроде тоже самое примитивное из рекомендаций ниста/банкиров, никаких достижений математиков за последние 10+ лет в этой области там не увидел.
— AES там две реализации, обычная и AES-NI. С первой — подозреваю что тоже тормозной референс. AES-NI у меня дома только в одном проце есть, остальные коредуо, дуалкоры, атом и закат — там нет AES-NI, сомневаюсь что у других пользователей оно повсеместно есть. Поскольку это позиционируется как решение для не только х86 платформ, то там то точно AES-NI нет.
Зато в LibreSSL есть ещё и ГОСТовские алгоритмы, да и прочих других тоже по больше.
За удобство API ничего не скажу, но вроде у LibreSSL есть более новое относительно OpenSSL, а API OpenSSL оставлено для совместимости.
sseroshtan
01.06.2016 11:29реализации далеки от оптимальных в плане оптимизации по скорости
Если скорость не устраивает, рекомендую использовать fork от VirgilSecurity и кривую Curve25519, при этом для вычислений используется оптимизированный вариант на базе Ed25519
Уверен что там пачка варнингов при сборке вылезает, если врубить -Wall
Это голословное заявление :) MbedTLS собирается на Travis-CI с флагами
-Wall -Werror
Ivan_83
01.06.2016 13:22У меня есть собственная реализация ECDSA+ГОСТ, я уж лучше ей воспользуюсь, или LibreSSL освою.
sseroshtan
01.06.2016 17:06
Ivan_83
А NIST SP 800-90A (RNG) у вас в либе с дыркой?
https://en.wikipedia.org/wiki/NIST_SP_800-90A