简体   繁体   中英

AES/CTR/NoPadding last block is lost when sending encrypted data between PC and Android

I'm using AES/CTR/NoPadding algorithm to encrypt data sent using socket between PC and Android.

I wrote unit-test, it sends [1;512] bytes to Android device and receive back the same data - echo service. Received data must be equal to data that was sent.

Test client:

for (int n = 1; n <= 512; n++) {
... skip ...
    try {
        Object connection = socketFilter.openConnection(socket);
        in = new CipherInputStream(socket.getInputStream(), encryptor);
        out = new CipherOutputStream(socket.getOutputStream(), decryptor);

        byte buf[] = new byte[n];
        byte received[] = new byte[n];

        TestUtils.numbers(buf);

        out.write(buf, 0, buf.length);
        socket.shutdownOutput();

        int len = in.read(received, 0, received.length);

        if (buf.length != len) {
            System.err.println("Expected: " + buf.length + " but was: " + len);
        }
    }
    finally {
        ... skip close streams ... 
    }

}

Echo server:

Socket clientSocket = socket.accept();
CipherInputStream in = new CipherInputStream(clientSocket.getInputStream(), decryptor);
CipherOutputStream out = new CipherOutputStream(clientSocket.getOutputStream(), encryptor);

try {
    byte buf[] = new byte[512];
    int len;

    if ((len = in.read(buf)) > 0) {
        out.write(buf, 0, len);
        out.close();
    }
}
finally {
    in.close();
    out.close();
}

I tested this code with localhost - all works fine.

When i testing it with Android device, the last block is lost if it's not full. So, if it was 30 bytes, then only 16 bytes received.

Messages from the test:

... skip ...
Expected: 30 but was: 16
Expected: 31 but was: 16
Expected: 33 but was: 32
... skip ...
Expected: 207 but was: 192
Expected: 209 but was: 208
Expected: 210 but was: 208
... skip ...

What can be wrong?

It seems that the problem is caused by the fact that Android and Hotspot JVM uses different Cipher Provider.

Android uses one called Bouncy Castle which has a known 'bug' in AES/CTR mode. It will miss the last block when doing encryption/decryption. (see many other stackoverflow questions)

If you want only CTR mode. Known work around is that you implement it yourself on Android by generating blocks of keystream repeatedly "on the fly"(by encrypt byte array of 0's), and XOR them with your buffer.

Hope this helps

Have you completely flushed the encryption streams before you close them? AES processes data in block sized chunks, in CTR mode it is the keystream. If you don't fully flush the stream before closing it, on either encryption or decryption, you will probably lose the last block.

Similarly, you need to be sure that you have written/read everything from the transfer file streams between Android and PC. Your last pieces of data may have been sitting in a file transfer buffer somewhere waiting to be written when the buffer was closed.

Android does differ from Java, so I suspect that your error is probably on the Android side. Perhaps try Android -> Android as well as PC -> PC, just to make sure that everything on the Android side is fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM