GetEncoded returns a copy of the secret key (so clearing that has no effect on the secret key data), and destroy by default throws DestroyFailedException which is worse than useless. It is also only available in 1.8+ so Android is out of luck. Here's a hack that uses introspection to (1) invoke destroy if available and does not throw an exception, otherwise (2) zero the key data and set the reference to null.
package kiss.cipher;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.crypto.spec.SecretKeySpec;
/**
* Created by wmacevoy on 10/12/16.
*/
public class CloseableKey implements AutoCloseable {
// forward portable to JDK 1.8 to destroy keys
// but usable in older JDK's
static final Method DESTROY;
static final Field KEY;
static {
Method _destroy = null;
Field _key = null;
try {
Method destroy = SecretKeySpec.class.getMethod("destroy");
SecretKeySpec key = new SecretKeySpec(new byte[16], "AES");
destroy.invoke(key);
_destroy = destroy;
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
}
try {
_key = SecretKeySpec.class.getDeclaredField("key");
_key.setAccessible(true);
} catch (NoSuchFieldException | SecurityException ex) {
}
DESTROY = _destroy;
KEY = _key;
}
static void close(SecretKeySpec secretKeySpec) {
if (secretKeySpec != null) {
if (DESTROY != null) {
try {
DESTROY.invoke(secretKeySpec);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
throw new IllegalStateException("inconceivable: " + ex);
}
} else if (KEY != null) {
try {
byte[] key = (byte[]) KEY.get(secretKeySpec);
Arrays.fill(key, (byte) 0);
KEY.set(secretKeySpec, null);
} catch (IllegalAccessException | IllegalArgumentException ex) {
throw new IllegalStateException("inconceivable: " + ex);
}
}
}
}
public final SecretKeySpec secretKeySpec;
CloseableKey(SecretKeySpec _secretKeySpec) {
secretKeySpec = _secretKeySpec;
}
@Override
public void close() {
close(secretKeySpec);
}
}
The way to use this is like
try (CloseableKey key =
new CloseableKey(new SecretKeySpec(data, 0, 16, "AES"))) {
aesecb.init(Cipher.ENCRYPT_MODE, key.secretKeySpec);
}
I use the Closeable interface because Destroyable is a 1.8+ feature only. This version works on 1.7+ and is pretty efficient (it does a trial destroy on one key to decide to ever use it again).