How to make null secret key in java?

Is the following Java code sufficient to clear the private key in memory (setting all of its byte value to 0)?

zerorize(SecretKey key) { byte[] rawKey = key.getEncoded(); Arrays.fill(rawKey, (byte) 0); } 

In other words, does the getEncoded method return a copy or link to the actual key? If the copy is returned, how can I delete the secret key as a security measure?

+6
source share
8 answers

Before attempting to clear a key, you must first check to see if the SecretKey interface implements the javax.security.auth.Destroyable interface. If so, prefer that, of course.

+6
source

getEncoded() seems to basically return a clone of the key (from an Oracle 1.6 source, e.g. javax.security.auth.kerberos ):

 public final byte[] getEncoded() { if (destroyed) throw new IllegalStateException("This key is no longer valid"); return (byte[])keyBytes.clone(); } 

therefore, clearing the returned data does not erase all copies of the key from memory.

The only way to erase a key from SecretKey is to pass it to javax.security.auth.Destroyable if it implements the interface and calls the destroy() method:

 public void destroy() throws DestroyFailedException { if (!destroyed) { destroyed = true; Arrays.fill(keyBytes, (byte) 0); } } 

Oddly enough, it seems that the entire Key implementation does not implement javax.security.auth.Destroyable . com.sun.crypto.provider.DESedeKey does not use javax.crypto.spec.SecretKeySpec for AES. Both of these key implementations also clone the key in the getEncoded method. So, it seems that for these very common 3DES and AES algorithms, we have no way to erase the memory for the private key?

+3
source

GetEncoded returns a copy of the private key (so a cleanup that does not affect the data of the private key) and destroy by default raises a DestroyFailedException, which is worse than useless. It is also available only in version 1.8+, so Android is out of luck. Here, a hack that uses introspection for (1) causes destruction if it is available and does not throw an exception, otherwise (2) null key data and set a reference to zero.

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

Way to use this as

 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 only a 1.8+ feature. This version works on 1.7+ and is quite efficient (it destroys one key on one key to decide to use it again).

+1
source

I am sure that clearing rawKey will not affect the data in key .

I don’t think there is a way to clear the data in SecretKey at all. Specific implementation classes can provide this, but I don't know what this does. In Android, the risk of exiting fuzzy data is very low. Each application runs in its own process, and its memory is not visible from the outside.

I assume that there is an attack scenario in which a process with root privileges can take snapshots of memory and send them to some supercomputer somewhere for analysis, hoping to find secret keys. But I have never heard of such an attack, and it seems to me uncompetitive with other ways to access the system. Is there a reason you are worried about this particular hypothetical vulnerability?

0
source

Depending on the technology using the garbage collector, any object can be moved (i.e. copied) to physical memory at any time, so you cannot be sure that you will really destroy the key by zeroing out the array - considering that you can get access to the "array" that contains the key, not a copy of it.

In short: if your security model and contextual call to nullify keys, then you should not use Java at all (or almost nothing but C and assembly).

0
source

In other words, does the getEncoded method return a copy or a link to the actual key?

key.getEncoded() will return an array reference.

If the contents of the key are discarded when you make Array.fill, it depends on whether the key is supported by the returned array. Given the documentation, it seems to me that key encoding is another representation of the key, that is, that the key is not supported by the returned array.

Easy to find out. Try the following:

 byte[] rawKey = key.getEncoded(); Arrays.fill(rawKey, (byte) 0); byte[] again = key.getEncoded(); Log.d(Arrays.equals(rawKey, again)); 

If the output is false , you know that the key is still stored in SecretKey .

-1
source

With the exception of primitive values, everything else in Java is always passed by reference, including arrays, so yes, you correctly clear this byte array.

However, the SecretKey class probably still contains the data needed to create this byte array, including ultimately another copy of this byte array, so you should learn how to clear this data.

-1
source

Taking a slightly different stickiness, once you have identified the correct memory area for rewriting, you can do this more than once:

 zerorize(SecretKey key) { byte[] rawKey = key.getEncoded(); Arrays.fill(rawKey, (byte) 0xFF); Arrays.fill(rawKey, (byte) 0xAA); Arrays.fill(rawKey, (byte) 0x55); Arrays.fill(rawKey, (byte) 0x00); } 
-2
source

Source: https://habr.com/ru/post/895881/


All Articles