Почему библиотека JNCryptor/RNCryptor генерирует исключение InvalidHMACException?
Я хочу использовать симметричное шифрование AES256 для приложения iPhone и сокета Java-сервера. В настоящее время я использую библиотеку Роба Напира RNCryptor/JNCryptor. Шифрование на iPhone, кажется, работает хорошо, так как я могу снова расшифровать зашифрованные строки. Но как только я пытаюсь расшифровать строку на своем сокете Java-сервера, выдается следующее исключение:
com.acme.crypto.InvalidHMACException: Incorrect HMAC value.
at com.acme.crypto.AES256JNCryptor.decryptV3Data(AES256JNCryptor.java:248)
at com.acme.crypto.AES256JNCryptor.decryptV3Data(AES256JNCryptor.java:323)
at com.acme.crypto.AES256JNCryptor.decryptData(AES256JNCryptor.java:280)
com.acme.crypto.CryptorException: Unrecognised version number: 61.
at com.acme.crypto.AES256JNCryptor.decryptData(AES256JNCryptor.java:283)
Вот соответствующий фрагмент кода клиента для отправки зашифрованных данных (iOS/Objective-C):
// add line break and send message to server
NSString* message = [NSString stringWithFormat:@"%@\n", output];
NSData* data = [[NSData alloc] initWithData:[message dataUsingEncoding:NSUTF8StringEncoding
allowLossyConversion:NO]];
// encrypt outgoing data with AES-256
NSError *error1;
NSData *cypher = [RNEncryptor encryptData:data
withSettings:kRNCryptorAES256Settings
password:@"mypassword"
error:&error1];
// write encrypted data to output stream
if (error1==nil) {
NSInteger result = [outputStream write:[cypher bytes] maxLength:[cypher length]];
} else {
NSLog(@"Encryption of outgoing data failed: %@", error1);
}
А вот соответствующий код сервера, который получает зашифрованные данные на свой сокет (Linux/Java):
// initializing cryptor object during object construction
JNCryptor cryptor = new AES256JNCryptor();
// setting up the input stream on the server socket
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
// this is within the client thread ...
String line;
while((line=input.readLine())!=null) {
try {
// ... the exception is thrown at the following line ...
byte[] decrypted = cryptor.decryptData(line.getBytes(), password.toCharArray());
line = new String(decrypted, StandardCharsets.UTF_8);
// message handling ...
} catch (Exception ex) {
// print exception ...
}
}
Кто-нибудь знает, что я делаю не так? Нужно ли использовать Base64 или аналог для кодирования перед отправкой данных? Любая помощь высоко ценится.
РЕДАКТИРОВАТЬ: Вот решение. Вместо того, чтобы использовать символьный поток ввода, я теперь использую InputStream из сокета напрямую, чтобы прочитать необработанные байты для подачи алгоритма дешифрования:
@Override
public void run() {
try {
int bytes;
byte[] buffer = new byte[4096];
while((bytes=input.read(buffer))!=-1) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(buffer, 0, bytes);
byte[] decrypted = cryptor.decryptData(baos.toByteArray(), password.toCharArray());
String line = new String(decrypted, StandardCharsets.UTF_8);
// handle input ...
} catch (Exception ex) {
// handle exception ...
}
}
} catch (Exception ex) {
// handle exception ...
}
}
2 ответа
InvalidHMACException
означает, что либо ваш пароль неверен, либо ваши данные повреждены.
(Моя Java немного слабовата, но я вполне уверен, что я правильно понял документацию здесь.)
Я подозреваю, что ваша проблема в том, что вы используете InputStreamReader
, Это соединяет потоки байтов с символьными потоками (в вашем случае поток UTF-8). Зашифрованный текст - это абсолютно случайные байты, которые в большинстве случаев будут недопустимыми символами UTF-8 (не просто "бред", а фактически недопустимы и не могут быть декодированы). Я бы, конечно, ожидал коррупцию во время туда и обратно от байтов->utf8-> байтов.
Вам не нужно использовать InputStreamReader
здесь вообще. Просто избавься от этого.
У меня была та же проблема, моя версия была 65 и JNCryptor ожидает 2 или 3.
Попробуйте не использовать baos.toByteArray().
byte[] decrypted = cryptor.decryptData(baos.toByteArray(), password.toCharArray());
Вместо этого используйте:
byte[] decrypted = cryptor.decryptData(Base64.decodeBase64(baos), password.toCharArray());
Надеюсь, это поможет, Диего