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();
}
}