Почему библиотека 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());

Надеюсь, это поможет, Диего

Другие вопросы по тегам