2011-08-25 103 views
6

以下java代碼是否足以清除內存中的密鑰(將其所有字節值設置爲0)?如何在java中清零密鑰?

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

換句話說,它的getEncoded方法返回一個副本或參考實際的鑰匙?如果返回了副本,那麼我怎樣才能清除密鑰作爲安全措施?

回答

-1

換句話說,getEncoded方法是否返回實際密鑰的副本或引用?

key.getEncoded()將返回參考到陣列。

如果當你做Array.fill取決於密鑰是否是由返回數組支持密鑰的內容被丟棄。鑑於文檔,在我看來,假設關鍵的編碼是關鍵,即的另一種表示,這關鍵是用返回數組支持。

雖然很容易找到。請嘗試以下操作:

byte[] rawKey = key.getEncoded(); 
Arrays.fill(rawKey, (byte) 0); 

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

如果輸出爲false,你知道,關鍵仍存儲在SecretKey

-1

除了原始值,一切都在其他的Java總是通過引用傳遞,包括數組,所以是的,你是正確清除字節數組。

然而,SecretKey的類可能仍然持有生成的字節數組,還有包括給定的字節數組的最終另一個副本所需的數據,所以你應該研究如何清除這些數據也是如此。

+4

-1:* Java中的所有其他東西總是通過引用傳遞* - Nooo,Java總是*通過值傳遞!你無法按值傳遞*對象*的原因是因爲沒有變量可以首先包含對象! – aioobe

+0

@aioobe ..你確定我們正在談論相同的Java? int通過值傳遞,boolean通過值傳遞,Integer是一個引用,以及任何對象,數組等等... Java傳遞一個「值」,它實際上是一個對象的「引用」,所以它通過參考。 –

+0

@Joachim ..我指的是「給定的字節數組」,但是進一步澄清說可能有另一個副本。 –

0

我敢肯定,清理rawKey不會影響key數據。

我不認爲有一種方法可以清除SecretKey中的數據。具體的實施類可能規定,但我不知道有任何這樣做。在Android中,數據丟失的風險非常低。每個應用程序都在其自己的進程中運行,並且其內存從外部不可見。

我想有一個攻擊場景,其中一個根priviledged過程可能需要的內存快照,並送他們過一些超級計算機的地方進行分析,希望能發現別人的祕密密鑰。但我從來沒有聽說過這樣的攻擊,它讓我覺得沒有其他方式獲得系統的權利。您是否有理由擔心這種特定的假設漏洞?

6

在嘗試清除密鑰之前,應首先檢查接口SecretKey的實現是否也實現javax.security.auth.Destroyable接口。如果是這樣,那當然更喜歡。

+0

只能工作在1.8+以上,通常只會拋出DestroyFailedException –

-2

以一個略有不同的策略,一旦你已經確定的內存覆蓋正確的區域,你可能想要做一次以上:

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

取決於技術的垃圾收集器供電,任何單一對象可以隨時移動(即複製)在物理內存中,所以你不能確定你真的會通過置零數組來銷燬該鍵 - 假設你可以訪問持有該鍵的「數組」,而不是其副本。簡而言之:如果您的安全模型和上下文要求調零鍵,那麼您根本不應該使用Java(或者幾乎不使用Java,除C和程序集之外的任何其他語言)。

+0

但是,如果您必須使用Java,請快速將其清零,然後GC可能會重新打包數據或操作系統將其移至交換位置等。 –

3

getEncoded()似乎大多返回鍵的克隆(從例如javax.security.auth.kerberos的甲骨文1.6源):

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

因此擦拭返回數據不從內存中刪除密鑰的所有副本。

擦拭從SecretKey密鑰的唯一方法是,如果它實現了接口,將它轉換爲javax.security.auth.Destroyable並調用destroy()方法:

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

奇怪的是,這一切似乎主要執行不執行javax.security.auth.Destroyablecom.sun.crypto.provider.DESedeKey沒有也沒有javax.crypto.spec.SecretKeySpec用於AES。這兩個關鍵實現也都克隆了getEncoded方法中的密鑰。因此,對於這些非常常見的算法3DES和AES,我們似乎沒有辦法擦除祕密密鑰的內存?

1

GetEncoded返回密鑰的副本(因此清除對祕密密鑰數據沒有影響),並且默認情況下銷燬拋出DestroyFailedException,這比無用的更糟糕。它也只在1.8+以上,所以Android運氣不好。這裏有一個使用自省的黑客(1)調用destroy(如果可用)並且不拋出異常,否則(2)清零關鍵數據並將引用設置爲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); 
    } 
} 

使用的方法就是像

try (CloseableKey key = 
     new CloseableKey(new SecretKeySpec(data, 0, 16, "AES"))) { 
    aesecb.init(Cipher.ENCRYPT_MODE, key.secretKeySpec); 
} 

我用Closeable接口,因爲銷燬的只是一個1.8+功能。這個版本工作在1.7+以上,效率很高(它試圖破壞一個密鑰以決定再次使用它)。

+0

這是一個破解,並且GC可以重新打包內存或操作系統將數據移動到交換位置,這可能會泄漏關鍵數據。儘快關閉鑰匙,以儘量減少由於GC或OS副作用而泄漏的可能性。 –