Как получить ключи (HKDF) из двух пар ключей EC в Dart/Flutter?
В настоящее время я внедряю ISO 18013-5 (мобильное водительское удостоверение) с использованием Dart и Flutter (извините, не могу поделиться полным документом спецификации). Вкратце, для предотвращения подслушивания во время обмена данными между считывателем (например, полицией) и гражданином, два эфемерных ключа (EDeviceKey
иEReaderKey
) генерируются (с использованием ECDH), по одному в каждом устройстве. После этого происходит обмен открытыми ключами для создания одного сеансового ключа на устройство (skDevice
иskReader
). Эти ключи получены с использованием ECKA-DH (как определено в BSI TR-03111).
Чтобы объяснить мою борьбу, давайте сосредоточимся на одном приложении, гражданине: чтобы получить сеансовый ключ, мне нужно использовать HKDF с закрытым ключом гражданина и открытым ключом читателя. Для этого у меня есть следующее:
Future<SimpleKeyPair> generateEDeviceKeyPair() async {
final algorithm = Ecdh.p256(length: 256);
final keyPair = await algorithm.newKeyPair();
return keyPair;
}
// The public key will be generated on the reader application, using this method
Future<EcKeyPair> generateEReaderKeyPair() async {
final algorithm = Ecdh.p256(length: 256);
final keyPair = await algorithm.newKeyPair();
return keyPair;
}
Future<SecretKey> generateSKDevice(EcKeyPairData eDeviceKeyPriv, EcKeyPublic eReaderKeyPub) async {
final zAB = null;
final algorithm = Hkdf(
hmac: Hmac(Sha256()),
outputLength: 32,
);
// HOW TO "MERGE" BOTH KEYS?
final secretKey = eDeviceKeyPriv + eReaderKeyPub // ??
final output = await algorithm.deriveKey(secretKey: secretKey, info: utf8.encode("SKDevice"));
return output;
}
Затем, чтобы использовать сеансовый ключ для шифрования данных:
Future<List<int>> encryptMDocResponse(Object mdocResponse, SecretKey skDevice) async {
final algorithm = AesGcm.with256bits();
// Encrypt
final secretBox = await algorithm.encrypt(
utf8.encode(json.encode(mdocResponse)),
secretKey: skDevice
);
return secretBox.cipherText;
}
Проблема заключается вgenerateSKDevice
метод. Он должен получить закрытый ключ от гражданина (eDeviceKeyPriv
) и открытый ключ от считывателя (eReaderKeyPub
). Мой вопрос: как объединить оба ключа, чтобы получить один, используяderiveKey
метод (криптобиблиотека: https://pub.dev/packages/cryptography)? Делая параллель с Javascript, я мог бы использоватьcrypto.subtle.deriveKey
метод.
Поскольку я впервые работаю с криптографией, я надеюсь, что это не пустой вопрос.
Заранее спасибо!
РЕДАКТИРОВАТЬ:
import 'package:cryptography/cryptography.dart';
import 'dart:convert';
Object getMDoc() {
return {
"test": "test",
"test2": {"a": "b"},
"test3": 3
};
}
Future<SimpleKeyPair> generateEDeviceKeyPair() async {
final algorithm = X25519();
final keyPair = await algorithm.newKeyPair();
return keyPair;
}
// Changed algorithm to X25519
Future<SimpleKeyPair> generateEReaderKeyPair() async {
final algorithm = X25519();
final keyPair = await algorithm.newKeyPair();
return keyPair;
}
// Generated a sharedSecretKey to derive the session key
Future<SecretKey> generateSKDevice(
SimpleKeyPairData eDeviceKeyPriv, SimplePublicKey eReaderKeyPub) async {
final algorithm = X25519();
final sharedSecretKey = await algorithm.sharedSecretKey(
keyPair: eDeviceKeyPriv, remotePublicKey: eReaderKeyPub);
final deriveAlgorithm = Hkdf(
hmac: Hmac(Sha256()),
outputLength: 32,
);
final skDevice = await deriveAlgorithm.deriveKey(
secretKey: sharedSecretKey,
info: utf8.encode("SKDevice"),
nonce: <int>[1, 2]);
return skDevice;
}
Future<SecretKey> generateSKReader(
SimpleKeyPairData eReaderKeyPriv, SimplePublicKey eDeviceKeyPub) async {
// final algorithm = Ecdh.p256(length: 256);
final algorithm = X25519();
final sharedSecret = await algorithm.sharedSecretKey(
keyPair: eReaderKeyPriv, remotePublicKey: eDeviceKeyPub);
final deriveAlgorithm = Hkdf(
hmac: Hmac(Sha256()),
outputLength: 32,
);
final output = await deriveAlgorithm.deriveKey(
secretKey: sharedSecret,
info: utf8.encode("SKReader"),
nonce: <int>[1, 2]);
return output;
}
Future<SecretBox> encryptMDocResponse(
Object mdocResponse, SecretKey skDevice) async {
final algorithm = AesGcm.with256bits();
final secretBox = await algorithm.encrypt(
utf8.encode(json.encode(mdocResponse)),
secretKey: skDevice,
);
return secretBox;
}
Future<List<int>> decryptMDocResponse(
SecretBox mdocResponse, SecretKey skReader) async {
final algorithm = AesGcm.with256bits();
final chipherText = await algorithm.decrypt(
mdocResponse,
secretKey: skReader,
);
return chipherText;
}
// Example of use
Future<void> main(List<String> arguments) async {
// Holder: get mdoc
final mdoc = getMDoc();
// Holder & Reader: generate Ephemeral Keys
final eDeviceKey = await generateEDeviceKeyPair();
final eReaderKey = await generateEReaderKeyPair();
// Holder: generate Session Key (skDevice)
final skDevice = await generateSKDevice(
await eDeviceKey.extract(), await eReaderKey.extractPublicKey());
// Holder: encrypt device response
final response = await encryptMDocResponse(mdoc, skDevice);
// Reader: generate Session Key (skDevice)
final skReader = await generateSKReader(
await eReaderKey.extract(), await eDeviceKey.extractPublicKey());
// Reader: decrypt device response
final decrypted = await decryptMDocResponse(response, skReader); // Error here
}
1 ответ
Как обсуждалось с user9014097 в комментариях:
- Во-первых: пришлось изменить алгоритм на
X25519
для поддержки Flutter на мобильных устройствах; - Второе: чтобы получить сеансовый ключ, мне пришлось создать
sharedSecret
:
final sharedSecret = await algorithm.sharedSecretKey(keyPair: eReaderKeyPriv, remotePublicKey: eDeviceKeyPub);
- Третье: используйте то же самое
info
параметр в приложениях
(Остальной код доступен в разделе EDITed исходного сообщения)