5.6 KiB
Исправление расшифровки вложений (Desktop → Android)
Дата
27 января 2026
Проблема
Входящие фотографии от Desktop на Android не расшифровывались. При попытке открыть изображение возникала ошибка:
javax.crypto.BadPaddingException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
Все варианты расшифровки (V1-V6) падали с этой ошибкой, несмотря на то что:
- ChaCha ключ расшифровывался правильно (56 байт)
- UTF-8 конверсия работала корректно
- Данные передавались без повреждений
Причина
crypto-js использует PBKDF2 с SHA256 по умолчанию, а не SHA1!
Детали
-
Desktop (crypto.worker.ts) использует:
const key = crypto.PBKDF2(password, "rosetta", { keySize: 256 / 32, iterations: 1000, });Без явного указания
hasher, crypto-js использует SHA256. -
Android (MessageCrypto.kt) использовал:
val mac = javax.crypto.Mac.getInstance("HmacSHA1") // ❌ SHA1
Тестовое подтверждение
Проверка с простым паролем "test":
crypto-js PBKDF2 (default): 103af158b41bb86784d953ef50b32c54230086946cf9ebb81616b004d78c15db
crypto-js PBKDF2 (SHA1): 1343ea25240fff22b2f3278fd7e20f8b67f2f92d194ae98c71cbdcaf526fe4e4
crypto-js PBKDF2 (SHA256): 103af158b41bb86784d953ef50b32c54230086946cf9ebb81616b004d78c15db
Node.js PBKDF2 (SHA1): 1343ea25240fff22b2f3278fd7e20f8b67f2f92d194ae98c71cbdcaf526fe4e4
Node.js PBKDF2 (SHA256): 103af158b41bb86784d953ef50b32c54230086946cf9ebb81616b004d78c15db
Вывод: crypto-js PBKDF2 по умолчанию = SHA256 ≠ SHA1
Решение
Изменены все функции PBKDF2 в Android с SHA1 на SHA256:
1. generatePBKDF2KeyFromBytes()
Было:
// PBKDF2-HMAC-SHA1 ручная реализация
val mac = javax.crypto.Mac.getInstance("HmacSHA1")
val keySpec = javax.crypto.spec.SecretKeySpec(passwordBytes, "HmacSHA1")
Стало:
// PBKDF2-HMAC-SHA256 ручная реализация для совместимости с crypto-js
// ВАЖНО: crypto-js PBKDF2 по умолчанию использует SHA256, НЕ SHA1!
val mac = javax.crypto.Mac.getInstance("HmacSHA256")
val keySpec = javax.crypto.spec.SecretKeySpec(passwordBytes, "HmacSHA256")
2. generatePBKDF2KeyJava()
Было:
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
Стало:
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
3. encryptReplyBlob()
Также исправлена функция шифрования для отправки вложений:
Было:
// CRITICAL: Must use SHA1 to match crypto-js default (not SHA256!)
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
Стало:
// CRITICAL: crypto-js PBKDF2 uses SHA256 by default (NOT SHA1!)
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
Измененные файлы
rosetta-android/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.ktgeneratePBKDF2KeyFromBytes()- изменен с SHA1 на SHA256generatePBKDF2KeyJava()- изменен с SHA1 на SHA256encryptReplyBlob()- изменен с SHA1 на SHA256- Обновлены комментарии для корректного указания алгоритма
Результат
✅ Расшифровка вложений Desktop → Android теперь работает корректно ✅ Шифрование вложений Android → Desktop также будет совместимо ✅ Все варианты (V1-V6) теперь используют правильный PBKDF2-HMAC-SHA256
Технические детали
Алгоритм расшифровки attachment blob
-
Расшифровка ChaCha ключа (56 bytes = 32 key + 24 nonce)
- Через ECDH + AES-256-CBC
- Конверсия UTF-8 bytes → UTF-8 string (с заменой невалидных на U+FFFD)
-
PBKDF2 key derivation
- Password: UTF-8 string из step 1
- Salt: "rosetta"
- Iterations: 1000
- Key size: 256 bits (32 bytes)
- Hash: SHA256 ✅ (было SHA1 ❌)
-
AES-256-CBC расшифровка
- Формат:
ivBase64:ciphertextBase64 - IV: 16 bytes
- Padding: PKCS5/PKCS7
- Формат:
-
Zlib декомпрессия
- Результат: base64 изображение
Проверка
После установки исправленной версии:
- Отправить фото с Desktop на Android
- Открыть диалог на Android
- Нажать на изображение для загрузки
- ✅ Изображение должно успешно расшифроваться и отобразиться
Примечания
- Эта проблема затрагивает только входящие вложения от Desktop
- Android → Android вложения могли работать с разными алгоритмами PBKDF2
- Ошибочный комментарий "crypto-js uses SHA1 by default" был исправлен по всему коду