E2EE между клиентом и сервером с помощью Nodejs и Webcrypto API браузера через ECDH P-256
End2End encryption IOException: ошибка синтаксического анализа algid, а не последовательность
Я создал проект и написал эти коды на Backend-стороне:
export function generateDHKeys(foreignPublicKeyB64) {
try {
const ecdh = crypto.createECDH('prime256v1');
ecdh.generateKeys();
const publicKeyB64 = ecdh.getPublicKey('base64');
const privateKeyB64 = ecdh.getPrivateKey('base64');
const webcryptoRawB64 = Buffer.from(foreignPublicKeyB64, 'base64')
.slice(-65)
.toString('base64'); // the last 65 bytes
const sharedSecretB64 = ecdh.computeSecret(
foreignPublicKeyB64,
'base64',
'base64'
);
const sharedSecretHashB64 = crypto
.createHash('sha256')
.update(sharedSecretB64, 'base64')
.digest('base64');
return {
publicKeyB64,
privateKeyB64,
sharedSecretB64,
sharedSecretHashB64,
};
} catch (e) {
throw {
error: { code: e.code, message: e.message },
};
}
}
И я шифрую все сообщения, как показано ниже:
export function encrypt(privateKey: string, message: string) {
const cipher = crypto.createCipheriv(
'aes-256-gcm',
Buffer.from(privateKey, 'base64'),
getRandomIV()
);
let encrypted = cipher.update(message, 'utf8', 'base64');
encrypted += cipher.final('base64');
return Buffer.from(encrypted, 'base64').toString('base64');
}
export function getRandomIV() {
return crypto.randomBytes(12);
}
Пример шифрования:
// I have `sharedSecretHashB64` by the User's deviceId when they received a publicKey from the Server by their `publicKey` before.
const { sharedSecretHashB64 } = JSON.parse(this.db.get(deviceId));
return encrypt(sharedSecretHashB64, JSON.stringify([name: "Test Name", family: "Test Family"]));
На стороне клиента: 1.
export const generateKeyPair = async (): Promise<CryptoKeyPair> => {
return window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-256",
},
true,
["deriveKey", "deriveBits"],
);
};
export const convertKeyToAsn1 = async (key: CryptoKey): Promise<string> => {
const DER = await window.crypto.subtle.exportKey("spki", key);
return arrayBufferToBase64(DER);
};
Использование:
const keyPair: CryptoKeyPair = await generateKeyPair();
const clientPublicKey: string = await convertKeyToAsn1(keyPair.publicKey!);
Отправьте publicKey серверу, что клиентский PublicKey выглядит примерно так:
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZQeF2h28zD5Zdo226gJmDbchD9fa6hNUAvhwcO9ogyAKbsCMcWXGX26+wWE+f5oNoY5LGhkBeC/1SzQLRId0Tw==
И publicKey сервера в ответ:
BGDiM1k24nlz7FuEfOXpQoD6bAdFi52C6chxDxN6C5GG8Puo9becrNq/hICnu1JY7tj3uLUIr5Gr+TpFAx7hNNI=
Теперь я создаю свой собственный ключ AES:
export async function importServerPublicKey(publicKey: string) {
const publicKeyInUint8Array = new Uint8Array(base64ToArrayBuffer(publicKey));
return window.crypto.subtle.importKey(
'raw',
publicKeyInUint8Array,
{
name: 'ECDH',
namedCurve,
},
true,
[]
);
}
export async function ECDHDeriveKey(
serverPublicKey: CryptoKey,
privateKey: CryptoKey
) {
const sharedSecret = await window.crypto.subtle.deriveBits(
{
name: 'ECDH',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
namedCurve,
public: serverPublicKey,
},
privateKey,
256
);
const aesSecret = await window.crypto.subtle.digest('SHA-256', sharedSecret);
return window.crypto.subtle.importKey('raw', aesSecret, 'AES-GCM', true, [
'encrypt',
'decrypt',
]);
}
export async function getAESKey(
serverPublicKey: string,
privateKey: CryptoKey
) {
const convertServerPublicKeyToCryptoKey = await importServerPublicKey(
serverPublicKey
);
return ECDHDeriveKey(convertServerPublicKeyToCryptoKey, privateKey);
}
Моя первая расшифровка:
const aesKey = await getAESKey(
serverResponse.data.publicKey,
keyPair.privateKey!
);
const decrypted = await decrypt(aesKey, serverResponse);
Я получил
DOMException
ошибка здесь, и я не знаю почему?
Заранее спасибо за помощь, я очень это ценю.