0

So for an Android project I need to write to and read from a NFC tag. My NFCReader code works just fine when I try to write and read plain text records. However, to make the system secure, I'm trying to seal my TagProfile object in a SealedObject with a Cipher, and then serialize that SealedObject to write it into the NFC tag. To read the tag, I'm trying to deserialize the SealedObject and then get the TagProfile object from it.

The write code seems to work just fine. However, when I try to read the tag, I get the following error: java.io.StreamCorruptedException: invalid stream header: 02656EAC

I'm wondering if I'm either writing or reading the data wrong. I think it has to do with the serialization more than with the encryption. I've looked for this error but I can't find an example with the same header that's being thrown. I found this but I'm using ByteArrayStreams and ObjectStreams for both input and output. In any case, here are my èncryptProfile() and decryptProfile() methods, which also perform the serialization. The former is called when creating the NdefRecord to be written, and the latter when building a TagProfile from the NdefRecord payload.

    private byte[] encryptProfile(TagProfile tagProfile) throws InvalidKeyException, IOException, IllegalBlockSizeException {

        cipher.init(Cipher.ENCRYPT_MODE, key);
        SealedObject sealedProfile = new SealedObject(tagProfile, cipher);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(sealedProfile);
        oos.flush();
        return bos.toByteArray();
    }

...

    private TagProfile decryptProfile(byte[] bytes) throws IOException, ClassNotFoundException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {

        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        SealedObject sealedObject = (SealedObject) ois.readObject();
        cipher.init(Cipher.DECRYPT_MODE, key);
        return (TagProfile) sealedObject.getObject(cipher);
    }

The error is thrown when I instance and initialize the ObjectInputStream ois at decryptProfile(). The Cipher and Key objects are instanced globally.

EDIT: I'm adding the code I use to write the NdefMessage to the tag and to build the TagProfile from it. Worked fine using plain texts (albeit with a slightly different implementation).

// ---------- WRITE

    public void writeToTag(TagProfile profile, Tag tag) throws IOException, FormatException, IllegalBlockSizeException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException {

/*      Plain text implementation:

        NdefRecord[] records = {
                createRecord(profile.id),
                createRecord(profile.name),
                ...
        };
        NdefMessage message = new NdefMessage(records);
*/      
        NdefRecord[] encryptedRecord = {
                createRecord(profile)
        };
        NdefMessage encryptedMessage = new NdefMessage(encryptedRecord);

        Ndef ndef = Ndef.get(tag);
        ndef.connect();
        ndef.writeNdefMessage(encryptedMessage);
        ndef.close();
    }

    private NdefRecord createRecord(TagProfile tagProfile) throws IOException, NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidAlgorithmParameterException {

        byte[] profileBytes = encryptProfile(tagProfile);
        String lang       = "en";
        byte[] langBytes  = lang.getBytes("US-ASCII");
        int    langLength = langBytes.length;
        int    bytesLength = profileBytes.length;
        byte[] payload    = new byte[1 + langLength + bytesLength];

        payload[0] = (byte) langLength;

        System.arraycopy(langBytes, 0, payload, 1, langLength);
        System.arraycopy(profileBytes, 0, payload, 1 + langLength, bytesLength);

        NdefRecord recordNFC = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,  NdefRecord.RTD_TEXT,  new byte[0], payload);

        return recordNFC;
    }

// ---------- READ

    private TagProfile buildProfile(NdefMessage[] msgs) throws EmptyTagException, ImproperTagException{

        try {
            if (msgs == null || msgs.length == 0) throw new EmptyTagException();
            String[] records = new String[msgs[0].getRecords().length];

            if(records.length == tagLength) { // tagLength is the number of records I expect.
                return decryptProfile(msgs[0].getRecords()[0].getPayload());
            } else throw new ImproperTagException();

        } catch (Exception e) {
            e.printStackTrace();
            return new TagProfile();
        }
    }

  • This code should work provided you're passing the same key and it is a symmetric key. – user207421 Apr 29 '20 at 00:29
  • And assuming you passed around the byte array losslessly. – user207421 Apr 29 '20 at 00:49
  • @user207421 the thing is it doesn't even get to the decryption part; it breaks when instancing the `ois`. I'm adding the code that I use to write the `NdefMessage` and to build the profile. – Alberto Jurado Apr 29 '20 at 01:47
  • So that means you must have broken the byte array. It should start with 0XACED. Have you converted it to `String` and back for example? NB Your post says that it happened 'when I try to read the tag', which I assumed meant calling `SealedObject.getObject()`. – user207421 Apr 29 '20 at 03:52
  • Simple. 02 is the length of `en`, and `en` in hex is 0X656E. You have failed to remove the length and language bytes from the payload before decrypting. – user207421 Apr 30 '20 at 03:12

0 Answers0